MySQL8-管理手册-全-

MySQL8 管理手册(全)

原文:zh.annas-archive.org/md5/D5BC20BC3D7872C6C7F5062A8EE852A4

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

对于任何系统,有必要以有组织的方式管理数据。在大型系统中,有必要处理各种配置以确保安全。MySQL 是处理企业级应用程序的流行解决方案之一。在本书中,我们将解释如何配置用户、他们的角色、多个实例等。

许多组织使用 MySQL 来管理其网站或商业产品,对于他们来说,管理数据存储并根据业务需求分析数据是非常具有挑战性的。本书将向您展示如何实施索引和查询优化以获得更好的性能。此外,我们还将介绍 MySQL 服务器的可扩展性和高可用性如何帮助管理故障场景。此外,复制和分区概念将通过示例详细解释。

本书通过针对不同级别的用户,从初学者到数据库管理员,描述了 MySQL 8 的各种功能。本书从安装开始,带着对 MySQL 8 概念的基本理解。然后我们转向具有管理级别功能的配置。在本书的结尾,您将学习到非常有趣的功能,如优化、扩展和故障排除。

本书适合对象

本书适用于寻求涵盖所有 MySQL 管理相关任务的便捷指南的 MySQL 管理员。如果您是想开始使用 MySQL 管理的数据库管理员,本书也会对您有所帮助。需要具备基本数据库概念知识才能开始阅读本书。

本书涵盖的内容

第一章《MySQL 8 简介》作为 MySQL 8 的入门指南。它简要定义了 MySQL 中可用的核心功能以及 MySQL 8 中新增或增强的功能。在本章的后半部分,我们重点介绍了 MySQL 8 的优势以及实际应用。

第二章《安装和升级 MySQL 8》描述了在不同平台上安装 MySQL 8 的详细步骤。它还解释了如何升级或降级到 MySQL 8。

第三章《MySQL 8 - 使用程序和实用工具》介绍了 MySQL 8 服务器和客户端的命令行程序。它还提供了有关可用 GUI 工具及其配置的信息。

第四章《MySQL 8 数据类型》着重于对 MySQL 8 数据类型的详细解释。它还根据内容类型对数据类型进行分类。我们还介绍了每个类别中的数据类型及其属性。我们还介绍了数据类型的存储要求。

第五章《MySQL 8 数据库管理》主要探讨了 MySQL 8 的管理部分。本章涵盖了组件和插件管理,以及用户和角色管理。此外,它还解释了全球化配置、缓存技术以及 MySQL 8 中可用的不同类型的日志。

第六章《MySQL 8 存储引擎》解释了几种类型的存储引擎以及 InnoDB 存储引擎的详细信息。本章提供了有关自定义存储引擎创建的信息,以及使其可插拔安装在 MySQL 8 中的步骤。

第七章《MySQL 8 中的索引》解释了索引,以及实施索引的可能方式。它比较了不同类型的索引。

第八章《MySQL 8 中的复制》解释了复制以及 MySQL 8 中可用的不同类型的复制。它还描述了复制的配置和实现,以及不同的方法。

第九章《MySQL 8 中的分区》,解释了几种类型的分区设置,分区的选择和修剪。它还解释了在分区时如何应对限制和限制。

第十章《MySQL 8-可扩展性和高可用性》,解释了如何进行扩展和在实施过程中处理不同的挑战。读者可以了解在 MySQL 8 中实现高可用性的多种方式。

第十一章《MySQL 8-安全》,侧重于 MySQL 8 数据库安全。本章涵盖了影响安全性的一般因素,核心 MySQL 8 文件的安全性,访问控制以及保护数据库系统本身的安全性。本章还包括安全插件的详细信息。

第十二章《优化 MySQL 8》,解释了如何配置 MySQL 8 以获得更好的性能。本章还描述了一些性能结果的用例,以验证。这将帮助您了解在处理优化 MySQL 8 时要注意的各种接触点。

第十三章《扩展 MySQL 8》,展示了如何扩展 MySQL 8 并添加新功能,以及调试和移植到 MySQL 8。

第十四章《MySQL 8 最佳实践和基准测试》,解释了使用 MySQL 的最佳实践。它还解释了为 MySQL 8 进行的各种基准测试。

第十五章《MySQL 8 故障排除》,解释了 MySQL 8 的许多常见和现实世界的故障排除场景。

为了充分利用本书

我们建议您在阅读本书之前先了解 MySQL(任何版本)和 SQL 命令的基础知识。

本书还涵盖了实际场景和命令执行,因此如果可能的话,请安装一个工具,以便轻松执行 MySQL 命令。

下载示例代码文件

您可以从www.packtpub.com的帐户中下载本书的示例代码文件。如果您在其他地方购买了本书,您可以访问www.packtpub.com/support并注册,以便直接将文件发送到您的邮箱。

您可以按照以下步骤下载代码文件:

  1. 登录或注册www.packtpub.com

  2. 选择“支持”选项卡。

  3. 点击“代码下载和勘误”。

  4. 在搜索框中输入书名,然后按照屏幕上的说明操作。

下载文件后,请确保使用以下最新版本解压或提取文件夹:

  • WinRAR/7-Zip for Windows

  • Zipeg/iZip/UnRarX for Mac

  • 7-Zip/PeaZip for Linux

该书的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/MySQL-8-Administrators-Guide。我们还有来自丰富书籍和视频目录的其他代码包可供选择,网址为github.com/PacktPublishing/。去看看吧!

使用的约定

本书中使用了许多文本约定。

CodeInText:指示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。这是一个例子:“它将下载winMD5Sum.exe到您的计算机上。”

任何命令行输入或输出都是这样写的:

CREATE TABLE working_days (
year INT,
week INT,
days BIT(7),
PRIMARY KEY (year, week));

粗体:表示一个新术语、一个重要词或屏幕上看到的词。例如,菜单或对话框中的单词会以这种方式出现在文本中。这是一个例子:“在页面上点击下载 WinMD5Sum 选项。”

警告或重要说明看起来像这样。

提示和技巧看起来像这样。

联系我们

我们始终欢迎读者的反馈。

一般反馈:发送电子邮件至 feedback@packtpub.com,并在主题中提及书名。如果您对本书的任何方面有疑问,请发送电子邮件至 questions@packtpub.com

勘误:尽管我们已经尽最大努力确保内容的准确性,但错误确实会发生。如果您在本书中发现错误,我们将不胜感激地接受您的报告。请访问 www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表格链接,并输入详细信息。

盗版:如果您在互联网上发现我们作品的任何形式的非法副本,我们将不胜感激地接受您提供的位置地址或网站名称。请通过 copyright@packtpub.com 与我们联系,并提供材料链接。

如果您有兴趣成为作者:如果您在某个专业领域有专长,并且有兴趣撰写或为书籍做出贡献,请访问 authors.packtpub.com

评论

请留下评论。在阅读并使用本书后,为什么不在购买书籍的网站上留下评论呢?潜在的读者可以看到并使用您的客观意见来做出购买决定,我们在 Packt 可以了解您对我们产品的看法,我们的作者也可以看到您对他们书籍的反馈。谢谢!

有关 Packt 的更多信息,请访问 packtpub.com

第一章:MySQL 8 简介

MySQL 是一个著名的开源结构化数据库,因为其性能、易用性和可靠性。这是关系数据库的最常见选择。在当前市场上,成千上万的基于 Web 的应用程序依赖于 MySQL,包括 Facebook、Twitter 和 Wikipedia 等巨头行业。它还被证明是“软件即服务”(SaaS)应用程序的数据库选择,如 Twitter、YouTube、SugarCRM、Supply Dynamics、Workday、RightNow、Omniture、Zimbra 等等。我们将在本章后面的“MySQL 的用例”部分详细讨论这一点。MySQL 由瑞典公司 MySQL AB 开发,现在由 Oracle Corporation 分发和支持。MySQL 带有宝贵的历史。

MySQL 一直在不断改进,以成为企业级数据库管理系统。预计 MySQL 8 将成为一个改变游戏规则的版本,因为今天我们处于数字化时代。MySQL 8 已经调整好了,以满足以前版本中难以实现的许多新用例。一些产生大量数据的用例包括社交网络、电子商务、银行/信用卡交易、电子邮件、存储在云上的数据等等。对所有这些结构化、非结构化或半结构化的普遍数据进行分析有助于发现隐藏的模式、市场趋势、相关性和个人偏好。

"我们每个人都有很多东西"

  • 詹姆斯·特鲁斯洛·亚当斯

让我们深入了解 MySQL 8 的新功能、优势、用例以及 MySQL 8 的一些限制,这是令人兴奋的,让我们做好准备。

MySQL 概述

结构化查询语言(SQL)用于操作、检索、插入、更新和删除关系数据库管理系统(RDBMS)中的数据。简单地说,SQL 告诉数据库要做什么,以及它需要什么。SQL 是所有 RDBMS 系统(如 MySQL、MS Access、MS SQL、Oracle、Postgres 等)使用的标准语言。

关系数据库管理系统是 SQL 和所有现代数据库系统(如 MS SQL Server、IBM DB2、Oracle、MySQL 和 Microsoft Access)的基础。

SQL 允许用户从 MySQL 访问数据并定义和操作数据。要嵌入其他语言,您可以利用 SQL 模块、库和预编译器,这些可以帮助您创建/删除数据库和表,允许用户创建视图和存储过程、函数等等。它还可以执行各种其他操作,如允许用户在表、存储过程和视图上设置权限。

MySQL 作为关系数据库管理系统

关系数据库中的数据以有组织的格式存储,以便可以轻松检索信息。数据将存储在由行和列组成的不同表中。然而,还可以在不同表之间建立关系,有效地存储大量数据并有效地检索所选数据。这为数据库操作提供了巨大的速度和灵活性。

作为关系数据库,MySQL 具有与不同表建立关系的能力,如一对多、多对一和一对一,通过提供主键、外键和索引。它还可以执行表之间的连接以检索精确的信息,如内连接和外连接。

SQL 用作与 MySQL 中的关系数据交互的接口。SQL 是美国国家标准学会(ANSI)的标准语言,我们可以用它操作数据,如创建、删除、更新和检索。

MySQL8 的许可要求

许多行业更喜欢开源技术,因为该技术具有灵活性和节省成本的特点,而 MySQL 通过成为最受欢迎的 Web 应用程序关系数据库而在市场上留下了自己的足迹。开源意味着您可以查看 MySQL 的源代码,并根据自己的需求进行定制,而无需任何费用。您可以从其网站下载源代码或二进制文件,并相应地使用它们。

MySQL 服务器受到通用公共许可证GNU)的保护,这意味着我们可以自由地将其用于 Web 应用程序,研究其源代码,并修改以满足我们的需求。它还有企业版,包括高级功能。许多企业仍然购买 MySQL 的支持合同,以获取有关各种问题的帮助。

可靠性和可扩展性

MySQL 具有出色的可靠性,无需进行大量故障排除即可良好运行。它还包括许多性能增强机制,如索引支持,负载实用程序和内存缓存。MySQL 使用 InnoDB 作为存储引擎,提供高效的 ACID 兼容事务功能,确保高性能和可扩展性。为了处理不断增长的数据库,MySQL 复制和集群有助于扩展数据库。

平台兼容性

MySQL 具有出色的跨平台可用性,这使其更受欢迎。它灵活地运行在主要平台上,如 RedHat,Fedora,Ubuntu,Debian,Solaris,Microsoft Windows 和 Apple macOS。它还提供应用程序编程接口APIs)以与各种编程语言(如 C,C++,C#,PHP,Java,Ruby,Python 和 Perl)进行互连。

发布

到目前为止,MySQL 的主要版本发布列表如下:

  • MySQL 5.0 GA 版本于 2005 年 10 月 19 日发布

  • MySQL 5.1 GA 版本于 2008 年 11 月 14 日发布

  • MySQL 5.5 GA 版本于 2010 年 12 月 3 日发布

  • MySQL 5.6 GA 版本于 2013 年 2 月 5 日发布

  • MySQL 5.7 GA 版本于 2015 年 10 月 21 日发布

现在是时候进行主要版本发布了--MySQL 8--它于 2016 年 9 月 12 日宣布,并仍处于开发里程碑模式。

MySQL 的核心功能

让我们回顾一下 MySQL 的一些核心功能。随着我们的进展,我们将在整本书中详细讨论各种功能。

结构化数据库

结构化数据库是传统数据库,许多企业已经使用了 40 多年。然而,在现代世界中,数据量变得越来越大,一个常见的需求已经出现--数据分析。随着数字数据的数量和速度日益增长,使用结构化数据库进行分析变得困难;我们需要以一种有效和高效的方式满足这些需求。在开源世界中,最常用作结构化数据库的数据库是 MySQL。

许多组织使用结构化数据库以有组织的方式存储其数据。基本上,结构化数据库中的数据具有固定字段,预定义的数据长度,并定义了要存储的数据类型,例如数字,日期,时间,地址,货币等。简而言之,在插入数据之前已经定义了结构,这使得我们更清楚地知道哪些数据可以驻留在那里。使用结构化数据库的主要优势是数据易于存储,查询和分析。

非结构化数据库与此相反;它没有可识别的内部结构。它可以有一个庞大的无组织的聚集或各种对象。主要,结构化数据的来源是机器生成的,这意味着信息是从机器生成的,没有人为干预,而非结构化数据是人为生成的数据。组织使用结构化数据库来存储诸如 ATM 交易、航空公司预订、库存系统等数据。同样,一些组织使用非结构化数据,如电子邮件、多媒体内容、文字处理文档、网页、商业文件等。

数据库存储引擎和类型

让我们现在来看一下不同 MySQL 存储引擎的概述。这是一个重要的部分,它简要介绍了不同的数据库存储引擎;我们将在第六章中详细讨论这个问题,MySQL 8 存储引擎。MySQL 将数据存储在数据库中作为子目录。在每个数据库中,数据被存储为表。当你创建一个表时,MySQL 将表定义存储在与表名相同的.frm文件中。您可以使用SHOW TABLE STATUS命令来显示有关您的表的信息:

mysql> SHOW TABLE STATUS LIKE 'admin_user' \G;
*************************** 1\. row ***************************
 Name: admin_user
 Engine: InnoDB
 Version: 10
 Row_format: Dynamic
 Rows: 2
 Avg_row_length: 8192
 Data_length: 16384
 Max_data_length: 0
 Index_length: 16384
 Data_free: 0
 Auto_increment: 3
 Create_time: 2017-06-19 14:46:49
 Update_time: 2017-06-19 15:15:08
 Check_time: NULL
 Collation: utf8_general_ci
 Checksum: NULL
 Create_options:
 Comment: Admin User Table
1 row in set (0.00 sec)

这个命令显示这是一个带有列名EngineInnoDB表。还有其他信息,您可以参考其他用途,比如行数、索引长度等。

存储引擎是处理不同表类型的 SQL 操作的方式。每个存储引擎都有其自己的优点和缺点。了解每个存储引擎的特性,并选择最适合您的表的存储引擎,以最大限度地提高数据库的性能是很重要的。在 MySQL 8 中创建新表时,InnoDB是默认的存储引擎。

MySQL 服务器使用即插即用的存储引擎架构。您可以使用SHOW ENGINES命令从 MySQL 服务器加载所需的存储引擎,并卸载不必要的存储引擎。

mysql> SHOW ENGINES \G;
*************************** 1\. row ***************************
 Engine: InnoDB
 Support: YES
 Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
 XA: YES
 Savepoints: YES
*************************** 2\. row ***************************
 Engine: MRG_MYISAM
 Support: YES
 Comment: Collection of identical MyISAM tables
Transactions: NO
 XA: NO
 Savepoints: NO
*************************** 3\. row ***************************
 Engine: MEMORY
 Support: YES
 Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
 XA: NO
 Savepoints: NO
*************************** 4\. row ***************************
 Engine: BLACKHOLE
 Support: YES
 Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
 XA: NO
 Savepoints: NO
*************************** 5\. row ***************************
 Engine: MyISAM
 Support: DEFAULT
 Comment: MyISAM storage engine
Transactions: NO
 XA: NO
 Savepoints: NO
*************************** 6\. row ***************************
 Engine: CSV
 Support: YES
 Comment: CSV storage engine
Transactions: NO
 XA: NO
 Savepoints: NO
*************************** 7\. row ***************************
 Engine: ARCHIVE
 Support: YES
 Comment: Archive storage engine
Transactions: NO
 XA: NO
 Savepoints: NO
*************************** 8\. row ***************************
 Engine: PERFORMANCE_SCHEMA
 Support: YES
 Comment: Performance Schema
Transactions: NO
 XA: NO
 Savepoints: NO
*************************** 9\. row ***************************
 Engine: FEDERATED
 Support: NO
 Comment: Federated MySQL storage engine
Transactions: NULL
 XA: NULL
 Savepoints: NULL
9 rows in set (0.00 sec)

InnoDB 概述

InnoDB是所有其他可用存储引擎中广泛使用的默认存储引擎。它于 2008 年作为插件与 MySQL 5.1 一起发布。MySQL 5.5 及更高版本将InnoDB作为默认存储引擎。它于 2005 年 10 月被 Oracle Corporation 收购,从芬兰公司 Innobase Oy 手中接管。

InnoDB 表支持符合 ACID 的提交、回滚和崩溃恢复功能,以保护用户数据。它还支持行级锁定,有助于更好的并发性和性能。它将数据存储在聚集索引中,以减少基于主键的所有 SQL 选择查询的 I/O 操作。它还支持FOREIGN KEY约束,允许更好地维护数据库的数据完整性。InnoDB 表的最大大小可以扩展到 64 TB,这应该足够满足许多实际用例的需求。

MyISAM 概述

MyISAM是 MySQL 5.5 之前的默认存储引擎。MyISAM存储引擎表不支持 ACID 兼容,与InnoDB相反。MyISAM表只支持表级锁定,因此MyISAM表不是事务安全的;但是,它们经过了优化,用于压缩和速度。通常在需要主要读操作和最小事务数据的情况下使用。MyISAM表的最大大小可以增长到 256 TB,这有助于数据分析等用例。MyISAM支持全文索引,可以帮助进行复杂的搜索操作。使用全文索引,我们可以索引存储在BLOBTEXT数据类型中的数据。

内存概述

内存存储引擎通常被称为堆存储引擎。它用于极快地访问数据。这种存储引擎将数据存储在 RAM 中,因此不需要 I/O 操作。由于数据存储在 RAM 中,服务器重新启动时所有数据都会丢失。这种表基本上用于临时表或查找表。该引擎支持表级锁定,从而限制了高写并发性。

关于内存表的重要说明如下:

  • 由于内存表将数据存储在 RAM 中,RAM 的存储容量非常有限;如果尝试向内存表中写入过多数据,它将开始将数据交换到磁盘,然后您将失去内存存储引擎的好处

  • 这些表不支持TEXTBLOB数据类型,也不需要,因为它的存储容量有限

  • 这种存储引擎可用于缓存结果;例如查找表、邮政编码和州名

  • 内存表支持 B 树索引和哈希索引

存档概述

这种存储引擎用于存储大量历史数据而不需要任何索引。存档表没有任何存储限制。存档存储引擎针对高插入操作进行了优化,并支持行级锁定。这些表以压缩和小的格式存储数据。存档引擎不支持DELETEUPDATE操作;它只允许INSERTREPLACESELECT操作。

BLACKHOLE 存储引擎概述

这种存储引擎接受数据但不存储数据。它在每次INSERT后丢弃数据而不是存储数据。

现在,这种存储引擎的用途是什么;为什么有人会使用它?为什么我们要运行一个不向表中插入任何内容的INSERT查询?

这种引擎对于具有大量服务器的复制非常有用。BLACKHOLE存储引擎充当主服务器和从服务器之间的过滤服务器,不存储任何数据,只应用replicate-do-*replicate-ignore-*规则并写入binlogs。这些binlogs用于在从服务器中执行复制。我们将在第八章中详细讨论这一点,MySQL 8 中的复制

CSV 概述

逗号分隔值CSV)引擎使用逗号分隔值格式将数据存储在.csv文件类型中。该引擎从数据库中提取数据并将其复制到数据库之外的.csv中。如果从电子表格创建 CSV 文件并将其复制到 MYSQL 数据文件夹服务器中,则可以使用 select 查询读取数据。同样,如果在表中写入数据,外部程序可以从 CSV 文件中读取数据。此存储引擎用于软件或应用程序之间的数据交换。CSV 表不支持索引和分区。CSV 存储引擎中的所有列都需要使用NOT NULL属性进行定义,以避免在创建表时出现错误。

合并概述

这种存储引擎也被称为MRG_MyISAM存储引擎。这种存储引擎合并MyISAM表并将其创建为单个视图。对于合并表,所有列都按相同顺序列出。这些表非常适用于数据仓库环境。

该表通常用于管理与日志相关的表。您可以在单独的MyISAM表中创建不同月份的日志,然后使用合并存储引擎合并这些表。

MyISAM表对操作系统有存储限制,但MyISAM(合并)表集合没有存储限制。因此,使用合并表可以将数据分割成多个MyISAM表,有助于克服存储限制。

合并表不支持分区。此外,您不能将合并表或合并表的任何基础MyISAM表分区到不同的分区中。

联合概述

这个存储引擎允许您在多个物理服务器上创建一个单一的数据库。它打开到另一个服务器的客户端连接,并对那里的表执行查询,根据需要检索和发送行。它最初被宣传为支持许多企业级专有数据库服务器的竞争特性,如 Microsoft SQL Server 和 Oracle,但至少可以说,这总是一个牵强的说法。尽管它似乎能够实现很多灵活性和巧妙的技巧,但它已经被证明是许多问题的根源,并且默认情况下是禁用的。这个存储引擎在 MySQL 中默认是禁用的;要启用它,您需要使用联合选项启动 MySQL 服务器二进制文件。

NDB 集群概述

NDB 集群(也称为NDB)是一种内存存储引擎,提供高可用性和数据持久性功能。

NDB 集群存储引擎可以配置一系列故障转移和负载平衡选项,但最容易的方法是从集群级别开始使用存储引擎。NDB 集群使用 NDB 存储引擎,其中包含完整的数据集,仅依赖于集群中可用的其他数据集。

NDB 集群的部分是独立配置的,与 MySQL 服务器无关。在 NDB 集群中,集群的每个部分被视为一个节点。

每个存储引擎都有其自己的优势和可用性,如下所示:

  • 搜索引擎:NDBCluster

  • 事务 数据InnoDB

  • 会话数据MyISAM或 NDBCluster

  • 本地化计算:内存

  • 字典MyISAM

以下图表将帮助您了解哪种存储引擎适合您的需求:

现在您对各种存储引擎以及不同用例有了更好的了解,这将帮助您根据自己的需求做出决策。

现在是时候转到我们的下一个主题,我们将看看 MySQL 8 中可用的令人愉快的新功能。

MySQL 8 中的改进功能

MySQL 数据库开发团队最近宣布了其主要版本 MySQL 8 开发里程碑发布DMR)。它包含了对迫切需要的问题的重大更新和修复。

您可能会想知道为什么是 5.7 之后的 8!中间版本,也就是 6 和 7,被跳过了吗?当然不是!实际上,6.0 作为更频繁和及时发布的转变的一部分被保留了下来,而 7.0 则是 MySQL 的集群版本。

让我们看看在这个最新版本中引入的一些令人兴奋的功能,如下图所示:

现在是时候详细了解 MySQL 8 的功能了,这将使我们对升级到 MySQL 的主要版本有兴奋和信心的理由。

事务数据字典

直到上一个版本,MySQL 的数据字典是存储在不同的元数据文件和非事务表中的,但从这个版本开始,它将有一个事务性数据字典来存储关于数据库的信息。不再有.frm.trg.par文件。所有信息将存储在数据库中,这消除了执行繁重文件操作的成本。文件系统元数据存储存在许多问题,如文件系统的脆弱性、过多的文件操作、难以处理崩溃恢复故障或复制;还很难添加与新功能相关的元数据。现在这次升级通过集中存储信息使其变得简单,并且由于这个数据字典对象可以被缓存在内存中,因此性能得到了改进,类似于其他数据库对象。

这个数据字典将包含执行 SQL 查询所需的数据,如目录信息、字符集、排序规则、列类型、索引、数据库信息、表、存储过程、函数和触发器等。

角色

在 MySQL 8 中,通过引入角色来改进了权限模块,这意味着权限的集合。现在我们可以创建具有多个权限的角色,并将它们分配给多个用户。

以前版本的问题是我们无法为一组用户定义通用权限,每个用户都有单独的权限。假设已经存在 1,000 个具有共同权限的用户,并且您想要删除这 1,000 个用户的写入权限,那么在以前的版本中该怎么做呢?您将不得不采取耗时的方法来更新每个用户,对吗?哎呀!那是一个漫长的任务。

现在使用 MySQL 8,很容易更新权限的任何更改。角色将定义所有所需的权限,并且该角色将分配给那 1,000 个用户。我们只需要在角色中进行任何权限更改,所有用户将自动继承相应的权限。

角色可以创建、删除、授予或撤销权限,从用户账户中授予或撤销权限,并且可以在当前会话中指定默认角色。

InnoDB 自动增量

MySQL 8 已经改变了自动增量计数器值存储机制。以前,它存储在内存中,在服务器重启或服务器崩溃时管理起来相当困难。然而,现在自动增量计数器值在值发生更改时会被写入重做日志,并且在每个检查点上,它将被保存在系统表中,这使得它在服务器重启时是持久的。

在以前的版本中,更新自动增量值可能会导致重复条目错误。假设您在序列中更新了自动增量值,该值大于当前最大值,但随后的插入操作无法识别未使用的值,这可能会导致重复条目问题。通过持久化自动增量值来防止这种情况发生,因此随后的插入操作可以获取新值并正确分配它。

如果发生服务器重启,自动增量值会在以前的版本中丢失,因为它存储在内存中,InnoDB需要执行查询来找出最大使用的值。这已经改变了,因为新版本具有在服务器重启时持久化其值的能力。在服务器重启期间,InnoDB使用数据字典表中存储的最大值来初始化内存中的计数器值。在服务器崩溃的情况下,InnoDB初始化自动增量计数器值,该值大于数据字典表和重做日志。

不可见的索引

MySQL 8 提供了一个功能,可以使索引不可见。这种索引不能被优化器使用。如果您想在没有索引的情况下测试查询性能,使用此功能可以通过使它们不可见来实现,而不是删除和重新添加索引。在索引应该在大型数据集上被删除和重新创建时,这是一个方便的功能。

所有索引默认可见。要使它们不可见或可见,分别使用INVISIBLEVISIBLE关键字,如下面的代码片段所述:

ALTER TABLE table1 ALTER INDEX ix_table1_col1 INVISIBLE;
ALTER TABLE table1 ALTER INDEX ix_table1_col1 VISIBLE;

改进降序索引

降序索引在 5.7 版本中也存在,但是它们是以相反顺序扫描的,这会导致性能障碍。为了提高性能,MySQL 8 对此进行了优化,并以正向顺序扫描降序索引,这大大提高了性能。当最有效的扫描顺序对某些列具有升序顺序,对其他列具有降序顺序时,它还为优化器带来了多列索引。

SET PERSIST变体

在服务器运行时可以全局和动态地配置服务器变量。有许多系统变量可以使用SET GLOBAL来设置:

SET GLOBAL max_connections = 1000;

但是,这些设置将在服务器重新启动后丢失。为了避免这种情况,MySQL 8 引入了SET PERSIST变体,可以在服务器重新启动后保留变量。

SET PERSIST max_connections = 1000; 

扩展的 GIS 支持

在以前的版本中,它仅支持一个坐标系统,一个无单位的 2D 位置,不参考地球上的位置。现在 MySQL 8 添加了对空间参考系统SRS)的支持,具有地理参考椭球体和 2D 投影。SRS 有助于为位置分配坐标,并建立这些坐标集之间的关系。这些空间数据可以在数据字典存储中进行管理,如ST_SPATIAL_REFERENCE_SYSTEMS表。

默认字符集

默认字符集已从latin1更改为UTF8UTF8是主导字符集,尽管在以前的 MySQL 版本中不是默认字符集。随着字符集默认值的更改,排序规则已从latin1_swedish_ci更改为utf8mb4_800_ci_ai。随着这些全球接受的变化,字符集和排序规则现在基于UTF8;一个常见的原因是因为UTF8支持大约 21 种不同的语言,这使得系统提供多语言支持。

扩展的位操作

在 MySQL 5.7 中,位操作和函数仅适用于BIGINT(64 位整数)数据类型。我们需要将BIGINT作为参数传递,它将返回BIGINT作为结果。简而言之,它的最大范围为 64 位,可以执行操作。用户需要将其他数据类型转换为BIGINT数据类型,以便执行操作。对于大于 64 位的数据类型,这种类型转换是不可行的,因为它会截断实际值,导致不准确。

MySQL 8 通过支持其他二进制数据类型(如BinaryVarBinaryBLOB)改进了位操作。这使得可以对大于 64 位的数据执行位操作。不再需要类型转换!这允许接受大于 64 位的参数并返回结果。

InnoDB Memcached

现在InnoDB memcached 插件支持多个获取操作,这将真正有助于提高读取性能。现在,可以在单个 memcached 查询中获取多个键值对。由于可以一次获取多个数据,频繁的通信流量也得到了最小化。

InnoDB Memcached 插件还支持范围查询。通过指定特定范围,可以简化范围搜索并检索此范围内的值。

NOWAIT 和 SKIP LOCKED

当其他事务锁定您要访问的行时,您需要等待该事务释放对同一行的锁,以便您可以相应地访问它。为了避免等待其他事务,InnoDB添加了NOWAITSKIP LOCKED选项的支持。NOWAIT将立即返回错误,如果请求的行被锁定,而不是进入等待模式,SKIP LOCKED将跳过锁定的行,永远不会等待获取行锁。因此,SKIP LOCKED不会在结果集中考虑锁定的行。

SELECT * FROM table1 WHERE id = 5 FOR UPDATE NOWAIT;
SELECT * FROM table1 FOR UPDATE SKIP LOCKED;

JSON

MySQL 5.7 中已实现了 JSON 支持;这是一个被广泛认可的功能。在 MySQL 8 中,它添加了各种函数,允许我们以 JSON 数据格式获取数据集结果,虚拟列,以及大约 15 个 SQL 函数,允许您在服务器端搜索和使用 JSON 数据。在 MySQL8 中,还添加了额外的聚合函数,可以在 JSON 对象/数组中使用,以更优化的方式表示加载的数据。以下是 MySQL8 中引入的两个 JSON 聚合函数:

  • JSON_OBJECTAGG()

  • JSON_ARRAYAGG()

在 MySQL 8 中引入了一个新选项innodb_dedicated_server,这对服务器的垂直扩展将非常有帮助。它实际上会自动检测分配给虚拟服务器的内存,并适当地设置 MySQL 8,而无需更改配置文件。考虑到虚拟化和云的采用,这些功能将非常方便。实际上,使用这种配置,您甚至可能不需要获取服务器的 shell 访问权限来编辑配置文件。您可以使用新的SET PERSIST功能从 MySQL 命令行本身设置相关配置,这可以进一步增强安全性,因为您几乎不需要服务器的 shell 访问权限。

资源管理

MySQL 8 推出了一个出色的资源管理功能,可以让您为在服务器上运行的线程分配资源,这些资源将根据为该组配置的资源而执行。目前,CPU 时间是可以为组配置的资源。通过这个功能,您可以在 MySQL 内部进行虚拟资源管理来调整工作负载。MySQL 将在启动时识别可用的虚拟 CPU 数量,之后具有适当权限的用户可以将虚拟 CPU 映射到资源组,并将线程管理对齐到这些组。

我们期望在 MySQL 8 可供一般使用时看到更多功能。现在让我们来看看使用 MySQL 8 的好处。

使用 MySQL 8 的好处

无论您是开发人员还是企业,您显然会选择一个相对于其他相关产品提供更多好处和结果的产品。MySQL 在这个竞争激烈的市场中作为首选提供了许多优势。它具有各种强大的功能,使其成为更全面的数据库。现在让我们来看看使用 MySQL 的一些好处。

安全性

首先想到的是保护数据,因为现在数据变得非常宝贵,如果不满足法律义务,可能会影响业务连续性;事实上,情况可能会很糟糕,甚至可能会迅速关闭您的业务。MySQL 是最安全可靠的数据库管理系统,被许多知名企业使用,如 Facebook、Twitter 和维基百科。它提供了一个良好的安全层,可以保护敏感信息免受入侵者的侵害。MySQL 提供访问控制管理,因此很容易向用户授予和撤销所需的访问权限。还可以为用户定义角色,并列出可以授予或撤销的权限。所有用户密码都以加密格式存储,使用特定的插件算法。

可扩展性

随着技术在多种方式上的广泛使用,每天数据量都在增加。因此,负载平均值正在飙升。在某些情况下,数据可能无法超过某个限制,或者用户数量可能超出范围。可扩展的数据库将是一个可取的解决方案,以便在任何时候都能满足意外需求的扩展。MySQL 是一个有回报的数据库系统,因为它具有可扩展性,可以在数据和应用程序查询的负载方面进行水平和垂直扩展;跨多个 MySQL 服务器分布数据库和应用程序查询负载是相当可行的。向 MySQL 集群添加性能也非常容易,以处理负载。

一个开源的关系型数据库管理系统

MySQL 是一个开源的数据库管理系统,使得调试、升级和增强功能变得快速而简单。您可以查看源代码并进行相应的更改,以自己的方式使用它。您还可以分发 MySQL 的扩展版本,但需要获得许可证。

高性能

MySQL 提供了高速事务处理和最佳速度。它可以缓存结果,从而提高读取性能。复制和集群使系统能够处理更多并发并管理繁重的工作负载。数据库索引还可以加速SELECT查询语句对大量数据的性能。为了提高性能,MySQL 8 在性能模式中包含了索引以加快数据检索速度。

高可用性

如今,在竞争激烈的营销世界中,一个组织的关键点是让他们的系统正常运行。任何故障或停机直接影响业务和收入;因此,高可用性是一个不容忽视的因素。MySQL 相当可靠,并且使用集群和复制配置保持持续可用性。集群服务器可以立即处理故障并管理故障转移部分,以使系统几乎始终可用。如果一个服务器宕机,它将重定向用户的请求到另一个节点并执行所请求的操作。

跨平台能力

MySQL 提供了跨平台的灵活性,可以在 Windows、Linux、Solaris、OS2 等各种平台上运行。它对所有主要语言提供了很好的 API 支持,这使得它非常容易与诸如 PHP、C++、Perl、Python、Java 等语言集成。它也是全球范围内用于 Web 应用程序的Linux Apache MySQL PHPLAMP)服务器的一部分。

现在是时候动手尝试一下 MySQL 8 了;让我们从在我们的情况下在 Linux 平台上安装 MySQL 8 开始。我们更喜欢在 Linux 操作系统上使用 MySQL 8,因为这在许多组织中是一个常见的用例。我们将在第二章中讨论更多安装内容,安装和升级 MySQL 8。您可以在 MySQL 支持的其他平台上使用它,如 Windows、Solaris、HP-UNIX 等。Linux 提供了各种安装 MySQL 服务器的方法,如下所示:

  • RPM

  • YUM存储库

  • APT存储库

  • SLES存储库

  • Debian

  • TAR

  • 从源代码编译和安装

MySQL 8 的限制

一枚硬币有两面;同样,使用 MySQL 8 也会带来一些限制。现在让我们来看看 MySQL 8 的一些方面。

表或数据库的数量

数据库或表的数量对 MySQL 8 没有限制;但是,操作系统文件限制可能会成为 MySQL 8 的限制。存储引擎InnoDB允许扩展到四十亿张表。

表格大小

您可能会遇到最大表大小限制,这不是 MySQL 8 限制的,但可能是由于操作系统文件系统限制。

连接

在单个连接中,可以使用 61 个表,可以进行引用。这也适用于视图定义中引用的表。子查询和视图中的连接也被视为限制的一部分。

Windows 平台

当您在 Windows 平台上使用 MySQL 8 时,会有一些限制:

  • 内存:32 位架构限制了一个进程只能使用 2GB 的 RAM。

  • 端口:如果您有大量并发,您可能会遇到 Windows 平台的限制,总共只有 4000 个端口可用于客户端连接。

  • 大小写不敏感:Windows 平台不区分大小写,因此需要有意识地管理大小写不敏感的表和数据库。

  • 管道|,通常称为管道符号,在 Windows 上不完全受支持。在进行数据库管理活动时,您可能会在一些情况下遇到它们。

  • 路径名分隔符:MySQL 8 的转义字符是\,这是 Windows 的路径名分隔符。因此,在使用路径分隔符时,您可以使用双斜杠\\作为路径名分隔符的替代。

表列计数

MySQL 8 中每个表的表列限制为 4096 列。基于其他一些因素的列计数限制可能会有所不同,如下一节所述。

行大小

MySQL 表的行限制为 65,535 字节,尽管存储引擎如InnoDB能够支持更大的数据块。

InnoDB 存储引擎

InnoDB存储引擎的限制是我们将更具体地谈论的,因为InnoDB现在在 MySQL 8 中将发挥重要作用。

InnoDB存储引擎的限制

我们将快速浏览一下InnoDB存储引擎的一些限制:

  • 表支持的索引数量最多为 64

  • 对于使用压缩或动态行格式的表;索引键前缀长度限制为 3072

  • 对于使用紧凑或冗余行格式的表;索引键前缀长度限制为 767

  • 表中的总列数,包括虚拟生成的列,最多限制为 1,017

  • 多列索引的最大允许列数为 16

  • 组合的InnoDB日志文件大小不能超过 512 GB

  • InnoDB支持的最大表大小为 256 TB

  • 在使用 unix 套接字连接时不支持 AdminAPI

  • 多字节字符可能会在InnoDB集群中格式化结果时给出不可靠的对齐列

限制

现在我们将快速浏览一下InnoDB存储引擎的一些限制:

  • Delete from tablename:它实际上并不删除整个表,而是逐行删除表中的每一行。

  • Show table status:它不会始终提供准确的数据;它提供估计值。

  • 在计算行数时,由于并发性,count(*)提供的行数不准确;它只会计算当前事务可见的计数。

  • 如果执行了多个analyze table查询,后面的查询将被阻塞,直到第一个查询完成。

  • InnoDB在与auto_increment列关联的索引末尾保持独占锁。

  • 如果auto_increment整数用完了值;接下来的插入操作将显示重复键错误。

  • 级联的外键不能激活触发器。

  • MySQL 保留了一些InnoDB用于内部目的的列名。以下是一些这样的列名:

  • DB_ROW_ID

  • DB_TRX_ID

  • DB_ROLL_PTR

  • DB_MIX_ID

如果使用了这样的保留列名,我们可能会遇到以下示例中显示的输出:

 mysql> CREATE TABLE chintan (c1 INT, db_row_id INT) 
         ENGINE=INNODB;
        ERROR 1166 (42000): Incorrect column name 'db_row_id'
  • InnoDB锁在事务中止或提交后立即释放。

  • 不支持添加表锁,因为锁是隐式的commitunlock tables

数据字典

让我们来看看数据字典的一些已知限制:

  • 仅通过复制文件来支持备份和还原的个别MyISAM表。

  • MySQL 8 不支持手动创建数据库目录。例如,使用mkdir对 MySQL 服务器数据字典没有影响。

  • DDL操作所需的时间可能比预期的要长,因为这些操作被写入存储、撤消日志和重做,而不是.frm文件,这是我们在 MySQL 先前版本中所看到的。

MySQL8 中组复制的限制

现在是时候讨论一下 MySQL 8 中组复制的一些限制了:

  • 大事务:导致 GTID 内容的事务如果太大,则无法在组的其余成员之间复制。建议使用无法在大约五秒钟内复制到组成员的较小数据块,以避免失败。

  • 从组创建集群:如果您尝试从现有的组复制设置创建集群,将会导致错误,因为实例已经是复制组的一部分。目前只在 MySQL 的向导模式中注意到这一点;解决该问题的替代方法是禁用向导模式。

  • 可串行化隔离级别:当使用多主组时,不支持可串行化隔离级别,这是默认配置。

  • DDL 和 DML 操作:如果对同一数据对象执行并发的不同服务器上的 DDL 和 DML 操作,但使用了多主组模式,则不受支持。

  • 复制校验和:目前 MySQL 的设计限制限制了复制事件校验和的使用。

分区的限制

在本节中,我们将讨论分区的限制。

构造禁止

以下是在分区表达式中不允许的构造:

  • 声明的变量

  • 用户变量

  • 存储过程

  • 存储函数

  • UDFs

  • 插件

运营商

在分区表达式中不允许使用一些运算符,如<<>>|&~^。算术运算符如+-*的结果必须是整数值或NULL

以下是表分区的一些特定限制领域:

  • MySQL 8 支持的表的最大分区数为 8192。此限制还考虑了子分区。

  • 分区表不支持全文索引和搜索。

  • 临时表不能进行分区。

  • 日志表不能进行分区。

  • 在分区的InnoDB存储引擎上不支持外键。

  • 分区键的数据类型应该是整数列或者可以是整数的表达式。表达式或列的值可以是NULL;但是不支持包括ENUM的表达式。

  • 对于使用KEY进行分区的分区表进行升级,除了InnoDB存储引擎之外,都需要重新加载。

到目前为止,我们已经讨论了 MySQL 的概述、特性、优势和一些限制。现在让我们来看看 MySQL 的精彩用例。

MySQL 的用例

MySQL 之所以具有许多优势,是因为它在全球许多行业和各种用例中都有所涉足。MySQL 的重要性不仅取决于你拥有多少数据,更重要的是你将如何处理这些数据。数据可以来自不可预测的来源,并且可以用于解决许多问题。

现在让我们看看 MySQL 在著名场景上产生的具有现实重要性的用例:

上图帮助我们了解 MySQL 在哪些行业中发挥作用。虽然这不是 MySQL 在业务决策中发挥重要作用的行业的详尽列表,但现在让我们讨论一些行业。

社交媒体

社交媒体内容是信息,以及观看次数、点赞、人口统计学、分享、关注、独立访客、评论和下载等互动。最终,重要的是你的社交媒体相关努力如何为业务做出贡献。

一个著名的例子是 Facebook,Facebook 广泛使用了 MySQL。在 MySQL 上,使用了 PB 级的数据来提供点赞、分享和评论。Facebook 在 MySQL 的InnoDB存储引擎上开发了RocksDB存储引擎,利用了 InnoDB 存储引擎的许多优势,因为 Facebook 主要关注存储优化。尽管目前 MySQL 仍然主要用于其他常见应用。

政府

MySQL 时代在政府中也扮演着重要角色;政府机构广泛使用 MySQL,因为它带来了出色的投资回报和促进开源。事实上,政府部门正在全球范围内大量实施 MySQL。

这可能会让您感到惊讶;美国海军在其关键的飞行计划活动中使用 MySQL。有各种各样的活动,如天气条件、飞行计划、燃油效率、飞行维护等,都在 MySQL 的帮助下作为数据库进行跟踪。毫无疑问,它需要全天候运行并具有完全冗余性;MySQL 能够实现这一点,为美国海军飞机在全球范围内提供服务。

媒体和娱乐

YouTube 也是 MySQL 的重要用户之一。每当您在 YouTube 上观看视频时,它都会从关系数据库或使用 MySQL 的 blob 存储获取数据。 YouTube 还使用 Vitess;这是由 YouTube 发布的一个项目,用于前端 MySQL。 Vitess 有助于进行大量优化,并充当代理以使用 MySQL 为每个数据库请求提供服务。 YouTube 的实施中大量使用了 MySQL 副本;利用 MySQL 缓存是 YouTube 的另一个重要因素。

欺诈检测

在安全性、欺诈检测或合规性方面,如果您的解决方案能帮助您在问题发生之前识别和防止问题,那么它就成为业务的甜蜜点。大多数时候,欺诈检测发生在欺诈已经发生很久之后,那时您可能已经遭受了损失。接下来的步骤显然是尽量减少欺诈的影响,并改进可能帮助您防止再次发生的领域。

许多从事任何类型的交易处理或索赔的公司都广泛使用欺诈检测技术。 MySQL 有助于实时分析交易、索赔等,以及趋势或异常行为,以防止欺诈活动。

PayPal 是一个使用 MySQL 构建欺诈检测系统的典型用例。 PayPal 拥有超过 1 亿活跃用户,分布在美国、日本和欧洲的数据中心。对于这类用例来说,高可用性是一个关键标准,MySQL 已经能够如预期般提供性能。

业务映射

Netflix 拥有数百万订阅用户;它使用 MySQL 来运行其计费系统。 Netflix 在 MySQL 上的核心计费系统是任何业务的重要支撑。 Netflix 拥有数十亿行数据,自其成立以来一直在不断更新和维护数据。合规性是其中一个关键因素,同时还需要从 Oracle 迁移并最小化停机时间;MySQL 成功实现了这两点,并且每天都在不断扩展。

电子商务

Uber 是 MySQL 的另一个知名客户。 Uber 在全球范围内增长迅速,可扩展性、高可用性和投资回报率是需要重点关注的几个重要标准。 Uber 将 MySQL 作为其已知私人汽车运输服务的主要数据库。 Uber 在 MySQL 的基础上大量使用无模式数据库架构作为其后端。

有许多真实世界的 MySQL 用例已经改变了人类、技术、预测、健康、科学和研究、法律和秩序、体育、电子商务、能源和能源、金融交易、机器人技术等方面。 MySQL 是我们日常生活的一个重要组成部分,虽然并非总是明显,但它在许多方面都发挥着重要作用。

总结

在本章中,我们从概述 MySQL 以及 MySQL 数据库的主要特性开始,并探索了 MySQL 8 中新增的功能。之后,我们深入了解了 MySQL 8 的令人兴奋的新功能,以及在业务应用中使用 MySQL 的好处。我们了解了 MySQL 8 目前的限制和限制,这对我们在执行实施时非常重要。最后,我们浏览了一些在现实世界中扮演重要角色的令人印象深刻的用例,它们都将 MySQL 作为它们的数据库。

在下一章中,我们将学习在不同平台上安装 MySQL 8 的详细步骤。本章还涵盖了从 MySQL 8 升级或降级的方法,它们将都会被详细讨论。

第二章:安装和升级 MySQL 8

在上一章中,我们提供了 MySQL 的概述以及 MySQL 8 的新功能、用例和限制。MySQL 在平台方面非常灵活,例如 RedHat、Fedora、Ubuntu、Debian、Solaris、Microsoft Windows 等。它支持与不同语言连接的 API,例如 C、C++、C#、PHP、Java、Ruby 等。对于任何编程平台,设置环境与必要的软件工具是最重要和单调的任务。但对于 MySQL 8 来说,情况并非如此,因为本章重点是如何设置 MySQL 8 的环境。

本章详细解释了 MySQL 8 的安装步骤以及必要的先决条件。提供了在各种平台上设置 MySQL 8 的单独安装步骤。本章还涵盖了升级到 MySQL 8 或从 MySQL 8 降级的方法。

本章将涵盖以下主题:

  • MySQL 8 安装过程

  • MySQL 8 的后安装设置

  • MySQL 8 升级

  • MySQL 8 降级

MySQL 8 安装过程

本节将指导读者选择 MySQL 8 版本,从哪里获取 MySQL 8 以及如何安装 MySQL 8。它还解释了设置所需的后安装步骤。本章提供了有关如何升级或降级从 MySQL 8 的信息。

通用安装指南

MySQL 8 在许多操作系统上都有不同版本。MySQL 8 的发布以两种方式进行管理:

  • 开发发布:这具有最新功能,但不建议在生产中使用

  • 通用发布:这是一个稳定的发布,用户可以在生产中使用它

MySQL 8 的每个发布都遵循命名约定,表示其状态。每个发布名称由三个数字和一个可选后缀组成。例如,mysql.8.1.2-rc。数字的解释如下:

  • 第一个数字(8)表示发布的主要版本。

  • 第二个数字(1)表示发布的次要版本。主要和次要数字的组合描述了发布系列。

  • 第三个数字(2)表示发布系列中的版本。每个错误修复发布都会增加它。

最新版本的发布是最适合使用的。示例中给出的后缀表示 MySQL 8 发布的稳定性。MySQL 8 发布遵循三个后缀:

  • 开发里程碑发布dmr):MySQL 8 遵循里程碑模型,其中每个里程碑表示经过彻底测试的功能。

  • 发布候选人rc):新功能可能会在此版本中发布,但目的是修复先前发布的功能中的错误。

  • 没有后缀:这表示通用可用性GA)或生产发布。此版本稳定,并通过了早期阶段。它可靠且适用于生产。

如前所述,每个发布之前都是 DMR,然后是 RC,最后是 GA 发布状态。现在,在决定安装 MySQL 8 版本之后,是时候选择分发格式了。

二进制分发被推荐用于通用用途。它以原生格式提供给许多平台。例如,Linux 的 RPM 软件包和 OS X 的 DMG 软件包。

下载 MySQL 8

要从官方网站获取 MySQL 8,请参考以下网址:dev.mysql.com/downloads/。MySQL 还提供了一个镜像网站:dev.mysql.com/downloads/mirrors.html。当您到达下载页面时,您可以在页面底部看到版本选择选项卡,其中显示了两个选项卡:

  • 通用可用性GA)发布

  • 开发发布

根据前一节,从列表中选择合适的版本,然后单击下载按钮。

验证软件包的完整性

这是下载软件包可用并准备安装的阶段。这是一个可选步骤,但我们建议这样做,以避免在安装过程中出现错误。有三种不同的方法可用于检查完整性:

  • 使用 MD5 校验和

  • 使用加密签名

  • 使用 RPM 完整性验证机制

使用 MD5 校验和

这是检查完整性的最简单方法,需要很少的工作。MySQL 下载页面本身提供了一个 MD5 校验和,对于每个 MySQL 产品都是唯一的。下载 MySQL 8 后,我们只需确保下载文件的校验和与下载页面上提供的校验和匹配即可。有许多工具可用于不同操作系统来比较校验和。在这里,我们提供了一个使用命令行和一个名为 winMD5Sum 的图形工具的 MD5 校验和示例,用于 Windows 操作系统。

执行以下步骤以执行命令行:

  1. www.fourmilab.ch/md5/下载实用程序

  2. E:\Softwares位置下解压文件

  3. 转到命令行并执行以下命令:

 E:\Softwares\md5>md5.exe 
        E:\Softwares\mysql-installer-community-5.7.19.0.msi
 2578BFC3C30273CEE42D77583B8596B5 
        E:\Softwares\mysql-installer-community-5.7.19.0.msi

执行以下步骤以执行图形工具:

  1. 打开链接:www.nullriver.com/index/products/winmd5sum

  2. 点击页面上的下载 WinMD5Sum 选项。它将下载winMD5Sum.exe到您的计算机上。

  3. 运行下载的Install-winMD5Sum.exe并将其安装在本地计算机上。

  4. 安装成功后,打开 winMD5Sum 工具。这将打开一个对话框,在对话框中您必须选择下载的MySQL.msi文件。

  5. 点击计算按钮。这将计算已下载文件的 MD5 校验和。

  6. 在比较文本框中输入 MySQL 下载页面上提供的 MD5 校验和,然后点击比较按钮。

使用加密签名

这种完整性验证技术需要一个公共的 GPG 构建密钥。该密钥可以从pgp.mit.edu/ URL 获取。下载构建密钥后,您必须执行以下步骤:

  1. 导入构建密钥

  2. 从 MySQL 网站下载所需的 MySQL 8 软件包及其相关签名

确保 MySQL 软件包名称及其下载的签名文件名称相同。这两个文件必须放在一个共同的存储位置下。

  1. 现在,是时候执行以下命令进行验证了:
 cmd> gpg --verify package_name.asc

对于 Microsoft Windows,还有一些 GUI 工具可用于完整性检查。其中最流行的之一是Gpg4win。要在 Linux 上执行相同的检查,我们有可用的命令,因为 RPM 软件包本身包含 GPG 签名和 MD5 校验和。执行以下命令以验证软件包:

cmd> rpm --checksig package_name.rpm

这种验证技术比 MD5 校验更可靠,但非常复杂,需要更多的完整性检查工作。

在 Microsoft Windows 上安装 MySQL 8

MySQL 提供 32 位和 64 位版本。有多种安装 MySQL 8 在 Microsoft Windows 上的方法。最常见的方法是使用安装程序,在本地系统上安装和配置 MySQL 8。

在安装 MySQL Community 8.0 服务器之前,请确保系统上已安装了 Microsoft Visual C++ 2015 可再发行包。

MySQL 8 可以作为标准应用程序运行,也可以作为 Windows 服务运行。使用服务,用户可以使用 Windows 服务管理工具控制和测量操作。每个平台都有三种主要的分发格式:

  • 安装程序分发:这包括 MySQL 8 服务器以及其他产品,如 MySQL Workbench、MySQL for Excel 和 MySQL Notifier。安装程序还可用于将产品升级到其他版本。

  • 源分发:顾名思义,这包含所有源代码以及所有支持的文件。需要 Visual Studio 编译器才能使其可执行。

  • 二进制分发:此分发以 ZIP 文件格式提供。它包含除安装程序之外的所有必需文件。用户必须将文件解压缩到所选目录中。

Windows 特定注意事项

在 Microsoft Windows 上安装 MySQL 8 之前,请考虑以下几点:

  • 防病毒软件:众所周知,防病毒软件使用指纹技术,将快速更改的文件视为潜在的安全风险。在 MySQL 8 中,有一些目录包含 MySQL 8 相关数据和临时表信息,并且经常更新。因此,防病毒软件有可能将这些文件视为垃圾邮件。这也会影响性能。

防病毒软件提供配置以排除一些目录,因此建议排除 MySQL 8 数据目录和临时目录。默认情况下,MySQL 8 将临时数据存储到 Microsoft Windows 临时目录中。要更改 MySQL 8 中的此默认配置,请参考my.ini文件的tempdir参数。

  • 大表支持:在 NTFS 或任何新文件系统上使用 MySQL 8 以支持大小超过 4 GB 的大表。对于这些更大的表,用户必须在创建表时定义MAX_ROWSAVG_ROW_LENGTH属性。

MySQL 8 安装布局

微软 Windows 默认情况下,将C:\Program Files目录视为 MySQL 8 安装目录。然而,在安装时我们可以选择目录。无论安装位置如何,安装后的子目录结构保持不变。对于 Microsoft Window 布局,请参考以下表格:

目录 目录内容 备注
bin mysqld服务器,客户端和实用程序
%PROGRAMDATA%\MySQL\MySQL Server 8.0\ 日志文件,数据库 Windows 系统变量%PROGRAMDATA%默认为C:\ProgramData
examples 示例程序和脚本
include 包括(header)文件
lib
share 包括错误消息,字符集文件,示例配置文件,用于数据库安装的 SQL 等各种支持文件

您可以在dev.mysql.com/doc/refman/8.0/en/windows-installation-layout.html了解更多关于此主题的信息。

选择正确的安装包

在 Windows 上安装 MySQL 8 时,有多种软件包格式可供选择。MySQL 提供了使用程序数据库pdb)文件调试安装过程的功能。这些文件以 ZIP 分发方式提供:

  • 安装程序包:这是一个基于向导的过程,易于使用。安装程序包仅适用于 32 位,但也可以在 64 位配置上安装 MySQL 8。它不包含 MYSQL 的调试组件;我们必须单独下载 ZIP 文件。安装程序包有两种不同的格式:

  • Web Community:顾名思义,这是用于 Web 安装的。这意味着需要互联网才能使用 Web 社区进行安装。其大小约为 19 MB。其名称定义为通过附加版本的 MySQL-installer-community。

  • 社区:此软件包格式用于离线安装。其大小约为 301 MB。其名称定义为通过附加版本的 MySQL-installer-web-community。

安装程序是 MySQL 产品安装和升级的最常见方式。

  • Noinstall Archives:这是一个包含不完整安装包文件的手动安装过程。由于这是一个手动过程,因此没有 GUI 可用。用户必须手动安装和配置 MySQL 8 和其他产品(如果需要)。与安装程序不同,它以 ZIP 格式为 32 位和 64 位配置提供两个不同的文件。

MySQL 8 安装程序

MySQL 8 安装程序主要用于简化安装过程以及在 Windows 平台上运行的 MySQL 产品的管理。在产品列表中,我们可以考虑:

  • MySQL 服务器

  • MySQL 应用程序

  • MySQL 连接器

  • 文档和示例

MySQL 8 安装程序有两个版本:

  • 社区版:可在dev.mysql.com/downloads/installer/下载。如前所述,安装程序提供 Web 社区和社区包格式。

  • 商业版:请参阅edelivery.oracle.com/下载商业版。商业版包含社区版中可用的所有产品以及以下产品:

  • Workbench SE/EE

  • MySQL 企业备份

  • MySQL 企业防火墙

初始设置信息

如前所述,安装程序将引导用户通过向导。一旦我们在主机机器上启动安装程序,它将检测已安装的 MySQL 产品,并将其列入要管理的产品列表。以下是安装程序初始设置中所需的步骤:

  1. MySQL 安装程序许可和支持身份验证:这是用户必须在开始 MySQL 8 安装之前接受许可协议的步骤。接受条款后,用户可以添加、更新或删除 MySQL 产品。在商业版中,需要凭据来解除捆绑产品,并且必须与用户在支持站点的 Oracle 帐户匹配。

  2. 选择设置类型:这是用户必须选择要安装的 MySQL 产品的步骤。安装程序还提供了预定义设置的选项,其中包含一组 MySQL 产品。因此,您可以根据自己的需求选择一个设置类型。以下是安装程序中可用的一些设置。

  3. 开发者默认:这将安装在下载时选择的 MySQL 8 服务器版本:

  • MySQL 服务器

  • MySQL shell

  • MySQL 路由器

  • MySQL Workbench

  • MySQL for Visual Studio

  • MySQL for Excel

  • MySQL 通知程序

  • MySQL 连接器

  • MySQL 实用程序

  • MySQL 文档

  • MySQL 示例和示例

  1. 仅服务器:这只安装 MySQL 服务器。

  2. 仅客户端:这与开发者默认设置类型相同,只是不包含 MySQL 8 服务器或任何特定于客户端的包。

  3. 完整:这将安装 MySQL 的所有可用产品,如mysql-servermysql-clientmysqladmin等。

  4. 自定义:此选项仅安装用户从目录中选择的产品。在这里,用户可以自由选择所需的产品,而不是安装完整的产品包。

  5. 路径冲突:当托管系统已经包含一个 MySQL 产品,并且用户试图在相同路径上安装该 MySQL 产品的不同版本时,安装程序将在向导中显示路径冲突错误。安装程序使用户能够以以下方式处理路径冲突:

  • 使用向导中的浏览按钮选择不同的位置

  • 通过自定义选择选择不同的设置类型或版本

  • 通过继续到下一步覆盖现有文件夹

  • 取消向导步骤,删除现有产品,然后重新启动安装程序

  1. 检查要求:每个 MySQL 产品都附有一个package-rules.xml文件,其中包含所有先决条件软件列表。在初始设置期间,安装程序将检查所需软件的可用性,并在缺少要求时提示用户更新主机。

  2. MySQL 安装程序配置文件:安装程序配置文件位于C:\Program Files。以下是配置文件的详细信息:

文件或文件夹 描述 文件夹层次结构
Windows 的 MySQL 安装程序 此文件夹包含运行 MySQL 安装程序和MySQLinstallerConsole.exe所需的所有文件,这是一个具有类似功能的命令行程序。 C:\Program Files (x86)
Templates Templates文件夹中有每个 MySQL 服务器版本的一个文件。Templates文件包含用于动态计算一些值的键和公式。 C:\ProgramData\MySQL\MySQL installer for Windows\Manifest
package-rules.xml 此文件包含要安装的每个产品的先决条件。 C:\ProgramData\MySQL\MySQL installer for Windows\Manifest
produts.xml products文件(或产品目录)包含可供下载的所有产品的列表。 C:\ProgramData\MySQL\MySQL installer for Windows\Manifest
Product Cache Product Cache文件夹包含与完整包或随后下载的所有独立MSI文件捆绑在一起。 C:\ProgramData\MySQL\MySQL installer for Windows

参考:dev.mysql.com/doc/refman/8.0/en/mysql-installer-setup.html

安装工作流程

MySQL 安装程序遵循每个产品的工作流程:

  1. 产品下载:安装程序将所有必需的产品 MSI 文件下载到Product Cache文件夹中。

  2. 产品安装:安装程序通过 Ready | Install | Installing | Complete 来管理每个产品的状态。

  3. 产品配置:此阶段使用逐步配置过程来配置产品。安装程序将状态从 Ready | Configure 更改。

  4. 安装完成:完成安装后,用户可以开始使用该应用程序。

InnoDB 集群沙盒测试设置

在 MySQL 8 中,使用安装程序有两种高可用性实现选项:

  • 独立的 MySQL 服务器/经典的 MySQL 复制默认):此选项手动配置多个服务器,或使用最新版本的 MySQL Shell 配置InnoDB集群。

  • InnoDB 集群沙盒测试设置仅用于测试):这也被称为沙盒集群。此选项允许您仅在本地系统上为测试目的创建InnoDB集群。MySQL 安装程序工具栏提供了一些InnoDB集群中实例的配置。

集群节点在不同的端口上运行。配置完成后,单击摘要选项卡以获取每个集群的端口详细信息。

服务器配置

MySQL 安装程序执行一些基本配置,包括:

  • 安装程序将为 MySQL 8 服务器创建一个my.ini配置文件。文件内容将根据安装过程中选择的选项确定。

  • 默认情况下,安装程序将为 MySQL 8 服务器添加 Windows 服务。

  • 安装程序将提供 MySQL 8 服务器的默认安装和数据路径。

  • 安装程序将为 MySQL 8 服务器创建一些带有角色和权限的用户帐户。它可以创建一个具有有限特权的 Windows 用户到 MySQL 8 服务器。

  • 使用显示高级选项,MySQL 安装程序允许定义日志选项的自定义路径。例如,您可以配置单独的错误日志路径,显示查询日志等。

MySQL 8 需要以下服务器配置:

  • 服务器配置类型:根据服务器配置类型,系统资源将分配给 MySQL 8 服务器。

  • 开发:通过将主机视为个人工作站,它配置 MySQL 8 以使用最少量的内存。

  • 服务器:作为服务器,机器上还运行着一些其他应用程序,因此它将配置中等数量的内存。

  • 专用:对于专用于 MySQL 8 服务器的专用机器,此选项配置了可用内存的最大使用。

  • 连接性:此选项指示与 MySQL 8 服务器的连接。以下选项可用:

  • TCP/IP:此选项启用与 MySQL 8 的 TCP/IP 连接。用户可以定义端口号以及网络访问端口的防火墙设置。

  • 命名管道:此选项允许您为连接定义管道名称。

  • 共享内存:这允许您为 MySQL 8 服务器定义内存名称。

  • 高级配置:此配置启用了额外的日志记录功能,将管理日志记录在单独的文件中。用户可以配置单独文件的路径。例如,配置二进制日志的自定义路径。

  • MySQL 企业防火墙:此选项仅适用于商业版。勾选启用企业防火墙选项以启用防火墙。

  • 帐户和角色:帐户和角色用于管理用户的访问权限。在安装过程中,MySQL 安装程序允许您设置根帐户密码和用户帐户。

  • 根帐户密码:在安装过程中需要输入根密码。安装程序将检查密码强度,并在违反预定义策略时发出警告。

  • MySQL 用户帐户:这是一个可选步骤,其中定义了一个新的 MySQL 用户帐户,并具有现有用户角色。预定义角色有其自己的权限。

  • Windows 服务:MySQL 8 服务可以以下两种方式进行配置:

  • 配置为 Windows 服务:这是安装过程中默认选择的选项。它进一步提供了两个选项:

  • 系统启动时启动服务:此选项默认选中,将在系统启动时自动启动 MySQL 8 服务。

  • 作为 Window 服务运行:此选项允许将用户帐户与 MySQL 8 服务关联。默认情况下选择系统帐户,其中服务被视为网络服务。使用自定义用户,首先使用 Microsoft Windows 中的“本地安全策略”为用户设置权限。

  • 配置为可执行程序:在安装过程中取消选择 Windows 服务选项。

  • 插件和扩展:此步骤适用于新安装。如果用户希望从较旧的 MySQL 版本升级,则用户需要在 MySQL 安装程序中选择重新配置选项。

  • 高级选项:要启用此选项,请在类型和网络步骤中选中显示高级配置复选框。此选项允许用户定义特定路径的日志文件,例如错误日志、常规日志、慢查询日志和二进制日志。

  • 应用服务器配置:用户在 MySQL 安装程序中完成所有配置后,单击“执行”按钮使其可用。安装完成后,单击“完成”按钮,MySQL 安装程序和所有已安装的 MySQL 产品将在 Windows“开始”菜单中可用。

MySQL 安装程序产品目录和仪表板

本节包含有关MySQL 安装程序如何处理产品目录和管理仪表板的详细信息。

产品目录是一个组件,其中包含所有发布的支持 Microsoft Windows 的 MySQL 产品列表。MySQL 安装程序每天更新目录,并提供手动更新目录的选项。产品目录执行以下操作来管理列表:

  • 定期更新可用产品列表

  • 检查在主机中安装的产品的更新

产品目录列出了所有开发、一般或任何次要版本中可用的产品。

MySQL 安装程序仪表板提供了在主机工作站中管理 MySQL 产品安装的功能。以下是使用仪表板管理产品的方法:

  • MySQL 安装程序提供了一个配置,可以在特定时间间隔更新目录。用户可以通过配置启用或禁用自动更新。仪表板在产品级别显示一个特殊图标,表示其新版本已经可用。

  • 用户可以使用以下操作来管理产品:

  • 添加:用于下载和安装一个或多个产品。

  • 修改:用于在已安装产品中添加或删除功能。

  • 升级:用于升级产品。确保在可升级产品窗格中产品级别的复选框被选中。

  • 删除:用于从已填充列表中卸载产品。

  • 仪表板提供了重新配置功能,用户可以更改已配置的选项和值。应用更改后,MySQL 安装程序将停止 MySQL 8 服务器,然后重新启动以使更改生效。

  • 仪表板提供了下载产品目录而不进行升级的功能。此时不更新复选框可用于在不下载的情况下检查与产品相关的当前更改。要执行此功能,请选择复选框,然后单击目录链接。

MySQL 安装程序控制台

MySQL 安装程序包括**M**ySQLinstallerConsole.exe文件,它提供了使用命令提示符执行命令的功能。这个功能在安装 MySQL 安装程序时默认安装。有一些命令可用于管理 MySQL 产品。要查看这些命令的详细信息,请执行help命令。

使用 ZIP 文件安装 MySQL 8

要使用 ZIP 存档安装 MySQL 8,请执行以下步骤:

  1. 提取安装存档:在此步骤中,选择一个特定目录来提取已安装的 MySQL 8 存档。Microsoft Windows 默认将其安装到c:\mysql位置。在安装过程中,请确保登录用户具有管理员权限。

确保在安装 MySQL 作为 Windows 服务时没有运行任何 MySQL 服务。

在执行命令时,首先输入-- install,然后指定--default –file 选项;否则,mysqld.exe文件将启动 MySQL 8 服务器。

  1. 创建选项文件:选项文件是用户可以配置与 MySQL 8 相关命令的地方。MySQL 8 在每次服务器启动时都会引用这个文件。在 Microsoft Windows 中,当 MySQL 8 服务器启动时,它会在 Windows 目录和 MySQL 8 基本目录中搜索选项文件。主要使用my.inimy.cnf文件作为选项文件,它们是以纯文本形式存在的。

使用 Windows 操作系统:

  1. 要获取Windows目录,请参考WINDIR环境变量。

  2. my.ini位于 MySQL 服务器安装的默认位置。

  3. 选项文件中的路径名使用正斜杠而不是反斜杠指定。如果要使用反斜杠,则需要双反斜杠。

如前所述,选项文件与普通文本文件相同;用户可以使用任何文本编辑器修改它。考虑一个例子,MySQL 8 安装目录和数据目录位于不同位置。在这种情况下,用户必须在mysqld部分的选项文件中提及两个目录的位置:

 [mysqld]
 # set basedir to your installation path
 basedir=E:\\mysql
 # set datadir to the location of your data directory
 datadir=E:\\mydata\\data

请记住,ZIP 存档不会初始化数据目录。要初始化数据目录并在 MySQL 8 系统数据库中填充表,用户必须执行initialize命令。这个过程将在后面的后安装部分中介绍。

  1. 选择服务器类型:在 MySQL 8 中,为 Microsoft Windows 提供了以下两种服务器:
二进制 描述
mysqld 带有命名管道支持的优化二进制文件
mysqld-debug 类似于mysqld,但是编译时带有完整的调试和自动内存分配检查

MySQL 8 在所有 Microsoft Windows 平台上都支持 TCP/IP 和管道。但默认选项是正常模式,因为管道选项会影响性能。它会减慢整体性能。您可以在此处了解更多信息:dev.mysql.com/doc/refman/8.0/en/windows-select-server.html

  1. 启动 MySQL 8 服务器:此步骤描述了如何首次启动 MySQL 8 服务器。可以使用命令行或作为 Windows 服务启动。要使用命令行启动它,请执行以下命令。假设 MySQL 8 安装在E:\MySQL\MySQL Server 8文件夹下:
 E:\> “E:\MySQL\MySQL Server 8\bin\mysqld”

在执行上述命令后,用户可以看到一系列消息,帮助用户识别错误(如果存在)。命令提示符中的最后两行显示如下。它们表明 MySQL 8 服务已启动,并准备好接受服务器-客户端请求:

 mysqld: ready for connections
 Version: '8.0.4' socket: '' port: 3306

用户可以省略控制台以获取错误日志,因为 MySQL 8 会在数据目录下的单独日志文件中维护日志,扩展名为.err。启动 MySQL 8 服务器时,用户必须每次使用命令提示符执行相同的命令。要停止 MySQL 8 服务器,请执行以下命令:

 E:\> “E:\MySQL\MySQL Server 8\bin\mysqladmin” -u root shutdown

最初安装 MySQL 8 时,根帐户密码未设置。首次启动 MySQL 8 服务器后,用户必须手动设置密码。设置密码的步骤在后续安装后的部分中有详细描述。

mysqldmysqladmin的路径可能会根据 MySQL 8 的安装位置而有所不同。如果在 MySQL 8 服务器的根用户上设置了密码,则使用-p 选项执行命令并提供密码以成功执行。

  1. 设置 MySQL 8 的环境变量。如前所述,我们已经用 MySQL 8 服务器安装的完整路径编写了命令。为了简化这个命令,在 Windows 10 中,我们必须通过以下步骤定义 MySQL 8 的环境变量:

  2. 右键单击我的电脑,选择属性

  3. 点击“高级系统设置”。

  4. 点击右下方的“环境变量”按钮。

  5. 转到“系统变量”部分,找到“PATH”变量。

  6. 选择“PATH”变量,点击“编辑”按钮。

  7. 一个新的对话框打开;点击“新建”按钮,输入 MySQL 8 服务器安装的路径,直到bin文件位置。例如,E:\MySQL\MySQL Server 8\bin

  8. 在 Windows 中按下所有相关对话框中的“确定”按钮以应用此更改。

应用环境变量后,用户可以在命令提示符中执行任何 MySQL 命令,而无需提供 MySQL 8 的完整路径。

  1. 将 MySQL 8 作为 Windows 服务启动:建议将 MySQL 8 作为 Windows 服务使用,因为它将在 Windows 启动时启动,并在 Windows 停止时停止。在这种情况下,无需显式启动 MySQL 8 服务。使用 Microsoft Windows 服务实用程序来管理 MySQL 8 服务。要将 MySQL 服务器安装为服务,请使用以下命令:
 E:\> “E:\MySQL\MySQL Server 8\bin\mysqld” --install

此命令有以下选项用于附加参数:

  • 如果我们没有定义服务名称,那么该命令将考虑默认服务为MySQL

  • 使用-- default -file参数来指定包含服务名称的选项文件名

  • 用户还可以使用--local-service选项,后面跟着服务名称

使用以下命令将 MySQL 服务设置为 Windows 服务,并引用文件my-opts.conf作为 MySQL 8 配置的选项文件:

E:\> “E:\MySQL\MySQL Server 8\bin\mysqld” --install MySQL --defaults-file=E:\my-opts.cnf

到目前为止,我们已经讨论了 MySQL 8 服务作为 Windows 服务,但是还有一个带有--install-manual选项的命令可用于启动 MySQL 8 而不使用 Windows 服务。要删除 MySQL 8 服务,请使用--remove命令,如下所示:

E:\> “E:\MySQL\MySQL Server 8\bin\mysqld” --install-manual
E:\> "E:\MySQL\MySQL Server 8\bin\mysqld" --remove

在 Linux 上安装 MySQL 8

对于 Linux 上的 MySQL 8 安装,有各种解决方案可用。用户可以根据自己的需求选择任何发行版。以下是不同的发行版,其中有三种进行了详细描述:

  • 使用Yum仓库进行安装

  • 使用APT仓库进行安装

  • 使用SLES仓库进行安装

  • 使用RPM软件包进行安装

  • 使用Debian软件包进行安装

  • 使用 Docker 进行安装

  • 使用本地软件仓库进行安装

  • 使用 Juju 进行安装

使用 Yum 仓库进行安装

执行以下步骤以使用Yum仓库安装 MySQL 8:

  1. dev.mysql.com/downloads/repo/yum/链接下载Yum仓库。

  2. 选择适合您安装的 MySQL 软件包。

  3. 执行安装命令将 MySQL Yum仓库添加到您的local仓库中:

 shell> sudo yum localinstall package_name.rpm
 shell> yum repolist enabled | grep "mysql.*-community.*"

用实际的 RPM 软件包名称替换package_name。安装后,执行第二个命令以检查Yum仓库是否已正确安装。

  1. 选择发布系列:MySQL Yum 仓库包含各种发布系列供安装。执行以下命令以检查可用系列的列表:
 shell> yum repolist all | grep mysql

在 MySQL Yum 仓库中,默认情况下启用了最新的 GA 系列进行安装,但除此之外,其他开发系列也以禁用状态可用。对于开发系列的安装,请执行以下两个命令以禁用 GA 发布并启用所需的开发系列:

 shell> sudo yum-config-manager --disable mysql57-community
 shell> sudo yum-config-manager --enable mysql80-community

定义发布系列的另一种方式是在仓库文件中使用手动条目。例如,将以下条目添加到/etc/yum.repos.d/mysql-community.repo文件中:

 mysql80-community]
 name=MySQL 8.0 Community Server
 baseurl=http://repo.mysql.com/yum/mysql-8.0-community/el/6/$basearch/
 enabled=1

在这里,enabled=1表示启用此系列,enabled=0表示禁用此系列。Yum 一次只允许一个启用的子仓库用于一个发布;如果启用了多个发布系列,则 Yum 仓库将只选择最新的系列。在保存配置文件中的更改后,执行以下命令以检查是否已选择正确的子仓库:

 shell> yum repolist enabled | grep mysql
  1. 安装 MySQL 8:执行以下命令:
 shell> sudo yum install mysql-community-server

它将安装带有所有依赖项的 MySQL 8 服务器,例如客户端、客户端的字符集和所需的库。

  1. 启动 MySQL 8 服务器:第一个命令将启动 MySQL 服务,第二个命令显示 MySQL 服务的当前状态:
 shell> sudo service mysqld start 
 shell> sudo service mysqld status

在初始启动期间,执行以下任务:

  • 服务器已初始化

  • SSL 证书和密钥文件在数据目录中生成

  • 插件名称validate_password_plugin已安装并已启用

  • 创建了一个超级账户

使用 RPM 软件包进行安装

RPM 软件包可从 Yum 仓库和 SLES 仓库获取,用于 MySQL 8。这是 MySQL 8 安装的推荐方式。RPM 软件包遵循packagename-version-distribution-arch.rpm的语法,其中 distribution 和 arch 分别表示 Linux 发行版和处理器。RPM软件包是所有必需软件包的捆绑包,它们彼此依赖。RPM 软件包遵循与 Yum 仓库安装中讨论的相同步骤。在基于 RPM 的系统中,MySQL 服务不会自动启动。要手动启动它,请执行以下命令:

shell> sudo service mysqld start

与其他安装一样,RPM 软件包安装还会在系统上创建文件和目录,路径如下:

文件或资源 位置
客户端程序和脚本 /usr/bin
- mysqld 服务器 /usr/sbin
- 配置文件 /etc/my.cnf
- 数据目录 /var/lib/mysql
- 错误日志文件 对于 RHEL、Oracle Linux、CentOS 或 Fedora 平台:/var/log/mysqld.log 对于 SLES:/var/log/mysql/mysqld.log
- secure_file_priv 的值 /var/lib/mysql-files
- System V init 脚本 对于 RHEL、Oracle Linux、CentOS 或 Fedora 平台:/etc/init.d/mysqld 对于 SLES:/etc/init.d/mysql
- Systemd 服务 对于 RHEL、Oracle Linux、CentOS 或 Fedora 平台:mysqld 对于 SLES:mysql
- Pid 文件 /var/run/mysql/mysqld.pid
- Socket /var/lib/mysql/mysql.sock
- 密钥环目录 /var/lib/mysql-keyring
- Unix 手册页 /usr/share/man
- 包括(header)文件 /usr/include/mysql
- 库 /usr/lib/mysql
- 其他支持文件(例如错误消息和字符集文件) /usr/share/mysql

使用 Debian 包进行安装

MySQL APT 仓库或 MySQL Developer Zone 的下载区域提供了 Debian 包。每个 MySQL 组件都有自己的 Debian 包进行安装,但也准备了一个 tarball 捆绑包,将不同的 Debian 包组合成一个单独的捆绑包。tarball 命名约定为 mysql-server_MVER-DVER_CPU.deb-bundle.tar,其中 MVER 表示 MySQL 版本,DVER 表示 Linux 发行版版本,CPU 表示处理器。要使用 Debian 包安装 MySQL 8,请执行以下步骤:

  1. 从 MySQL 站点下载所需的 tar 包。

  2. 使用以下命令解压包:

 shell> tar -xvf mysql-server_MVER-DVER_CPU.deb-bundle.tar
  1. 可能需要 Libaio1 库,因此执行库安装命令:
 shell> sudo apt-get install libaio1
  1. 执行以下命令对 MySQL 服务器进行预配置:
 shell> sudo dpkg-preconfigure mysql-community-server_*.deb

这是在安装前实施的进程,适用于一组 Debian 包和其他使用 debconf 的配置脚本来检查系统的包。在此过程中,系统将要求输入 MySQL 8 安装的 root 用户密码。

  1. 安装 MySQL 8 所需的依赖项:
 shell>sudo apt-get -f install

MySQL 8 配置文件在 Debian 包中的以下路径下可用:

  • 配置文件存储在 /etc/mysql

  • 数据目录存储在 /var/lib/mysql

  • 二进制文件、库和头文件存储在 /user/binuser/sbin

MySQL 8 的后安装设置

后安装是描述用户在 MySQL 8 安装后必须执行的基本步骤或配置的过程。

数据目录初始化

在前面的部分中,我们已经看到了 MySQL 8 安装的不同方法。其中一些方法将自动为 MySQL 8 创建数据目录。对于通用二进制分发和源分发,必须创建数据目录。数据目录初始化可以通过以下两个命令之一执行:

E:\> bin\mysqld –-initialize 
E:\> bin\mysqld --initialize-insecure

可以根据用户的需求选择这两个命令中的任何一个来生成一个随机的初始密码。无论数据目录初始化的平台如何,用户都可以使用这两个命令。初始化是一种生成随机初始 root 密码的方法。在 initialize-insecure 的情况下,不会生成密码。

另一个选项是使用命令行参数指定安装目录和数据目录,如下命令所示:

E:\> bin\mysqld --initialize --basedir E:\mysql --datadir :\mydata\data

用户还可以在名为 Option 文件的单独文件中指定这些目录,该文件位于 mysqld 参数下。此配置在本章的 Option 文件部分中有详细描述。当用户执行这两个命令中的任何一个时,mysqld 将执行以下步骤:

  1. 它检查数据目录的存在

  2. MySQL 8 服务器将创建系统数据库及其表、授予表、帮助表和时区表

  3. 它使用InnoDB表的数据结构初始化系统表空间

  4. 将创建root@localhost超级用户账户和其他保留账户

保护初始的 MySQL 账户

本节解释了在工作站上首次执行 MySQL 8 服务器时如何为 root 账户分配密码。当用户在 Windows 中使用安装程序或在 Linux 中使用Debian软件包安装 MySQL 8 时,安装过程提供了输入密码并将该密码分配给 root 账户的选项。但是使用RPM软件包时,将为 root 账户生成随机密码,该密码在安装过程中写入服务器日志文件。初始 root 账户可能有也可能没有密码。要在初始阶段分配密码,请使用以下任一程序:

  • 如果 root 账户有随机密码:
  1. 查看服务器日志文件以获取自动生成的密码。

  2. 通过执行以下命令连接到 MySQL 8 服务器,使用自动生成的密码:

 shell> mysql -u root -p
 Enter password: (enter the random root password here) 
  • root账户设置新密码:
 mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'newPassword';
  • 如果 root 账户没有密码:
  1. 无需密码连接到 MySQL 8 服务器:
 mysql -u root
  1. root账户设置新密码:
 mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'newPassword';

分配新密码后,每当您想要连接到 MySQL 8 时,都必须使用新密码。

MySQL 8 服务的启动和故障排除

本节解释了如何启动 MySQL 8 服务器以及如何在启动过程中解决问题。在 Linux 系统上成功安装 MySQL 8 服务器后,执行以下命令启动 MySQL 8 服务:

shell> sudo service mysqld start

要详细检查服务是否已启动,请参考日志文件。您可以使用 status 命令来检查 MySQL 8 服务的状态:

shell> sudo service mysqld status

在收到服务启动/运行消息后,您可以使用以下命令连接到 MySQL 8:

shell> mysql -uroot -p

上述命令会提示输入密码,因此输入密码并按下Enter键。MySQL 提示将显示在那里,您可以输入mysql命令进行执行。在执行上述命令时,可能会出现一些常见问题,因此在这里我们将提出以下故障排除建议:

  1. 查看log文件以找到服务启动期间发生的确切错误。如前一节所述,error文件和log文件位于数据目录下。它们的命名约定为host_name.errhost_name.log。通过阅读文件的最后几行,您可以确定在上次执行的命令期间发生的问题。

  2. 检查所需的端口/套接字是否可用。以下错误将表明所需的端口和套接字不可用,意味着它们正在被其他程序使用。要识别这一点,通过禁用服务来跟踪所有问题。另一个原因是防火墙设置阻止了对所需端口的访问,因此修改防火墙设置并允许对所需端口的访问:

  • 无法启动服务器:在 TCP/IP 端口上绑定:地址已在使用中

  • 无法启动服务器:在 Unix 套接字上绑定

  1. 建议将特定参数定义到Option文件中。如果参数未定义到文件中,则 MySQL 8 将考虑默认参数,因此在使用之前,请参考 MySQL 8 提供的所有可用参数。

  2. 验证data目录路径和权限是否正确定义。该目录用作 MySQL 8 的当前目录。要查找数据目录的当前设置路径,请使用mysqld命令执行--verbose--help命令。如果data目录位于 MySQL 安装目录以外的其他位置,则使用mysqld命令的--datadir选项。对于权限,您将收到错误代码 13,表示权限被拒绝的错误。要解决此问题,请更改所需文件和文件夹的权限。另一种方法是使用 root 用户登录,但这在所有情况下都不可能,因此建议首先采用第一种方法来解决权限问题。

执行命令以测试服务器

执行上述步骤后,现在您的 MySQL 8 服务已启动,并已连接到指定用户。现在,通过执行以下基本命令来检查您的 MySQL 8 服务器是否正常工作:

shell> bin/mysqladmin version

该命令列出了与安装的 MySQL 服务器相关的所有信息,包括其版本详细信息、协议版本等。连接到 MySQL 8 后,执行以下命令以检查是否已正确从服务器检索到信息:

mysql>mysqlshow
mysql>mysqlshow mysql

第一个命令显示服务器中可用的数据库列表。列表可能因系统而异,但mysqlinformation_schema必须在列表中可用。第二个命令列出了在mysql数据库下创建的所有表。

升级 MySQL 8

在以前的 MySQL 版本中,数据字典存储在基于文件的系统中,而在 MySQL 8 中,它存储在数据字典结构中。因此,升级过程将文件式结构移动到数据字典结构中。从 MYSQL 5.7 GA 版本(即从 5.7.9 或更高版本)可以升级到 MySQL 8。对于 5.7 的非 GA 版本,无法进行升级。在开始升级过程之前,需要了解以下几点。

升级方法

有两种升级方法,它们通过其实施方法进行区分。让我们详细讨论这些方法。

MySQL 的就地升级

顾名思义,这是一个过程,我们将 MySQL 的现有旧版本包替换为更新版本。在开始之前,请确保旧服务器已停止,并在替换包后,使用 MySQL 升级重新启动现有数据目录上的 MySQL 8 服务器。

执行以下步骤进行就地升级:

  1. 使用以下命令对加密的InnoDB表空间进行主密钥旋转:
 ALTER INSTANCE ROTATE INNODB MASTER KEY;
  1. 使用innodb_fast_shutdown命令配置关闭参数:
 SET GLOBAL innodb_fast_shutdown = 1; -- fast shutdown
 SET GLOBAL innodb_fast_shutdown = 0; -- slow shutdown
  1. 使用以下命令关闭旧的 MySQL 版本:
 mysqladmin -u root -p shutdown
  1. 这是用户将旧包替换为新 MySQL 8 包的升级过程。

  2. 使用现有的data目录启动 MySQL 8 服务器。

在服务器启动时,它将检查data字典表。如果不存在,则服务器将在data目录中创建表,并使用其正常启动顺序填充元数据和进程。如果这些步骤成功执行,服务器将通过创建backup_metadata_57目录来执行清理。此外,服务器将事件和 proc 表重命名为event_backup_57proc_backup_57。如果此步骤失败,则服务器将恢复所有更改。

  1. 成功完成 MySQL 8 启动后,执行mysql_upgrade
 mysql_upgrade -u root -p
  1. 升级后,关闭并重新启动服务器,以检查是否已应用所有更改。

MySQL 8 的逻辑升级

导出或获取旧的 MySQL 版本的转储。安装新的 MySQL 8 版本,并使用 MySQL 升级将转储文件加载到新的 MySQL 8 版本。执行以下步骤来应用逻辑升级:

  1. 使用mysqldump命令导出数据:
 mysqldump -u root -p --add-drop-table --routines --events --all-databases   --
          force > data-for-upgrade.sql

--routines --events选项用于在转储文件中包含存储例程和事件,并明确定义这些选项以产生效果。

  1. 关闭旧的 MySQL 服务器。

  2. 安装新版本的 MySQL 8。

  3. 初始化data目录:

 mysqld --initialize --datadir=/path/to/8.0-datadir
  1. 使用新的data目录启动 MySQL 8 服务器:
 mysqld_safe --user=mysql --datadir=/path/to/8.0-datadir 
  1. 将 SQL 转储文件加载到新的MySQL数据库中:
 mysql -u root -p --force < data-for-upgrade.sql
  1. 使用以下命令升级 MySQL:
 mysql_upgrade -u root -p

MySQL 5.7 的升级先决条件

在开始升级之前,执行以下检查以避免后期失败:

  1. 执行以下命令以检查是否存在绝对数据类型或函数:
 mysqlcheck -u root -p--all-databases--check-upgrade
  1. 使用以下命令检查本机分区支持:
 SELECT TABLE_SCHEMA, TABLE_NAME
 FROM INFORMATION_SCHEMA.TABLES
 WHERE ENGINE NOT IN ('innodb', 'ndbcluster')
 AND CREATE_OPTIONS LIKE '%partitioned%';

此命令将列出使用不支持本机分区的存储引擎的表。在执行前述查询后,如果发现任何表,则删除表上的分区并更改存储引擎,如以下命令所示:

 ALTER TABLE table_name ENGINE = INNODB;
 ALTER TABLE table_name REMOVE PARTITIONING;
  1. 确保 MySQL 5.7 中不包含在 MySQL 8 中用作数据字典的任何表。

  2. 使用以下代码检查外键约束名称是否不超过 64 个字符:

 SELECT CONSTRAINT_SCHEMA, TABLE_NAME, CONSTRAINT_NAME
 FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
 WHERE LENGTH(CONSTRAINT_NAME) > 64;
  1. 确保 MySQL 5.7 不包含 MySQL 8 中不可用的功能,例如:
  • 如果表使用了 MySQL 8 不支持的存储引擎,则已更改为支持的存储引擎

  • 使用 MySQL 8 中不可用的选项或变量进行配置更改

MySQL 8 降级

降级是从升级的反向过程,我们将从 MySQL 的较高版本降级到 MySQL 的较低版本。在本节中,我们将介绍如何从 MySQL 8 降级到 MySQL 5.7。不支持版本跳过的降级意味着不支持从 MySQL 8 降级到 MySQL 5.6。在支持版本跳过的同一系列中,意味着您可以跳过 MySQL 8.y 版本,从 MySQL 8.z 降级到 MySQL 8.x。首先,我们将解释在开始降级之前需要理解的一些基本要点。

降级方法

原地降级意味着关闭 MySQL 8 的新版本,用旧版本的 MySQL 替换其二进制文件或软件包。重新启动旧版本意味着在现有数据目录中的 MySQL 5.7。此降级方法在同一系列的 GA 版本之间得到支持。执行以下步骤进行原地降级:

  1. 关闭较新版本的 MySQL 8。

  2. 关闭后,从data目录中删除InnoDB重做日志文件,以避免降级问题:

 rm ib_logfile*
  1. 将旧版本的 MySQL 定位到新版本的二进制文件或软件包的位置。

  2. 通过指定data目录启动降级版本的 MySQL,使用以下命令:

 mysqld_safe --user=mysql --datadir=/path/to/existing-datadir
  1. 执行mysql_upgrade命令:
 mysql_upgrade -u root -p
  1. 关闭并重新启动 MySQL 服务器,以检查所有更改是否已应用。

对于基于 APT、SLES 和 Yum 存储库安装的 MySQL 安装,不支持原地降级

逻辑降级

使用新版本中的mysqldump对所有表进行转储。安装新版本的 MySQL 8,并将旧版本的转储加载到新数据库中。此降级也支持在同一 GA 发布系列和发布级别中。使用逻辑降级方法支持 MySQL 8.0 到 5.7 的降级。

要执行逻辑降级,请按照以下步骤操作:

  1. 使用以下代码对数据库进行转储:
 mysqldump -u root -p --add-drop-table --routines --events
 --all-databases --force > data-for-downgrade.sql

  1. 关闭 MySQL 服务器,如下所示:
 mysqladmin -u root -p shutdown
  1. 将新的data目录初始化为旧的 MySQL 版本,使用以下代码:
 mysqld --initialize --user=mysql
  1. 使用新的data目录启动旧版 MySQL,使用以下代码:
 mysqld_safe --user=mysql --datadir=/path/to/new-datadir
  1. 将转储加载到旧的 MySQL 服务器中,如下所示:
 mysql -u root -p --force < data-for-upgrade.sql
  1. 执行mysql_upgrade
 mysql_upgrade -u root -p
  1. 重新启动服务器以应用所有更改,使用以下代码:
 mysqladmin -u root -p shutdown
 mysqld_safe --user=mysql --datadir=/path/to/new-datadir

降级前需要手动更改

本节描述了在降级之前用户需要手动执行的一些更改:

  • 系统表更改:MySQL 5.7 为系统表管理单独的表空间,而在 MySQL 8 中,系统表迁移到一个名为mysql.ibd的单个表空间文件中。因此,在降级到 MySQL 5.7 之前,使用以下命令将系统表移回单独的表空间文件:
 ALTER TABLE mysql.columns_priv TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.component TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.db TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.default_roles TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.engine_cost TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.func TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.general_log TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.global_grants TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.gtid_executed TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.help_category TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.help_keyword TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.help_relation TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.help_topic TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.innodb_index_stats TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.innodb_table_stats TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.plugin TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.procs_priv TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.proxies_priv TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.role_edges TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.server_cost TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.servers TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.slave_master_info TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.slave_relay_log_info TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.slave_worker_info TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.slow_log TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.tables_priv TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.time_zone TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.time_zone_leap_second TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.time_zone_name TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.time_zone_transition TABLESPACE=innodb_file_per_table; 
        ALTER TABLE mysql.time_zone_transition_type TABLESPACE=innodb_file_per_table;          
        ALTER TABLE mysql.user TABLESPACE=innodb_file_per_table;

在 MySQL 8.0.2 中,六个系统表的存储引擎从 MyISAM 更改为 InnoDB。它们的名称分别是columns_privdbprocs_privtables_privuser。因此,在降级之前,通过执行以下命令更改这些表的存储引擎。对于其余的表也应用相同的命令:

 ALTER TABLE mysql.columns_priv ENGINE='MyISAM' 
          STATS_PERSISTENT=DEFAULT

在 MySQL 8.0.2 中,通过添加两个表来更改了mysql.usertable,因此,在降级到 MySQL 5.7 之前,从表中删除这些列:

 ALTER TABLE mysql.user drop Create_role_priv;
 ALTER TABLE mysql.user drop Drop_role_priv;
  • InnoDB 更改:在进行原地降级之前,使用innodb_fast_shutdown选项关闭 MySQL。使用innodb_fast_shutdown=0关闭服务器。建议在进行原地降级时删除重做日志。

摘要

选择适当的软件及其版本进行开发是一个重要的阶段,对吧?在本章中,我们了解了如何通过了解其版本模式来选择 MySQL 8 的适当版本。我们还学习了在 Microsoft Windows 中使用安装程序和命令行安装 MySQL 8 的执行步骤。对于 Linux 平台,我们使用 Yum 存储库、RPM 包和 Debian 包安装了 MySQL 8。安装后描述了开始使用 MySQL 8 的基本配置。最后,我们解释了如何通过执行步骤从 MySQL 8 升级和降级。

在下一章中,我们将学习有关 MySQL 8 可用的各种程序和实用程序。主要关注如何在 MySQL 8 中使用这些程序以及命令行执行。

第三章:MySQL 8-使用程序和实用程序

在上一章中,我们安装了 MySQL 8,并了解了安装 MySQL 8 的替代方法。我们还学习了如何迁移和升级到 MySQL 8。以下是上一章中解释的摘要主题:

  • MySQL 8 安装

  • 安装后设置

  • MySQL 8 升级

  • MySQL 8 降级

在本章中,读者将了解 MySQL 8 中可用的各种程序和实用程序。读者还将了解如何在 MySQL 8 中使用程序和实用程序。读者将学习在 MySQL 8 中使用命令行程序的方法。读者将学习程序的语法以及它们如何使用特定选项来执行特定操作。以下是本章涵盖的主题摘要。

  • MySQL 8 程序概述

  • MySQL 8 命令行程序

  • MySQL 8 客户端程序

  • MySQL 8 管理程序

  • MySQL 8 环境变量

  • MySQL GUI 工具

MySQL 8 程序概述

MySQL 安装中有各种不同的程序。本节将简要概述这些程序。接下来的章节将详细描述每个程序,并且描述将有自己的调用语法和选项来执行操作。

大多数 MySQL 发行版都将拥有所有这些程序,除了特定于平台的程序;例如,在 Windows 中不使用服务器启动脚本。RPM(Red-hat 软件包管理器)发行版非常专业化,并且是分发中所有程序的例外。RPM 发行版有何专业之处?嗯,它们为不同的操作有不同的程序;例如,一个程序将用于服务器,第二个程序将用于客户端,依此类推。如果您的安装中似乎缺少一个或多个程序,那就不用担心。请参阅第二章,安装和升级 MySQL 8,了解可用的分发类型以及其中包含的内容。也许您的分发不包括所有程序,您需要安装额外的软件包。

每个 MySQL 8 程序都有自己的选项,但大多数程序都有一个--help选项,可用于检索有关程序的所有选项的描述。例如,在命令行上尝试mysql --help(即您的 shell 或命令提示符)。

第一行的描述将包含有关已安装的 MySQL 的特定版本信息,以及操作系统和许可信息。下一行将以Usage: mysql [OPTIONS] [database]开头,即程序命令用法的语法,后面的行描述了可用的选项,以便根据使用说明使用它们。这只是我们将要查看的内容的一瞥:带有选项的程序详细信息,它们的用法和默认选项,以及在各种命令行程序、客户端程序、管理程序等中覆盖默认选项值。

有关在命令行中执行程序和指定程序选项的详细信息,请参阅MySQL 8 命令行程序部分随后将列出安装、客户端和服务器启动以及其他实用程序。

简要介绍 MySQL 程序

让我们先从 MySQL 服务器程序开始!

mysqld是第一个程序,也被认为是 MySQL 安装的主要程序。它与几个脚本一起工作,帮助启动和停止服务器。以下是根据其操作范围划分的程序:

  • 启动程序

  • 安装/升级程序

  • 客户端程序

  • 管理和实用程序

启动程序

启动程序是在 MySQL 启动期间使用的程序,并根据配置启动所需的后台服务。

  • mysqld:这是 MySQL 服务器守护程序。所有其他客户端程序都使用这个服务器程序与数据库交互。除了维护之外,它必须始终处于启动和运行状态。

  • mysqld_safe:这是服务器启动程序脚本之一,尝试启动mysqld程序。

  • mysql.server:另一个服务器启动程序脚本,用于那些使用包含脚本的 V-style 运行目录的系统。它在特定运行级别启动系统服务。它调用mysqld_safe来启动 MySQL 服务器。

  • mysqld_multi:顾名思义,这是一个启动程序脚本,用于在系统上启动或停止多个 MySQL 服务器。

安装/升级程序

以下是安装和升级操作相关的程序及其各自的用法:

  • comp_err:这个程序用于从错误源文件编译错误消息文件,并在 MySQL 构建或安装操作期间使用。

  • mysql_secure_installation:这个程序用于更新安装 MySQL 时的安全配置,以启用安全性。

  • mysql_ssl_rsa_setup:顾名思义,这个程序用于生成SSL证书和密钥文件以及RSA 密钥对文件,如果这些文件丢失并且需要支持安全连接。

  • mysql_tzinfo_to_sql:这个程序获取主机系统时区信息数据库的内容(描述时区的文件),并将信息加载到 MySQL 的时区表中。

  • mysql_upgrade:顾名思义,用于升级操作。它检查是否存在任何不兼容性,并在必要时进行修复。它还根据 MySQL 新版本中的任何更改更新授权表。

客户端程序

客户端程序是常用于连接到 MySQL 数据库并执行不同查询操作的程序之一:

  • mysql:这是最常用的程序。它是一个用于直接执行 SQL 语句或使用批处理模式中的文件的交互式命令行工具。详细信息在下一个MySQL 8 命令行程序部分中介绍。

  • mysqladmin:这个程序负责执行各种管理操作,如创建或删除数据库、刷新表、重新加载授权表、重新打开日志文件等。该程序还用于从服务器检索信息,如版本、进程和状态。

  • mysqlcheck:这是用于维护表、执行分析、检查、修复和优化表的客户端程序。

  • mysqldump:这是客户端程序,将 MySQL 数据库转储到文本、SQL 或 XML 格式的文件中。它通常被称为数据库备份程序。

  • mysqlimport:这是客户端程序,使用LOAD_DATA_INFILE将文本文件导入到相应的表中。它也通常被称为数据导入程序。

  • mysqlpump:将 MySQL 数据库转储到 SQL 文件的客户端程序。

  • mysqlshow:显示数据库、表、列和索引信息的客户端。

  • mysqlslap:这是用于检查 MySQL 服务器客户端负载能力的客户端程序。该程序模拟多个客户端访问服务器。

管理和实用程序

以下是执行各种管理活动的程序。它们与一些辅助程序一起描述,这些辅助程序有助于管理操作:

  • innochecksum:用于InnoDB离线文件校验和的程序。

  • myisam_ftdump:提供MyISAM表中全文索引信息的实用程序。

  • myisamchk:用于检查、描述、修复和优化MyISAM表的程序。

  • myisamlog:处理MyISAM日志文件内容的实用程序。

  • myisampack:通过压缩生成更小的只读MyISAM表的实用程序。

  • mysql_config_editor:可以在加密和安全的登录路径文件mylogin.cnf中启用身份验证凭据存储的实用程序。

  • mysqlbinlog:可以读取二进制日志文件语句的实用程序。在服务器崩溃的情况下,执行二进制日志文件语句可以提供很大的帮助。

  • mysqldumpslow:可以读取和总结慢查询日志内容的实用程序。

环境变量

使用以下环境变量的 MySQL 客户端程序与 MySQL 服务器进行通信:

  • MYSQL_UNIX_PORT:此变量负责提供用于连接到本地主机的默认 Unix 套接字文件

  • MYSQL_TCP_PORT:此变量负责提供默认端口号,并在 TCP/IP 连接中使用

  • MYSQL_PWD:此变量负责提供默认密码。

  • MYSQL_DEBUG:此变量负责在调试操作期间提供调试跟踪选项

  • TMPDIR:此变量负责提供临时文件和表将被创建的目录

有关程序中环境变量的详细列表和用途,请参见MySQL 8 环境变量部分。使用MYSQL_PWD是不安全的。

MySQL GUI 工具

由 Oracle 公司提供的 MySQL Workbench GUI 工具用于管理 MySQL 服务器和数据库,用于创建,执行和评估查询。它还用于从其他关系数据库管理系统迁移模式和数据以供与 MySQL 一起使用。还有其他 GUI 工具,包括 MySQL Notifier,MySQL for Excel,phpMyAdmin 等。

MySQL 8 命令行程序

在上一节中,我们介绍了 MySQL 8 提供的各种类型的程序,并简要概述了它们的用法。

在本节中,我们将查看命令行程序,并学习如何从命令行执行程序。我们将详细了解选项的提供方式以及它们如何用于管理。

从命令行执行程序

从命令行(shell 或命令提示符)执行程序是 MySQL 中最常用的管理形式之一。许多程序已经添加了管理选项。

执行 MySQL 程序

要执行 MySQL 程序,请输入程序名称,然后输入选项或其他参数,以告诉程序您希望它执行什么操作。以下是一些示例执行命令。这里shell>表示命令解释器。典型的提示符将是c:>\(对于使用command.comcmd.exe作为命令解释器的 Windows 机器),$(对于使用shkshbash作为命令解释器的 Unix 机器),以及(对于使用cshtcsh作为命令解释器的 Mac 机器)。

shell> mysql --verbose --help
shell> mysql --user=root --password=******** mysampledb
shell> mysqldump -u root personnel
shell> mysqlshow --help 

有非选项参数,即没有任何前导破折号的参数,为程序提供补充信息。例如,如果您看到前面示例的第二行,它有一个带有数据库名称mysampledb的第三个非选项参数,因此命令mysql --user=root --password=******** mysampledb告诉mysql程序您要使用mysampledb作为数据库名称。

以单破折号或双破折号(-,--)开头的参数用于指定程序选项。指定程序选项表示程序将连接到服务器的连接类型或将影响操作模式。选项的语法将详细解释,参见为程序指定选项部分。

连接到 MySQL 服务器

在本节中,我们将解释如何建立与 MySQL 服务器的连接。我们将使用客户端程序连接到 MySQL 服务器。要连接到服务器程序,我们需要一些信息来指定 MySQL 帐户的“主机名”、“用户名”和“密码”,因为我们需要告诉客户端程序服务器运行在哪个主机上以及相关的用户名和密码。尽管这些选项都有默认选项值,但在必要时可以覆盖选项值。例如,考虑使用常见的客户端程序mysql:

shell> mysql

在前面的程序中,未指定任何选项,但将自动应用以下默认值:

  • 主机名默认值为localhost

  • 用户名默认值根据登录名(在 Windows 中为 ODBC 名称或在 Unix 中为登录名)

  • 如果未与程序一起指定-p--password选项,则不会向程序发送任何选项值

  • 第一个非选项参数被视为mysql程序的默认数据库名称,如果未指定此类选项,则mysql不会选择任何默认数据库

应用于客户端程序mysql的原则也适用于其他客户端程序,如mysqldumpmysqladminmysqlshow。现在让我们看一个具有特定选项值参数的示例客户端程序连接:

shell> mysql --host=localhost --user=root --password=mypwd mysampledb

如您在前面的示例中看到的特定选项值,主机应被视为localhost,用户值提供为myname。密码也被指定,最后,指定了一个非选项参数,告诉程序使用mysampledb作为默认数据库名称。

为程序指定选项

在前面的部分中,我们已经看到程序选项如何根据客户端程序中指定的参数值改变操作模式。在这里,我们将看几种指定 MySQL 程序选项的方法。这些包括:

  • 在程序名称后面提供命令行选项。这是提供选项的常见方式,但它仅适用于该时间执行程序的执行。

  • 在程序开始执行之前读取的选项文件中提供选项。这是提供您希望程序每次执行时使用的选项的常见方式。

  • 在环境变量中提供选项。通过使用这种方法,您还可以指定每次执行程序时要应用的选项。在一般实践中,使用选项文件通常用于此目的,但在某些情况下,在环境变量中指定选项值也非常有用;例如,在 Unix 系统上运行多个 MySQL 实例时。

MySQL 程序通过检查相关环境变量来确定首先给出哪些选项,然后处理选项文件,最后考虑命令行中的选项参数。因此,命令行选项具有最高优先级,环境变量具有最低优先级。但是,有一个例外适用,即数据目录中处理的mysqld-auto.cnf选项文件,因此它比命令行选项具有更高的优先级。

命令行选项

使用命令行程序选项时,请遵循以下规则:

  • 选项后跟程序名称。

  • 选项参数以单破折号或双破折号开头,具体取决于它们使用选项名称的简写形式还是长形式。

  • 选项名称区分大小写。例如,-V-v都是有效的,因为它们分别是--verbose--version的简写形式,因此对程序表示不同的含义。

  • 选项也可以带有后面跟着选项名称的值。例如,-h localhost--host=localhost告诉客户端程序将 localhost 作为主机名。

  • 使用短选项,值可以紧跟在选项字母后面,或者两者之间也可以用单个空格。唯一的例外是指定 MySQL 密码选项时。

  • 使用长选项,值和名称可以用=号分隔。

  • 在选项名称中可以互换使用(-)和(_),例如--skip-grant-table--skip_grant_table.。两者都是有效的,并且工作方式相同,但下划线不能用来代替前导破折号。

  • 带有数字值的选项值可以使用 K、M 或 G 的后缀表示 1,024 的倍数,大小写不限。考虑以下示例,命令告诉mysqladmin程序对服务器进行 1,024 次 ping,并且每次 ping 后休眠10秒:

 shell> mysqladmin --count=1k --sleep=10 ping
  • 对于文件名选项值,避免使用~元字符,因为它不会按预期解释。

  • 包含空格的选项值在命令行指定值时必须用引号括起来。

修改程序选项

一些选项是boolean类型的,控制可以打开或关闭的行为。让我们考虑一个例子,mysql程序。它支持--column-names选项,用于控制在查询结果的第一行中显示列名。为了禁用列名,以下规范将适用于我们:

--disable-column-names
--skip-column-names
--column-names=0

如前面的例子所示,=0 后缀--skip--disable 前缀 有相同的效果。当使用=1后缀和--enable前缀时也适用。

如果使用--loose 前缀指定选项,并且指定的选项不存在,程序将发出警告而不是退出。

对于某些程序,--maximum 前缀可用于与选项名称一起指定限制。它也可以与环境变量一起使用。

使用文件修改选项

大多数 MySQL 程序可以从选项文件中读取启动选项,有时也称为配置文件。这是一种非常方便的方式,可以提供常用的选项,一旦指定,就无需每次执行程序时都指定它们。要检查程序是否读取选项文件,请使用--help选项。例如,考虑mysqld程序,如果它读取选项文件,应该使用--verbose--help。帮助消息将指示它查找哪个选项文件以及哪个选项组:

使用--no-defualts选项的 MySQL 程序除了.mylogin.cnf之外不会读取任何选项文件。如果服务器程序启动时禁用了persisted_globals_load系统变量,则程序不会读取mysqld-auto.cnf文件。

大多数选项文件都是纯文本文件,可以由任何文本编辑器创建和编辑。这些文件中的例外包括.mylogin.cnf,它具有登录路径选项,由mysql_config_editor实用程序加密。

MySQL 按特定顺序检查 Windows 和 Unix 系统的选项文件,并遵循从读取全局选项开始的优先顺序,例如在 Windows 系统中:

  • %PROGRAMDATA%\MySQL\MySQL Server 8.0\my.ini 和 %PROGRAMDATA%\MySQL\MySQL Server 8.0\my.cnf

  • %WINDIR%\my.ini 和 %WINDIR%\my.cnf

  • C:\my.ini 和 C:\my.cnf

  • BASEDIR\my.ini 和 BASEDIR\my.cnf

  • --defaults-extra-file 指定的文件(如果有的话)

  • %APPDATA%\MySQL\.mylogin.cnf中的登录路径选项(仅限客户端程序)

  • 对于使用SET_PERSISTPERSIST_ONLY(如果是服务器程序)持久化的系统变量在DATADIR\mysql-auto.cnf

类似地,在 Unix 系统中,它遵循以下读取选项文件的优先顺序:

  • /etc/my.cnf

  • /etc/mysql/my.cnf

  • SYSCONFDIR/my.cnf

  • $MYSQL_HOME/my.cnf(仅限服务器程序)

  • 使用--defaults-extra-file指定的文件(如果有的话)

  • ~/.my.cnf用于特定用户的选项

  • ~/.mylogin.cnf用于特定用户的登录路径选项(仅限客户端程序)

  • 对于使用SET_PERSISTPERSIST_ONLY(如果是服务器程序)在DATADIR\mysql-auto.cnf中持久化的系统变量

在前面的选项中,~指的是当前用户的主目录。

选项文件中的空行将被忽略,以及注释。可以使用#;字符指定注释,#也可以在任何行的中间开始。

group

group是要设置选项的程序或组的名称。它们不区分大小写。一旦将组行添加到选项文件中,所有后续行都适用于命名组,直到指定另一个组行或选项文件结束。

opt_name

这类似于命令行中的--opt_name,打开了命名优化。

opt_name=value

这类似于命令行中的--opt_name,但是在值的位置上,您可以指定带有空格的值,而在命令行中则不行。

包含指令

可以在选项文件中使用!include指令包含另一个选项文件,使用!includedir搜索特定目录以检查选项文件。例如,!include /home/dev/myopt.cnf!includedir /home/dev,用于目录。MySQL 不考虑的唯一一件事是在目录搜索期间的任何顺序。

在 Windows 系统上,要在!includedir指令中使用的任何选项文件必须以.ini.cnf扩展名结尾,在 Unix 系统中,它们必须以.cnf结尾。

影响选项文件处理的命令行选项

大多数 MySQL 程序支持选项文件。由于它们会影响选项文件的处理,因此必须在命令行中给出,而不是作为选项文件的一部分。

为了使它们正常工作,必须在其他选项之前给出。以下是一些例外情况:--print-defaults可能会在--login-path--defaults-filedefaults-extra-file之后立即使用。

--no-defaults--print-defaults也用于修改选项文件处理。

使用选项设置程序变量

许多 MySQL 程序具有我们可以在运行时操作中使用SET语句设置的内部变量,也可以使用我们指定选项值的相同语法。这将在程序启动时起作用。例如,如果我们使用选项值语法,那么我们必须这样指定:shell> mysql --max_allowed_packet=16M。要使用SET指定运行时选项,我们可以这样指定:mysql> SET GLOBAL max_allowed_packet=16*1024*1024

要查看选项变量语法是否正确,可以转到mysql并使用以下内容:mysql> show variables like 'max%'

设置环境变量

在命令提示符中,我们可以设置环境变量,这将影响程序的运行时执行,或者可以设置为永久影响未来的执行。它们可以在启动文件中设置,也可以使用系统提供的界面进行设置。可以在MySQL 8 环境变量部分详细列出可以影响 MySQL 程序的环境变量。

要指定环境变量的值,语法将基于底层命令解释器。对于 Windows 系统,可以使用以下语法设置用户变量:

SET  USER=your_user_name

对于 Unix 系统,这取决于 shell,因此如果使用shkshzshbash,则需要使用以下语法:

MYSQL_TCP_PORT=3306 
export MYSQL_TCP_PORT

如果使用cshtcsh,请使用setenv,这将使 shell 变量可用于执行环境:

setenv MYSQL_TCP_PORT 3306

执行的设置环境变量的命令将立即影响正在执行的程序,但如果要使环境变量持久存在,您需要在系统提供的界面上指定它,或者可以在命令处理器在启动时使用的启动文件中设置它。在 Windows 上,可以从控制面板选项设置环境变量,在 Unix 上,可以根据您使用的命令行处理器进行设置。对于bash,您需要将值放在.bashrc.bash_profile中,对于tcsh,请使用.tcshrc

服务器和服务器启动程序

MySQL 提供了特定的程序,您需要首先执行这些程序才能使 MySQL 正常工作。在接下来的部分中,我们将看看可以根据您的需求使用多个选项的服务器程序和相关启动程序。

mysqld - MySQL 服务器程序

MySQL 服务器是一个守护程序。所有其他程序都通过该服务器连接到数据库,因此它应该始终运行。守护程序通常从名为mysqld_safe的脚本启动。需要该程序脚本,因为它设置适当的环境变量并使用所需的参数-option值执行mysqld程序。

选项

以下是详细介绍各种命令行可用选项的选项:

  • -?,-I,--help:显示程序的使用信息。

  • -# debuglevel,--debug=debuglevel:设置指定的调试级别。

  • -b directory,--basedir=directory:指定用于确定所有其他相关目录的基本目录。

  • --big-tables:用于允许大型结果集。它们被保存为临时结果文件。

  • --bind-address=ip-number:指定服务器将绑定到的 IP 地址。

  • -h directory,--datadir=directory:指定存储数据库数据文件的目录。

  • -l [logfile],--log[=logfile]:添加各种日志信息,包括连接和错误信息。如果未提供参数,则使用hostname.log作为日志文件,这里的hostname是服务器机器的名称。

  • --log-isam[=logfile]:在日志中添加对数据(ISAM)文件的更改。如果未提供参数,则使用isam.log作为日志文件,此选项生成的日志只能使用theisamlog实用程序读取和操作。

  • --log-update[=number]:记录数据库更新信息。日志文件被命名为hostname.num,其中hostname是服务器机器的名称,num是选项的参数,如果未指定参数,则生成唯一数字。

  • -L=language,--language=language:指定服务器的语言(英语、法语、德语等)。

  • -n,--new:启用新例程(可能是不安全的例程)。

  • -S,--skip-new:禁用/启用新例程(可能是不安全的例程)。

  • -O variable=value,--set-variable variable=value:指定并设置变量的值

  • --pid-file=file:获取运行服务器的进程 IDPID)的文件名。文件的默认值是hostname.pid,其中hostname是服务器机器的名称。

  • -P port,--port=port:指定网络端口号。

  • --secure:启用网络安全检查。但这会降低数据库性能。

  • --skip-name-resolve:指定仅使用 IP 号码(而不是名称)进行连接。这会增加网络性能。

  • --skip-networking:禁用网络连接,仅允许本地访问。

  • --skip-thread-priority:为所有线程提供相同的优先级。

  • -Sg:禁用访问检查。这允许所有用户对所有数据库拥有完全访问权限。

  • -Sl:指定不执行线程锁定。

  • --use-locking:启用线程锁定。

  • --socket=file:指定 Unix 套接字的文件名。

  • -T--exit-info:用于在关闭服务器时显示调试信息。

  • -v-V--version:显示服务器的版本信息。

mysqld_safe - MySQL 服务器启动脚本

这是在基于 Unix 的系统中启动 MySQL 服务器的最推荐方式,因为它增加了一些安全功能,例如在运行时将信息记录到错误日志中,如果出现错误,则重新启动服务器。

在某些 Unix 平台上,从 RPM 或 Debian 软件包安装的 MySQL 包括systemd支持来管理 MySQL 的启动和关闭操作,因此在这些系统上不安装mysqld_safe

mysqld_safe尝试执行mysqld并覆盖默认行为以指定您想要执行的服务器的名称。还可以使用--ledir指定目录的选项,以便mysqld_safe将在该目录中查找服务器。mysqld_safe中的大多数选项也在mysqld中可用,如果指定的选项对mysqld_safe是未知的,则会传递给mysqldmysqld_safe从选项文件中读取mysqld、服务器和mysqld_safe部分中的所有选项。为了向后兼容,mysqld_safe还读取safe_mysqld部分,但是您应该将这样的部分重命名为当前的mysqld_safe

如前所述,mysqldmysqld_safe中都指定了非常常见的选项,因此以下选项列表中排除了一些选项:

--core-file-size=size

指定mysqld应该创建的核心文件的大小。

--ledir=dir_name

如果mysqld无法找到服务器,则使用此选项指定服务器所在的目录的路径名。此选项仅可在命令行上使用,而不可在选项文件中使用。在使用systemd的平台上,该值应在MYSQLD_OPTS的值中给出。

--mysqld-safe-log-timestamps

此选项是指定mysqld_safe生成的日志输出中的timestamps格式。

--mysqld=prog_name

指定包含您想要启动的ledir目录中的服务器程序名称。如果mysqld_safe找不到服务器,请使用--ledir选项指定服务器所在的pathname目录。此选项仅在命令行上接受,而不是从选项文件中接受。

--open-files-limit=count

mysqld可以打开的文件数。

--plugin-dir=dir_name

指定插件目录的路径和名称。

--timezone=timezone

这个选项是将timezone环境变量设置为给定的选项值,具体取决于操作系统的时区规范格式。

--user={ username | user_id }

以您拥有用户名的名称运行mysqld服务器。指定user_name或指定数字user ID作为user_id

mysql.server - MySQL 服务器启动脚本

这是在 Unix/类 Unix 系统的 MySQL 发行版上使用的服务器启动脚本。它使用mysqld_safe来启动 MySQL 服务器程序。该程序还用于包含脚本的V-style运行目录的系统。它在特定运行级别启动系统服务。

basedir=dir_name

指定 MySQL 安装目录的路径。

datadir=data_dir

指定 MySQL 数据目录的路径。

pid-file=file_name

指定服务器写入其进程 ID 的路径名和文件名。

service-startup-timeout=seconds

指定等待服务器启动确认的秒数。如果服务器在此时间内未启动,mysql.server将以错误指示退出。该选项的默认值为 900 秒,值为 0 表示根本不等待启动,提供负值表示永远等待(不应该有超时)。

mysqld_multi - 管理多个 MySQL 服务器

mysqld_multi用于管理在不同 Unix 套接字文件和 TCP/IP 端口上监听连接的多个mysqld进程。它还可以启动或停止服务器并报告其当前状态。

mysqld_multimy.cnf或提供的文件中搜索名为mysqldN的组,作为--default-file选项。这里 N 可以是任何正数。这个数字被称为选项组号,即GNR。组号将选项组彼此分开,并且在mysqld_multi的参数中用于指定要启动、停止或请求状态的服务器。此组中的选项与用于启动mysqld[mysqld]组中使用的选项相同。

要执行mysqld_multi,使用以下语法:

shell> mysqld_multi [options] {start|stop|reload|report} [GNR[,GNR] ...]

在上述语法中,start、stop、reload(停止和重新启动)和 report 指的是要执行的操作。根据指定的GNR列表值,您可以对单个或多个服务器执行有针对性的操作。

请确保所有服务器的数据目录对启动特定mysqld进程的 Unix 帐户完全可访问。除非您确切知道要做什么,否则不要使用 root 帐户。

安装程序

本节讨论的程序在安装过程中或升级 MySQL 时使用,因此在对程序进行任何修改之前,请确保您正确理解它。

comp_err - 编译 MySQL 错误消息文件

这将创建errmsg.sys文件,mysqld用于识别错误消息并显示单独的错误代码。当构建 MySQL 时,通常会自动运行comp_errerrmsg.sys文件是从 MySQL 发行版中的文本文件sql/share/errmsg-utf8.txt编译而成。它还生成sql_state.hmysqld_ername.hmysqld_error.h头文件。

comp_err有几个选项,并且可以在前一个命令中使用--help选项检索。

mysql_secure_installation - 改进 MySQL 安装安全性

该程序有办法启用和改进 MySQL 安装的安全性,包括:

  • root帐户设置密码。

  • 删除可以在localhost之外访问的root帐户。

  • 删除匿名帐户。

  • 删除允许任何人访问以test_开头的数据库的测试数据库权限。

  • 如果您希望在本地 MySQL 服务器上进行正常使用,请执行mysql_secure_installation命令而不带任何参数。它将要求您进一步检查要执行的操作。

  • validate_password插件可用于加强密码检查。如果插件尚未安装,它将要求您安装,一旦安装并启用,它可以验证密码。

使用以下语法执行mysql_secure_installation

shell> mysql_secure_installation [options]

您可以在这里使用--help选项,并在需要时检索其他选项的列表。

mysql_ssl_rsa_setup - 创建 SSL/RSA 文件

正如其名称所示,此程序用于生成 SSL 证书和密钥文件以及 RSA 密钥对文件,如果这些文件丢失并且需要文件来支持安全连接。如果现有文件已过期,mysql_ssl_rsa_setup也可以用于创建新文件。

mysqlssl_rsa_setup使用Openssl命令,因此在使用它时,有必要在您的机器上安装OpenSSL。为了自动生成这些文件,服务器可以使用使用OpenSSL编译的 MySQL 发行版,这是生成SSLRSA文件的另一种方式。

执行mysql_ssl_rsa_setup,如下所示:

shell> mysql_ssl_rsa_setup [options]

在这里使用--help选项,并在需要时检索其他选项的列表。

使用ssl``mysql_ssl_rsa_setup降低了ssl的障碍,并使生成所需文件变得更容易,但生成的文件是自签名的,这并不是非常安全的。您可以考虑从相应的机构获取 CA 证书。

mysql_tzinfo_to_sql - 加载时区表

该程序获取hostsystem区域信息数据库(描述时区的文件)的内容,并将信息加载到 MySQL 的时区表中。如果系统没有区域信息数据库,您可以从dev.mysql.com/downloads/timezones.html下载包含POSIX标准timezone_2017c_posix_sql.zip和非POSIX标准timezone_2017c_leaps_sql.zip的可下载包。

mysql_tzinfo_to_sql可以以以下不同的方式执行:

shell> mysql_tzinfo_to_sql tz_dir
shell> mysql_tzinfo_to_sql tz_file tz_name
shell> mysql_tzinfo_to_sql --leap tz_file

运行mysql_tzinfo_to_sql程序后,强烈建议重新启动服务器,以便它不使用任何先前缓存的时区数据。

mysql_upgrade - 检查和升级 MySQL 表

顾名思义,这用于升级操作。它检查任何不兼容性,如果有必要进行修复,并且还会更新 MySQL 新版本中的授权表中的任何更改。它还更新系统表,以便您可以利用在较新版本中添加的任何新特权或兼容性。

在执行升级之前,始终备份当前的 MySQL 安装。

如果mysql_upgrade发现表可能存在不兼容性,它将执行表检查并尝试修复表,如果无法修复,它将要求手动修复表。

每次升级 MySQL 时都应执行mysql_upgrade。它直接与 MySQL 服务器通信,并发送所需的 SQL 语句以执行升级。

运行mysql_upgrade后,我们应该重新启动服务器。如果对系统表进行了任何更改,需要在运行之前确保服务器正在运行。

使用以下语法执行mysql_upgrade

shell> mysql_upgrade [options]

您可以在此处使用--help选项,并在需要时检索其他选项的列表。

MySQL 8 客户端程序

MySQL 8 客户端程序是通常用于连接到 MySQL 数据库并执行不同查询操作的程序。

以下子节中详细介绍了程序信息,包括mysql - 带有许多命令和相关选项和配置的命令行工具,用于loggingmysqlcheckmysqldumpmysqlimportmysqlshmysqladmin等等。

mysql - 命令行工具

这是最常用的程序。命令行工具用于直接执行 SQL 语句或以批处理模式使用文件。它支持交互和非交互模式。在本节中,我们将查看mysql命令行以及各种选项,命令,日志记录和其他相关程序。

mysql 选项

mysql是一个长期提供的命令行工具,因此有很多选项可以完成您的工作。以下是带有格式和描述的选项表:

格式 描述
--auto-rehash 启用自动重新散列
--auto-vertical-output 启用自动垂直结果集显示
--batch 不使用历史文件
--binary-as-hex 以十六进制表示法显示二进制值
--binary-mode 禁用 \r\n - 到 - \n 的转换和将 \0 视为查询结束
--bind-address 使用指定的网络接口连接到 MySQL 服务器
--character-sets-dir 安装字符集的目录
--column-names 在结果中写入列名
--column-type-info 显示结果集元数据
--comments 确定是否保留或剥离发送到服务器的语句中的注释
--compress 压缩客户端和服务器之间发送的所有信息
--connect-expired-password 指示服务器客户端可以处理过期密码沙箱模式
--connect_timeout 连接超时前的秒数
--database 要使用的数据库
--debug 写入调试日志;仅在 MySQL 构建时支持调试
--debug-check 在程序退出时打印调试信息
--debug-info 在程序退出时打印调试信息、内存和 CPU 统计信息
--default-auth 要使用的身份验证插件
--default-character-set 指定默认字符集
--defaults-extra-file 除了通常的选项文件之外,读取命名的选项文件
--defaults-file 仅读取命名的选项文件
--defaults-group-suffix 选项组后缀值
--delimiter 设置语句分隔符
--enable-cleartext-plugin 启用明文身份验证插件
--execute 执行语句并退出
--force 即使发生 SQL 错误也继续
--get-server-public-key 包含 RSA 公钥的文件路径名
--help 显示帮助消息并退出
--histignore 指定要忽略记录的语句模式
--host 连接到给定主机上的 MySQL 服务器
--html 生成 HTML 输出
--ignore-spaces 忽略函数名后的空格
--init-command 连接后要执行的 SQL 语句
--line-numbers 为错误写入行号
--local-infile 启用或禁用LOAD DATA INFILELOCAL功能
--login-path 从.mylogin.cnf 中读取登录路径选项
--max_allowed_packet 发送到服务器或从服务器接收的最大数据包长度
--max_join_size 使用--safe-updates时连接中的行的自动限制
--named-commands 启用命名的mysql命令
--net_buffer_length TCP/IP 和套接字通信的缓冲区大小
--no-auto-rehash 禁用自动重新哈希
--no-beep 发生错误时不发出哔声
--no-defaults 不读取任何选项文件
--one-database 忽略除了命令行上指定的默认数据库之外的语句
--pager 用于分页查询输出的给定命令
--password 连接到服务器时使用的密码
--pipe 在 Windows 上,使用命名管道连接到服务器
--plugin-dir 插件安装的目录
--port 用于连接的 TCP/IP 端口号
--print-defaults 打印默认选项
--prompt 将提示设置为指定的格式
--protocol 要使用的连接协议
--quick 不缓存每个查询结果
--raw 写入列值而不进行转义转换
--reconnect 如果与服务器的连接丢失,自动尝试重新连接
--i-am-a-dummy, --safe-updates 仅允许指定键值的UPDATEDELETE语句
--secure-auth 不以旧(4.1 之前)格式将密码发送到服务器
--select_limit 使用--safe-updatesSELECT语句的自动限制
--server-public-key-path 包含 RSA 公钥的文件路径名
--shared-memory-base-name 用于共享内存连接的共享内存名称
--show-warnings 如果有的话,在每个语句后显示警告
--sigint-ignore 忽略SIGINT信号(通常是键入Control+C的结果)
--silent 静默模式
--skip-auto-rehash 禁用自动重新哈希
--skip-column-names 在结果中不写入列名
--skip-line-numbers 跳过错误的行号
--skip-named-commands 禁用命名的 mysql 命令
--skip-pager 禁用分页
--skip-reconnect 禁用重新连接
--socket 对于本地主机的连接,要使用的 Unix 套接字文件或 Windows 命名管道
--ssl-ca 包含受信任的 SSL CA 列表的文件路径
--ssl-capath 包含 PEM 格式的受信任 SSL CA 证书的目录路径
--ssl-cert 包含 PEM 格式 X509 证书的文件路径
--ssl-cipher 用于连接加密的允许密码列表
--ssl-crl 包含证书吊销列表的文件路径
--ssl-crlpath 包含证书吊销列表文件的目录路径
--ssl-key 包含 PEM 格式 X509 密钥的文件路径
--ssl-mode 与服务器连接的安全状态
--syslog 将交互式语句记录到 syslog
--table 以表格格式显示输出
--tee 将输出的副本附加到指定文件
--tls-version 加密连接允许的协议
--unbuffered 在每次查询后刷新缓冲区
--user 连接到服务器时要使用的 MySQL 用户名
--verbose 详细模式
--version 显示版本信息并退出
--vertical 垂直打印查询输出行(每个列值一行)
--wait 如果无法建立连接,则等待并重试,而不是中止
--xml 生成 XML 输出

参考:dev.mysql.com/doc/refman/8.0/en/mysql-command-options.html

要获取有关各个选项的更多信息,请将该选项与--help选项一起使用。

mysql 命令

您发出的每个 SQL 语句都将发送到服务器以进行执行。mysql本身也有一系列命令。要获取所有这些命令的列表,请在mysql>提示处键入\h\help

每个命令都有长格式和短格式,可以使用; 除了短格式不能在多行注释中使用。长格式不区分大小写,但短格式命令区分大小写。

help [arg], \h [arg],? [arg], ? [arg]

help arg[]命令用于显示帮助消息,并列出mysql中所有可用的命令。

charset charset_name, \C charset_name

要更改默认字符集,请发出SET_NAMES语句。

clear, \c

要清除命令行中的当前输出或先前的查询结果。

connect [db_name host_name], \r [db_name host_name]

通过提供数据库host_name参数来重新连接服务器。

edit, \e

要编辑当前提供的输入语句。

exit, \q

退出mysql命令行。

prompt [str], \R [str]

要指定一个字符串并使用mysql提示重新配置它。

quit, \q

退出mysql命令行。

status, \s

这用于检查当前正在使用的服务器连接的状态。

use db_name, \u db_name

要指定使用提供的db_name作为默认数据库。

mysql logging

mysql程序可以按以下类型进行记录。

在 Unix 系统上,它将日志写入默认名称为.mysql_history的历史文件中。

在所有平台上,如果提供了--syslog选项,它将将语句写入系统日志实现。在 Unix 上,它是syslog,在 Windows 上,它是事件日志,在 Linux 发行版上,它通常会写入/var/log/message文件。

mysql 服务器端帮助

要从mysql获取服务器端帮助,使用以下语法。

mysql> help search_string

如果在 help 命令之后提供任何参数,mysql将使用该参数来搜索字符串,以从 MySQL 参考手册内容中访问服务器端帮助。如果没有匹配搜索的字符串,搜索操作将失败,并显示如下:

mysql> help me
Nothing found
Please try to run 'help contents' fro a list of all accessible topics

如果search_string匹配主题的多个内容,则显示匹配主题项的列表。还可以将主题用作search_string,并查找主题的条目。它还包含通配符字符_,这些字符对LIKE运算符执行的匹配操作具有相同的含义。

从文本文件执行 sql

mysql客户端通常用于交互式使用,但也允许您从文件中执行 SQL 语句。为此,请创建一个包含需要执行的多个语句的text_file,如下所示:

shell> mysql db_name < text_file

如果将USE db_name保留为text_file的第一条语句,则可以在命令行中省略指定db_name

shell> mysql < text_file

如果已经使用mysql连接,则使用 source 或\.命令:

mysql> source file_name

通过使用--verbose选项,每个语句在产生结果之前都会显示出来。

mysqladmin - 用于管理 MySQL 服务器的客户端

mysqladmin是用于管理操作的客户端。它可以用于检查服务器的配置、连接状态、删除和创建数据库等。

mysqladmin命令的执行语法如下:

shell> mysqladmin [options] command [command-arg] [command [command-org]] ...

mysqladmin支持大量的程序命令,从create db_name创建名为db_name的新数据库,debug获取调试信息,drop db_name删除数据库,flush-xxxx,其中xxxx可以替换为 logs、hosts、privileges、status、tables、threads 等。kill id杀死服务器线程或多个线程,password new_password设置新密码,ping检查服务器的可用性,shutdown停止服务器,start-slave在从服务器上启动复制,stop-slave停止从服务器上的复制,variables显示服务器系统变量及其相应的值。

mysqladmin status命令显示了有关服务器的配置、连接状态、删除和创建数据库等信息。

除了命令列表之外,还有一些在从服务器检索特定信息时非常有用的选项。可以使用mysqladmin --help命令检索此类信息。

mysqlcheck - 表维护程序

该程序用于表维护。它检查、修复、优化或分析表。该程序可能会耗费时间,特别是对于大型表。mysql_upgrade使用mysqlcheck命令来检查和修复所有表。必须运行mysqld服务器才能使用mysqlcheck命令。

mysqlcheck以用户方便的方式使用CHECK TABLEREPAIR TABLEANALYZE TABLEOPTIMIZE TABLE。如果mysqlcheck修复表失败,则需要手动修复表。

以下是mysqlcheck命令的执行语法:

shell> mysqlcheck [options] db_name [tbl_name ...]
shell> mysqlcheck [options] --databases db_name ...
shell> mysqlcheck [options] --all-databases

mysqlcheck具有一个特殊功能,即检查表的默认行为。可以通过重命名二进制文件来更改它,例如通过创建mysqlcheck的副本并为mysqlcheck添加符号链接,将mysqlcheck重命名为mysqlrepair程序,之后mysqlrepair可以修复表。这也适用于mysqlanalyzemysqloptimize选项,使它们成为mysqlcheck命令的默认操作。

与其他管理程序类似,该程序也有许多选项可用于获取特定信息,通过使用mysqlcheck --help命令,可以检索到选项列表。

mysqldump - 数据库备份程序

该程序是一个实用程序,用于通过生成一组 SQL 语句进行逻辑备份,以便重新生成原始数据库表数据和对象定义。它可以备份一个或多个数据库,也可以转移到另一个 SQL 服务器。它还可以以不同格式生成数据输出,如 CSV、XML 或其他分隔文本文件。

性能和可扩展性

不应将其视为备份大量数据的快速或可扩展的解决方案。备份需要一些时间,恢复数据可能非常慢,因为 SQL 语句涉及索引创建、插入的磁盘 I/O 等。它可以逐行检索和转储表数据行;否则,它可以获取整个表并将其缓冲在内存中以进行转储。

以下是mysqldump的执行语法:

shell> mysqldump [options] db_name [tbl_name ...] 
shell> mysqldump [options] --databases db_name ...
shell> mysqldump [options] --all-databases

mysqldump命令有超过 25 个选项可用于修改其操作,可以通过使用mysqldump --help命令来检索。根据您的需求,可以在此命令中使用特定的修改选项,例如调试选项、帮助选项、连接选项、DDL 选项等。

mysqlimport - 数据导入程序

此客户端程序提供了LOAD_DATA_INFILE SQL 语句的CLI接口。大多数选项对应于LOAD_DATA_INFILE语法的子句。

mysqlimport的执行语法如下:

shell> mysqlimport [options] db_name textfile1 [textfile2 ...]

命令的选项可以根据您的需求在 CLI 上或在选项文件的[mysqlimport][client]组中指定。它提供了检索和修改数据导入操作的选项,例如使用不同的分隔符格式、调试、强制文件路径、提供默认值、忽略和锁定表等,可以通过使用mysqlimport --help命令来检索。

mysqlpump - 数据库备份程序

该程序是一个实用程序,用于通过生成一组 SQL 语句来进行逻辑备份,以便执行以重现原始数据库表数据和对象定义。它可以备份一个或多个数据库,也可以转移到另一个 SQL 服务器。

mysqlpump具有以下重要功能:

  • 数据库和数据库对象的并行处理,加快了转储处理速度

  • 更多控制要转储的数据库对象和数据库

  • 将用户帐户数据转储到帐户管理语句,而不是插入数据到系统数据库

  • 创建压缩输出的能力

  • 显示进度指示器

  • 转储文件重新加载;对于InnoDB表,在插入行后添加索引

mysqlpump转储了所有数据库,如下代码中明确指定的:

shell> mysqlpump --all-databases

要转储多个数据库,请指定--databases,然后是要转储的数据库名称。您还可以指定--exclude-databases=,然后是不要转储的数据库名称。有许多选项可用于转储数据库或对象,例如用于指定排除或包含数据库选项的选项,适用于对象(如表、触发器、例程、事件、用户等),如果它们支持多个选项条目:

shell> mysqlpump --include-databases=db1,db2 --exclude-tables=db1.t1,db2.t2

上述命令转储了数据库db1db2,但它将从数据库db1中排除表t1,从数据库db2中排除表t2

mysqlpump使用并行处理来实现并发处理,可以在数据库之间或数据库内进行。--default-parallelism=N指定程序创建的队列中使用的默认线程数,N的默认值为2--parallel-schemas=[N:]db_list根据提供的数据库名称列表设置处理队列。因此,可以控制额外的队列和线程数。

mysqlpump默认不转储performance_schemanbdinfosys,但可以通过指定--include-databases选项来执行,同样它也不转储INFORMATION_SCHEMA

mysqlsh - MySQL Shell

MySQL 的高级命令行客户端和编辑器是非常著名的 MySQL Shell。它具有使用 Python 和 JavaScript 进行脚本编写的功能。当使用 X 协议连接到 MySQL 服务器时,X DevAPI 可以处理文档和关系数据。它包括 AdminAPI,可以与InnoDB集群一起使用。

MySQL Shell 有许多与之相关的选项,但重要的选项如下:

--port=port_num, -P port_num

要使用的 TCP/IP 端口号为port_num。默认端口为33060

--node

使用 X 协议创建到单个服务器的节点会话,在 8.0.3 中已弃用。

--js

启动 JavaScript 模式。

--py

启动 Python 模式。

--sql

启动 SQL 模式。

--sqlc

在 ClassicSession 中启动 SQL 模式。

--sqln

在 NodeSession 中启动 SQL 模式。

--sqlx

通过创建 X 协议连接启动 SQL 模式。

--ssl* 

--ssl 开头的选项指定使用 SSL 连接服务器,并查找证书和 SSL 密钥。它的工作方式与 MySQL 服务器相同,并接受 SSL 选项:--ssl-crl--ssl-crlpath--ssl-mode--ssl-ca--ssl-capath--ssl-cert--ssl-cipher--ssl-key--tls-version

使用 mysqlsh --help 命令可以获取未列出的其他选项。

mysqlshow - 显示数据库、表和列信息

这是主要用于快速检查哪些数据库、它们的表、列和索引是否存在的客户端。它提供了一些 SQL SHOW 语句的接口。

该命令的执行语法如下:

shell> mysqlshow [options] [db_name [tbl_name [col_name]]]

通过执行上述命令,您将获得有关您具有权限的数据库、表或列的信息:

  • 如果没有给出数据库,则显示数据库列表

  • 如果没有给出表,则显示数据库中所有匹配的表的列表

  • 如果没有给出列,则显示表中所有匹配的列和列类型的列表

在前面的命令执行中,如果使用了 SQL 通配符字符(*?%_),则会显示与通配符匹配的名称。如果给出了 *? 通配符字符,则会将它们转换为 SQL %_ 通配符字符。当尝试显示具有表或列名中的 _ 的列时,它可能会造成混淆,它只显示与模式匹配的名称,但可以通过在命令行的末尾作为单独的参数添加额外的 % 来轻松解决。

该程序有许多选项,可以通过使用特定选项参数来获取所需的信息。以下是一些重要的选项:

--character-sets-dir=dir_name

指定安装字符集的目录名称。

--compress, -C

如果客户端和服务器都支持压缩,则发送的所有信息都将被压缩。

--enable-cleartext-plugin

启用 cleartext 认证插件。

--get--server-public-key

从服务器请求 RSA 公钥,用于基于密钥对的密码交换。此外,如果客户端使用安全连接连接到服务器,则不需要 RSA 密码交换,并且将被忽略。

--keys, -k

显示表索引。

--ssl*

指定以 --ssl 开头的选项,使用证书和 SSL 密钥连接到服务器的 SSL 连接。

使用 mysqlshow --help 命令可以获取这里未列出的许多其他选项。

mysqlslap - 负载仿真客户端

这是用于检查 MySQL 服务器的客户端负载能力的诊断客户端程序。该程序模拟多个客户端访问服务器。

执行语法如下:

shell> mysqlslap [options]

有许多选项可用于修改执行中的命令,其中一些选项,如 --create--query,提供了指定具有特定分隔符的 SQL 语句或文件的方法。

mysqlslap 在三个不同的阶段运行:

  1. 创建表、模式和可选的存储程序或数据进行测试。它仅使用单个客户端连接。

  2. 运行负载测试。它使用多个客户端连接。

  3. 清理(如果之前指定了,删除表,并断开连接)。它使用单个客户端连接。

例如,使用 20 个客户端和每个客户端 100 次查询创建我们自己的查询语句将如下所示 CLI:

mysqlslap --delimiter=";" --create="CREATE TABLE t (i int);INSERT INTO t VALUES (21)" --query="SELECT * FROM t" --concurrency=20 --iterations=100

mysqlslap 也可以添加或创建自己的查询语句,如下面的代码块所示:

mysqlslap --concurrency=7 --iterations=20 --number-int-cols=2 --number-char-cols=2 --auto-generate-sql

在这里,mysqlslap 将使用两个 INT 列和两个 VARCHAR 列的表构建一个语句,并七个客户端对每个表查询 20 次。它还支持指定用于分别创建和查询的语句文件,并运行负载测试。它提供了许多这样的负载测试执行的变化选项,您可以通过在命令行上执行 mysqlslap --help 命令来检查。

MySQL 8 管理程序

本节描述了不同的管理程序以及一些实用程序,这些程序将有助于进行管理操作,如执行校验和、压缩和提取等。

ibdsdi - InnoDB 表空间 SDI 提取实用程序

这是一个实用程序,从InnoDB表空间文件中提取序列化的字典信息。SDI 数据中的序列化字典信息将始终存在于所有持久的InnoDB表空间文件中。它可以在文件表空间文件和通用表空间文件、系统表空间文件和数据字典表空间文件上运行,但不支持使用临时表空间或撤消表空间。

ibd2sdi可以在服务器离线或运行时使用。它从指定的表空间中读取 SDI 的未提交数据,并撤消日志和重做日志,这些日志是不可访问的。

idb2sdi的执行将如下命令行所示:

shell> ibd2sdi [options] file_name1 [file_name2 file_name3 ...] 

ibd2sdi还支持多个表空间,但不像InnoDB系统表空间那样一次运行多个表空间。指定每个文件将按如下方式工作:

shell> ibd2sdi ibdata1 ibdata2

ibd2sdi以 JSON 格式输出 SDI 数据。

该程序有许多选项可供使用,可以使用ibd2sdi --help命令检索。

innochecksum - 离线 InnoDB 文件校验和实用程序

这是一个用于InnoDB文件的校验和实用程序。它读取InnoDB表空间文件,为它们计算校验和,并将其与存储的校验和值进行比较。如果比较失败,它将报告带有损坏页面详细信息的错误。它是为了验证停电后的完整性而开发的,但也可以在复制文件后使用。在运行InnoDB时,如果发现任何损坏的页面,它非常有用。它将关闭正在运行的服务器,因此为了避免由于损坏的页面而导致任何生产问题,需要注意。

如果表空间文件是打开的,则无法使用innochecksum。命令的执行语法将如下所示:

shell> innochecksum [options] file_name

innochecksum命令还有一些选项,用于显示正在验证的页面的信息,并可以使用innochecksum --help命令检索。

myisam_ftdump - 显示全文索引实用程序

这是一个用于显示MyISAM表和FULLTEXT索引信息的实用程序。它将扫描和转储整个索引,这可能是一个缓慢和冗长的过程。如果服务器已经在运行,则需要确保首先插入FLUSH TABLES语句。

myisam_ftdump命令的执行将如下代码块所示:

shell> myisam_ftdump [options] <table_name> <index_num>

在前面的示例中,table_name应该是带有.MYI索引扩展名的MyISAM表的名称。

假设测试数据库有一个名为 mytexttable 的表,其定义如下:

CREATE TABLE mytexttable ( id INT NOT NULL, txt TEXT NOT NULL, PRIMARY KEY (id), FULLTEXT (txt) ) ENGINE=MyISAM;

FULLTEXT索引上创建的 id 为 0,在txt上为 1。如果工作目录是测试数据库目录,则执行myisam_ftdump如下:

shell> myisam_ftdump mytexttable 1

myisam_ftdump也可以用来按出现频率生成索引条目列表,如下所示(Windows 中的第一行,Unix 系统中的第二行):

shell> myisam_ftdump -c mytexttable 1 | sort /R 
shell> myisam_ftdump -c mytexttable 1 | sort -r

myisam_ftdump还有一些选项,可以使用myisam_ftdump --help命令检索。

myisamchk - MyISAM 表维护实用程序

myisamchk是一个命令行工具,用于获取关于数据库表的信息,检查、修复和优化非分区的MyISAM表。它适用于MyISAM表。

CHECK_TABLEREPAIR_TABLE语句也可用于检查和修复MyISAM表。

myisamchk命令的执行如下代码块所示:

shell> myisamchk [OPTIONS] tables[.MYI | .MYD]

在运行myisamchk命令之前,必须确保没有其他程序在使用表。否则,它将显示警告消息,提示:警告:客户端正在使用或未正确关闭表。为了有效地做到这一点,关闭 MySQL 服务器或锁定myisamchk正在使用的所有表。

该程序有许多选项可用于执行表维护。

myisamlog - 显示 MyISAM 日志文件内容

该程序是用于处理 MyISAM 日志文件内容的实用程序。启动 MySQL 服务器时,使用--log-isam=log_file选项。

执行myisamlog命令的语法如下代码块所示:

shell> myisamlog [options] [file_name [tbl_name] ...]

默认操作标记为更新,如果进行恢复,则所有写入、更新和删除都会被执行,只计算错误。myisam.log是默认的日志文件名。

该程序具有一些用于指定偏移量、恢复、打开多个文件等的选项,可以使用myisamlog --help命令检索。

myisampack - 生成压缩的只读 MyISAM 表

这是一个压缩MyISAM表的实用程序。它分别压缩表中的每一列,并将数据文件压缩约 40%至 70%。

MySQL 最好使用mmap()函数对压缩表进行内存映射;否则,它将使用正常的读/写文件操作。

myisampack不支持分区表。

一旦表被打包,它们就变成了只读。

停止服务器,然后进行压缩表是执行压缩操作的安全方式。

myisampack的执行语法如下命令行中的块:

shell> myisampack [options] file_name ...

指定索引文件名时,可以带有或不带有.MYI文件,并且如果不在数据库目录中,还可以添加pathname

使用myisampack压缩表后,应使用myisampack -rq重新构建压缩表的索引。它支持一些常见选项,如版本控制、调试等,以及特定的压缩检查,如--test--backup--join=big_tbl_name--silent等。如果您想进行详细检查,可以在命令行上执行myisampack --verbose --help命令。

mysql_config_editor - MySQL 配置实用程序

这是一个实用程序,用于在加密的登录路径文件中存储和更新认证凭据,文件名为.mylogin.cnf

有关详细信息,请使用mysql_config_editor --verbose --help命令在命令行上执行。

mysqlbinlog - 用于处理二进制日志文件的实用程序

该程序是用于处理服务器的二进制日志文件的实用程序。二进制日志文件包含描述对数据库内容的修改的事件数据。服务器以二进制格式将此类内容写入文件。为了将它们转换为可读(文本)格式,使用mysqlbinlog实用程序。

mysqlbinlog还可以用于显示由从服务器在复制设置期间写入的中继日志文件的内容,因为中继日志文件和二进制日志文件的格式相同。

程序语法的执行如下代码块所示:

shell> mysqlbinlog [options] log_file ...

有几个选项可以修改mysqlbinlog的输出格式和用法。它们列如下:

  • 它可以转换为包含字节位置、事件时间戳和发生的事件类型的十六进制转储格式

  • 它还提供了一种行事件显示格式,以伪 SQL 语句的形式显示数据修改信息

  • 它还用于通过提供所需的选项值(如备份文件的路径和备份中的类型或格式)来备份二进制日志文件的内容

  • 当使用mysqlbinlog连接到 MySQL 服务器时,它会提供一个特定的服务器 ID 来标识自己,并从服务器请求二进制日志文件

该程序具有一些常见选项,未列出,但可以使用mysqlbinlog --help命令检索。

mysqldumpslow - 汇总慢查询日志文件。

该程序是一个实用程序,用于读取慢查询日志文件的日志内容,其中包含执行时间较长的查询。它解析 MySQL 慢查询日志文件并打印出查询内容的摘要。

它通常将类似的查询分组在一起,除了特定的数字或字符串数据值。它会将这些值抽象化,并分别使用 NS 显示摘要输出。-n-a 选项用于修改值的抽象行为。

在命令行语法中执行程序的方式如下所示:

shell> mysqldumpslow [options] [log_file ...]

您可以通过使用一些选项来修改输出,例如限制结果的数量 -t N,其中 N 是要显示的查询结果的数量。-s 代表按查询时间、锁定时间或行数排序类型,-r 代表反转排序顺序。

MySQL 8 环境变量

在本节中,我们将查看直接或间接用于不同 MySQL 程序的环境变量的数量,并使用环境变量改变它们的行为。

命令行提供的选项优先于选项文件和环境变量中指定的值,同样,选项中的值优先于环境变量;因此,在大多数情况下,最好使用选项文件而不是环境变量来修改行为。

以下是环境变量和变量描述的列表:

  • CXX: 用于运行 CMake 的 C++ 编译器的名称

  • CC: 用于运行 CMake 的 C 编译器的名称

  • DBI_USER: Perl DBI 的默认用户名

  • DBI_TRACE: Perl DBI 的跟踪选项

  • HOME: mysql 历史文件的默认路径是 $HOME/.mysql_history

  • LD_RUN_PATH: 用于指定 libmysqlclient.so 的位置。

  • LIBMYSQL_ENABLE_CLEARTEXT_PLUGIN: 启用 mysql_clear_password 认证插件

  • LIBMYSQL_PLUGIN_DIR: 查找客户端插件的目录

  • LIBMYSQL_PLUGINS: 预加载的客户端插件

  • MYSQL_DEBUG: 调试时的跟踪选项

  • MYSQL_GROUP_SUFFIX: 可选的组后缀值(例如指定 --defaults-group-suffix

  • MYSQL_HISTFILE: mysql 历史文件的路径;如果设置了此变量,其值将覆盖 $HOME/.mysql_history 的默认值

  • MYSQL_HISTIGNORE:指定不应将其记录到 $HOME/.mysql_historyorsyslog if --syslog 的语句模式

  • MYSQL_HOME: 服务器特定的 my.cnf 文件所在目录的路径

  • MYSQL_HOST: 服务器特定的 my.cnf 文件所在目录的路径

  • MYSQL_PWD: 连接到 mysqld 时的默认密码;使用此选项是不安全的

  • MYSQL_TCP_PORT:默认的 TCP/IP 端口号

  • MYSQL_TEST_LOGIN_FILE.mylogin.cnf 登录路径文件的名称

  • PATH: 用于 shell 查找 MySQL 程序

  • PKG_CONFIG_PATH: mysqlclient.pc pkg-config 文件的位置

  • TMPDIR: 创建临时文件的目录

  • TZ: 这应该设置为您的本地时区

  • UMASK: 创建文件时的用户文件创建模式

  • UMASK_DIR: 创建目录时的用户目录创建模式

  • USER: 连接到 mysqld 时 Windows 上的默认用户名

MYSQL_TEST_LOGIN_FILE 是由 mysql_config_editor 创建的登录路径文件的路径名。

UMASKUMASK_DIR 变量用作模式而不是掩码。

如果使用 pkg-config 构建 MySQL 程序,则必须设置 PKG_CONFIG_PATH

MySQL GUI 工具

有许多 MySQL GUI 工具可用于执行各种操作,从创建数据库到执行日常管理任务。

MySQL Workbench

MySQL Workbench 是一个与 MySQL 服务器和数据库一起工作的图形工具。它完全支持 MySQL 版本 5.1 及以上。在本节中,我们将简要讨论 MySQL Workbench 的功能。

MySQL Workbench 提供的五个主要功能如下:

  • SQL 开发:创建和管理数据库连接,并配置连接参数。它使用内置的 SQL 编辑器执行 SQL 语句,并替换了之前提供的独立应用程序查询浏览器。

  • 数据建模:这将以图形方式创建数据库模式的模型,并在两个不同的模式之间进行反向和正向工程,以及在实时数据库上进行。它提供了一个全面的表编辑器,易于使用,用于编辑表、列、触发器、索引、选项、插入、分区、例程、视图和权限。

  • 服务器管理:这将创建、维护和管理服务器实例。

  • 数据迁移:这允许从 PostgreSQL、SQLite、Sybase ASE、Microsoft SQL Server 和其他关系数据库管理系统对象、表和数据迁移到 MySQL。它还便于从早期版本迁移到最新版本。

  • MySQL 企业支持:这为产品提供企业级支持;例如,MySQL 企业备份,MySQL 审计。

MySQL Workbench 有两个不同的版本,商业版和社区版。社区版是免费提供的。商业版提供额外的功能,如数据库文档生成。

MySQL Notifier

MySQL Notifier 是一个简单的工具,用于监视和调整本地/远程 MySQL 服务器实例的状态。它是一个放置在系统托盘中的指示器。它是与 MySQL 安装程序一起安装的。

MySQL Notifier 充当快速启动器,其中列出的操作可以很容易地从系统托盘中进行操作和监视,并且它会根据指定的间隔进行监视,并在状态更改时通知。

MySQL Notifier 的使用

MySQL Notifier 停留在系统托盘中,并提供了一个点击选项,用于 MySQL 的状态和维护。以下是 MySQL Notifier 的重要用途。

  • MySQL Notifier 提供 MySQL 服务器实例的启动、停止和重启。

  • MySQL Notifier 配置 MySQL 服务器服务,并自动检测并添加新的 MySQL 服务器服务。

  • MySQL Notifier 监视本地和远程 MySQL 实例。

摘要

在本章中,我们深入研究了用于 MySQL 服务器几乎所有活动的命令,从安装、服务器启动、客户端程序到管理程序,以及用于数据库管理的各种不同目的的多个实用程序。本章还提供了制作数据库备份和导入数据库的工作知识,包括具体表或不包括具体表。

下一章将详细介绍 MySQL 8 数据类型。它将根据其内容类型对数据类型进行分类,并详细描述每个类别的属性和在表和列设计期间应该牢记的存储级别细节。

第四章:MySQL 8 数据类型

在上一章中,我们学习了如何使用 MySQL 8 命令行程序和实用程序对 MySQL 8 数据库执行各种操作。拥有对命令行工具的掌握总是很好的。它提供了在非 GUI 环境中工作的灵活性。本章的重点是数据类型。了解编程语言支持的数据类型或存储引擎可以存储的数据类型是不是很有趣?这是任何编程语言或数据库的基本特性。同时,它也是最被忽视的话题。大多数程序员没有足够的时间来评估代码中使用的变量的存储需求。实际上,了解数据库支持的基本和自定义数据类型非常重要,这也是本章存在的原因。

以下是本章要涵盖的主题列表:

  • MySQL 8 数据类型概述

  • 数值数据类型

  • 日期和时间数据类型

  • 字符串数据类型

  • JSON 数据类型

  • 数据类型的存储要求

  • 为列选择正确的数据类型

MySQL 8 数据类型概述

MySQL 支持所有标准 SQL 数据类型。这些数据类型分为几个类别,如数值类型、字符串类型、日期和时间类型以及 JSON 数据类型。当我们为列分配数据类型时,必须遵循某些约定。这些约定对于 MySQL 允许在列中存储值是必要的:

  • M表示整数类型的最大显示宽度。对于浮点和定点类型,它是可以存储的总位数。对于字符串类型,它是最大长度。允许的最大值取决于数据类型。

  • D 适用于浮点和定点类型。它表示小数点后的位数。允许的最大值为 30,但必须小于或等于 M-2。

  • fsp 适用于日期和时间类型。它表示分数秒精度,即小数点后秒的小数部分的位数。

本概述简要介绍了每种数据类型的特性,详细描述将在后续主题中涵盖。

数值数据类型

MySQL 8 数值数据类型包括整数或精确数据类型、十进制或近似数据类型和位数据类型。

默认情况下,REAL数据类型的值存储为DOUBLE。如果我们在 MySQL 上设置了REAL_AS_FLOAT标志,REAL数据类型的值将存储为FLOAT。与DOUBLE相比,FLOAT占用的空间更小。

整数类型

MySQL 支持所有标准 SQL 整数类型。

以下是描述每种整数类型所需存储和范围的表。除了标准整数数据类型外,MySQL 还支持TINYINTMEDIUMINTBIGINT

类型 存储(字节) 最小值 最大值
有符号/无符号 有符号/无符号
TINYINT 1 -128 127
0 255
SMALLINT 2 -32768 32767
0 65535
MEDIUMINT 3 -8388608 8388607
0 16777215
INT 4 -2147483648 2147483647
0 4294967295
BIGINT 8 -9223372036854775808 9223372036854775807
0 18446744073709551615

参考:dev.mysql.com/doc/refman/8.0/en/integer-types.html

有符号数的范围包括负数和正数,而无符号数的范围仅包括正数。

以下是无符号整数列的列声明:

CREATE TABLE employees
(salary INTEGER(5) UNSIGNED);

INTINTEGER可以互换使用。但是考虑一下,如果我们声明一个列:

CREATE TABLE employees
(id INT(255));

INTEGER列可以存储的最大值要么是 2147483647(对于有符号的INTEGER),要么是 4294967295(对于无符号的INTEGER)。这里的255定义了数字的可见长度。一方面,显示一个 255 位长的数字是不切实际的。另一方面,INTEGER支持最大值为 10 位数。因此,在前面的情况下,它将被转换为INT(11)。现在,这又引发了另一个问题:如果最大整数数字的位数为 10,那么为什么应该将其转换为INT(11)而不是INT(10)?原因是保留了一位数字用于存储符号。

ZEROFILL是一个属性,它表示如果数字值的长度小于列的长度,那么数字值应该以零填充。CREATE语句演示了声明带有ZEROFILL属性的列的方法。以下是一个例子:

CREATE TABLE documents
(document_no INT(5) ZEROFILL);

我们指定要存储的值为111;如果我们提供了ZEROFILL选项,它将被存储为00111

固定点类型

固定点类型表示小数点或基数点后具有固定位数的数字。MySQL 有DECIMALNUMERIC作为固定点或精确值数据类型。这些值以二进制格式存储。固定点数据类型在存储货币值进行乘法和除法运算时特别有用。固定点数据类型的值是由特定因子缩放的整数。例如,值 1.11 可以以111的形式表示为固定点,缩放因子为 1/100。同样,1,110,000 可以以1110的形式表示,缩放因子为 1000。

以下代码块演示了DECIMAL数据类型的声明:

CREATE TABLE taxes
(tax_rate DECIMAL(3, 2));

在前面的例子中,3是精度,2是标度。一个例子值可以是 4.65,其中4是精度,65是标度。

  • 精度:表示存储值的有效数字位数

  • 标度:表示小数点后的数字位数

精度和标度定义了可以存储在列中的值的范围。因此,在前面的列声明中,tax_rate可以存储在-9.99 和 9.99 之间的值。

标准 SQL 中定义DECIMAL类型的语法如下:

DECIMAL(M)

在 MySQL 中,这相当于:

DECIMAL(M, 0)

在 MySQL 中,声明带有DECIMAL的列等同于DECIMAL(M, 0)

在 MySQL 中,如果没有提供M,则10M的默认值。

DECIMAL类型支持的最大数字位数为 65,包括精度和标度。我们可以通过精度和标度限制可以输入列的值的数字位数。如果用户输入的值的数字位数大于标度允许的数字位数,那么该值将被截断以匹配允许的标度。

DECIMAL通常被认为是DOUBLEFLOAT的替代品。如前所述,DECIMAL数字是数学中REAL数字的精确表示。DECIMAL数据类型唯一的问题是,即使对于小数字,它也占用了更多的空间。例如,要存储值 0.000003,列声明应该将数据类型定义为DECIMAL(7, 6)

如果标度为0,则列值没有小数点或分数值。

浮点类型

浮点数在计算中表示实数。实数对于测量连续值(如重量、高度或速度)非常有用。

MySQL 有两种用于存储近似值的浮点数据类型:FLOATDOUBLE

对于浮点数,精度是一个重要因素。精度定义了准确度的度量。MySQL 支持单精度和双精度浮点数。使用FLOAT数据类型存储单精度浮点数需要四个字节,而使用DOUBLE数据类型存储双精度浮点数需要八个字节。

在 MySQL 中,REALDOUBLE PRECISION的同义词。如前所述,如果启用了REAL_AS_FLOAT,则使用REAL数据类型定义的列将类似于FLOAT

前面的描述将FLOATDOUBLE描述为类似于DECIMAL。不,它不是。它们之间有很大的区别。如前所述,固定点数据类型如DECIMALNUMERIC可以存储精确值,直到小数点后的最大数字位数,而浮点数据类型如FLOATDOUBLE存储近似值。存储的值足够详细,但并非完全准确。仍然存在一些小的不准确性。

让我们通过以下代码示例来理解这一点:

mysql> CREATE TABLE typed_numbers(id TINYINT, float_values FLOAT, decimal_values DECIMAL(3, 2));

mysql> INSERT INTO typed_numbers VALUES(1, 1.1, 1.1), (2, 1.1, 1.1), (3, 1.1, 1.1);

mysql> SELECT * FROM typed_numbers;
+------+--------------+------------------+
| id   | float_values | decimal_values   |
+------+--------------+------------------+
|   1  |          1.1 |             1.10 |
|   2  |          1.1 |             1.10 |
|   3  |          1.1 |             1.10 |
+------+--------------+------------------+
mysql> SELECT SUM(float_values), SUM(decimal_values) FROM typed_numbers;
+--------------------+---------------------+
| SUM(float_values)  | SUM(decimal_values) |
+--------------------+---------------------+
| 3.3000000715255737 |                3.30 |
+--------------------+---------------------+

在前面的例子中:

  1. 我们创建了一个包含FLOATDECIMAL类型列的表。

  2. 我们在两个列中插入了相同的值,分别命名为float_valuesdecimal_values

  3. 我们执行了一个select查询来获取存储值的总和。

尽管值相同,输出却不同。decimal_values的总和看起来比float_values的更精确。float_values的总和看起来不够精确。这是因为 MySQL 引擎对浮点数据类型执行的内部舍入,导致存储的值是近似值。

标准 SQL 允许在定义FLOAT列时指定精度。精度是在关键字FLOAT后的括号内指定的位数。MySQL 也支持为FLOATDOUBLE指定精度值,但精度用于确定大小:

  • 从 0 到 23 的精度会导致 4 字节单精度FLOAT

  • 从 24 到 53 的精度会导致 8 字节双精度DOUBLE

以下是FLOAT列声明属性的示例:

FLOAT(M, D) 
where,
M - number of digits in total
D - number of digits may be after the decimal point

因此,定义为以下内容的列将存储值,例如 99.99:

FLOAT(4, 2)

在存储浮点值时,MySQL 执行舍入。因此,插入为 99.09 的值到FLOAT(4, 2)列可能会以 99.01 的近似结果存储。

尽管浮点列定义支持指定精度,但建议使用没有精度或数字位数的FLOATDOUBLE PRECISION,以便充分利用最大的灵活性和可移植性。

浮点值的问题

如前所述,浮点数据类型存储近似的实数。尝试存储精确值并在比较操作中考虑精确值可能会导致各种问题。此外,浮点值以平台和实现相关的方式进行解释。例如,不同的 CPU 或操作系统可能以不同的方式评估浮点数。这基本上意味着,打算存储在浮点数据类型列中的值可能与实际存储或内部表示的值不同。

当我们在比较中使用浮点数时,前面的观点变得至关重要。考虑以下例子:

mysql> CREATE TABLE temp(id INT, col1 DOUBLE, col2 DOUBLE);

mysql> INSERT INTO temp VALUES (1, 5.30, 2.30), (1, -3.00, 0.00),
 (2, 0.10, -10.00), (2, -15.20, 4.00), (2, 0.00, -7.10),
 (3, 0.00, 2.30), (3, 0.00, 0.00);

mysql> SELECT id, SUM(col1) as v1, SUM(col2) as v2 FROM temp
 GROUP BY id HAVING v1 <> v2;
+------+--------+--------+
|  id  |   v1   |   v2   |
+------+--------+--------+
|    1 |    2.3 |    2.3 |
|    2 |  -15.1 |  -13.1 |
|    3 |    0.0 |    2.3 |
+------+--------+--------+

在前面的例子中,输出的前两行似乎有相似的数字。在浮点类型的情况下可能不是这样。如果我们想要确保在前面的情况下,类似的值被认为是相似的,我们必须根据精度比较差异。例如,在前面的情况下,如果我们修改HAVING子句以检查条件ABS(v1 - v2) > 0.1,它将返回预期的输出。

由于浮点数的解释取决于平台,如果我们尝试插入超出浮点数据类型支持的值范围的值,可能会插入+- inf 或+- 0。

位值类型

您是否曾经遇到过存储数字的二进制表示的要求?您能想到这样的用例吗?这样的用例之一是存储一年中每周的工作日信息。我们稍后将在本节中介绍这个例子。

BIT数据类型用于存储二进制位或位值组。它也是存储布尔值、是/否或0/1值的选项之一。

BIT类型的列可以定义为:

column_name BIT
or
column_name BIT(m)
where m = number of bits to be stored

对于BIT数据类型,m可以从1变化到64。提供m是可选的。m的默认值为1

以下是定义BIT列的示例:

CREATE TABLE working_days (
year INT,
week INT,
days BIT(7),
PRIMARY KEY (year, week));

BIT数据类型列声明之后,接下来是在列中存储位值。位值是零(0)和一(1)的组合。使用b'value'表示法来指定位值。

以下是在BIT列中存储 11 和 55 的示例:

CREATE TABLE bit_values (val BIT(7));

INSERT INTO bit_values VALUES(b'1011');
INSERT INTO bit_values VALUES(b'110111');

如果存储在BIT列中的值少于列定义中指定的位数(m),会发生什么?MySQL 将在数字左侧用 0 填充该值。因此,对于前面的示例,存储的值将分别为 0001011 和 0110111。

我们如何定义一个BIT列来存储boolean_values?以下代码块显示了这一点:

CREATE TABLE boolean_values (value BIT(1));
or
CREATE TABLE boolean_values (value BIT);

INSERT INTO boolean_values VALUES(b'0');
INSERT INTO boolean_values VALUES(b'1');

位值字面值

要在表列中存储位值,我们必须了解位字面值。如前所述,位字面值可以使用b'val'表示法编写。还有另一种表示法,即0bval表示法。

关于b'val'0bval表示法的一个重要说明是,前导b的大小写不重要。我们可以指定bB。前导的0b是大小写敏感的,不能用0B替换。

以下是合法和非法的位值字面值列表。

合法的位值字面值:

  • b'10'

  • B'11'

  • 0b10

非法的位值字面值:

  • b'3'10是唯一的二进制数字)

  • 0B010B无效;应为0b

作为默认值,位字面值是一个二进制字符串。我们可以通过查询来确认这一点,如下面的代码块所示:

mysql> SELECT b'1010110', CHARSET(b'1010110');
+--------------+----------------------+
| b'1010110'  | CHARSET(b'1010110') |
+--------------+----------------------+
|    V         |     binary           |
+--------------+----------------------+

mysql> SELECT 0b1100100, CHARSET(0b1100100);
+--------------+----------------------+
|  0b1100100   |  CHARSET(0b1100100)  |
+--------------+----------------------+
|    d         |     binary           |
+--------------+----------------------+

BIT 的实际用途

让我们继续以一年中每周的工作日为例。请参考之前提供的working_days表模式。

我们如何指定2017年第4周的星期一和星期五为非工作日?以下是此操作的INSERT查询:

INSERT INTO working_days VALUES(2017, 4, 0111011);

如果我们使用SELECT查询获取working_days记录,输出如下:

mysql> SELECT year, week, days FROM working_days;
+--------+---------+--------+
|  year  |   week  |  days  |
+--------+---------+--------+
|   2017 |       4 |     59 |
+--------+---------+--------+

在前面的输出中,尽管日期是位数据类型,但显示的是整数值。我们如何在输出中显示位值呢?

答案是BIN() MySQL 函数。该函数将整数值转换为其二进制表示:

mysql> SELECT year, week, BIN(days) FROM working_days;
+--------+---------+------------+
|  year  |   week  |    days    |
+--------+---------+------------+
|   2017 |       4 |    111011  |
+--------+---------+------------+

如您所见,在输出中,日期的位值中的前导零被移除了。为了在输出中实现表示,除了BIN函数之外,我们还可以使用LPAD MySQL 函数:

mysql> SELECT year, week, LPAD(BIN(days), 7, '0') FROM working_days;
+--------+---------+------------+
|  year  |   week  |    days    |
+--------+---------+------------+
|   2017 |       4 |    0111011 |
+--------+---------+------------+

类型属性

如前所示,在定义整数列时,我们还可以指定一个可选的显示宽度属性。例如,INT(5)表示具有5位数字的整数。当此列在SELECT查询中使用时,输出将显示左填充空格的数字。因此,如果存储在INT(5)列中的值为123,则将显示为__123_在实际输出中将是一个空格。

然而,显示宽度不限制可以存储在INT(5)列中的值的范围。那么问题来了:如果我们存储的值大于指定的显示宽度,会怎么样?显示宽度不会阻止比列的显示宽度更宽的值正确显示。因此,比列显示宽度更宽的值将以全宽显示,使用的数字数量超过了显示宽度指定的数量。

如前所述,MySQL 列定义提供了一个名为ZEROFILL的可选属性。当指定了这个可选属性时,它会用零替换左填充的空格。例如,对于以下定义的列,检索到的值为 00082:

INT(5) ZEROFILL

这个可选属性在需要正确格式化数字的情况下非常有用。

当列值用于表达式或UNION查询时,ZEROFILL属性将被忽略。

当在查询中使用复杂的连接来存储中间结果时,MySQL 会创建临时表。在这种情况下,如果我们指定了具有显示宽度的列,可能会遇到问题。在这些情况下,MySQL 认为数据值适合于显示宽度。

另一个重要的属性是UNSIGNEDUNSIGNED属性只允许在列中存储非负值。当我们需要支持相同数据类型的更大范围的值时,这也是非常有用的。

UNSIGNED也支持浮点类型和定点类型。

如果为列指定了ZEROFILL属性,UNSIGNED会自动添加到列中。

整数和浮点列的另一个重要属性是AUTO_INCREMENT。当我们在具有AUTO_INCREMENT属性的列中插入一个NULL值时,MySQL 会存储value+1而不是NULL。值为 0 将被视为NULL值,除非启用了NO_AUTO_VALUE_ON_ZERO模式。在这里,值是存储在列中的最大值。非常重要的是,列被定义为NOT NULL。否则,NULL值将被存储为NULL,即使提供了AUTO_INCREMENT属性。

溢出处理

当在 MySQL 中的数字类型列中存储超出范围的值时,存储的值取决于 MySQL 模式:

  • 如果启用了strict模式,MySQL 将不接受该值并抛出错误。insert操作失败。

  • 如果启用了restrictive模式,MySQL 会将值裁剪为适当的值,并将其存储在列中。

日期和时间数据类型

DATETIMEDATETIMETIMESTAMPYEAR构成了用于存储时间值的日期和时间数据类型组。每种类型都有一定范围的允许值。除了允许的值之外,还可以使用特殊的值来指定 MySQL 无法表示的无效值。零值可以是 00-00-0000。MySQL 允许将此值存储在date列中。这有时比存储NULL值更方便。

在处理日期和时间类型时,我们必须注意以下一般考虑事项。

MySQL 对于日期或时间类型的存储和检索操作在格式的上下文中是不同的。基本上,对于存储在表中的日期或时间类型值,MySQL 以标准输出格式检索值。在输入日期或时间类型值的情况下,MySQL 尝试对提供的输入值应用不同的格式。因此,预期提供的值是有效的,否则如果使用不受支持的格式中的值,则可能会出现意外结果。

尽管 MySQL 可以解释多种不同格式的输入值,但日期值的部分必须以年-月-日的格式提供。例如,2017-10-22 或 16-02-14。

提供两位数年份会导致 MySQL 解释年份时出现歧义,因为世纪未知。以下是必须遵循的规则,使用这些规则,MySQL 解释两位数年份值:

  • 70-99 年之间的年份值会被转换为 1970-1999

  • 00-69 年之间的年份值会被转换为 2000-2069

可以按照一定的规则将一个时间类型的数值转换为另一个时间类型。我们将在本章后面讨论这些规则。

如果日期或时间数值在数值上下文中使用,MySQL 会自动将该数值转换为数字。

我们有一个有趣的用例。我们想要开发一个审计日志功能,用于存储用户输入的每个数值。假设在其中一个日期字段中,用户输入了一个无效的日期,2017-02-31。这会被存储在审计日志表中吗?当然不会。那么我们该如何完成这个功能呢?MySQL 有一个 ALLOW_INVALID_DATES 模式。如果启用了这个模式,它将允许存储无效的日期。启用了这个模式后,MySQL 会验证月份是否在 1-12 的范围内,日期是否在 1-31 的范围内。

由于 ODBC 无法处理日期或时间的零值,通过 Connector/ODBC 使用这些数值时会被转换为 NULL

下表显示了不同数据类型的零值:

数据类型 零值
DATE 0000-00-00
TIME 00:00:00
DATETIME 0000-00-00 00:00:00
TIMESTAMP 0000-00-00 00:00:00
YEAR 0000

参考:dev.mysql.com/doc/refman/8.0/en/date-and-time-types.html

上表显示了不同时间数据类型的零值。这些是特殊值,因为它们被 MySQL 允许,并且在某些情况下非常有用。我们还可以使用 '0'0 来指定零值。MySQL 有一个有趣的模式配置:NO_ZERO_DATE。如果启用了这个配置,当时间类型的数值为零时,MySQL 会显示警告。

DATE、DATETIME 和 TIMESTAMP 类型

本节描述了最常用的 MySQL 日期和时间数据类型:DATEDATETIMETIMESTAMP。本节解释了这些数据类型之间的相似之处和不同之处。

DATE 数据类型适用于我们希望存储的数值具有日期部分,但缺少时间部分的情况。标准的 MySQL 日期格式是 YYYY-MM-DD。日期数值在未应用 DATE 函数的情况下以标准格式检索和显示。MySQL 支持的数值范围是 1000-01-01 到 9999-12-31。这里的“支持”意味着这些数值可能有效,但不能保证。DATETIME 数据类型也是如此。

DATETIME 数据类型适用于包含日期和时间部分的数值。标准的 MySQL DATETIME 格式是 YYYY-MM-DD HH:MM:SS。支持的数值范围是 1000-01-01 00:00:00 到 9999-12-31 23:59:59。

DATETIME 类似,TIMESTAMP 数据类型也适用于包含日期和时间部分的数值。然而,TIMESTAMP 数据类型支持的数值范围是从 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC。

尽管它们看起来相似,DATETIMETIMESTAMP 数据类型有着显著的不同:

  • TIMESTAMP 数据类型需要 4 个字节来存储日期和时间数值。DATETIME 数据类型需要 5 个字节来存储日期和时间数值。

  • TIMESTAMP 可以存储值直到 2038-01-19 03:14:07 UTC。如果希望存储超过 2038 年的值,则应使用 DATETIME 数据类型。

  • TIMESTAMP 在存储数值时将 UTC 视为时区,DATETIME 则在存储数值时不考虑时区。

让我们通过一个例子来理解 time_zone 上下文中 DATETIMETIMESTAMP 之间的差异。

假设初始的 time_zone 值设置为 +00:00

SET time_zone = '+00:00';

让我们创建一个名为datetime_temp的表。该表有两列;一列是DATETIME,另一列是TIMESTAMP类型。我们将在两列中存储相同的日期和时间值。借助SELECT查询,我们将尝试了解输出中表示的差异:

mysql> CREATE TABLE datetime_temp(
 ts TIMESTAMP,
 dt DATETIME);

mysql> INSERT INTO datetime_temp
VALUES(NOW(), NOW());

mysql> SELECT ts, dt FROM datetime_temp;
+------------------------+-------------------------+
>|          ts            |            dt           |
+------------------------+-------------------------+
|  2017-10-14 18:10:25   |  2017-10-14 18:10:25    |
+------------------------+-------------------------+

在上面的例子中,NOW()是 MySQL 函数,它返回当前的日期和时间值。从输出来看,似乎TIMESTAMPDATETIME的表示是相同的。这是因为time_zone值设置为 UTC。默认情况下,TIMESTAMP显示考虑 UTC time_zone的日期时间值。另一方面,DATETIME显示不带time_zone的日期时间。

让我们更改time_zone并观察输出:

mysql> SET time_zone = '+03:00';

mysql> SELECT ts, dt FROM datetime_temp;
+------------------------+-------------------------+
|          ts            |            dt           |
+------------------------+-------------------------+
|  2017-10-14 21:10:25   |  2017-10-14 18:10:25    |
+------------------------+-------------------------+

从输出来看,很明显TIMESTAMP考虑了 MySQL 中设置的time_zone值。因此,当我们更改时区时,TIMESTAMP值会调整。DATETIME不受影响,因此即使在更改时区后,输出也不会改变。

如果使用TIMESTAMP存储日期和时间值,我们在将数据迁移到位于不同时区的不同服务器时必须认真考虑它。

如果需要更高精度的时间值,DATETIMETIMESTAMP可以包括最多微秒(六位数字)的尾随分数秒。因此,如果我们插入一个带有微秒值的日期时间值,它将存储在数据库中。格式,包括分数部分,是 YYYY-MM-DD HH:MM:SS[.fraction],范围是从 1000-01-01 00:00:00.000000 到 9999-12-31 23:59:59.999999。TIMESTAMP的范围,包括分数,是 1970-01-01 00:00:01.000000 到 2038-01-19 03:14:07.999999。

时间值的小数部分通过小数点与时间值分隔,因为 MySQL 不识别其他分数秒的分隔符。

使用TIMESTAMP数据类型存储的日期和时间值会从服务器的时区转换为 UTC 进行存储,并从 UTC 转换为服务器的时区进行检索。如果我们存储了一个TIMESTAMP值,然后更改了服务器的时区并检索该值,则检索到的值将与我们存储的值不同。

以下是 MySQL 中日期值解释的属性列表:

  • MySQL 支持以字符串指定的值的宽松格式。在宽松格式中,任何标点字符都可以用作日期部分或时间部分之间的分隔符。这有点令人困惑。例如,值10:11:12可能看起来像一个时间值,因为使用了:,但被解释为2010-11-12日期。

  • 在其余时间部分和分数秒部分之间的唯一识别分隔符是小数点。

  • 预期月份和日期值是有效的。在禁用strict模式的情况下,无效日期将转换为相应的zero值,并显示警告消息。

  • TIMESTAMP值中,如果日期或月份列中包含零,则不是有效日期。这条规则的例外是zero值。

如果 MySQL 以启用MAXDB模式运行,TIMESTAMPDATETIME相同。如果在表创建时启用了此模式,则TIMESTAMP值将转换为DATETIME

MySQL DATETIME 函数

NOW()是用于获取系统当前日期和时间的函数:

mysql> SET @dt = NOW();
mysql> SELECT @dt;
+---------------------+
|       @dt           |
+---------------------+
| 2017-10-15 13:43:17 |
+---------------------+

DATE()函数用于从DATETIME值中提取日期信息:

mysql> SELECT DATE(@dt);
+------------------+
|    DATE(@dt)     |
+------------------+
|    2017-10-15    |
+------------------+

TIME()函数用于从日期时间值中提取时间信息:

mysql> SELECT TIME(@dt);
+------------------+
|    TIME(@dt)     |
+------------------+
|     13:43:17     |
+------------------+

当您希望基于日期或时间值显示或查询数据库表时,DATE()TIME()函数非常有用,但表中存储的实际值包含日期和时间信息。

如果我们想从DATETIMETIMESTAMP值中提取YEARMONTHDAYQUARTERWEEKHOURMINUTESECOND信息,相应的函数是可用的:

mysql> SELECT
 HOUR(@dt),
 MINUTE(@dt),
 SECOND(@dt),
 DAY(@dt),
 WEEK(@dt),
 MONTH(@dt),
 QUARTER(@dt),
 YEAR(@dt);
+-----------+-------------+-------------+---------+----------+
| HOUR(@dt) | MINUTE(@dt) | SECOND(@dt) | DAY(@dt)| WEEK(@dt)| 
+-----------+-------------+-------------+---------+----------+
+------------+--------------+-----------+
| MONTH(@dt) | QUARTER(@dt) | YEAR(@dt) |
+------------+--------------+-----------+
+-----------+-------------+-------------+---------+----------+
|        13 |          43 |          17 |      15 |       41 | 
+-----------+-------------+-------------+---------+----------+
+------------+--------------+-----------+
|         10 |            4 |      2017 |
+------------+--------------+-----------+

TIME 类型

MySQL 的DATETIMETIMESTAMP数据类型用于表示特定日期的特定时间。只存储一天中的时间或两个事件之间的时间差怎么办?MySQL 的TIME数据类型可以满足这一需求。

存储或显示TIME数据类型值的标准 MySQL 格式是HH:MM:SS。时间值表示一天中的时间,小于 24 小时,但是如前所述,TIME数据类型也可以用于存储经过的时间或两个事件之间的时间差。因此,TIME列可以存储大于 24 小时的值。

MySQL 的TIME列定义如下:

column_name TIME;

TIME数据类型列中可以存储的值的范围是-838:59:59 到 838:59:59。

MySQL 的TIME列还可以存储小数秒部分,最多可以达到微秒(六位数字),类似于DATETIME列。考虑到小数秒精度,值的范围从-838:59:59.000000 到 838:59:59.00000。

MySQL 的TIME列也可以有一个可选值:

column_name TIME(N);
where N represents number of fractional part, which is up to 6 digits.

TIME值通常需要 3 个字节来存储。在包括小数秒精度的TIME值的情况下,将需要额外的字节,取决于小数秒精度的数量。

以下表格显示了存储小数秒精度所需的额外字节数:

小数秒精度 存储(字节)
0 0
1, 2 1
3, 4 2
5, 6 3

MySQL 支持TIME列的缩写值。MySQL 有两种不同的方式来解释缩写值:

  • 如果缩写值有冒号(:),MySQL 将其解释为一天中的时间。例如,11:12 被解释为 11:12:00,而不是 00:11:12。

  • 如果缩写值没有冒号(:),MySQL 假定最右边的两位数字代表秒。这意味着该值被解释为经过的时间,而不是一天中的时间。例如,'1214'和 1214 被 MySQL 解释为 00:12:14。

MySQL 接受的唯一分隔符是小数点,用于将小数秒精度与时间值的其余部分分开。

MySQL 默认情况下,将超出允许值范围的值裁剪到范围的最近端点。例如,-880:00:00 和 880:00:00 存储为-838:59:59 和 838:59:59。无效的TIME值转换为 00:00:00。由于 00:00:00 本身是有效的TIME值,很难知道值 00:00:00 是有意存储的,还是从无效的TIME值转换而来。

MySQL 接受字符串和数字值作为TIME值。

时间函数

CURRENT_TIME()函数可用于查找服务器上的当前时间。还可以使用ADDTIMESUBTIME函数添加或减去时间值。例如,以下示例将两小时添加到服务器的当前时间:

mysql> SELECT 
 CURRENT_TIME() AS 'CUR_TIME',
 ADDTIME(CURRENT_TIME(), 020000) AS 'ADDTIME',
 SUBTIME(CURRENT_TIME(), 020000) AS 'SUBTIME';

+----------+-----------+-----------+
| CUR_TIME |  ADDTIME  |  SUBTIME  |
+----------+-----------+-----------+
| 10:12:34 |  12:12:34 | 08:12:34  |
+----------+-----------+-----------+

UTC_TIME()函数可用于获取 UTC 时间。

年份类型

存储制造年份的首选数据类型是什么?MySQL 的答案是YEAR数据类型。YEAR数据类型需要 1 个字节来存储年份信息。

YEAR列可以声明为:

manufacturing_year YEAR
or
manufacturing_year YEAR(4)

值得注意的是,早期的 MySQL 版本支持YEAR(2)类型的列声明。从 MySQL 8 开始,不再支持YEAR(2)。可能需要将旧的 MySQL 数据库升级到 MySQL 8 数据库。在后面的部分中,我们将解释从YEAR(2)YEAR(4)的迁移细节。

MySQL 以 YYYY 格式表示YEAR值。值的范围是从 1901 年到 2155 年和 0000 年。

以下是输入YEAR值支持的格式列表:

  • 从 1901 年到 2155 年的四位数。

  • 从 1901 年到 2155 年的四位字符串。

  • 0 到 99 范围内的一位或两位数字。YEAR值从 1 到 69 转换为 2001 到 2069,从 70 到 99 转换为 1970 到 1999。

  • 范围为 0 到 99 的一位或两位数字字符串。YEAR值从 1 到 69 转换为 2001 到 2069,从 70 到 99 转换为 1970 到 1999。

  • 插入数字 0 的显示值为 0000,内部值为 0000。如果我们想要插入 0 并希望它被解释为 2000,我们应该将其指定为字符串 0 或 00。

  • 返回可接受值YEAR上下文的函数的结果,例如NOW()

MySQL 将无效的YEAR值转换为 0000。

将 YEAR(2)迁移到 YEAR(4)

如前所述,MySQL 8 不支持YEAR(2)类型。尝试创建一个数据类型为YEAR(2)的列将会产生以下错误:

mysql> CREATE TABLE temp(year YEAR(2));
ERROR 1818 (HY000): Supports only YEAR or YEAR(4) column.

重建表的ALTER TABLE查询将自动将YEAR(2)转换为YEAR(4)。在将数据库升级到 MySQL 8 数据库后,YEAR(2)列仍然保持为YEAR(2),但查询会报错。

有多种方法可以从YEAR(2)迁移到YEAR(4)

  • 使用带有FORCE属性的ALTER TABLE查询将YEAR(2)列转换为YEAR(4)。但它不会转换值。如果ALTER TABLE查询应用于复制主机,复制从机将复制ALTER TABLE语句。因此,更改将在所有复制节点上可用。

  • 使用二进制升级,无需转储或重新加载数据,是将YEAR(2)升级到YEAR(4)的另一种方法。随后运行mysql_upgrade会执行REPAIR_TABLE并将YEAR(2)转换为YEAR(4,而不更改值。与前一个替代方案类似,如果应用于复制主机,则会在复制从机中复制此更改。

需要注意的一点是,在升级时,我们不应该使用mysqldump转储YEAR(2)数据,并在升级后重新加载转储文件。这种方法有可能显著改变YEAR(2)的值。

在进行YEAR(2)YEAR(4)迁移之前,必须审查应用程序代码:

  • 选择以两位数字显示YEAR值的代码。

  • 不处理数字0插入的代码。将0插入YEAR(2)会得到2000,而将0插入YEAR(4)会得到0000

字符串数据类型

哪种数据类型是表示值最广泛需要和使用的?字符串还是字符数据类型;很容易,对吧?MySQL 支持各种字符串数据类型,以满足不同的存储需求。字符串数据类型分为两类:固定长度和可变长度。CHARVARCHARBINARYVARBINARYBLOBTEXTENUMSET是 MySQL 支持的字符串数据类型。每种数据类型的存储需求都不同,将在单独的部分中进行解释。

CHAR 和 VARCHAR 数据类型

CHAR数据类型是 MySQL 中的固定长度字符串数据类型。CHAR数据类型通常声明为可以存储的最大字符数,如下所示:

data CHAR(20);

在前面的例子中,数据列可以存储能够存储最大字符的字符串值。

CHARVARCHAR在许多方面相似,但也有一些区别。如果要存储的字符串值是固定大小的,首选CHAR数据类型。与对固定大小字符串使用VARCHAR相比,它将提供更好的性能。

长度从 0 到 255 不等。CHAR列中的值不能超过表创建时声明的最大长度。如果字符串的长度小于允许的最大长度,MySQL 会在右侧添加填充以达到指定的长度。在检索时,尾随空格会被移除。以下是一个例子:

mysql> CREATE TABLE char_temp (
 data CHAR(3)
);

mysql> INSERT INTO char_temp(data) VALUES('abc'), (' a ');

mysql> SELECT data, LENGTH(data) 
 FROM char_temp;
+-------+--------------+
| data  | LENGTH(data) |
+-------+--------------+
|  abc  |      3       |
+-------+--------------+
|   a   |      2       |
+-------+--------------+

正如我们在前面的例子中所观察到的,第二条记录被插入为' a ', 但在输出中,尾随空格被移除。因此,长度显示为2而不是3

大多数 MySQL 排序规则都有填充属性。它确定如何处理非二进制字符串的尾随空格进行比较。有两种类型的排序规则:PAD SPACENO PAD。在PAD SPACE排序规则的情况下,尾随空格在比较时不被考虑。字符串在不考虑尾随空格的情况下进行比较。

NO PAD排序规则的情况下,尾随空格被视为任何其他字符。以下是一个示例:

mysql> CREATE TABLE employees (emp_name CHAR(10));

mysql> INSERT INTO employees VALUES ('Jack');

mysql> SELECT emp_name = 'Jack', emp_name = 'Jack ' FROM employees;
+-------------------+--------------------+ 
| emp_name = 'Jack' | emp_name = 'Jack ' | 
+-------------------+--------------------+ 
|                1  |                 1  | 
+-------------------+--------------------+ 
mysql> SELECT emp_name LIKE 'Jack', emp_name LIKE 'Jack ' FROM employees; 
+----------------------+------------------------+ 
| emp_name LIKE 'Jack' | emp_name LIKE 'Jack '  | 
+----------------------+------------------------+ 
|                    1 |                      0 | 
+----------------------+------------------------+

LIKE是 MySQL 中用于WHERE子句中的比较的运算符。它专门用于在字符串中进行模式搜索。在使用LIKE运算符比较字符串值时,尾随空格是重要的。

如果启用了PAD_CHAR_TO_FULL_LENGTH模式,在检索时,尾随空格将不会被移除。

MySQL VARCHAR数据类型是一个最大长度为 65,535 个字符的可变长度字符串数据类型。VARCHAR值由 MySQL 存储为一个或两个字节的长度前缀,以及实际数据。VARCHAR的实际最大长度取决于最大行大小,最大行大小为 65,536 字节,共享在所有列之间。

如果VARCHAR值需要的字节数少于 255 字节,则使用一个字节来确定长度前缀。如果值需要的字节数超过 255 字节,则使用两个字节来确定长度前缀。

如果启用了 MySQL 严格模式,并且要插入的CHARVARCHAR列值超过了最大长度,则会生成错误。如果禁用了严格模式,则该值将被截断为最大允许长度,并生成警告。

CHAR数据类型不同,要存储在VARCHAR中的值不会填充。此外,检索值时也不会去除尾随空格。

BINARY 和 VARBINARY 数据类型

另一组 MySQL 字符串数据类型是BINARYVARBINARY。这些与CHARVARCHAR数据类型类似。CHAR/VARCHARBINARY/VARBINARY之间的一个重要区别是BINARY/VARBINARY数据类型包含的是二进制字符串而不是字符字符串。BINARY/VARBINARY使用二进制字符集和排序规则。BINARY/VARBINARYCHAR BINARYVARCHAR BINARY数据类型不同。基本区别在于所涉及的字符集和排序规则。

允许值的最大长度与CHARVARCHAR的最大长度类似。唯一的区别是BINARYVARBINARY的长度是以字节而不是字符计算的。

MySQL 如何比较二进制值?答案是基于值中字节的数值进行比较。

CHAR/VARCHAR数据类型类似,如果值的长度超过列长度,将截断值并生成警告(如果未启用strict模式)。如果启用了strict模式,将生成错误。

BINARY值在指定列长度右侧填充了填充值 0x00(零字节)。插入时添加填充值,但在检索时不会删除尾随字节。在比较BINARY值时,所有字节都被视为重要。这也适用于ORDER BYDISTINCT运算符。当与0x00 < space进行比较时,零字节和空格是不同的。以下是插入二进制值的示例:

mysql> CREATE TABLE temp(
 data BINARY(3));

mysql> INSERT INTO temp(data) VALUES('a ');

在这种情况下,插入时'a'变成'a\0''a\0'转换为'a\0\0'。在检索时,值保持不变。

VARBINARY是一个可变长度字符串数据类型。与BINARY不同,VARBINARY在插入时不会添加填充,在检索时也不会去除字节。与BINARY类似,所有字节在比较VARBINARY时都是重要的。

如果表在列上有唯一索引,那么在列中插入仅在尾随填充字节数量上不同的值将导致重复键错误。例如,如果这样的列包含'a ',并且我们尝试插入'a\0',将导致重复键错误。

以下示例解释了在比较中BINARY值的填充:

mysql> CREATE TABLE bin_temp (data BINARY(3));

mysql> INSERT INTO bin_temp(data) VALUES('c');

mysql> SELECT data = 'c', data = 'c\0\0' from bin_temp;
+------------+-------------------+
| data = 'c' |    data = 'c\0\0' |
+------------+-------------------+
|          0 |                 1 |
+------------+-------------------+

在需要检索与指定的相同值但不需要填充的情况下,最好使用VARBINARY

如果检索的值必须与指定的存储值相同且不填充,可能更适合使用VARBINARYBLOB数据类型之一。

BLOB 和 TEXT 数据类型

在什么情况下我们可能需要将数据存储在二进制大对象BLOB)列中?有任何想法吗?存储文件或图像,你说?这部分是正确的。在我们决定将图像或文件存储在数据库或文件系统之前,我们需要评估情况。如果文件存储在文件系统中并迁移到另一个操作系统,可能会导致文件指针损坏。这将需要额外的工作来修复文件指针。在这种情况下,将文件存储在数据库中更可取。但是,如果我们在数据库中存储大型拥挤的文件或图像数据,可能会影响性能。

BLOB是 MySQL 用于存储可变长度大型二进制信息的解决方案。MySQL 有四种BLOB类型:TINYBLOBBLOBMEDIUMBLOBLONGBLOB。这些数据类型之间的唯一区别是我们可以存储的值的最大长度。这些数据类型的存储要求在本章后面的部分中有解释。

BLOB类似,TEXT数据类型有TINYTEXTTEXTMEDIUMTEXTLONGTEXT。它们具有与BLOB数据类型类似的最大长度和存储要求。

BINARY数据类型一样,BLOB值被存储为字节字符串,并具有二进制字符集和排序。对列值的数字值进行比较和排序。TEXT值被存储为非二进制字符串。

对于BLOBTEXT数据类型,如果值包含多余的尾随空格,MySQL 会截断并发出警告,无论 MySQL 模式如何。MySQL 在插入时不会填充BLOBTEXT列的值,并且在检索时不会剥离字节。

对于索引的TEXT列,索引比较会在值的末尾添加尾随空格作为填充。因此,如果现有TEXT值和要插入的TEXT值之间的唯一区别在于尾随空格,则可能会在插入时发生重复键错误。BLOB可以被视为VARBINARYTEXT可以被视为VARCHAR,对值的长度没有限制。

以下是VARBINARYVARCHARBLOBTEXT之间的区别:

  • BLOBTEXT列上创建索引时,必须指定索引前缀长度。

  • BLOBTEXT不能有默认值

BLOBTEXT值在内部表示为具有单独分配的对象,与其他数据类型不同,其他数据类型的存储是每列分配一次。

ENUM 数据类型

MySQL 提供了一种数据类型,可以在创建表时预定义允许的值列表。该数据类型是ENUM。如果我们希望限制用户插入超出一定范围的值,应该定义数据类型为ENUM的列。MySQL 将用户输入的字符串值编码为ENUM数据类型的数字。

ENUM提供了以下提到的好处:

  • 紧凑的数据存储

  • 可读的查询和输出

以下是展示ENUM何时有用的示例:

mysql> CREATE TABLE subjects (
 name VARCHAR(40),
 stream ENUM('arts', 'commerce', 'science')
);

mysql> INSERT INTO subjects (name, stream) VALUES ('biology','science'), ('statistics','commerce'), ('history','arts');

mysql> SELECT name, stream FROM subjects WHERE stream = 'commerce';
+------------+----------+
|    name    |  stream  |
+------------+----------+
| statistics | commerce |
+------------+----------+

mysql> UPDATE subjects SET stream = 'science' WHERE stream = 'commerce';

ENUM值需要一个字节的存储。在这个表中存储一百万条这样的记录将需要一百万字节的存储空间,而不是VARCHAR列所需的六百万字节。

以下是需要考虑的重要限制:

  • ENUM值在内部存储为数字。因此,如果ENUM值看起来像数字,字面值可能会与其内部索引数字混淆。

  • ORDER BY 子句中使用 ENUM 列需要额外小心。ENUM 值根据列出顺序分配索引号。ENUM 值根据其索引号排序。因此,重要的是确保 ENUM 值列表按字母顺序排列。此外,列应按字母顺序而不是按索引号排序。

  • ENUM 值必须是带引号的字符串文字。

  • 每个 ENUM 值都有一个从 1 开始的索引。空字符串或错误值的索引为 0。我们可以通过在 WHERE 子句中查询具有 enum_column_value = 0 的表来找到无效的 ENUM 值。NULL 值的索引为 NULL。索引是指值在 ENUM 值列表中的位置。

  • 在创建表时,MySQL 会自动删除 ENUM 成员值的尾随空格。检索时,ENUM 列中的值以列定义中使用的大小写显示。如果要在 ENUM 列中存储数字,则该数字将被视为可能值的索引。存储的值是具有该索引的 ENUM 值。对于带引号的数字值,如果在枚举值列表中没有匹配的字符串,则仍将其解释为索引。

  • 如果声明 ENUM 列包含 NULL 值,则将考虑 NULL 值作为列的有效值,并且 NULL 成为默认值。如果不允许 NULL,则第一个 ENUM 值将成为默认值。

如果在数字上下文中使用 ENUM 值,则使用索引。以下是在数字上下文中使用 ENUM 值的示例查询:

mysql> SELECT stream+1 FROM subjects;
+--------------+
|   stream+1   |
+--------------+
|      4       |
|      3       |
|      2       |
+--------------+

SET 数据类型

MySQL SET 是一种数据类型,可以具有零个或多个值。在创建表时指定了一个允许值列表。每个值必须来自允许值列表中。多个集合成员由逗号(,)分隔的值列表指定。SET 最多可以有 64 个不同的成员。如果启用了 strict 模式,则如果在列定义中发现重复的值,则会生成错误。

必须注意 SET 成员值不包含逗号;否则,它们将被解释为 SET 成员分隔符。

指定为 SET('yes', 'no') NOT NULL 的列可以具有以下任一值:

  • ''

  • '是'

  • '否'

  • '是,否'

SET 成员值会自动删除尾随空格。检索时,SET 列值将使用在列定义中使用的大小写显示。

以下是在 SET 数据类型中插入值的示例:

mysql> CREATE TABLE temp(
 hobbies SET('Travel', 'Sports', 'Fine Dining', 'Dancing'));

mysql> INSERT INTO temp(hobbies) VALUES(9);

SET 值存储在 MySQL 表中,其中每个元素由一个位表示。在前面的情况下,SET 中的每个元素都被分配一个位。如果行具有给定元素,则相关位将为一。由于这种方法,每个元素都有一个关联的十进制值。此外,由于位图,尽管只有四个值,SET 将占用一个字节。以下是解释这一点的表:

元素 SET 值 十进制值
旅行 00000001 1
体育 00000010 2
精致餐饮 00000100 4
跳舞 00001000 8

可以通过添加它们的十进制值来表示多个 SET 元素。在前面的情况下,十进制值 9 被解释为旅行,跳舞。

SET 数据类型并不常用。这是因为虽然它是一个字符串数据类型,但在实现上有点复杂。可以存储的值限制为 64 个元素。我们不能将逗号作为 SET 值的一部分添加,因为逗号是标准的 SET 值分隔符。从数据库设计的角度来看,使用 SET 意味着数据库不是规范化的。

JSON 数据类型

JSON 代表 JavaScript 对象表示法。假设我们想要在数据库中存储 Web 应用程序的用户偏好设置。通常,我们可能选择创建一个单独的表,其中包含iduser_idkeyvalue字段。这对于少量用户可能效果不错,但对于成千上万的用户来说,维护成本是无法承受的,与其增加 Web 应用程序的价值相比。

在 MySQL 中,我们可以利用 JSON 数据类型来满足这个需求。MySQL 支持原生的 JSON 数据类型,可以有效地存储 JSON 文档。MySQL 支持对存储在 JSON 列中的 JSON 文档进行自动验证。尝试存储无效的 JSON 文档会产生错误。存储在 JSON 列中的 JSON 文档会被转换为内部格式。该格式是二进制的,并且结构化,使服务器能够直接查找subojbects或嵌套值,通过键或数组索引,而无需读取其他值。

JSON 列不能有默认值。JSON 数据类型需要与LONGTEXTLONGBLOB相似的存储。与其他字符串数据类型不同,JSON 列不会直接进行索引。

以下是在表中插入 JSON 值的示例:

mysql> CREATE TABLE users(
 user_id INT UNSIGNED NOT NULL,
 preferences JSON NOT NULL);

mysql> INSERT INTO users(user_id, preferences)
 VALUES(1, '{"page_size": 10, "hobbies": {"sports": 1}}');

mysql> SELECT preferences FROM users;
+---------------------------------------------------------+
|                   preferences                           |
+---------------------------------------------------------+
|    {"hobbies": {"sports": 1}, "page_size": 10}          |
+---------------------------------------------------------+

在前面的示例中,我们已经格式化了 JSON 值。作为替代,我们也可以使用内置的JSON_OBJECT函数。该函数接受一组键/值对并返回一个 JSON 对象。以下是一个示例:

mysql> INSERT INTO users(user_id, preferences)
 VALUES(2, JSON_OBJECT("page_size", 1, "network", JSON_ARRAY("GSM", "CDMA", "WIFI")));

前面的INSERT查询将插入 JSON 值{"page_size": 1, "network": ["GSM", "CDMA", "WIFI"]}。我们也可以使用嵌套的JSON_OBJECT函数。JSON_ARRAY函数在传递一组值时返回一个 JSON 数组。

如果多次指定相同的键,则只保留第一个键值对。在 JSON 数据类型的情况下,对象键会被排序,并且键值对之间的尾随空格会被移除。JSON 对象中的键必须是字符串。

只有在 JSON 文档有效的情况下,才能在 JSON 列中插入 JSON 值。如果 JSON 文档无效,MySQL 会产生错误。

MySQL 还有一个重要且有用的操作 JSON 值的函数。JSON_MERGE函数接受多个 JSON 对象并生成一个单一的聚合对象。

JSON_TYPE函数以 JSON 作为参数并尝试将其解析为 JSON 值。如果有效,则返回值的 JSON 类型,否则会产生错误。

JSON 值的部分更新

如果我们想要更新存储在 JSON 数据类型列中的 JSON 文档中的值,我们应该怎么做?其中一种方法是删除旧文档并插入带有更新的新文档。这种方法似乎不太好,对吧?MySQL 8.0 支持对存储在 JSON 数据类型列中的 JSON 文档进行部分、就地更新。优化器要求更新必须满足以下条件:

  • 列必须是 JSON 类型。

  • JSON_SET()JSON_REPLACE()JSON_REMOVE()三个函数中的一个可以用来更新列。MySQL 不允许直接对列值进行部分更新。

  • 输入列和目标列必须相同。例如,像UPDATE temp SET col1 = JSON_SET(col2, 'one', 10)这样的语句不能作为部分更新执行。

  • 更改只会更新现有数组或对象,不会向父对象或数组添加新元素。

  • 替换值不能大于被替换的值。

数据类型的存储要求

本节解释了 MySQL 中不同数据类型的存储要求。存储要求取决于不同的因素。存储引擎以不同的方式表示数据类型并存储原始数据。

表的最大行大小为 65,535 字节,即使存储引擎能够支持更大的行。BLOBTEXT数据类型被排除在外。

以下表格解释了数字数据类型的存储细节:

数据类型 所需存储空间
TINYINT 1 字节
SMALLINT 2 字节
MEDIUMINT 3 字节
INT,INTEGER 4 字节
BIGINT 8 字节
FLOAT(p) 如果0<=p<=24,则为 4 字节,如果25<=p<=53,则为 8 字节
浮点 4 字节
DOUBLE [精度],REAL 8 字节
DECIMAL(M, D),NUMERIC(M, D) 变化
BIT(M) 大约(M+7)/8字节

参考:dev.mysql.com/doc/refman/8.0/en/storage-requirements.html

以下表格解释了 DATE 和 TIME 数据类型的存储需求:

数据类型 存储需求
YEAR 1 字节
日期 3 字节
时间 3 字节 + 分数秒存储
日期时间 5 字节 + 分数秒存储
TIMESTAMP 4 字节 + 分数秒存储

以下表格解释了分数秒精度所需的存储空间:

分数秒精度 存储需求
0 0 字节
1, 2 1 字节
3, 4 2 字节
5, 6 3 字节

以下表格解释了字符串数据类型的存储需求:

数据类型 存储需求
--- ---
CHAR(M) M × w 字节,0 <= M <= 255,其中w是字符集中最大长度字符所需的字节数
BINARY(M) M 字节,0 <= M <= 255
VARCHAR(M),VARBINARY(M) 如果列值需要 0 − 255 字节,则为L + 1 字节,如果值可能需要超过 255 字节,则为L + 2 字节
TINYBLOB,TINYTEXT L + 1 字节,其中L < 28
BLOB,TEXT L + 2 字节,其中L < 216
MEDIUMBLOBMEDIUMTEXT L + 3 字节,其中L < 224
LONGBLOB,LONGTEXT L + 4 字节,其中L < 232
ENUM('value1','value2',...) 取决于枚举值的数量,为 1 或 2 字节(最多 65,535 个值)
SET('value1','value2',...) 取决于集合成员的数量,为 1、2、3、4 或 8 字节(最多 64 个成员)

参考:dev.mysql.com/doc/refman/8.0/en/storage-requirements.html

在字符串数据类型的情况下,使用值的长度和长度前缀存储可变长度字符串。长度前缀根据数据类型的不同而变化,可以是一到四个字节。

JSON 数据类型的存储需求与 LONGBLOB 和 LONGTEXT 相似。然而,由于 JSON 文档以二进制表示存储,因此在存储 JSON 文档时会产生开销。

选择列的正确数据类型

作为一般惯例,我们应该使用最精确的类型来存储数据。例如,应该使用 CHAR 数据类型来存储长度从 1 到 255 个字符的字符串值。另一个例子是,应该使用 MEDIUMINT UNSIGNED 来存储从 1 到 99999 的数字。

基本操作,如加法减法乘法除法,使用DECIMAL数据执行,精度为 65 个小数位。

根据准确性或速度的重要性,应选择使用 FLOAT 或 DOUBLE。存储在 BIGINT 中的定点值可用于更高的精度。

这些是一般指导方针,但是应该根据前面各数据类型单独解释的详细特性来决定使用正确的数据类型。

总结

这是一个有重要内容需要学习的有趣章节,对吧?在这一章中,我们了解了 MySQL 中数据类型的重要性。我们看到了 MySQL 数据类型被分类的不同类别。我们深入学习和了解了每种数据类型的特性和规格。我们还学习了 MySQL 数据操作函数,并了解了一些 MySQL 设置和模式。在本章的后面部分,我们学习了数据类型的存储需求。最后,我们学习了选择正确数据类型的一般指导方针。

进入下一章,我们将学习 MySQL 数据库管理。本章将重点介绍服务器管理,了解 MySQL 服务器的基本构建模块,如数据字典、系统数据库等。本章将解释如何在单台机器上运行多个服务器实例以及 MySQL 角色和权限。

第五章:MySQL 8 数据库管理

在上一章中,我们学习了 MySQL 8 数据类型,详细解释了可用的数据类型及其分类。每种数据类型都有各种属性,存储容量也因类型而异。上一章还为您提供了对 MySQL 8 数据类型的深入了解。现在是时候获得一些关于 MySQL 8 管理功能的实际知识了。了解更多关于 MySQL 8 管理功能的信息,如何为其进行配置等,这难道不是很有趣吗?对于管理员来说,详细了解 MySQL 8 的全球化工作原理、如何维护日志以及如何增强服务器的功能非常重要。现在,让我们从一些基本概念开始。

本章将涵盖以下主题:

  • MySQL 8 服务器管理

  • 数据目录

  • 系统数据库

  • 在单台机器上运行多个实例

  • 组件和插件管理

  • 角色和权限

  • 缓存技术

  • 全球化

  • MySQL 8 服务器日志

MySQL 8 服务器管理

MySQL 8 有许多可用的操作参数,其中所有必需的参数在安装过程中默认设置。安装后,您可以通过删除或添加特定参数设置行的注释符(#)来更改选项文件。用户还可以使用命令行参数或选项文件在运行时设置参数。

服务器选项和不同类型的变量

在本节中,我们将介绍 MySQL 8 启动时可用的服务器选项系统变量状态变量

  • 服务器选项:如前一章所述,MySQL 8 使用选项文件和命令行参数来设置启动参数。有关所有可用选项的详细信息,请参阅dev.mysql.com/doc/refman/8.0/en/mysqld-option-tables.htmlmysqld接受许多命令选项。要获得简要摘要,请执行以下命令:
 mysqld --help

要查看完整列表,请使用以下命令:

 mysqld –verbose --help

服务器 SQL 模式

MySQL 8 提供了不同的模式,这些模式将影响 MySQL 支持和数据验证检查。此选项使用户更容易在不同环境中使用 MySQL。为了设置不同的模式,MySQL 提供了sql_mode系统变量,可以在全局或会话级别设置。详细了解模式的以下要点:

设置 SQL 模式

可以使用--sql-mode="modes"选项在启动时设置 SQL 模式。用户还可以在选项文件中定义此选项为sql-mode="modes"您可以通过添加逗号分隔的值来定义多个节点。MySQL 8 默认使用以下模式:ONLY_FULL_GROUP_BYSTRICT_TRANS_TABLESNO_ZERO_IN_DATENO_ZERO_DATEERROR_FOR_DIVISION_BY_ZERONO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION。要在运行时更改模式,请执行以下命令:

SET GLOBAL sql_mode = 'modes';
SET SESSION sql_mode = 'modes';

要检索这两个变量的值,请执行以下命令:

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

可用的 SQL 模式

此部分描述了所有可用的 SQL 模式。其中,前三个是最重要的 SQL 模式:

  • ANSI:此模式用于更改语法和行为,使其更接近标准 SQL。

  • STRICT_TRANS_TABLES:顾名思义,此模式与事务有关,主要用于事务存储引擎。当此模式对非事务表启用时,MySQL 8 将无效值转换为最接近的有效值,并将调整后的值插入列中。如果值丢失,则 MySQL 8 将插入与列数据类型相关的隐式默认值。在这种情况下,MySQL 8 将生成警告消息而不是错误消息,并继续执行语句而不中断。然而,在事务表的情况下,MySQL 8 会给出错误并中断执行。

  • TRADITIONAL:此模式通常表现得像传统的 SQL 数据库系统。它表示在将不正确的值插入列时产生错误而不是警告。

  • ALLOW_INVALID_DATES:此模式仅检查日期值的月份范围和日期范围。换句话说,月份范围必须在 1 到 12 之间,日期范围必须在 1 到 31 之间。此模式适用于DATEDATETIME数据类型,而不适用于timestamp数据类型。

  • ANSI_QUOTES:用于将"视为标识符引用字符而不是字符串引用字符。当启用此模式时,您不能使用双引号引用字符串文字。

  • ERROR_FOR_DIVISION_BY_ZERO:用于处理除以零的情况。此模式的输出还取决于严格的 SQL 模式状态:

  • 如果未启用此模式,除以零会插入NULL并且不会产生警告。

  • 如果启用了此模式,除以零会插入NULL并产生警告。

  • 如果启用了此模式和严格模式,除以零会产生错误,除非也给出了IGNORE。对于INSERT IGNOREUPDATE IGNORE,除以零会插入NULL并产生警告。

  • HIGH_NOT_PRECEDENCE:此模式用于为NOT运算符设置高优先级。例如,当启用此模式时,表达式NOT a BETWEEN b AND c被解析为NOT (a BETWEEN b AND c)而不是(NOT a) BETWEEN b AND c

  • IGNORE_SPACE:此模式适用于内置函数,而不适用于用户定义的函数或存储过程。

  • NO_AUTO_CREATE_USER:此模式用于防止通过自动创建新用户帐户的GRANT语句。

  • NO_AUTO_VALUE_ON_ZERO:此模式用于自动增量列。当找到 0 时,MySQL 为该字段创建一个新的序列号,这在加载转储时会造成问题。在重新加载转储之前启用此模式以解决此问题。

  • NO_BACKSLASH_ESCAPES:如果启用此模式,反斜杠将成为普通字符。

  • NO_DIR_IN_CREATE:此选项对于从属复制服务器非常有用,在表创建时会忽略INDEX DIRECTORYDATA DIRECTORY指令。

  • NO_ENGINE_SUBSTITUTION:用于提供默认存储引擎的替换。当启用此模式并且所需的引擎不可用时,MySQL 会给出错误,表不会被创建。

  • NO_FIELD_OPTIONS:这表示,在SHOW_CREATE_TABLE的输出中不打印 MySQL 特定的列选项。

  • NO_KEY_OPTIONS:这表示,在SHOW_CREATE_TABLE的输出中不打印 MySQL 特定的索引选项。

  • NO_TABLE_OPTIONS:这表示,在SHOW_CREATE_TABLE的输出中不打印 MySQL 特定的表选项。

  • NO_UNSIGNED_SUBTRACTION:当启用此模式时,它确保减法结果必须是有符号值,即使操作数中的任何一个是无符号的。

  • NO_ZERO_DATE:此模式的效果取决于下面定义的严格模式:

  • 如果未启用,允许使用 0000-00-00,MySQL 在插入时不会产生警告

  • 如果启用此模式,则允许 0000-00-00,并且 MySQL 记录警告

  • 如果同时启用此模式和严格模式,则不允许 0000-00-00,并且 MySQL 在插入时产生错误

  • NO_ZERO_IN_DATE:此模式的影响也取决于如下定义的严格模式:

  • 如果未启用,允许具有零部分的日期,并且 MySQL 在插入时不会产生警告

  • 如果启用此模式,则允许具有零部分的日期并产生警告

  • 如果启用此模式和严格模式,则不允许具有零部分的日期,并且 MySQL 在插入时产生错误

  • ONLY_FULL_GROUP_BY:如果启用此模式,MySQL 将拒绝查询,其中select列表,order by列表和HAVING条件引用非聚合列。

  • PAD_CHAR_TO_FULL_LENGTH:此模式适用于数据类型设置为CHAR的列。启用此模式时,MySQL 通过填充以获取列值的完整长度。

  • PIPES_AS_CONCAT:当启用此模式时,| |将被视为字符串连接运算符,而不是OR

  • REAL_AS_FLOAT:默认情况下,MySQL 8 将REAL视为DOUBLE的同义词,但当启用此标志时,MySQL 将REAL视为FLOAT的同义词。

  • STRICT_ALL_TABLES:在此模式下,无效的数据值将被拒绝。

  • TIME_TRUNCATE_FRACTIONAL:此模式指示是否允许对TIMEDATETIMESTAMP列进行截断。默认行为是对值进行四舍五入而不是截断。

组合 SQL 模式

MySQL 8 还提供了一些特殊模式,作为模式值的组合:

  • ANSI:它包括REAL_AS_FLOATPIPES_AS_CONCATANSI_QUOTESIGNORE_SPACEONLY_FULL_GROUP_BY模式的影响。

  • DB2:它包括PIPES_AS_CONCATANSI_QUOTESIGNORE_SPACENO_KEY_OPTIONSNO_TABLE_OPTIONSNO_FIELD_OPTIONS模式的影响。

  • MAXDB:它包括PIPES_AS_CONCATANSI_QUOTESIGNORE_SPACENO_KEY_OPTIONSNO_TABLE_OPTIONSNO_FIELD_OPTIONSNO_AUTO_CREATE_USER的影响。

  • MSSQL:它包括PIPES_AS_CONCATANSI_QUOTESIGNORE_SPACENO_KEY_OPTIONSNO_TABLE_OPTIONSNO_FIELD_OPTIONS的影响。

  • MYSQL323:它包括MYSQL323HIGH_NOT_PRECEDENCE模式的影响。

  • MYSQL40:它包括MYSQL40HIGH_NOT_PRECEDENCE模式的影响。

  • ORACLE:它包括PIPES_AS_CONCATANSI_QUOTESIGNORE_SPACENO_KEY_OPTIONSNO_TABLE_OPTIONSNO_FIELD_OPTIONSNO_AUTO_CREATE_USER模式的影响。

  • POSTGRESQL:它包括PIPES_AS_CONCATANSI_QUOTESIGNORE_SPACENO_KEY_OPTIONSNO_TABLE_OPTIONSNO_FIELD_OPTIONS模式的影响。

  • TRADITIONAL:它包括STRICT_TRANS_TABLESSTRICT_ALL_TABLESNO_ZERO_IN_DATENO_ZERO_DATEERROR_FOR_DIVISION_BY_ZERONO_AUTO_CREATE_USERNO_ENGINE_SUBSTITUTION模式的影响。

严格的 SQL 模式

严格模式用于管理无效数据丢失数据。如果未启用严格模式,则 MySQL 将通过调整值和生成警告消息来管理插入和更新操作。我们可以通过启用INSERT IGNOREUPDATE IGNORE选项在严格模式下执行相同的操作。让我们以一个键插入的例子来说明,其中键值超过了最大限制。如果启用了严格模式,MySQL 会产生错误并停止执行,而在相反的情况下,它会通过截断允许键值。同样,在SELECT语句的情况下,如果数据没有更改,MySQL 仍会产生错误,在严格模式下,如果存在无效值,则会生成警告消息。如果启用了STRICT_ALL_TABLESSTRICT_TRANS_TABLES选项,则严格模式生效。这两个选项在事务表的情况下行为类似,在非事务表的情况下行为不同。

  • 对于事务表:如果启用了任一模式,则 MySQL 将在出现无效或缺少值的情况下产生错误并中止语句执行。

  • 对于非事务表:当表是非事务性的时,MySQL 的行为将取决于以下因素:

  • STRICT_ALL_TABLES: 在这种情况下,将生成错误并停止执行。但仍然存在部分数据更新的可能性。为了避免这种错误情况,使用单行语句,如果在第一行插入/更新期间发生错误,将中止执行。

  • STRICT_TRANS_TABLES: 此选项提供了将无效值转换为最接近有效值的灵活性。在缺少值的情况下,MySQL 将数据类型的默认值插入到列中。在这里,MySQL 生成警告消息并继续执行。

严格模式影响对零的除法、零日期和日期中的零的处理,如前面的点中所述,使用ERROR_FOR_DIVISION_BY_ZERONO_ZERO_DATENO_ZERO_IN_DATE模式。

SQL 模式将应用于以下 SQL 语句:

ALTER TABLE
CREATE TABLE
CREATE TABLE ... SELECT
DELETE (both single table and multiple table)
INSERT
LOAD DATA
LOAD XML
SELECT SLEEP()
UPDATE (both single table and multiple table)

您可以访问:dev.mysql.com/doc/refman/8.0/en/sql-mode.html 以获取 MySQL 中严格 SQL 模式相关错误的详细列表。

IGNORE 关键字

MySQL 提供了一个可选的IGNORE关键字,用于语句执行。IGNORE关键字用于将错误降级为警告,并适用于多个语句。对于多行语句,IGNORE关键字允许您跳过特定行,而不是中止。以下语句支持IGNORE关键字:

  • CREATE TABLE ... SELECT: 单独的CREATESELECT语句不支持此关键字,但是当我们使用SELECT语句插入表时,具有唯一键值的行将被丢弃。

  • DELETE: 如果此语句执行IGNORE选项,MySQL 将避免执行期间发生的错误。

  • INSERT: 在行插入期间,此关键字将处理唯一键中的重复值和数据转换问题。MySQL 将在列中插入最接近的可能值并忽略错误。

  • LOAD DATALOAD XML: 在加载数据时,如果发现重复,该语句将丢弃它并继续插入剩余数据,如果定义了IGNORE关键字。

  • UPDATE: 在语句执行期间,如果唯一键发生重复键冲突,MySQL 将使用最接近的识别值更新列。

IGNORE关键字也适用于一些特定的错误,列在这里:dev.mysql.com/doc/refman/8.0/en/sql-mode.html

IPv6 支持

MySQL 8 提供了对IPv6的支持,具有以下功能:

  • MySQL 服务器将接受来自具有 IPv6 连接性的客户端的 TCP/IP 连接

  • MySQL 8 帐户名称允许 IPv6 地址,这使得 DBA 可以为连接到服务器的客户端指定特权,使用 IPv6

  • IPv6 功能使字符串和内部 IPv6 地址格式之间的转换成为可能,并检查这些值是否表示有效的 IPv6 地址

服务器端帮助

MySQL 8 提供了HELP语句,以从 MySQL 参考手册中获取信息。为了管理这些信息,MySQL 使用系统数据库的几个表。为了初始化这些表,MySQL 提供了fill_help_tables.sql脚本。此脚本可在dev.mysql.com/doc/index-other.html下载并解压缩后,执行以下命令,以调用HELP函数:

mysql -u root mysql < fill_help_tables.sql

在安装过程中发生内容初始化。在升级的情况下,将执行上述命令。

服务器关闭过程

服务器关闭过程执行以下步骤:

  1. 关闭过程已启动:有几种方法可以初始化关闭过程。执行mysqladmin shutdown命令,可以在任何平台上执行。还有一些特定于系统的方法来初始化关闭过程;例如,基于 Unix 的系统在接收到SIGTERM信号时将开始关闭。同样,基于 Windows 的系统将在服务管理器告知它们时开始关闭。

  2. 如果需要,服务器将创建一个关闭线程:根据关闭初始化过程,服务器将决定是否创建新线程。如果客户端请求,将创建一个新线程。如果收到信号,则服务器可能会创建一个线程,或者自行处理。如果服务器尝试为关闭过程创建一个单独的线程,并且发生错误,则会在错误日志中产生以下消息:

 Error: Can't create thread to kill server
  1. 服务器停止接受新连接:当关闭活动启动时,服务器将停止接受新的连接请求,使用网络接口的处理程序。服务器将使用 Windows 功能(如命名管道、TCP/IP 端口、Unix 套接字文件以及 Windows 上的共享内存)来监听新的连接请求。

  2. 服务器终止当前活动:一旦关闭过程启动,服务器将开始与客户端断开连接。在正常情况下,连接线程将很快终止,但正在工作或处于进行中的活动阶段的线程将需要很长时间才能终止。因此,如果一个线程正在执行打开的事务,并且在执行过程中被回滚,那么用户可能只会得到部分更新的数据。另一方面,如果线程正在处理事务,服务器将等待直到事务完成。此外,用户可以通过执行KILL QUERYKILL CONNECTION语句终止正在进行的事务。

  3. 服务器关闭或关闭存储引擎:在此阶段,服务器刷新缓存并关闭所有打开的表。在这里,存储引擎执行所有必要的表操作。InnoDB刷新其缓冲池,将当前 LSN 写入表空间并终止其线程。MyISAM刷新挂起的索引。

  4. 服务器退出:在此阶段,服务器将向管理进程提供以下值:

  • 0 = 成功终止(未重新启动)

  • 1 = 未成功终止(未重新启动)

  • 2 = 未成功终止(已重新启动)

数据目录

数据目录是 MySQL 8 存储自身管理的所有信息的位置。数据目录的每个子目录代表一个数据库目录及其相关数据。所有 MySQL 安装都具有以下标准数据库:

  • sys目录:表示 sys 模式,其中包含用于性能模式信息解释的对象。

  • performance schema目录:此目录用于观察 MySQL 服务器在运行时的内部执行。

  • mysql目录:与 MySQL 系统数据库相关的目录,其中包含数据字典表和系统表。一旦 MySQL 服务器运行,它包含 MySQL 服务器所需的信息。

系统数据库

系统数据库主要包含存储对象元数据和其他操作目的的系统表的数据字典表。系统数据库包含许多系统表。我们将在接下来的部分中了解更多信息。

数据字典表

数据字典表包含有关数据对象的元数据。该目录中的表是不可见的,并且不会被一般的 SQL 查询(如SELECTSHOW TABLESINFORMATION_SCHEMA.TABLES等)读取。MySQL 主要使用INFORMATION_SCHEMA选项公开元数据。

授予系统表

这些表用于管理和提供用户、数据库和相关权限的授权信息。MySQL 8 使用授权表作为事务表,而不是非事务表(例如MyISAM),因此对事务的所有操作要么完成,要么失败;不会出现部分情况。

对象信息系统表

这些表包含与存储程序、组件和服务器端插件相关的信息。以下主要表用于存储信息:

  • 组件: 作为服务器的注册表。MySQL 8 服务器在启动时加载此表列出的所有组件。

  • Func: 这个表包含与所有用户定义函数UDF)相关的信息。MySQL 8 在服务器启动时加载此表中列出的所有 UDF。

  • 插件: 包含与服务器端插件相关的信息。MySQL 8 服务器在启动时加载所有可用的插件。

日志系统表

这些表对记录和使用 csv 存储引擎很有用。例如,general_logslow_log函数。

服务器端帮助系统表

这些表用于存储帮助信息。在这个类别中有以下表:

  • help_category: 提供关于帮助类别的信息

  • help_keyword: 提供与帮助主题相关的关键字

  • help_relation: 用于帮助关键字和主题之间的映射

  • help_topic: 帮助主题内容

时区系统表

这些表用于存储时区信息。在这个类别中有以下表:

  • time_zone: 提供时区 ID 以及它们是否使用闰秒

  • time_zone_leap_second: 当闰秒发生时会派上用场

  • time_zone_name: 用于时区 ID 和名称之间的映射

  • time_zone_transitiontime_zone_transition_type: 时区描述

复制系统表

这些表对支持复制功能很有用。当配置为以下表中所述时,它有助于存储复制相关信息。在这个类别中有以下表:

  • gtid_executed: 用于创建存储 GTID 值的表

  • ndb_binlog_index: 为 MySQL 集群复制提供二进制日志信息

  • slave_master_infoslave_relay_log_infoslave_worker_info: 用于在从服务器上存储复制信息

优化器系统表

这些表对优化器很有用。在这个类别中有以下表:

  • innodb_index_statsinnodb_table_stats: 用于获取InnoDB持久优化器统计信息

  • server_cost: 包含了对一般服务器操作的优化器成本估算。

  • engine_cost: 包含特定存储引擎操作的估算

其他杂项系统表

不属于上述类别的表属于这个类别。在这个类别中有以下表:

  • servers: 被FEDERATED存储引擎使用

  • innodb_dynamic_metadata: 被InnoDB存储引擎用于存储快速变化的表元数据,如自增计数器值和索引树损坏标志

您可以在以下链接了解更多关于不同系统表的信息:dev.mysql.com/doc/refman/8.0/en/system-database.html

在单台机器上运行多个实例

可能会有一些情况需要在一台机器上安装多个实例。这可能是为了检查两个不同版本的性能,或者可能需要在不同的 MySQL 实例上管理两个单独的数据库。原因可能是任何,但是 MySQL 允许用户通过提供不同的配置值在同一台机器上执行多个实例。MySQL 8 允许用户使用命令行、选项文件或设置环境变量来配置参数。MySQL 8 用于此的主要资源是数据目录,对于两个实例,它必须是唯一的。我们可以使用--datadir=dir_name函数来定义相同的值。除了数据目录,我们还将为以下选项配置唯一的值:

  • --port=port_num

  • --socket={file_name|pipe_name}

  • --shared-memory-base-name=name

  • --pid-file=file_name

  • --general_log_file=file_name

  • --log-bin[=file_name]

  • --slow_query_log_file=file_name

  • --log-error[=file_name]

  • --tmpdir=dir_name

设置多个数据目录

如上所述,每个 MySQL 实例必须有一个单独的数据目录。用户可以使用以下方法定义单独的目录:

  • 创建新的数据目录:在这种方法中,我们必须遵循第二章中定义的相同过程,安装和升级 MySQL。对于 Microsoft Windows,当我们从 Zip 存档安装 MySQL 8 时,将其数据目录复制到要设置新实例的位置。在 MSI 软件包的情况下,连同数据目录一起,在安装目录下创建一个原始的template数据目录,命名为 data。安装完成后,复制数据目录以设置额外的实例。

  • 复制现有数据目录:在这种方法中,我们将现有实例的数据目录复制到新实例的数据目录。要复制现有目录,请执行以下步骤:

  1. 停止现有的 MySQL 实例。确保它被干净地关闭,以便磁盘中没有未决的更改。

  2. 将数据目录复制到新位置。

  3. 将现有实例使用的my.cnfmy.ini选项文件复制到新位置。

  4. 根据新实例修改新选项。确保所有唯一的配置都正确完成。

  5. 使用新的选项文件启动新实例。

在 Windows 上运行多个 MySQL 实例

用户可以通过使用命令行和传递值或通过窗口服务在单个 Windows 机器上运行多个 MySQL 实例。

  • 在 Windows 命令行上启动多个 MySQL 实例:要使用命令行执行多个实例,我们可以在运行时指定选项,也可以在选项文件中设置它。选项文件是启动实例的更好选择,因为无需在启动时每次指定参数。要设置或配置选项文件,请按照第二章中描述的相同步骤,安装和升级 MySQL

  • 在 Windows 服务上启动多个 MySQL 实例:要在 Windows 上启动多个实例作为服务,我们必须指定具有唯一名称的不同服务。如第二章中所述,安装和升级 MySQL,使用–install--install-manual选项将 MySQL 定义为 Windows 服务。以下选项可用于将多个 MySQL 实例定义为 Windows 服务:

  • 方法 1:为实例创建两个单独的选项文件,并在其中定义mysqld组。例如,使用函数C:\my-opts1.cnf。以下是相同代码供您参考:

 [mysqld]
 basedir = C:/mysql-5.5.5
 port = 3307
 enable-named-pipe
 socket = mypipe1

我们也可以使用C:\my-opts2.cnf函数来做同样的事情。以下代码描述了该过程:

 [mysqld]
 basedir = C:/mysql-8.0.1
 port = 3308
 enable-named-pipe
 socket = mypipe2

您可以使用以下命令安装 MySQL8 服务:

 C:\> C:\mysql-5.5.5\bin\mysqld --install mysqld1 --
                defaults-file=C:\my-opts1.cnf
 C:\> C:\mysql-8.0.1\bin\mysqld --install mysqld2 --
                defaults-file=C:\my-opts2.cnf
    • 方法 2:为两个服务创建一个公共选项文件C:\my.cnf
 # options for mysqld1 service
 [mysqld1]
 basedir = C:/mysql-5.5.5
 port = 3307
 enable-named-pipe
 socket = mypipe1

 # options for mysqld2 service
 [mysqld2]
 basedir = C:/mysql-8.0.1
 port = 3308
 enable-named-pipe
 socket = mypipe2
  • 执行以下命令安装 MySQL 服务:
 C:\> C:\mysql-5.5.9\bin\mysqld --install mysqld1
 C:\> C:\mysql-8.0.4\bin\mysqld --install mysqld2
  • 要启动 MySQL 服务,请执行以下命令:
 C:\> NET START mysqld1
 C:\> NET START mysqld2

组件和插件管理

MySQL 服务器支持基于组件的结构,以扩展服务器功能。MySQL 8 使用INSTALL COMPONENTUNINSTALL COMPONENT SQL 语句在运行时加载和卸载组件。MySQL 8 将组件详细信息管理到mysql.component系统表中。因此,每次安装新组件时,MySQL 8 服务器都会执行以下任务:

  • 将组件加载到服务器中以立即可用

  • 将服务注册的组件加载到mysql.component系统表中。

当我们卸载任何组件时,MySQL 服务器将执行相同的步骤,但顺序相反。要查看可用的组件,请执行以下查询:

SELECT * FROM mysql.component;

MySQL 8 服务器插件

MySQL 8 服务器具有插件 API,可用于创建服务器组件。使用 MySQL 8,您可以在运行时或启动时灵活安装插件。在接下来的主题中,我们将了解 MySQL 8 服务器插件的生命周期。

安装插件

插件的加载因其类型和特性而异。为了更清楚地了解这一点,让我们来看看以下内容:

  • 内置插件:服务器知道内置插件并在启动时自动加载它们。用户可以通过任何激活状态来改变插件的状态,这将在下一节中讨论。

  • mysql.plugin系统表中注册的插件:在启动时,MySQL 8 服务器将加载在mysql.plugin表中注册的所有插件。如果服务器使用--skip-grant-tables选项启动,则服务器将不加载那里列出的插件。

  • 使用命令行选项命名的插件:MySQL 8 提供--plugin-load--plugin-load-add--early-plugin-load选项,用于在命令行加载插件。--plugin-load--plugin-load-add选项在安装内置插件后在服务器启动时加载插件。但是,我们可以使用--early-plugin-load选项在初始化内置插件和存储引擎之前加载插件。

  • 使用INSTALL PLUGIN语句安装的插件:这是一个永久的插件注册选项,它将在mysql.plugin表中注册插件信息。它还将加载插件库中的所有可用插件。

激活插件

要控制插件的状态(如激活或停用),MySQL 8 提供以下选项:

  • --plugin_name=OFF:禁用指定的插件。一些内置插件,如asmysql_native_password插件,不受此命令影响。

  • --plugin_name[=ON]:此命令启用指定的插件。如果在启动时插件初始化失败,MySQL 8 将以禁用插件的状态启动。

  • --plugin_name=FORCE:这与上述命令相同,只是服务器不会启动。这意味着如果在启动时提到了插件,它会强制服务器与插件一起启动。

  • --plugin_name=FORCE_PLUS_PERMANENT:与FORCE选项相同,但另外防止插件在运行时被卸载。

卸载插件

MySQL 8 使用UNINSTALL PLUGIN语句卸载插件,而不考虑它是在运行时还是在启动时安装的。但是,此语句不允许我们卸载内置插件和通过--plugin_name=FORCE_PLUS_PERMANENT选项安装的插件。此语句只是卸载插件并将其从mysql.plugin表中删除,因此需要mysql.plugin表上的额外delete权限。

获取已安装插件的信息

有多种方法可以获取有关已安装插件的信息。以下是其中一些,供您参考:

  • INFORMATION_SCHEMA.PLUGINS表包含插件的详细信息,如PLUGIN_NAMEPLUGIN_VERSIONPLUGIN_STATUSPLUGIN_TYPEPLUGIN_LIBRARY等等。该表的每一行都代表有关插件的信息:
 SELECT * FROM information_schema.PLUGINS;
  • SHOW PLUGINS语句显示了每个单独插件的名称、状态、类型、库和许可证详情。如果库的值为NULL,则表示它是一个内置插件,因此无法卸载。
 SHOW PLUGINS;
  • mysql.plugin表包含了所有通过INSTALL PLUGIN函数注册的插件的详细信息。

角色和权限

简而言之,角色是一组权限。在 MySQL 8 中创建角色,您必须具有全局的CREATE ROLECREATE USER权限。MySQL 8 提供了各种权限,可附加到角色和用户上。有关可用权限的更多详细信息,请参阅dev.mysql.com/doc/refman/8.0/en/privileges-provided.html

现在,让我们举个例子来理解角色创建和权限分配的作用。假设我们已经在当前数据库中创建了一个hr_employee表,并且我们想要将这个表的访问权限赋予hrdepartment角色。这个困境可以通过使用以下代码来解决:

CREATE ROLE hrdepartment;
grant all on hr_employee to hrdepartment;

上述代码将帮助我们创建hrdepartment角色并授予它所有必要的访问权限。这个主题将在第十一章中详细介绍,安全

缓存技术

缓存是一种用于提高性能的机制。MySQL 使用多种策略在缓冲区中缓存信息。MySQL 8 在存储引擎级别使用缓存来处理其操作。它还在准备好的语句和存储程序中应用缓存以提高性能。MySQL 8 引入了各种系统级变量来管理缓存,例如binlog_stmt_cache_sizedaemon_memcached_enable_binlogdaemon_memcached_w_batch_sizehost_cache_size等等。我们将在第十二章中详细介绍缓存,优化 MySQL 8

全球化

全球化是一个功能,为应用程序提供多语言支持,比如启用本地语言的使用。用我们自己的母语理解消息要比其他语言容易得多,对吧?为了实现这一点,全球化就出现了。使用全球化,用户可以将数据存储、检索和更新为多种语言。在全球化中有一些参数需要考虑。我们将在接下来的章节中详细讨论它们。

字符集

在详细讨论字符集之前,需要了解字符集实际上是什么,以及它的相关术语,对吧?让我们从术语本身开始;字符集是一组符号和编码。与字符集相关的另一个重要术语是校对规则,用于比较字符。让我们举一个简单的例子来理解字符集和校对规则。考虑两个字母,PQ,并为每个分配一个数字,使得P=1Q=2。现在,假设P是一个符号,1 是它的编码。在这里,这两个字母及它们的编码的组合被称为字符集。现在假设我们想要比较这些值;最简单的方法是参考编码值。由于 1 小于 2,我们可以说P小于Q,这就是校对规则。这是一个简单的例子来理解字符集和校对规则,但在现实生活中,我们有许多字符,包括特殊字符,同样的校对规则也有许多规则。

字符集支持

MySQL 8 支持多种字符集,具有各种排序规则。字符集可以在列、表、数据库或服务器级别定义。我们可以在InnoDBMyISAMMemory存储引擎中使用字符集。要检查 MySQL 8 的所有可用字符集,请执行以下命令:

mysql> show character set;
+----------+---------------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+---------------------------------+---------------------+--------+
| armscii8 | ARMSCII-8 Armenian | armscii8_general_ci | 1 |
| ascii | US ASCII | ascii_general_ci | 1 |
| big5 | Big5 Traditional Chinese | big5_chinese_ci | 2 | .........
.........
+----------+---------------------------------+---------------------+--------+
41 rows in set (0.01 sec)

同样,要查看字符的排序规则,请执行以下命令:

mysql> SHOW COLLATION WHERE Charset = 'ascii';
+------------------+---------+----+---------+----------+---------+---------------+
| Collation | Charset | Id | Default | Compiled | Sortlen | Pad_attribute |
+------------------+---------+----+---------+----------+---------+---------------+
| ascii_bin | ascii | 65 | | Yes | 1 | PAD SPACE |
| ascii_general_ci | ascii | 11 | Yes | Yes | 1 | PAD SPACE |
+------------------+---------+----+---------+----------+---------+---------------+
2 rows in set (0.00 sec)

排序规则将具有以下三个特征:

  • 两个不同的字符集合不能具有相同的排序规则。

  • 每个字符集都有一个默认的排序规则。如上所示,show character set命令显示了字符集的默认排序规则。

  • 排序规则遵循预定义的命名约定,稍后将对其进行解释。

  • 字符集合:repertoire是数据集中的字符集合。任何字符串表达式都将具有 repertoire 属性,并且将属于以下值之一:

  • ASCII:包含 Unicode 范围 U+0000 到 U+007F 的字符的表达式。

  • UNICODE:包含 Unicode 范围 U+0000 到 U+10FFFF 的字符的表达式。这包括基本多语言平面BMP)范围(U+0000 到 U+FFFF)和 BMP 范围之外的补充字符(U+01000 到 U+10FFFF)。

从这两个值的范围中,我们可以确定 ASCII 是 UNICODE 范围的子集,我们可以安全地将 ASCII 值转换为 UNICODE 值而不会丢失数据。Repertoire 主要用于将表达式从一个字符集转换为另一个字符集。在某些转换情况下,MySQL 8 会抛出类似“illegal mix of collations”的错误;为了处理这些情况,需要 repertoire。要了解其用法,请考虑以下示例:

CREATE TABLE employee (
 firstname CHAR(10) CHARACTER SET latin1,
 lastname CHAR(10) CHARACTER SET ascii
);

INSERT INTO employee VALUES ('Mona',' Singh');

select concat(firstname,lastname) from employee;
+----------------------------+
| concat(firstname,lastname) |
+----------------------------+
| Mona Singh |
+----------------------------+
1 row in set (0.00 sec)
  • 用于元数据的 UTF-8:元数据是关于数据的数据。在数据库方面,我们可以说描述数据库对象的任何内容都称为元数据。例如:列名,用户名等。MySQL 遵循以下两条元数据规则:

  • 包括所有语言中的所有字符以用于元数据;这使用户可以使用自己的语言作为列名和表名。

  • 为所有元数据管理一个共同的字符集合。否则,INFORMATION_SCHEMA中的表的SHOWSELECT语句将无法正常工作。

为了遵循上述规则,MySQL 8 将元数据存储为 Unicode 格式。请注意,MySQL 函数(如USER()CURRENT_USER()SESSION_USER()SYSTEM_USER()DATABASE()VERSION())默认使用 UTF-8 字符集。MySQL 8 服务器已定义character_set_system来指定元数据的字符集。确保在 Unicode 中存储元数据并不意味着列标题和DESCRIBE函数将以元数据字符集的形式返回值。它将根据character_set_results系统变量工作。

添加字符集

本节介绍如何在 MySQL 8 中添加字符集。此方法可能因字符类型而异,可能简单或复杂。在 MySQL 8 中添加字符集需要以下四个步骤:

  1. <charset>元素添加到sql/share/charsets/Index.xml文件中的MYSET。有关语法,请参考已定义的其他字符集文件。

  2. 在此步骤中,简单字符集和复杂字符集的处理方式不同。对于简单字符集,创建一个配置文件MYSET.xml,描述字符集属性,放在sql/share/charsets目录中。对于复杂字符集,需要 C 源文件。例如,在 strings 目录中创建ctype-MYSET.c类型。对于每个<collation>元素,提供ctype-MYSET.c文件。

  3. 修改配置信息:

  4. 编辑mysys/charset-def.c,并注册新字符集的排序规则。将这些行添加到declaration部分:

 #ifdef HAVE_CHARSET_MYSET
 extern CHARSET_INFO my_charset_MYSET_general_ci;
 extern CHARSET_INFO my_charset_MYSET_bin;
 #endif

将这些行添加到registration部分:

 #ifdef HAVE_CHARSET_MYSET
 add_compiled_collation(&my_charset_MYSET_general_ci);
 add_compiled_collation(&my_charset_MYSET_bin);
 #endif
    1. 如果字符集使用ctype-MYSET.c,请编辑strings/CMakeLists.txt并将ctype-MYSET.c添加到STRINGS_SOURCES变量的定义中。
  1. 编辑cmake/character_sets.cmake进行以下更改:

  • 按字母顺序将MYSET添加到CHARSETS_AVAILABLE的值中。

  • 按字母顺序将MYSET添加到CHARSETS_COMPLEX的值中。即使对于简单的字符集,也需要这样做,否则CMake将无法识别DDEFAULT_CHARSET=MYSET

  1. 重新配置、重新编译和测试。

配置字符集

MySQL 8 提供了--character-set-server--collation-server选项来配置字符集。默认字符集已从latin1更改为UTF8UTF8是主导字符集,尽管在 MySQL 的先前版本中它不是默认字符集。随着这些全球性变化的接受,字符集和排序规则

现在基于UTF8;一个常见的原因是因为UTF8支持大约 21 种不同的语言,这使得系统提供多语言支持。在配置排序规则之前,请参考dev.mysql.com/doc/refman/8.0/en/show-collation.html上提供的排序规则列表。

语言选择

MySQL 8 默认使用英语语言的错误消息,但允许用户选择其他几种语言。例如,俄语、西班牙语、瑞典语等。MySQL 8 使用lc_messages_dirlc_messages两个系统变量来管理错误消息的语言,并具有以下属性:

  • lc_messages_dir:这是一个系统变量,在服务器启动时设置。它是全局变量,因此通常由所有客户端在运行时使用。

  • lc_messages:此变量在全局和会话级别上都被使用。允许个别用户使用不同的语言来显示错误消息。例如,如果在服务器启动时设置了en_US,但如果要使用法语,则执行以下命令:

 SET lc_messages = 'fr_FR';

MySQL 8 服务器遵循以下三条错误消息文件规则:

  • MySQL 8 将在由两个系统变量lc_messages_dirlc_messages构成的位置找到文件。例如,如果使用以下命令启动 MySQL 8,则mysqld将将区域设置nl_NL映射到荷兰语,并在/usr/share/mysql/dutch目录中搜索错误文件。MySQL 8 将所有语言文件存储在MySQL8 Base Directory/share/mysql/LANGUAGE目录中。默认情况下,语言文件位于 MySQL 基目录下的share/mysql/LANGUAGE目录中。
 mysqld --lc_messages_dir=/usr/share/mysql --lc_messages=nl_NL
  • 如果目录下不存在消息文件,则 MySQL 8 将忽略lc_messages变量的值,并将lc_messages_dir变量的值视为要查找的位置。

  • 如果 MySQL 8 服务器找不到消息文件,则它会在错误日志文件中显示一条消息,并对消息使用英语。

MySQL8 的时区设置

MySQL 8 服务器以三种不同的方式管理时区:

  • 系统时区:这由system_time_zone系统变量管理,可以通过--timezone=timezone_name或在执行 mysqld 之前使用TZ环境变量来设置。

  • 服务器当前时区:这由time_zone系统变量管理。time_zone变量的默认值是SYSTEM,这意味着服务器时区与系统时区相同。MySQL 8 允许用户在启动时通过在选项文件中指定default-time-zone='*timezone*'来设置time_zone全局变量的值,并在运行时使用以下命令:

 mysql> SET GLOBAL time_zone = timezone;
  • 预连接时区:这由time_zone变量管理,特定于连接到 MySQL 8 服务器的客户端。此变量从全局time_zone变量获取其初始值,但 MySQL 8 允许用户通过执行以下命令在运行时更改它:
 mysql> SET time_zone = timezone;

此会话变量影响区域特定值的显示和存储。例如,由NOW()CURTIME()函数返回的值。另一方面,此变量不会影响以 UTC 格式显示和存储的值,例如UTC_TIMESTAMP()函数。

区域设置支持

MySQL 8 使用lc_time_names系统变量来控制语言,这将影响显示的日期、月份名称和缩写。DATE_FORMAT()DAYNAME()MONTHNAME()函数的输出取决于lc_time_names变量的值。首先浮现在脑海中的问题是,这些区域设置是在哪里定义的,我们如何获取它们?不用担心,参考www.iana.org/assignments/language-subtag-registry。所有区域设置都由互联网编号分配机构IANA)以语言和地区缩写定义。默认情况下,MySQL 8 将en_US设置为系统变量的区域设置。用户可以在服务器启动时设置值,或者如果具有SYSTEM_VARIABLES_ADMINSUPER特权,则可以设置GLOBAL。MySQL 8 允许用户检查和设置其连接的区域设置。执行以下命令在您的工作站上检查区域设置:

mysql> SET NAMES 'utf8';
Query OK, 0 rows affected (0.09 sec)

mysql> SELECT @@lc_time_names;
+-----------------+
| @@lc_time_names |
+-----------------+
| en_US |
+-----------------+
1 row in set (0.00 sec)

mysql> SELECT DAYNAME('2010-01-01'), MONTHNAME('2010-01-01');
+-----------------------+-------------------------+
| DAYNAME('2010-01-01') | MONTHNAME('2010-01-01') |
+-----------------------+-------------------------+
| Friday | January |
+-----------------------+-------------------------+
1 row in set (0.00 sec)

mysql> SELECT DATE_FORMAT('2010-01-01','%W %a %M %b');
+-----------------------------------------+
| DATE_FORMAT('2010-01-01','%W %a %M %b') |
+-----------------------------------------+
| Friday Fri January Jan |
+-----------------------------------------+
1 row in set (0.00 sec)

mysql> SET lc_time_names = 'nl_NL';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@lc_time_names;
+-----------------+
| @@lc_time_names |
+-----------------+
| nl_NL |
+-----------------+
1 row in set (0.00 sec)

mysql> SELECT DAYNAME('2010-01-01'), MONTHNAME('2010-01-01');
+-----------------------+-------------------------+
| DAYNAME('2010-01-01') | MONTHNAME('2010-01-01') |
+-----------------------+-------------------------+
| vrijdag | januari |
+-----------------------+-------------------------+
1 row in set (0.00 sec)

mysql> SELECT DATE_FORMAT('2010-01-01','%W %a %M %b');
+-----------------------------------------+
| DATE_FORMAT('2010-01-01','%W %a %M %b') |
+-----------------------------------------+
| vrijdag vr januari jan |
+-----------------------------------------+
1 row in set (0.00 sec)</strong>

MySQL 8 服务器日志

MySQL 8 服务器提供了以下不同类型的日志,使用户能够跟踪服务器在各种情况下的活动:

日志类型 写入日志的信息
错误日志 启动、运行或停止mysqld时遇到的问题
通用查询日志 已建立的客户端连接和从客户端接收到的语句
二进制日志 更改数据的语句(也用于复制)
中继日志 从复制主服务器接收到的数据更改
慢查询日志 执行时间超过long_query_time秒的查询
DDL 日志(元数据日志) DDL 语句执行的元数据操作

您可以在dev.mysql.com/doc/refman/8.0/en/server-logs.html了解有关不同类型日志的更多信息。

MySQL 8 不会生成 MySQL 8 中的日志,除非在 Windows 中的错误日志中启用。默认情况下,MySQL 8 将所有日志存储在数据目录下的文件中。当我们谈论文件时,会有很多问题涌入我们的脑海,对吧?例如;文件的大小是多少?会生成多少个文件?我们如何刷新日志文件?MySQL 8 提供了各种配置来管理日志文件;我们将在本章的后面部分看到所有这些配置。另一个重要的问题是我们在哪里存储日志?在表中还是在文件中?以下是一些描述表与文件相比的优点的要点:

  • 如果日志存储在表中,则其内容可通过 SQL 语句访问。这意味着用户可以执行带有所需条件的选择查询,以获得特定的输出。

  • 任何远程用户都可以连接到数据库并获取日志的详细信息。

  • 日志条目由标准格式管理。您可以使用以下命令检查日志表的结构:

通用日志的代码:

SHOW CREATE TABLE mysql.general_log;

慢查询日志的代码:

SHOW CREATE TABLE mysql.slow_log;

错误日志

此日志用于记录从 MySQL 8 启动到结束期间发生的错误、警告和注释等诊断消息。MySQL 8 为用户提供了各种配置和组件,以便根据其要求生成日志文件。当我们开始写入文件时,会有一些基本问题涌入脑海;我们要写什么?我们如何写?我们要写到哪里?让我们从第一个问题开始。MySQL 8 使用log_error_verbosity系统变量,并分配以下过滤选项来决定应将哪种类型的消息写入错误日志文件:

  • 仅错误

  • 错误和警告

  • 错误,警告和注释

要在目的地位置写入 MySQL 使用以下格式,其中时间戳取决于log_timestamps系统变量:

timestamp thread_id [severity] message 

写入日志文件后,首先要考虑的问题是,我们如何刷新这些日志?为此,MySQL 8 提供了三种方法:FLUSH ERROR LOGSFLUSH LOGSmysqladmin flush-logs。这些命令将关闭并重新打开正在写入的日志文件。当我们谈论如何写入以及在哪里写入时,有很多事情要理解。

组件配置

MySQL 8 使用log_error_services系统变量来控制错误日志组件。它允许用户通过分号分隔的方式定义多个组件以进行执行。在这里,组件将按照定义的顺序执行。用户可以在以下约束条件下更改此变量的值:

  • 安装组件:要启用任何日志组件,我们必须首先使用此命令安装它,然后通过在log_error_services系统变量中列出该组件来使用该组件。按照以下命令添加log_sink_syseventlog组件:
 INSTALL COMPONENT 'file://component_log_sink_syseventlog';
 SET GLOBAL log_error_services = 'log_filter_internal; 
          log_sink_syseventlog';

执行安装命令后,MySQL 8 将注册该组件到mysql.component系统表中,以便在每次启动时加载。

  • 卸载组件:要禁用任何日志组件,首先从log_error_services系统变量列表中删除它,然后使用此命令卸载它。执行以下命令以卸载组件:
 UNINSTALL COMPONENT 'file://component_log_sink_syseventlog';

要在每次启动时启用错误日志组件,请在my.cnf文件中定义它,或使用SET_PERSIST。当我们在my.cnf中定义它时,它将从下一次重新启动开始生效,而SET_PERSIST将立即生效。使用以下命令进行SET_PERSIST

 SET PERSIST log_error_services = 'log_filter_internal; 
          log_sink_internal; 
          log_sink_json'; 

MySQL 8 还允许用户将错误日志写入系统日志:对于 Microsoft,请考虑事件日志,对于基于 Unix 的系统,请考虑 syslog。要将错误日志记录到系统logfibf中,配置log_filter_internal和系统日志写入器log_sink_syseventlog组件,并按照上述说明执行相同的指令。另一种方法是将 JSON 字符串写入日志文件配置log_sink_json组件。关于 JSON 写入器的一个有趣的点是,它将通过添加 NN(两位数)来管理文件命名约定。例如,将文件名视为file_name.00.jsonfile_name.01.json等。

默认错误日志目的地配置

错误日志可以写入日志文件或控制台。本节描述了如何在不同环境中配置错误日志的目的地。

Windows 上的默认错误日志目的地

  • --console:如果给出此选项,则控制台将被视为默认目的地。在定义了两者的情况下,--console优先于--log-error。如果默认位置是控制台,那么 MySQL 8 服务器将log_error变量的值设置为stderror

  • --log-error:如果未给出此选项,或者给出但未命名文件,则默认文件名为host_name.err,并且该文件将在数据目录中创建,除非指定了--pid-fileoption。如果在–pid-file选项中指定了文件名,则命名约定将是数据目录中带有.err后缀的PID文件基本名称。

Unix 和类 Unix 系统上的默认错误日志目的地

在 Unix 系统中,Microsoft Windows 中提到的所有上述情况将由–log_error选项管理。

  • --log-error:如果未给出此选项,则默认目的地是控制台。如果未给出文件名,则与 Windows 一样,它将在数据目录中创建一个名为host_name.err的文件。用户可以在mysqldmysqld_safe部分的选项文件中指定–log-error

一般查询日志

一般查询日志是一个通用日志,用于记录mysqld执行的所有操作。在此日志中,文件语句按接收顺序编写,但执行顺序可能与接收顺序不同。它从客户端连接开始记录,并持续到断开连接。除了 SQL 命令,它还记录了connection_type,即协议客户端连接的方式,例如 TCP/IP、SSL、Socket 等。由于它记录了mysqld执行的大部分操作,当我们想要查找客户端发生了什么错误时,它非常有用。

默认情况下,此日志被禁用。我们可以使用--general_log[={0|1}]命令来启用它。当我们不指定任何参数或将 1 定义为参数时,表示启用一般查询日志,而 0 表示禁用日志。此外,我们可以使用--general_log_file=file_name命令指定日志文件名。如果命令未指定文件名,则 MySQL 8 将考虑默认名称为host_name.log。设置日志文件名对日志记录没有影响,如果日志目的地值不包含FILE。服务器重新启动和日志刷新不会导致生成新的一般查询日志文件;您必须使用rename(对于 Microsoft Windows)或mv(对于 Linux)命令来创建新文件。MySQL 8 提供了第二种在运行时重命名文件的方法,方法是使用以下命令禁用日志:

SET GLOBAL general_log = 'OFF';

禁用日志后,使用ON选项重命名日志文件并再次启用日志。同样,要在特定连接的运行时启用或禁用日志,请使用会话sql_log_off变量和ONOFF选项。另一个选项是与一般日志文件对齐的,即--log-output。通过使用此选项,我们可以指定日志输出的目的地;这并不意味着日志已启用。

此命令提供了以下三种不同的选项:

  • TABLE:记录到表中

  • FILE:记录到文件中

  • NONE:不记录到表或文件中。如果存在NONE,则优先于任何其他指定符。

如果省略了--log-output选项,则默认值为文件。

二进制日志

二进制日志是一个文件,其中包含描述数据库事件的所有事件,例如表创建、数据更新和表中的删除。它不用于SELECTSHOW语句,因为它不会更新任何数据。二进制日志写入会稍微降低数据库操作的性能,但它使用户能够使用复制设置和操作还原。二进制日志的主要目的是:

  1. 用于主从架构的复制:基于二进制文件的复制,主服务器执行插入和更新操作,这些操作在二进制日志文件中反映出来。现在,从节点被配置为读取这些二进制文件,并且相同的事件在从服务器的二进制文件中执行,以便将数据复制到从服务器上。

  2. 数据恢复操作:一旦备份被还原到数据库中,二进制日志的事件将被记录,并以重新执行的形式执行这些事件,从而使数据库从备份点更新到最新状态。

二进制日志默认启用,这表明 log_bin 系统变量设置为 ON。要禁用此日志,请在启动时使用--skip-log-bin--disable-log-bin选项。要删除所有二进制日志文件,请使用 RESET MASTER 语句,或者使用PURGE BINARY LOGS删除其中的一部分。MySQL 8 服务器使用以下三种日志格式将信息记录到二进制日志文件中:

  1. 基于语句的日志记录:通过使用--binlog-format=STATEMENT命令启动服务器来使用此格式。这主要是 SQL 语句的传播。

  2. 基于行的日志记录:在服务器启动时使用--binlog-format=ROW启用基于行的日志记录。此格式指示行受到的影响。这是默认选项。

  3. 混合日志记录:使用--binlog-format=MIXED选项启动 MySQL 8 以启用混合日志记录。在此模式下,默认情况下可用语句基础日志记录,并且在某些情况下 MySQL 8 将自动切换到基于行的日志记录。

MySQL 8 允许用户在全局和会话范围内在运行时更改格式。全局格式适用于所有客户端,而会话格式适用于单个客户端。以下分别设置全局和会话范围的格式:

mysql> SET GLOBAL binlog_format = 'STATEMENT';
mysql> SET SESSION binlog_format = 'STATEMENT';

有两种特殊情况下我们无法更改格式:

  • 在存储过程或函数中

  • 在设置为行格式并且临时表处于打开状态的情况下

MySQL 8 具有--binlog-row-event-max-size变量,用于以字节为单位控制二进制日志文件的大小。将此变量的值分配为 256 的倍数;此选项的默认值为 8192。MySQL 8 的各个存储引擎都有其自己的日志记录能力。如果存储引擎支持基于行的日志记录,则称为行日志能力,如果存储引擎支持基于语句的日志记录,则称为语句日志能力。有关存储引擎日志记录能力的更多信息,请参考下表。

存储引擎 支持行日志记录 支持语句日志记录
ARCHIVE
BLACKHOLE
CSV
EXAMPLE
FEDERATED
HEAP
InnoDB 当事务隔离级别为REPEATABLEREADSERIALIZABLE时为是;否则为否。
MyISAM
MERGE
NDB

如本节所述,二进制日志将根据语句类型(安全、不安全或二进制注入)、日志格式(ROWSTATEMENTMIXED)以及存储引擎的日志功能(行可用、语句可用、两者都可用或两者都不可用)进行工作。要了解二进制日志记录的所有可能情况,请参考此链接中给出的表格:dev.mysql.com/doc/refman/8.0/en/binary-log-mixed.html

慢查询日志

慢查询日志用于记录执行时间长的 SQL 语句。MySQL 8 为慢查询的时间配置定义了以下两个系统变量:

  • long_query_time:用于定义查询执行的理想时间。如果 SQL 语句的执行时间超过此时间,则被视为慢查询,并将语句记录到日志文件中。默认值为 10 秒。

  • min_examined_row_limit:执行每个查询所需的最短时间。默认值为 0 秒。

MySQL 8 不会将获取锁的初始时间计入执行时间,并且在所有锁释放并完成查询执行后将慢查询日志返回到文件中。当启动 MySQL 8 时,默认情况下禁用慢查询日志;要启动此日志,请使用slow_query_log[={0|1}]命令,其中0表示禁用慢查询日志,1 或无参数用于启用它。要记录不使用索引的管理语句和查询,请使用log_slow_admin_statementslog_queries_not_using_indexes变量。这里,管理语句包括ALTER TABLEANALYZE TABLECHECK TABLECREATE INDEXDROP INDEXOPTIMIZE TABLEREPAIR TABLE。MySQL 8 允许用户使用--slow_query_log_file=file_name命令指定日志文件的名称。如果未指定文件名,则 MySQL 8 将在数据目录中使用host_name-slow.log命名约定创建文件。要将最少的信息写入此日志文件,请使用--log-short-format选项。

上述所有描述的参数由 MySQL 8 按以下顺序控制:

  1. 查询必须不是管理语句,或者log_slow_admin_statements必须已启用

  2. 查询必须至少花费long_query_time秒,或者启用了log_queries_not_using_indexes,并且查询必须没有使用索引进行行查找

  3. 查询必须至少检查min_examined_row_limit

  4. 查询不应根据log_throttle_queries_not_using_indexes设置被抑制

--log-output选项也适用于此日志文件,并具有与通用日志相同的实现和效果。

DDL 日志

正如名称所示,此日志文件用于记录所有与 DDL 语句执行相关的详细信息。MySQL 8 使用此日志文件来从在元数据操作执行期间发生的崩溃中恢复。让我们举一个例子来理解不同的情况:

  • 删除表 t1,t2:我们必须确保 t1 和 t2 表都被删除

当我们执行任何 DDL 语句时,这些操作的记录将被写入 MySQL 8 数据目录下的ddl_log.log文件中。该文件是一个二进制文件,不是人类可读的格式。用户不允许更新此日志文件的内容。在 MySQL 服务器的正常执行中不需要记录元数据语句;只有在需要时才启用它。

服务器日志维护

为了维护日志文件,我们必须定期清理以管理磁盘空间。对于基于 RPM 的 Linux 系统,mysql-log-rotate脚本会自动提供。对于其他系统,没有这样的脚本,因此我们必须自己安装一个简短的脚本来管理日志文件。MySQL 8 提供了expire_logs_days系统变量,用于管理二进制日志文件。使用此变量,二进制日志文件将在指定期限后自动删除。

此变量的默认值为 30 天;您可以通过配置更改其值。二进制日志文件在服务器启动时或日志刷新时删除。在复制的情况下,您还可以使用binlog_expire_logs_seconds系统变量来管理主服务器和从服务器的日志。日志刷新执行以下任务:

  • 如果启用了一般查询日志或慢查询日志到日志文件,服务器将关闭并重新打开查询日志文件

  • 如果启用了二进制日志记录,服务器将关闭当前的二进制日志文件,并打开下一个序列号的新日志文件

  • 如果服务器是使用--log-error选项启动的,以导致错误日志被写入文件,服务器将关闭并重新打开日志文件

在生成新的日志文件之前备份或重命名旧的日志文件,可以在 Unix 系统中使用mv(移动)命令,在 Windows 中使用rename函数。对于一般查询和慢查询日志文件,可以通过使用以下命令禁用日志来重命名文件:

SET GLOBAL general_log = 'OFF';

重命名日志文件后,使用以下命令启用日志:

SET GLOBAL general_log = 'ON';

总结

这对于任何 MySQL 8 用户来说都是一个有趣的章节,不是吗?在本章中,我们了解了 MySQL 8 如何管理不同的日志文件,以及在什么时候使用哪个日志文件。同时,我们还涵盖了许多管理功能,例如全球化、系统数据数据库和组件和插件配置,并解释了如何在单台机器上运行多个实例。本章的后半部分涵盖了日志维护。

接下来,我们将为您提供有关存储引擎的信息,例如不同类型的存储引擎是什么,哪种适合您的应用程序,以及如何为 MySQL 8 创建自定义存储引擎。

第六章:MySQL 8 存储引擎

在上一章中,我们学习了如何设置新系统、数据字典和系统数据库。提供了有关缓存技术、全球化、不同类型组件和插件配置以及对管理非常重要的几种类型日志文件的详细信息。

本章详细介绍了 MySQL 8 存储引擎。它详细解释了InnoDB存储引擎及其特性,并提供了有关自定义存储引擎创建以及如何使其可插拔以便安装在 MySQL 8 中的实用指南。本章将涵盖以下主题:

  • 存储引擎概述

  • 多种类型的存储引擎

  • InnoDB存储引擎

  • 创建自定义存储引擎

存储引擎概述

存储引擎是 MySQL 组件,用于处理不同类型表格中使用的 SQL 操作。MySQL 存储引擎旨在管理不同类型环境中的不同类型任务。了解并选择最适合系统或应用需求的存储引擎非常重要。在接下来的章节中,我们将详细了解存储引擎的类型、默认存储引擎以及自定义存储引擎的创建。

让我们来看看为什么存储引擎是数据库中非常重要的组件,包括 MySQL 8。存储引擎与数据库引擎一起在不同环境中执行各种类型的任务。它们以语句的形式在数据库中对数据执行创建、读取、更新和删除操作。当您在创建表语句中提供ENGINE参数时,看起来很简单,但对于每个通过 SQL 语句发送的请求,需要对数据执行大量操作的配置。它远不止是持久化数据 - 引擎还负责存储限制、事务、锁定粒度/级别、多版本并发控制、地理空间数据类型、地理空间索引、B 树索引、T 树索引、Hash索引、全文搜索索引、聚集索引、数据缓存、索引缓存、压缩数据、加密数据、集群数据库、复制、外键、备份、查询缓存以及更新数据字典的统计信息。

MySQL 存储引擎架构

MySQL 存储引擎的可插拔架构允许数据库专业人员为任何特定应用程序选择任何存储引擎。MySQL 存储引擎架构提供了一个简单的应用模型和 API,具有一致性,可以隔离数据库管理员和应用程序员免受存储级别的所有底层实现细节的影响。因此,应用程序始终在不同存储引擎的不同功能之上运行。它提供了标准的管理和支持服务,适用于所有底层存储引擎。

存储引擎在物理服务器级别上对持久化数据执行活动。这种模块化和高效的架构为任何特定应用程序的特定需求提供了解决方案,例如事务处理、高可用性情况或数据仓库,并且同时具有独立于底层存储引擎的接口和服务的优势。

数据库管理员和应用程序员通过连接器 API 和服务与 MySQL 数据库进行交互,这些 API 和服务位于存储引擎之上。MySQL 服务器架构使应用程序免受存储引擎的详细级别复杂性的影响,通过提供易于使用的 API,这些 API 在所有存储引擎上都是一致的和适用的。如果应用程序需要更改底层存储引擎,或者添加一个或多个存储引擎以支持应用程序的需求,那么不需要进行重大的编码或流程更改即可使事情正常运行。

多种类型的存储引擎

现在我们知道了存储引擎的重要性,以及从众多可用的存储引擎中选择使用哪些存储引擎的关键决策。让我们看看有哪些可用的存储引擎以及它们的规格。当您开始考虑存储引擎时,InnoDB是您首先想到的名字,对吧?

InnoDB 是 MySQL 8 中的默认和最通用的存储引擎,Oracle 建议将其用于表以及特殊用例。MySQL 服务器具有可插拔的存储引擎架构,可以从已经运行的 MySQL 服务器中加载和卸载存储引擎。

在 MySQL 8 中,识别服务器支持的存储引擎非常容易。我们只需进入 MySQL shell 或提示符,并使用SHOW ENGINES语句。在提示时输入该语句,结果将是一列引擎,包括 Engine、Support、Transactions、Savepoints 和 Comment。

支持列中的值 DEFAULT、YES 和 NO 表示存储引擎是否可用,并当前设置为默认存储引擎。

InnoDB 存储引擎概述

InnoDB是 MySQL 8 中默认的、最通用的存储引擎,提供高可靠性和高性能。

如果您没有配置不同的默认存储引擎,那么在 MySQL 8 中发出不带ENGINE =子句的 SQL 语句CREATE TABLE将创建一个具有存储引擎InnoDB作为默认引擎的表。

InnoDB存储引擎提供的功能和优势将在InnoDB 存储引擎部分中进行解释。

自定义存储引擎

MySQL 5.1 和所有后续版本以及 MySQL 8 中的存储引擎架构都利用了灵活的存储引擎架构。

存储引擎可插拔架构提供了创建和添加新存储引擎的能力,而无需重新编译服务器,直接添加到正在运行的 MySQL 服务器。这种架构使得开发和部署新的存储引擎到 MySQL 8 变得非常容易。

我们将在即将到来的创建自定义存储引擎部分中使用 MySQL 存储引擎架构的可插拔特性来开发一个新的存储引擎。

多种类型的存储引擎

在这一部分,我们将更仔细地查看 MySQL 8 支持的广泛使用的存储引擎。但在查看它们之前,让我们看看存储引擎架构是如何可插拔的,并提供了灵活性,以便在同一模式或服务器中使用多个存储引擎。

以下是 MySQL 8 支持的存储引擎列表。

  • InnoDB:MySQL 8 的默认存储引擎。它是一个符合ACID(事务安全)的存储引擎,具有提交、回滚和崩溃恢复,用于保护用户数据和引用完整性约束以维护数据完整性,等等。

  • MyISAM:具有小占用空间的表的存储引擎。它具有表级锁定,因此主要用于只读或读最多的数据工作负载,例如数据仓库和 Web 配置。

  • Memory:以前被称为HEAP引擎的存储引擎。它将数据保存在 RAM 中,提供更快的数据访问,主要用于非关键数据环境的快速查找。

  • CSV:这种存储引擎使用文本文件和表中的逗号分隔值作为表。它们没有索引,主要用于以CSV格式导入和转储数据。

  • 存档:这种存储引擎包括紧凑的、无索引的表,旨在存储和检索大量历史、归档或安全审计数据。

  • 黑洞:这种存储引擎包括用于复制配置的表。查询总是返回一个空集。DML SQL 语句被发送到从服务器。它接受数据,但数据不会被存储,就像在 Unix 的/dev/null设备中使用一样。

  • 合并:这种存储引擎提供了将一系列相似的MyISAM表逻辑分组并将它们称为一个对象的能力,而不是单独的表。

  • 联合:这种存储引擎可以将许多独立的物理 MySQL 服务器链接成一个逻辑数据库。它非常适合数据仓库或分布式环境。

  • 示例:这种存储引擎什么也不做,只是作为一个存根。它主要由开发人员使用,用来演示如何在 MySQL 源代码中开始编写新的存储引擎。

MySQL 不限制在整个服务器或模式上使用相同的存储引擎;相反,在表级别指定引擎使其根据数据类型和应用程序的用例变得灵活。

可插拔存储引擎架构

MySQL 服务器使用可插拔存储引擎架构,可以从已运行的 MySQL 服务器中加载和卸载存储引擎:

  • 安装存储引擎:在服务器中使用存储引擎之前,必须使用INSTALL PLUGIN SQL 语句将存储引擎插件共享库加载到 MySQL 中。如果您创建了一个名为MyExampleMYEXAMPLE引擎插件,并且共享库的名称为MyExample.so,那么您需要使用以下语句加载它们:
 mysql> INSTALL PLUGIN MyExample SONAME 'MyExample.so';

要安装存储引擎,发出前述语句的用户必须对mysql.plugin表具有INSERT权限,并且插件文件必须存在于 MySQL 插件目录中。共享库也必须存在于plugin_dir变量中给出的 MySQL 服务器插件目录中。

  • 卸载存储引擎:在卸载存储引擎之前,请确保没有表在使用该存储引擎。如果卸载了一个存储引擎,并且任何现有表需要该存储引擎,那么这些表将变得不可访问,并且只会存在于适用的磁盘上。如果您卸载了名为MyExampleMYEXAMPLE引擎插件,然后执行以下语句来卸载存储引擎:
 mysql> UNINSTALL PLUGIN MyExample ;

常见的数据库服务器层

MySQL 可插拔存储引擎负责在实际数据上执行 I/O 操作,并满足特定应用程序的需求,包括在需要时启用和强制执行所需的功能。使用特定或单一存储引擎更有可能导致更高的效率和更高的数据库性能,因为该引擎仅启用特定应用程序所需的功能,从而减少数据库的系统开销。

存储引擎支持以下独特的基础设施组件或键:

  • 并发性:一些应用程序对锁级别(如行级锁)的要求比其他应用程序更细粒度。选择正确/错误的锁定策略以及多版本并发控制或快照读取功能都可能影响整体性能和由于锁定而产生的开销。

  • 事务支持:存在非常明确定义的要求,比如ACID兼容性,如果应用程序需要事务,则还有更多要求。

  • 引用完整性:服务器可以使用DDL定义的外键来强制关系数据库引用完整性,如果需要的话。

  • 物理存储:这包括从表和索引的页面大小到在物理磁盘上存储数据所使用的格式等一切。

  • 索引支持:这包括基于应用程序需求的索引策略,因为每个存储引擎都有自己的索引方法。

  • 内存缓存:这是基于应用程序需求的缓存策略,因为每个存储引擎都有自己的缓存方法,以及所有存储引擎的通用内存缓存。

  • 性能辅助:这涉及到大量插入处理、数据库检查点、多个 I/O 线程进行并行操作、线程并发性等。

  • 其他目标特性:这可能包括对某些数据操作的安全限制、地理空间操作和其他类似特性的支持。

前述的基础设施组件都是为了支持特定应用程序需求的一组特定功能而设计的,因此非常重要的是要非常仔细地了解应用程序的需求,并选择正确的存储引擎,因为这可能会影响整个系统的效率和性能。

设置存储引擎

当使用CREATE TABLE语句创建新表时,可以使用ENGINE表选项指定要为表使用的引擎。如果不指定ENGINE表选项,则将使用默认的存储引擎。InnoDB是 MySQL 8.0 的默认引擎。您还可以使用ALTER TABLE语句将表从一个存储引擎转换为另一个存储引擎,如下例所示:

CREATE TABLE table1 (i1 INT) ENGINE = INNODB;
CREATE TABLE table3 (i3 INT) ENGINE = MEMORY;
ALTER TABLE table3 ENGINE = InnoDB;

可以通过设置default_storage_engine变量为当前会话设置默认存储引擎,如下例所示:

SET default_storage_engine=MEMORY;

使用CREATE TEMPORARY TABLE创建TEMPORARY表的默认存储引擎可以通过在启动或运行时设置default_tmp_storage_engine变量来单独设置。

MyISAM存储引擎

MyISAM存储引擎使用占用空间小的表。它实现了表级锁定,因此主要用于只读或读取大部分数据负载的情况,例如数据仓库和 Web 配置。每个MyISAM表都存储在磁盘上的两个文件中。文件名以表名和其扩展类型开头,一个带有.MYD扩展名的数据文件,另一个带有.MYI扩展名的索引文件。

对于MyISAM引擎,有几个在mysqld中指定的启动选项可以改变MyISAM表的行为;例如:

--myisam-recover-options=mode

此选项将设置在MyISAM中崩溃表的自动恢复模式。

MyISAM中需要用于键的空间,MyISAM表使用B-Tree索引,并且在String索引中使用空间压缩。如果一个字符串是索引的第一部分,那么还会进行前缀压缩,这样整体使索引文件大小更小。前缀压缩有助于处理许多具有相似前缀的字符串。通过在MyISAM表中使用表选项PACK_KEYS=1,前缀压缩也可以应用于数字,如果有许多具有相似前缀的数字。

在 MySQL 8.0 中,不支持对MyISAM表进行分区。

MyISAM表的一些重要特性如下:

  • 存储的所有数据值都以低字节优先顺序存储,这使得数据独立于机器和操作系统

  • 所有数值键值都以高字节优先顺序存储,这允许更好的索引压缩

  • MyISAM表的行数限制为(2³²)²(1.844E+19)

  • MyISAM表的最大索引数限制为 64 个

  • MyISAM表的列索引最大数限制为 16 个

  • MyISAM中支持并发插入,如果表在数据文件中间没有空闲块

  • MyISAM中也可以对TEXTBLOB类型的列进行索引

  • 在索引列中,允许NULL

  • 每一列都可以有不同的字符集

  • 它还支持真正的 VARCHAR 类型列,其起始长度存储为 1 或 2 个字节,具有固定或动态行长度的 VARCHAR 列,以及任意长度的 UNIQUE 约束

  • MyISAM 表存储格式:MyISAM 支持以下三种不同类型的存储格式:

  • 静态表:MyISAM 存储引擎中表的默认格式,具有固定大小的列

  • 动态表:顾名思义,包含可变大小列的格式,包括 VARCHAR、BLOB 或 TEXT

  • 压缩表:用于在 MyISAM 存储引擎表中保存只读数据和压缩格式的表格格式

前两种格式,固定和动态,根据使用的列类型自动选择。压缩格式可以通过使用 myisampack 实用程序创建。

  • MyISAM 表问题:文件格式经过了广泛测试,但有些情况会导致数据库表损坏。让我们看看这些情况以及恢复这些表的方法。

在以下事件中可能会出现损坏的表:

  • 如果 mysqld 进程在写入过程中被杀死

  • 如果有意外的计算机关闭

  • 如果有任何硬件故障

  • 如果 MySQL 服务器和外部程序(如 myisamchk)同时修改表

  • MySQL 或 MyISAM 代码存在软件错误

使用 CHECK TABLE 语句检查表的健康状况,并尝试使用 REPAIR TABLE 语句修复任何损坏的 MyISAM 表。

MyISAM 表可能出现的问题是表没有被正确关闭。为了确定表是否被正确关闭,每个 MyISAM 索引文件在标头中保留一个计数器。在以下情况下,计数器可能不正确:

  • 如果表在不发出 LOCK TABLES 和 FLUSH TABLES 的情况下被复制

  • MySQL 在更新期间最终关闭之前崩溃

  • mysqld 正在使用表,同时被另一个程序修改:myisamcheck --recover 或 myisamchk --update-state

MEMORY 存储引擎

MEMORY 存储引擎,以前也称为 HEAP 引擎,将数据保存在 RAM 中,提供更快的数据访问。它主要用于快速查找非关键数据环境。它创建专用表,其中内容存储在内存中,但数据容易受到崩溃、停电和硬件问题的影响。因此,这些表用于临时工作区或在从其他表中提取数据后缓存只读数据。

您应该选择使用 MEMORY 还是 NDB Cluster。您应该检查应用程序是否需要重要的、高可用的或经常更新的数据,并考虑 NDB Cluster 是否是更好的选择。NDB Cluster 提供与 MEMORY 引擎相同的功能,但性能水平更高,并且具有 MEMORY 引擎不提供的其他功能。这些包括:

  • 客户端之间的低争用通过多线程操作和行级锁定

  • 包括写入的语句混合的可伸缩性

  • 数据耐久性;它支持可选的磁盘支持操作

  • 无共享架构,提供多主机操作而没有单点故障,为应用程序提供 99.999%的可用性

  • 自动数据分布跨节点

  • 支持可变长度数据类型,包括 BLOB 和 TEXT

MEMORY 表不支持分区

性能取决于服务器的繁忙程度以及单线程执行对更新处理期间的表锁开销的影响。在更新处理期间对表进行锁定会导致在 MEMORY 表上的多个会话的并发使用减慢。

MEMORY 表特点:表定义存储在 MySQL 数据字典中,并不在磁盘上创建任何文件。以下是表特性的亮点:

  • 100%动态哈希用于插入,并且空间分配在小块中。

  • 不需要额外的键空间、溢出区域或空闲列表的额外空间。通过将行放入链接列表中重用已删除的行来插入新记录。

  • 固定长度行存储格式,VARCHAR,以固定长度存储。无法存储BLOBTEXT列。

  • 支持AUTO_INCREMENT列。

MEMORY存储引擎支持HASHBTREE类型的索引。MEMORY表每个表最多有 64 个索引,每个索引最多有 16 列,最大键长度为 3,072 字节。MEMORY表也可以有非唯一键。

用户创建和临时表:服务器在处理查询时动态创建内部临时表。两种类型的表在存储转换上有所不同,其中MEMORY表不受转换的影响:

  • 当内部临时表变得太大时,服务器会自动将其转换为磁盘存储

  • 用户创建的MEMORY表不会被服务器转换

可以使用--init-file选项,使用INSERT INTO ... SELECTLOAD DATA INFILE语句从任何持久性数据源加载数据,如果需要的话。

CSV 存储引擎

该存储引擎以逗号分隔值的形式将数据存储在文本文件中。该引擎始终编译到 MySQL 服务器中,可以从 MySQL 分发的storage/csv目录中检查源代码。

服务器创建的数据文件以给定表和扩展名.CSV开头。数据文件是一个纯文本文件,以逗号分隔值格式包含数据。

MySQL 服务器创建一个与CSV表对应的元文件,该文件存储有关表状态和表中存在的行数的信息。元文件也与表名一起存储在以.CSM扩展名开头的位置。

  • 修复和检查CSV:存储引擎支持CHECKREPAIR语句来验证并可能修复损坏的CSV表。您可以使用CHECK TABLE语句来验证或验证表,并使用REPAIR TABLE语句来修复从现有CSV数据文件复制有效行并用新复制/恢复的行替换现有文件的表。

在修复过程中,只有从CSV数据文件到第一个损坏的行的行被复制到新表或复制的数据文件中。损坏行后的其余行将从表中删除,包括有效行,因此建议您在进行修复之前对数据文件进行足够的备份。

CSV存储引擎不支持索引或分区,所有使用CSV存储引擎创建的表必须在所有列上具有NOT NULL属性。

ARCHIVE 存储引擎

ARCHIVE存储引擎创建专用表,用于存储大量未索引数据,占用非常小的空间。

当创建ARCHIVE表时,它以表名开头,并以.ARZ扩展名结尾。在优化操作期间,可能会出现一个带有.ARN扩展名的文件。

引擎支持AUTO_INCREMENT列属性。它还支持INSERTREPLACESELECTBLOB列(除了空间数据类型),但不支持DELETEUPDATEORDERBY操作。

ARCHIVE存储引擎不支持分区:

  • 存储:该引擎使用zlib进行无损数据压缩,并在插入时对行进行压缩。它支持CHECK TABLE操作。引擎使用几种插入类型:

  • INSERT语句将行发送到压缩缓冲区,并根据需要刷新缓冲区。压缩缓冲区中的插入受锁保护,只有在请求SELECT时才会发生刷新。

  • 完成后可以看到一个批量缓冲区。只有在同时发生其他插入时才能看到。在加载任何正常插入时,刷新不会在SELECT时发生。

  • 检索:检索后,根据请求解压行,并且不使用任何行缓存。对于SELECT操作执行完整的表扫描:

  • SELECT检查当前有多少行可用,并且只读取该数量的行。它作为一次一致的读操作执行。

  • SHOW TABLE STATUS报告的行数对于ARCHIVE表始终是准确的。

  • 使用OPTIMIZE TABLEREPAIR TABLE操作以实现更好的压缩。

BLACKHOLE 存储引擎

BLACKHOLE存储引擎充当黑洞。它接受数据但不存储数据,查询总是返回空结果。

服务器只有在创建BLACKHOLE表并且没有文件与该表关联时,才会在全局数据字典中添加表定义。

BLACKHOLE存储引擎支持各种索引,因此可以在表定义中包含相同的内容。

BLACKHOLE存储引擎不支持分区。

对表的插入不会存储任何数据,但如果为语句启用了二进制日志记录,则会记录并复制到从服务器。这种机制可用作过滤器或中继器。

BLACKHOLE存储引擎有以下可能的用途:

  • 转储文件语法验证

  • 使用启用或禁用二进制日志记录的BLACKHOLE性能比较的开销测量

  • 它还可用于查找任何性能瓶颈,除了存储引擎本身

自增列:由于该引擎是一个无操作引擎,它不会增加任何字段值,但它对复制有影响,这可能非常重要。考虑以下情况:

  1. 主服务器具有带有主键的自增字段的BLOCKHOLE

  2. 从服务器上存在相同的表,但使用MyISAM引擎

  3. INSERT语句中插入到主服务器的表中,而不设置任何自增值或使用SET INSERT_ID语句

在上述情况下,主键列上的复制将失败,因为有重复条目。

MERGE 存储引擎

MERGE存储引擎,也称为MRG_MyISAM引擎,是一组类似的表,可以作为一个表来使用。这里的“类似”意味着所有表具有相似的列数据类型和索引信息。不可能合并列顺序不同的表,或者在各自列中具有相同的数据类型,或者以不同的顺序进行索引。

以下是不会限制合并的表中的差异列表:

  • 各自列和索引的名称可能不同。

  • 表,列和索引之间的注释可能不同。

  • AVG_ROW_LENGTHMAX_ROWSPACK_KEYS表选项可能不同。

创建MERGE表时,MySQL 还会在磁盘上创建一个.MRG文件,其中包含正在使用的MyISAM表的名称。表的格式存储在 MySQL 数据字典中,底层表不需要在与MERGE表相同的数据库中。

必须具有对与MERGE表映射的MyISAM表的SELECTUPDATEDELETE权限,因此可以使用SELECTINSERTUPDATEDELETE语句对MERGE表进行操作。

MERGE表上执行DROP TABLE语句将仅删除MERGE的规范,对底层表不会产生影响。

使用MERGE表存在以下安全问题。如果用户可以访问MyISAMt1,那么用户可以创建可以访问t1MERGEm1。现在,如果用户对表t1的权限被撤销,用户仍然可以通过使用表m1继续访问表t1

FEDERATED 存储引擎

FEDERATED存储引擎可以将许多独立的物理 MySQL 服务器链接成一个逻辑数据库,因此可以让您访问远程 MySQL 服务器的数据,而无需使用复制或集群技术。

当我们查询本地FEDERATED表时,会自动从远程联合表中提取数据,不需要将数据存储在本地表中。

FEDERATED存储引擎不是 MySQL 服务器的默认支持,但是使用--federated选项启动服务器将启用FEDERATED引擎选项。

创建FEDERATED表时,表定义与其他表相同,但关联数据的物理存储是在远程服务器上处理的。FEDERATED表包括以下两个元素:

  • 一个远程服务器,其中包含一个由表定义和相关表数据组成的数据库表。这种类型的表可以是远程服务器支持的任何类型,包括MyISAMInnoDB

  • 一个本地服务器,其中包含一个由远程服务器上相同的表定义组成的数据库表。表定义存储在数据字典中,本地服务器上没有关联的数据文件存储。相反,除了表定义之外,它还保留一个指向远程表本身的连接字符串。

当在FEDERATED表上执行 SQL 语句时,本地服务器和远程服务器之间的信息流如下:

  1. 该引擎检查表的每一列,并构建一个适当的 SQL 语句,引用远程表。

  2. MySQL 客户端 API 用于将 SQL 语句发送到远程服务器。

  3. 该语句由远程服务器处理,并且本地服务器检索相应的结果。

EXAMPLE 存储引擎

EXAMPLE存储引擎只是一个存根引擎,其目的是在 MySQL 源代码中提供示例,以帮助开发人员编写新的存储引擎。

要使用EXAMPLE引擎源代码,请查看 MySQL 源代码分发下载的storage/example目录。

如果使用EXAMPLE引擎创建表,则不会创建文件。数据不能存储在EXAMPLE引擎中,并且返回空结果。

EXAMPLE存储引擎不支持索引和分区。

InnoDB 存储引擎

InnoDB是最通用的存储引擎,也是 MySQL 8 中的默认引擎,提供高可靠性和高性能。

InnoDB存储引擎提供的主要优势如下:

  • DML操作遵循ACID模型,并且事务具有提交、回滚和崩溃恢复功能,以保护用户数据

  • Oracle-style提供一致的读取和行级锁定,增加了多用户并发性能

  • 每个InnoDB表都有一个主键索引,称为聚簇索引,它按顺序在磁盘上排列数据,以优化基于主键的查询,并在主键查找期间最小化 I/O

  • 通过支持外键,插入、删除和更新都会进行检查,以确保跨不同表的一致性,以维护数据完整性

使用InnoDB表的主要优势如下:

  • 如果服务器由于任何硬件或软件问题而崩溃,无论当时服务器正在处理什么更改,重新启动服务器后都不需要进行任何特殊操作。它具有崩溃恢复系统,可以处理在服务器崩溃期间提交的更改。它将转到这些更改并从处理中断的地方开始。

  • 引擎具有自己的缓冲池,用于根据访问的数据将表和索引数据缓存到内存中。经常使用的数据直接从缓存内存中获取,因此可以加快处理速度。在专用服务器中,它占用分配的物理内存的 80%用于缓冲池。

  • 使用外键设置将相关数据拆分到表中,强制执行引用完整性,防止在没有主表中相应数据的情况下向辅助表插入任何不相关的数据。

  • 如果内存或磁盘中存在损坏的数据,校验和机制会在我们使用之前提醒我们有损坏的数据。

  • 更改缓冲区会自动优化InsertUpdateDeleteInnoDB还允许对同一表进行并发读写访问,并缓存数据更改以简化磁盘 I/O。

  • 当从表中重复访问相同的数据行时,自适应哈希索引功能可以加快查找速度并提供性能优势。

  • 允许在表和相关索引上进行压缩。

  • 通过查询INFORMATION_SCHEMAPerformance Schema表,轻松监视存储引擎的内部工作和性能细节。

现在让我们看看存储引擎的每个区域,在这些区域中InnoDB被增强或优化以提供非常高效和增强的性能。

ACID 模型

ACID模型是一组数据库设计原则,强调可靠性,这对于关键任务应用程序和业务数据至关重要。

MySQL 具有诸如InnoDB存储引擎之类的组件,严格遵循ACID模型。因此,即使在硬件故障或软件崩溃的特殊情况下,数据也是安全且不会损坏。

使用 MySQL 8,InnoDB支持原子DDL,确保即使在执行操作时服务器停止,DDL操作也会完全提交或回滚。现在DDL日志可以写入mysql.innodb_ddl_log配置以用于数据字典表,并启用innodb_print_ddl_logs配置选项以将DDL恢复日志打印到stderr

多版本

InnoDB 是一种多版本存储引擎。这意味着它具有保留更改的旧版本行数据信息并支持事务特性(如并发性和回滚)的能力。信息存储在表空间、数据结构和命名回滚段中。

在内部,对于存储在数据库中的每一行,InnoDB创建三个字段:6 字节的DB_TRX_ID,7 字节的DB_ROLL_PTR(称为回滚指针)和 6 字节的DB_ROW_ID。有了这些字段,InnoDB创建了聚集索引,以保留数据库中更改的行数据信息。

架构

在本节中,我们将简要介绍InnoDB架构的主要组件:

  • 缓冲池:主内存区域,用于缓存表和索引数据以加快处理速度

  • 更改缓冲区:缓存对辅助索引页面的更改的特殊数据结构

  • 自适应哈希索引:使内存数据库能够在具有平衡和适当组合的缓冲池内存和工作负载的系统上进行查找和操作

  • 重做日志缓冲区:存储数据以便写入重做日志的内存区域

  • 系统表空间:存储doublewrite缓冲区、撤销日志和更改缓冲区的存储区域,在 MySQL 8 数据字典信息之前存储

  • 双写缓冲区:系统表空间中的存储区域,用于写入从缓冲池刷新的页面

  • 撤销日志:与任何单个事务相关联的撤销日志记录的集合

  • 每个表的表空间:添加到自己的数据文件的单表表空间

  • 通用表空间:使用CREATE TABLESPACE语法创建的共享表空间

  • 撤销表空间:一个或多个带有撤销日志的文件

  • 临时表空间:用于非压缩临时表及其相关对象

  • 重做日志:用于在崩溃恢复期间纠正不完整事务数据的基于磁盘的数据结构

在 MySQL 8 中,InnoDB存储引擎利用全局 MySQL 数据字典,而不是其自己的存储引擎特定数据字典。

锁定和事务模型

本节简要介绍了InnoDB使用的锁定和InnoDB实现的事务模型。InnoDB使用以下不同类型的锁定:

  • 共享和排他锁:实现了两种标准的行级锁定。共享锁允许您将一行读取到不同的事务中;排他锁用于更新或删除一行,并且不允许您将该行读取到任何不同的事务中。

  • 意向锁:表级锁,支持多粒度锁定,InnoDB实际上维护了行级锁和整个表级锁的共存。

  • 记录锁:索引记录锁,防止任何其他事务插入、更新或删除记录。

  • 间隙锁:锁定适用于索引记录之间的间隙(范围)。

  • 下一个键锁:在前一个索引记录的间隙上组合索引记录锁和间隙锁。

  • 插入意向锁INSERT操作在插入行之前设置的一种间隙锁类型。

  • AUTO-INC 锁:用于插入具有AUTO_INCREMENT列的记录的特殊表级锁。

  • 空间索引的谓词锁:对空间索引的锁定,使支持具有空间索引的表的隔离级别

遵循事务模型的目标是将传统的两阶段锁定与多版本数据库属性的最佳部分结合起来。执行行级锁定,并使用非锁定一致性读取运行查询。InnoDB负责事务隔离级别、自动提交、回滚和提交以及锁定读取。它允许根据需要进行非锁定一致性读取。InnoDB还使用一种机制来避免幻影行,并配置支持自动死锁检测。

配置

本节提供了有关InnoDB初始化启动中使用的配置和程序的简要信息,适用于不同的InnoDB组件:

  • InnoDB 启动配置:包括指定启动选项、日志文件配置、存储考虑事项、系统表空间数据文件、撤销表空间、临时表空间、页面大小和内存配置

  • 用于只读操作的 InnoDB:使用--innodb-read-only=1选项,可以将 MySQL 实例配置为只读操作,当使用只读介质(如CDDVD)时非常有用

  • InnoDB 缓冲池配置:配置缓冲池大小、多个实例、刷新和监控

  • InnoDB 更改缓冲:为辅助索引缓存配置更改缓冲选项

  • InnoDB的线程并发性:并发线程计数限制配置

  • 后台 InnoDB I/O 线程的数量:配置后台线程的数量,用于对数据页进行 I/O 读/写操作

  • 在 Linux 上使用异步 I/O:在 Linux 上使用本机异步 I/O 子系统的配置

  • InnoDB主线程 I/O 速率:配置后台工作的主线程的整体 I/O 容量,负责多个任务

  • 自旋锁轮询:配置自旋等待延迟周期,以控制多个线程之间频繁轮询以获取mutexesrw-locks的最大延迟

  • InnoDB 清除调度:为适用的可伸缩性配置清除线程。

  • InnoDB的优化器统计信息:配置持久和非持久的优化器统计参数。

  • 索引页的合并阈值:配置MERGE_THRESHOLD以减少合并分裂行为。

  • 启用专用 MySQL 服务器的自动配置:配置专用服务器选项--innodb_dedicated_server,以自动配置缓冲池大小和日志文件大小。

表空间

本节提供了关于表空间和在InnoDB中执行的表空间相关操作的简要信息:

  • 调整InnoDB系统表空间的大小:在启动/重新启动 MySQL 服务器时,增加和减少系统表空间的大小。

  • 更改InnoDB重做日志文件的数量或大小:在启动/重新启动 MySQL 服务器之前,分别配置my.cnf中的innodb_log_files_in_groupinnodb_log_file_size值。

  • 使用原始磁盘分区作为系统表空间的数据文件:配置原始磁盘分区以用作系统表空间中的数据文件。

  • InnoDB 每表表空间:默认启用了innodb_file_per_table功能,确保每个表和相关索引都存储在单独的.idb数据文件中。

  • 配置撤消表空间:配置设置撤消表空间的数量,其中撤消日志驻留。

  • 截断撤消表空间:配置innodb_undo_log_truncate以启用截断超过innodb_max_undo_log_size定义的最大限制的撤消表空间文件。

  • InnoDB 通用表空间:使用CREATE TABLESPACE语句创建的共享表空间。它类似于系统表空间。

  • InnoDB 表空间加密:支持以文件为基础的表空间存储的表的数据加密,使用AES分块加密算法。

表和索引

本节提供了关于InnoDB表和索引以及它们相关操作的简要信息:

  • 创建InnoDB:使用CREATE TABLE语句创建表。

  • InnoDB表的物理行结构:取决于表创建时指定的行格式。如果未指定,则使用默认的DYNAMIC

  • 移动或复制InnoDB:将一些或所有InnoDB表移动或复制到不同的实例或服务器的不同技术。

  • 将表从MyISAM转换为InnoDB:在将MyISAM表转换为InnoDB表时考虑指南和提示,但不支持分区表,这在 MySQL 8 中不受支持。

  • InnoDB中的AUTO_INCREMENT 处理:使用innodb_autoinc_lock_mode参数配置AUTO_INCREMENT的模式为 0、1 和 2,分别为传统、连续或交错,其中交错是 MySQL 8 的默认模式。

  • InnoDB表的限制:表最多可以包含 1,017 列,最多可以包含 64 个次要索引,以及基于页面大小、表大小和数据行格式定义的其他限制。

  • 聚集和次要索引InnoDB使用称为聚集索引的特殊索引。其余的索引称为次要索引。

  • InnoDB索引的物理结构:对于空间索引,InnoDB使用专门的数据结构R-tree。对于其他索引,使用B-tree数据结构。

  • 排序索引构建:在创建或重建索引进行插入时进行批量加载。它们被称为排序索引构建,并且不支持空间索引。

  • InnoDB FULLTEXT 索引:为基于文本的列(charvarchartext类型)创建。它们有助于加快查询和搜索操作的速度。

INFORMATION_SCHEMA 表

本节提供了InnoDB INFORMATION_SCHEMA表的用法示例和相关信息。

它提供了有关InnoDB存储引擎不同方面的元数据、统计和状态信息。

可以通过在INFORMATION_SCHEMA数据库上执行SHOW TABLES语句来检索InnoDB INFORMATION_SCHEMA表的列表:

mysql> SHOW TABLES FROM INFORMATION_SCHEMA LIKE 'INNODB%';
  • 关于压缩的表INNODB_CMPINNODB_CMP_RESET表提供了有关压缩操作次数和压缩相关信息所花费的时间。在压缩期间的内存分配在INNODB_CMPMEMINNODB_CMPMEM_RESET表中提供。

  • 事务和锁信息INNODB_TRX包含当前执行的事务信息,Performance Schema表中的data_locksdata_lock_waits表提供有关锁的信息。

  • 模式对象表:提供有关InnoDB模式对象的元数据信息。

  • FULLTEXT 索引表:提供有关FULLTEXT索引的元数据信息。

  • 缓冲池表:提供有关缓冲池中页面的状态信息和元数据。

  • 指标表:提供性能和资源相关信息。

  • 临时表信息表:提供有关当前在InnoDB实例中活动的所有用户和系统创建的临时表的元数据信息。

  • 检索InnoDB表空间元数据:提供有关InnoDB实例中所有类型的表空间的元数据信息。

已添加了一个新视图INNODB_TABLESPACES_BRIEF,用于提供名称、路径、标志、空间和空间类型数据。

已添加了一个新表INNODB_CACHED_INDEXES,用于提供缓冲池中每个索引的索引页数。

Memcached 插件

MySQL 8 为您提供了名为daemon_memcachedInnoDB memcached 插件,可以帮助我们轻松管理数据。它将自动从InnoDB表中存储和检索数据,并提供getsetincr操作,通过跳过 SQL 解析来消除性能开销,从而加快数据操作。memcached插件使用集成的memcached守护程序,自动从InnoDB表中检索和存储数据,使 MySQL 服务器能够快速将数据发送到键值存储。

使用InnoDB memcached插件的主要好处如下:

  • 直接访问InnoDB存储引擎,减少解析和规划 SQL 开销

  • memcached使用与 MySQL 服务器相同的进程空间,减少了网络开销

  • memcached协议编写或请求的数据会透明地从InnoDB表中写入或查询,减少了必须经过 SQL 层开销的情况

  • 通过自动在磁盘和内存之间传输,简化应用逻辑

  • MySQL 数据库存储数据,以防止损坏、崩溃或中断

  • 在主服务器上使用daemon_memcached插件和 MySQL 复制结合,确保高可用性

  • 使用InnoDB缓冲池缓存重复的数据请求,提供高速处理

  • 由于数据存储在InnoDB表中,数据一致性会自动执行

InnoDB memcached插件支持多个获取操作(在单个memcached查询中获取多个键/值对)和范围查询。

创建自定义存储引擎

MySQL AB 在 MySQL 5.1 中引入了可插拔存储引擎架构,包括 MySQL 8 在内的所有后续版本都利用了灵活的存储引擎架构。

存储引擎可插拔架构提供了在不重新编译服务器的情况下创建和添加新存储引擎的能力,直接添加到运行中的 MySQL 服务器。这种架构使得开发和部署新的存储引擎到 MySQL 8 变得非常容易。

在开发新的存储引擎时,需要注意为存储引擎工作的所有组件。这些包括安装处理程序、对表的操作(如创建、打开和关闭)、DML、索引等。

在本节中,我们将介绍如何可以在高层次基础上开始开发新的存储引擎,参考 MySQL 开发社区提供的文档。创建自定义存储引擎需要具备使用CCPP进行开发的工作知识,以及使用cmakeVisual Studio进行编译。

创建存储引擎源文件

实现新存储引擎的最简单方法是通过复制和修改EXAMPLE存储引擎开始。文件ha_example.ccha_example.h可以在 MySQL 源分发的storage/example目录中找到。

在复制文件时,将名称从ha_example.ccha_example.h更改为适合您的存储引擎的名称,例如ha_foo.ccha_foo.h

在复制和重命名文件后,必须将所有EXAMPLEexample的实例替换为您的存储引擎的名称。

添加特定于引擎的变量和参数

插件可以实现状态和系统变量,在本节中我们已经介绍了变量和参数的更改,以及适当的值和数据类型。

服务器插件接口使插件能够使用通用插件描述符的status_varssystem_vars成员公开状态和系统变量。

status_vars是通用插件描述符的成员。如果值不为 0,则指向一个st_mysql_show_var结构的数组,其中每个结构描述一个状态变量,后跟一个所有成员都设置为 0 的结构。st_mysql_show_var结构的定义如下:

struct st_mysql_show_var {   
  const char *name;   
  char *value;   
  enum enum_mysql_show_type type; 
};

插件安装后,插件名称和名称值用下划线连接,以形成SHOW STATUS语句显示的名称。

以下列表显示了允许的状态变量类型值以及相应的变量应该是什么:

  • SHOW_BOOL:这是一个指向boolean变量的指针

  • SHOW_INT:这是一个指向integer变量的指针

  • SHOW_LONG:这是一个指向长整型变量的指针

  • SHOW_LONGLONG:这是一个指向longlong integer变量的指针

  • SHOW_CHAR:这是一个String索引

  • SHOW_CHAR_PTR:这是一个指向String索引的指针

  • SHOW_ARRAY:这是一个指向另一个st_mysql_show_var array的指针

  • SHOW_FUNC:这是一个指向函数的指针

  • SHOW_DOUBLE:这是一个指向double的指针

所有会话和全局系统变量在使用之前都必须发布到mysqld。这是通过构建一个变量的NULL终止数组,并在插件公共接口中链接到它来实现的。

所有可变的和插件系统变量都存储在HASH结构中。

服务器命令行帮助文本的显示是通过编译所有相关变量的DYNAMIC_ARRAY,对其进行排序和迭代来显示每个选项。

在插件安装过程中,服务器处理命令行选项,插件成功加载后立即进行处理,但尚未调用插件初始化函数。

runtime加载的插件不受任何配置选项的影响,必须具有可用的默认值。一旦安装,它们将在mysqld初始化时加载,并且可以在命令行或my.cnf中设置配置选项。

插件中的thd参数应被视为只读。

创建 handlerton

handlerton(处理程序单例的简称)定义了存储引擎。它包含指向应用于整个存储引擎的方法的方法指针,而不是在每个表上工作的方法。此类方法的示例包括处理提交和回滚操作的事务方法。

EXAMPLE存储引擎的示例如下:

handlerton example_hton= {
 "EXAMPLE", /* Name of the storage engine */
 SHOW_OPTION_YES, /* It should be displayed in options or not */
 "Example storage engine", /* Description of the storage engine */
 DB_TYPE_EXAMPLE_DB, /* Type of storage engine it should refer to */
 NULL, /* Initialize handlerton */
 0, /* slot  available */
 0, /* define savepoint size. */
 NULL, /* handle close_connection */
 NULL, /* handle savepoint */
 NULL, /* handle rollback to savepoint */
 NULL, /* handle release savepoint */
 NULL, /* handle commit */
 NULL, /* handle rollback */
 NULL, /* handle prepare */
 NULL, /* handle recover */
 NULL, /* handle commit_by_xid */
 NULL, /* handle rollback_by_xid */
 NULL, /* handle create_cursor_read_view */
 NULL, /* handle set_cursor_read_view */
 NULL, /* handle close_cursor_read_view */
 example_create_handler, /* Create a new handler instance */
 NULL, /* handle drop database */
 NULL, /* handle panic call */
 NULL, /* handle release temporary latches */
 NULL, /* Update relevant Statistics */
 NULL, /* Start Consistent Snapshot for reference */
 NULL, /* handle flush logs */
 NULL, /* handle show status */
 NULL, /* handle replication Report Sent to Binlog */
 HTON_CAN_RECREATE
};

有 30 个handlerton元素,其中只有少数是强制性的。

处理处理程序安装

这是创建新处理程序实例所需的存储引擎中的第一个方法调用。

在源文件中定义handlerton之前,必须在方法头中定义实例化方法。以下是CSV引擎显示实例化方法的示例:

static handler* tina_create_handler(TABLE *table);

如前面的示例所示,该方法接受一个指向表的指针。处理程序负责管理和返回处理程序对象。在方法头定义之后,使用方法指针在create() handlerton元素中命名方法。这将标识该方法负责在请求时生成新的处理程序实例。

以下示例显示了MyISAM存储引擎的实例化方法:

static handler *myisam_create_handler(TABLE *table)
 {
 return new ha_myisam(table);
 }

定义文件扩展名

存储引擎必须提供与给定表及其数据和索引相关的存储引擎使用的扩展名列表给 MySQL 服务器。

扩展应以空终止的字符串数组的形式给出,并且在调用[custom-engine.html#custom-engine-api-reference-bas_ext bas_ext()]方法时返回相同的内容,如下面的块所示:

const char **ha_tina::bas_ext() const
{
 return ha_tina_exts;
}

通过提供扩展信息,您还可以跳过实现DROP TABLE功能,因为 MySQL 服务器将通过关闭表并删除指定扩展名的所有文件来实现相同的功能。

创建表

在处理程序实例化之后,应该遵循创建表方法。存储引擎必须实现[custom-engine.html#custom-engine-api-reference-create create()]方法,如下面的块所示:

virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;

前面显示的方法应该创建所有必要的文件,但不会打开表。MySQL 服务器将单独调用打开表。

*name参数用于传递表的名称,*form参数用于传递TABLE结构。表结构定义了表,并匹配tablename.frm的内容。存储引擎不得修改tablename.frm文件,否则将导致错误或不可预测的问题。

*info参数是包含有关CREATE TABLE语句的信息的结构。它用于创建表,结构在handler.h文件中定义。以下是参考结构:

typedef struct st_ha_create_information
{
 CHARSET_INFO *table_charset, *default_table_charset; /* charset in table */
 LEX_STRING connect_string; /* connection string */
 const char *comment,*password; /* storing comments and password values */
 const char *data_file_name, *index_file_name; /* data and index file names */
 const char *alias; /* value pointer for alias */
 ulonglong max_rows,min_rows;
 ulonglong auto_increment_value;
 ulong table_options;
 ulong avg_row_length;
 ulong raid_chunksize;
 ulong used_fields;
 SQL_LIST merge_list;
 enum db_type db_type; /* value for db_type */
 enum row_type row_type; /* value for row_type */
 uint null_bits; /* NULL bits specified at start of record */
 uint options; /* OR of HA_CREATE_ options specification */
 uint raid_type,raid_chunks; /* raid type and chunks info */
 uint merge_insert_method;
 uint extra_size; /* length of extra data segments */
 bool table_existed; /* 1 in create if table existed */
 bool frm_only; /* 1 if no ha_create_table() */
 bool varchar; /* 1 if table has a VARCHAR */
} HA_CREATE_INFO;

存储引擎可以忽略*info*form的内容,因为只有在存储引擎使用时才真正需要创建和初始化数据文件。

打开表

在对任何表执行任何读取或写入操作之前,MySQL 服务器调用[custom-engine.html#custom-engine-api-reference-open handler::open()]方法来打开表索引和数据文件:

int open(const char *name, int mode, int test_if_locked);

第一个参数是要打开的表的名称。第二个参数是要执行的文件操作。这些值在handler.h中定义:O_RDONLY - 只读打开O_RDWR - 读/写打开

最终选项决定处理程序在打开之前是否应检查表上的锁定。可以选择以下选项:

#define HA_OPEN_ABORT_IF_LOCKED 0 /* default */
#define HA_OPEN_WAIT_IF_LOCKED 1 /* wait if table is locked */
#define HA_OPEN_IGNORE_IF_LOCKED 2 /* ignore if locked */
#define HA_OPEN_TMP_TABLE 4 /* Table is a temp table */
#define HA_OPEN_DELAY_KEY_WRITE 8 /* Don't update index */
#define HA_OPEN_ABORT_IF_CRASHED 16
#define HA_OPEN_FOR_REPAIR 32 /* open even if crashed with repair */

典型的存储引擎将实现某种形式的共享访问控制,以防止在多线程环境中发生文件损坏。例如,查看sql/example/ha_tina.cc中的get_share()free_share()方法来实现文件锁定。

实现基本表扫描

最基本的存储引擎实现了只读级别的表扫描,并且可能用于支持 SQL 查询,以请求从 MySQL 之外填充的日志和其他数据文件中获取信息。

方法的实现是创建高级存储引擎的第一步。以下显示了在CSV引擎的九行表扫描期间进行的方法调用:

ha_tina::store_lock
ha_tina::external_lock
ha_tina::info
ha_tina::rnd_init
ha_tina::extra - ENUM HA_EXTRA_CACHE Cache record in HA_rrnd()
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::extra - ENUM HA_EXTRA_NO_CACHE End caching of records (def)
ha_tina::external_lock
ha_tina::extra - ENUM HA_EXTRA_RESET Reset database to after open

可以实施以下方法来处理特定操作:

  • 实现 store_lock(): 此方法可以修改锁级别,忽略或为多个表添加锁

  • 实现 external_lock(): 当发出LOCK TABLES语句时调用此方法

  • 实现 rnd_init(): 此方法用于在表扫描中在表的开始处重置计数器和指针

  • 实现 info(uinf flag): 此方法用于向优化器提供额外的表信息

  • 实现 extra(): 此方法用于向存储引擎提供额外的提示信息

  • 实现 rnd_next(): 此方法在扫描每一行直到达到EOF或满足搜索条件时调用

关闭表

当 MySQL 服务器完成与表的所有请求操作后,它将调用custom-engine.html#custom-engine-api-reference-close close()方法。它将关闭文件指针并释放所有相关资源。

使用共享访问方法的存储引擎在CSV引擎中可见。其他示例引擎必须从共享结构中删除相同的内容,如下所示:

int ha_tina::close(void)
 {
 DBUG_ENTER("ha_tina::close");
 DBUG_RETURN(free_share(share));
 }

存储引擎使用自己的共享管理系统。它们应使用所需的方法,以便从其处理程序中打开的相应表的共享中删除处理程序实例。

如果您的存储引擎编译为共享对象,在加载期间如果出现错误,例如undefined symbol: _ZTI7handler,则请确保使用与服务器相同的标志编译和链接您的扩展。此错误的常见原因是 LDFLAGS 缺少-fno-rtti选项。

高级自定义存储引擎的参考

我们已经详细介绍了前面的各个部分,为自定义存储引擎组件和所需的更改提供了高级信息。要在自定义存储引擎中实现INSERTUPDATEDELETE、索引等,需要具备使用C/CPP进行开发以及使用cmakeVisual Studio进行编译的工作知识。有关自定义存储引擎的高级开发,请参阅dev.mysql.com/doc/internals/en/custom-engine.html中提供的详细信息

摘要

到目前为止,您已经了解了 MySQL 8 中可用的不同数据库引擎,以及我们为什么应该关注存储引擎和 MySQL 8 中可用的存储引擎选项。我们已经详细介绍了InnoDB存储引擎以及InnoDB存储引擎中已经提供的重要功能。现在,您实际上可以根据系统要求创建自定义存储引擎,并将其插入到 MySQL 8 中。选择适合您系统的存储引擎是一个重要方面,我们已经详细介绍了这一点。

在下一章中,您将了解 MySQL 8 中索引的工作原理,与索引相关的新功能,不同类型的索引以及如何在表中使用索引。除此之外,还将提供比较以及深入了解各种索引实现方式。

第七章:MySQL 8 中的索引

在上一章中,我们了解了存储引擎。现在我们知道了有哪些类型的存储引擎可用,以及哪些存储引擎适合我们的需求。上一章还详细介绍了InnoDB存储引擎,以及其他存储引擎信息。它还描述了如何定义用于使用的自定义存储引擎,并提供了一个实际示例。现在是时候了解 MySQL 8 的另一个重要功能,即索引。我们将涵盖不同类型的索引及其功能,这将鼓励您使用索引,并为您提供如何使用它们的指导。因此,您的索引之旅已经开始!让我们开始吧。

本章将涵盖以下主题:

  • 索引概述

  • 列级索引

  • B-Tree 索引

  • 哈希索引

  • 索引扩展

  • 使用优化器进行索引

  • 不可见和降序索引

索引概述

在表上定义索引是改善SELECT操作性能的最佳方式。索引就像表行的指针,允许查询根据WHERE条件快速指向匹配的行。MySQL 8 允许您在所有数据类型上创建索引。尽管索引在查询中提供了良好的性能,但建议以正确的方式定义它,因为不必要的索引会浪费空间和时间(MySQL 8 需要找到最佳使用的索引)。此外,索引还会增加INSERTUPDATEDELETE操作的成本,因为在这些操作期间,MySQL 8 将更新每个索引。

正如我们之前所描述的,索引是一种改善操作速度的数据结构。根据结构,索引分为两种主要形式——聚集索引和非聚集索引:

  • 聚集索引:聚集索引定义了数据在表中的物理存储顺序。因此,每个表只允许一个聚集索引。当以顺序方式检索数据时,无论是相同顺序还是相反顺序,聚集索引都会大大提高检索速度。当选择一系列项目时,聚集索引也提供更好的性能。主键被定义为聚集索引。

  • 非聚集索引:非聚集索引不定义数据物理存储的顺序。这意味着非聚集索引存储在一个地方,数据存储在另一个地方。因此,每个表允许有多个非聚集索引。它指的是非主键。

正如我们所知,主键代表从表中获取记录最广泛使用的列或列集。主键与之关联的索引用于快速查询性能。它提供了相对较快的性能,因为主键不允许NULL值,因此不需要对NULL值进行检查。建议如果您的表没有列或列集来定义为主键,那么为了更好的性能,您可以定义一个自动增量字段作为主键。另一方面,如果您的表包含许多列,并且需要执行带有多列组合的查询,则建议将不经常使用的数据转移到单独的表中。将所有单独的表与主键和外键引用相关联,这将帮助您管理数据,并且查询检索会提供良好的性能。

MySQL 8 中索引的用途

索引主要用于在不迭代完整表的情况下找到特定值的行。如果未定义索引,则 MySQL 8 将从第一行开始搜索,然后读取整个表,这将导致昂贵的操作。MySQL 8 使用索引进行以下操作:

  • 在对索引的最左前缀进行排序或分组时。这意味着如果所有键都为DESC子句定义,那么键将按相反顺序考虑,如果所有键后跟ASC,则键将按正向顺序考虑。

  • 查找与WHERE子句匹配的行。

  • 对于多列索引,可以使用索引的任何最左前缀来查找行。本章后面将以详细示例介绍此主题。

  • 如果 MySQL 需要从多个选项中选择一个索引,则会选择具有最小行集的索引。

  • 有时,查询会被优化以获取值而不是引用行。例如,如果查询仅使用包含在索引中的列,MySQL 8 将从索引树中获取所选值:

 SELECT key_part3 FROM table_name WHERE key_part1=10;
  • 在执行连接时,如果列声明为相同的类型和大小,MySQL 8 将以更有效的方式使用索引。例如,VARCHAR(15)CHAR(15)将被视为相同,但VARCHAR(10)CHAR(15)将不被视为相同。

  • 对于MIN()MAX()函数,如果使用了索引列的一部分,优化器将检查索引列的所有其他部分是否在WHERE条件中可用。如果提到了,MySQL 8 将执行MIN()MAX()函数的单个查找,并用常量替换它们。例如:

 SELECT MIN(key_part2), MAX(key_part2) FROM tble_name WHERE 
          key_part1=10;

与索引相关的 SQL 命令

MySQL 8 提供了两个与索引相关的主要命令。我们将在以下部分讨论这些命令。

创建 INDEX 命令

以下命令允许用户向现有表中添加索引。此命令也可与CREATE TABLEALTER TABLE一起使用以创建索引:

CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
 [index_type]
 ON tbl_name (index_col_name,...)
 [index_option]
 [algorithm_option | lock_option] ...
index_col_name:
 col_name [(length)] [ASC | DESC]
index_option:
 KEY_BLOCK_SIZE [=] value
 | index_type
 | WITH PARSER parser_name
 | COMMENT 'string'
 | {VISIBLE | INVISIBLE}
index_type:
 USING {BTREE | HASH}
algorithm_option:
 ALGORITHM [=] {DEFAULT|INPLACE|COPY}
lock_option:
 LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}

使用col_name(length)语法,用户可以指定索引前缀长度,只考虑字符串值中指定数量的字符。在定义时,前缀考虑以下几点:

  • 对于CHARVARCHARBINARYVARBINARY列索引,前缀是可选的

  • BLOBTEXT列索引中必须指定前缀

  • MySQL 8 将考虑非二进制字符串类型(CHARVARCHARTEXT)的字符数和二进制类型(BINARYVARBINARYBLOB)的字节数作为前缀

  • 空间列不允许前缀

在本章后面的列索引部分将详细介绍前缀选项的示例。UNIQUE索引是一个约束,表示索引中的所有值都将是唯一的。如果尝试添加已经存在的值,MySQL 8 会显示错误。所有类型的存储引擎都允许在UNIQUE索引中存在多个空值。在使用NULL值时,确保列值在前缀内是唯一的。如果索引前缀超出其大小,MySQL 8 将按以下方式处理索引:

  • 对于非唯一索引:如果启用了严格的 SQL 模式,MySQL 8 会抛出错误,如果禁用了严格模式,则索引长度将减少到最大列数据类型大小,并产生警告。

  • 对于唯一索引:在这种情况下,无论 SQL 模式如何,MySQL 8 都会产生错误,因为这可能会破坏列的唯一性。这意味着您定义了一个长度为 25 的列,并尝试在相同列上定义一个前缀长度为 27 的索引,那么 MySQL 8 会报错。

空间索引特性

MySQL 8 遵循以下规则来处理空间索引特性:

  • 仅适用于InnoDBMyISAM存储引擎;如果尝试用于其他存储引擎,MySQL 8 会报错。

  • 不允许对索引列使用NULL值。

  • 此列不允许使用前缀属性。索引将考虑全宽度。

非空间索引特性

MySQL 8 遵循以下规则,用于非空间索引特性:

  • 对于 InnoDBMyISAMMEMORY 存储引擎,允许在索引列中使用 NULL 值。

  • 在每个空间列的情况下,必须指定列前缀长度,如果它存在于非空间索引中。前缀长度将以字节为单位。

  • 除了 ARCHIVE,它适用于所有支持空间列的存储引擎。

  • 对于此索引,允许使用 NULL 值,除非它被定义为 PRIMARY 键。

  • 对于 InnoDB 表,在创建表上的索引后,如果启用了 innodb_stats_persistent 设置,则运行 ANALYZE TABLE 语句。

  • 索引类型将取决于存储引擎;目前使用 B-Tree。

  • 只有在使用 InnoDBMyISAM 表定义时,才允许在 BLOBTEXT 列上使用非空间索引。

index_col_name 属性的默认值是升序的,对于具有此属性的 HASH 索引,不允许使用 ASCDESC 值。MySQL 8 提供以下任何一个值与 index_option

  • KEY_BLOCK_SIZE [=] value: 此参数定义了索引键块的大小(以字节为单位)。这是一个可选参数,其值被视为提示。如果需要,MySQL 8 可能会使用不同的大小。如果此参数在单个索引级别上定义,则它会覆盖表级别的 KEY_BLOCK_SIZE 值。InnoDB 引擎不支持此参数在索引级别上;它只允许在表级别上使用。

  • index_type:MySQL 8 允许用户在索引创建时定义索引类型。例如:

 create table employee (id int(11) not null,name varchar(50));
 CREATE INDEX emp_name_index ON employee (name) USING BTREE;

请参考以下表格,查找与存储引擎相关的允许的索引类型。在多种类型定义的情况下,将第一个索引类型视为默认类型。如果此表中未提及任何存储引擎,则表示该引擎不支持该索引类型。

存储引擎 允许的索引类型
InnoDB BTREE
MyISAM BTREE
MEMORY/HEAP HASHBTREE
NDB HASHBTREE

参考:dev.mysql.com/doc/refman/8.0/en/create-index.html

如果尝试定义存储引擎不支持的索引类型,则 MySQL 8 将其视为支持的索引类型,而不会影响查询结果。请参考以下表格,了解基于存储类型的索引特性:

存储引擎 索引类型 索引类 存储 NULL 值 允许多个 NULL 值 IS NULL 扫描类型 IS NOT NULL 扫描类型
InnoDB BTREE Primary key No No N/A N/A
Unique Yes Yes Index Index
Key Yes Yes Index Index
不适用 FULLTEXT Yes Yes Table Table
不适用 SPATIAL No No N/A N/A
MyISAM BTREE Primary key No No N/A N/A
Unique Yes Yes Index Index
Key Yes Yes Index Index
不适用 FULLTEXT Yes Yes Table Table
不适用 SPATIAL No No N/A N/A
MEMORY HASH Primary key No No N/A N/A
Unique Yes Yes Index Index
Key Yes Yes Index Index
BTREE Primary No No N/A N/A
Unique Yes Yes Index Index
Key Yes Yes Index Index

参考:dev.mysql.com/doc/refman/8.0/en/create-index.html

  • WITH PARSER parser_name:此选项仅适用于由 InnoDBMyISAM 存储引擎支持的 FULLTEXT 索引。如果 FULLTEXT 索引和搜索操作需要特殊处理,则 MySQL 8 将使用索引的解析器插件。

  • COMMENT 'string':此属性是可选的,允许在注释中使用最多 1024 个字符。此选项还支持MERGE_THRESHOLD参数,其默认值为 50。考虑以下命令来定义MERGE_THRESHOLD

 CREATE INDEX name_index ON employee(name) COMMENT 
          'MERGE_THRESHOLD=40'; 

如果索引的页满百分比低于MERGE_THRESHOLD值,那么InnoDB存储引擎将会将索引页与相邻的索引页合并。

  • VISIBLEINVISIBLE:此参数定义了索引的可见性。默认情况下,所有索引都是可见的。优化器在优化过程中不会使用不可见的索引。

当您尝试使用表进行读取或写入,并同时修改其索引时,ALGORITHMLOCK属性将产生影响。

删除索引命令

以下命令从表中删除索引。我们也可以将此语句映射到ALTER TABLE以从表中删除索引:

DROP INDEX index_name ON tbl_name
 [algorithm_option | lock_option]...
algorithm_option:
 ALGORITHM [=] {DEFAULT|INPLACE|COPY}
lock_option:
 LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}

在此命令中,只有两个选项可用:算法和锁。这两个选项在索引的并发访问和工作情况下非常有用,类似于CREATE INDEX命令。例如,要删除员工表的索引,请执行以下命令:

DROP INDEX name_index ON employee;

空间索引的创建和优化

MySQL 8 允许您在InnoDBMyISAM存储引擎上使用与前述主题中提到的相同语法创建空间索引。标准命令中唯一的变化是在创建索引时使用关键字spatial。在定义空间索引时,请确保列声明为NOT NULL。以下代码演示了在表上创建空间索引的方法:

CREATE TABLE geom_data (data GEOMETRY NOT NULL, SPATIAL INDEX(data));

默认情况下,空间索引会创建一个 R-Tree 索引。从 MySQL 8.0.3 开始,优化器会检查索引列的空间参考标识符SRID)属性,以找到用于比较和执行计算的空间参考系统SRS)。对于比较,空间索引中的每个列必须受到 SRID 的限制。这意味着每个列定义必须包含一个 SRID 属性,并且所有列值必须具有相同的 SRID。基于 SRID,空间索引执行以下两个操作:

  • 如果列被限制为笛卡尔 SRID,那么它会启用笛卡尔边界框计算

  • 如果列被限制为地理 SRID,那么它会启用地理边界框计算

如上所述,MySQL 8 将忽略没有 SRID 属性的列上的SPATIAL INDEX,但 MySQL 仍会管理这些索引,如下所示:

  • 这些类型的索引在表被使用INSERTUPDATEDELETE命令修改时会被更新。

  • 这些索引在转储备份中被考虑,并且可以通过向后兼容性进行恢复。如前所述,没有受到 SRID 限制列的空间索引不会被优化器使用,因此在这种情况下,所有这些列必须被修改。要修改它们,请执行以下步骤:

  1. 使用以下命令检查具有相同ST_SRID的列的所有值:
SELECT DISTINCT ST_SRID(column_name) FROM table_name;

如果查询返回多行,则表示该列包含混合的 SRID。如果是这样,请更改列的内容为相同的 SRID 值。

  1. 为列定义一个显式的 SRID。

  2. 重新创建SPATIAL INDEX

InnoDB 和 MyISAM 索引统计收集

MySQL 8 将根据值组对表统计信息进行考虑,这只是具有相同前缀值的一组行。存储引擎收集与表相关的统计信息,这些信息由优化器使用。从优化的角度来看,平均值组大小是一个重要的统计数据。如果组的平均值大小增加,那么索引就没有意义。因此,最好为每个索引定位少量行。这可以通过表基数来实现,即值组的数量。对于InnoDBMyISAM表,MySQL 8 通过myisam_stats_methodinnodb_stats_method系统变量提供了对统计信息的控制。以下是这些变量的可能值:

  • nulls_ignored:表示NULL值被忽略

  • nulls_equal:表示所有NULL值相同

  • nulls_unequal:表示所有NULL值不相同

innodb_stats_method系统变量具有全局值,而myisam_stats_method系统变量具有全局值和会话值。当我们设置变量的全局值时,它将影响相应存储引擎的表的统计信息收集。在会话值统计的情况下,仅对当前客户端连接可用。这意味着您必须为其他客户端重新生成表的统计信息,而不影响其他客户端,并且需要在会话值中设置它。要重新生成MyISAM统计信息,请使用以下方法之一:

  • 执行myisamchk --stats_method=method_name --analyze命令

  • 更改表以使其统计信息过时,然后设置myisam_stats_method并发出ANALYZE TABLE语句

在使用这两个变量之前,必须考虑一些要点:

  • 这些变量仅适用于InnoDBMyISAM表。对于其他存储引擎,只有一种方法可用于收集表统计信息,它非常接近nulls_equal方法。

  • MySQL 8 提供了一种明确为表生成统计信息的方法,但情况并非总是如此。有时,如果需要,MySQL 8 也会自动生成统计信息。例如,在任何操作的情况下,如果某些 SQL 语句修改了表数据,那么 MySQL 8 将自动收集统计信息。考虑批量插入或删除操作。

  • 我们无法确定用于生成表统计信息的方法。

列级索引

MySQL 8 允许您在单个列上创建索引,也可以在多个列上创建索引。每个表的最大索引数和最大索引长度取决于存储引擎。大多数存储引擎允许每个表至少有 16 个索引和至少 256 个字节的总索引长度,但大多数存储引擎允许更高的限制。

列索引

这是定义只涉及单个列的索引的最常见方法。MySQL 8 将列值的副本存储在数据结构中,以便可以快速访问行。MySQL 8 使用B-Tree数据结构来快速访问值。 B-Tree 执行将基于在where条件中定义的操作符,例如=,<,>,BETWEEN,IN等。您可以在下一个主题中了解有关 B-Tree 数据结构及其执行的详细信息。我们将在接下来的部分讨论列索引的特点。

索引前缀

此选项允许用户在字符串的情况下指定用于索引的字符数。MySQL 8 在索引创建中提供了column_name(N)选项,用于指定字符数。索引优先考虑只指定的字符,这将使索引文件更小。因此,在BLOBTEXT列的情况下,您必须为了更好的性能指定前缀长度。考虑以下示例,在BLOB类型上创建带有前缀长度的索引:

CREATE TABLE person (personal_data TEXT, INDEX(personal_data (8)));

此命令通过考虑前八个字符在personal_data列上创建索引。前缀长度根据存储引擎而变化。InnoDB存储引擎允许对REDUNDANTCOMPACT行格式最多有 767 字节的前缀长度,而对于DYNAMICCOMPRESSED行格式,它允许最多 3072 字节。在MyISAM存储引擎的情况下,前缀最多可以定义为 1000 字节。

前缀长度将以字节为单位测量二进制字符串类型,例如BINARYVARBINARYBLOB,而对于非二进制字符串类型,它将被视为字符数。

FULLTEXT 索引

正如其名称所示,FULLTEXT索引仅允许CHARVARCHARTEXT列。此索引受InnoDBMyISAM存储引擎支持。在这种类型中,索引将在整个列上进行,而不是在前缀长度上。MySQL 8 在查询执行的优化阶段评估全文表达式。在进行执行计划的过程中,优化会在进行估计之前评估全文表达式。因此,全文查询的EXPLAIN查询比非全文查询慢。全文查询在以下情况下很有用:

  • FULLTEXT查询返回文档 ID 或文档 ID 和搜索排名时

  • FULLTEXT查询按降序对匹配行进行排序并使用LIMIT子句获取N行数时,只应用单个降序ORDER BY子句,并且不要在其中使用WHERE子句进行优化

  • FULLTEXT查询从行中获取COUNT(*)值而没有任何额外的WHERE子句时,应用WHERE子句为WHERE MATCH(text) AGAINST ('other_text'),而不使用> 0 比较运算符

空间索引

MySQL 8 允许您在空间数据类型上创建索引。InnoDBMyISAM存储引擎支持空间数据的 R-Tree,而其他存储引擎使用 B-Tree。自 MySQL 5.7 以来,空间索引在MyISAMInnoDB数据库引擎中得到支持。

内存存储引擎中的索引

内存存储引擎支持HASH索引和 B-Tree 索引,但默认情况下,MEMORY存储引擎设置为HASH索引。

多列索引

MySQL 8 允许您在单个索引创建中使用多个列,这也被称为复合索引。它允许在复合索引中最多使用 16 列。在使用复合索引时,请确保遵循在索引创建期间提到的相同列的顺序。多列索引包含通过连接索引列的值生成的值。请考虑以下示例以了解多列索引:

CREATE TABLE Employee (
id INT NOT NULL,
lastname varchar(50) not null,
firstname varchar(50) not null,
PRIMARY KEY (id),
INDEX name (lastname, firstname)
);

如上所述,我们使用两列lastnamefirstname定义了复合索引。以下查询使用了名称索引:

SELECT * FROM Employee WHERE lastname='Shah';
SELECT * FROM Employee WHERE lastname ='Shah' AND firstname ='Mona';
SELECT * FROM Employee WHERE lastname ='Shah' AND (firstname ='Michael' OR firstname ='Mona');
SELECT * FROM Employee WHERE lastname ='Shah' AND firstname >='M' AND firstname < 'N';

在所有前述的查询中,我们可以看到列的顺序在WHERE条件中保持不变,类似于索引声明的顺序。当我们仅在WHERE子句中定义lastname列时,索引也可以起作用,因为它是索引中定义的最左边的列。现在,有一些查询中复合索引将不起作用:

SELECT * FROM Employee WHERE firstname='Mona';
SELECT * FROM Employee WHERE lastname='Shah' OR firstname='Mona';

请记住,在多列索引的情况下,优化器可以使用索引的任何最左前缀来搜索行。例如,如果索引是按顺序定义的三列column1column2column3,那么您可以在WHERE子句中定义它,使用索引功能(column1, column2, column3), (column1), (column1, column2)。

B-Tree 索引

B-Tree 索引的主要目的是减少物理读取操作的次数。B-Tree 索引是通过对搜索键进行排序并维护分层搜索数据结构来创建的,这有助于搜索正确的数据条目页。默认情况下,InnoDBMyISAM存储引擎使用 B-Tree 索引。B-Tree 设法使所有叶节点到根节点的距离相等。这个索引加快了数据访问,因为不需要扫描整个数据来获取所需的输出。相反,它从根节点开始。根节点保存子节点的指针,存储引擎遵循这些指针以找到下一个路径。它通过考虑节点页中的值来找到正确的路径。节点页定义了子节点中值的上限和下限。在搜索过程结束时,存储引擎要么成功到达叶页,要么得出结论,即没有与搜索相关联的值。请记住,叶页指向索引数据,而不是其他页面。现在,让我们参考一个图表,以更详细地了解 B-Tree 索引:

以下是 B-Tree 无法使用的查询:

现在,让我们通过考虑以下表格来了解 B-Tree 索引在选择查询中的工作原理:

CREATE TABLE Employee (
 lastname varchar(50) not null,
 firstname varchar(50) not null,
 dob date not null,
 gender char(1) not null,
 key(lastname, firstname, dob)
 );

根据表定义,索引将包含三列firstnamelastnamedob的组合值。它将根据先前给定的顺序对值进行排序;这意味着如果某些员工具有相似的名称,则它们将按其出生日期进行排序。考虑以下类型的查询,这些查询将受益于 B-Tree 索引:

  • 匹配完整值:查找名为 Mohan Patel 且出生于 1981 年 11 月 28 日的员工。

  • 精确匹配一部分并与另一列的范围匹配:查找姓氏为 Patel 且名字以 A 开头的员工。

  • 正如之前讨论的,当查询针对索引列执行时,MySQL 8 查询引擎从根节点开始,并通过中间节点到达叶节点。让我们举个例子,假设您想在索引列中找到值 89。在这种情况下,查询引擎会引用根节点以获取中间页的引用。因此,它将指向1-100。然后,它确定下一个中间级别,并指向值51-100。然后查询引擎转到第三页,即下一个中间级别,76-100。从那里,它将找到值 89 的叶节点。叶节点包含整行或指向该行的指针,具体取决于索引是聚集还是非聚集。

  • 与列前缀匹配:查找姓氏以 M 开头的员工。它只使用索引中的第一列。

  • 哈希索引

匹配值范围:查找姓氏为 Patel 和 Gupta 的员工。

  • 在范围条件之后不要使用任何条件:例如,您已经放置了WHERE条件lastname='Patel'firstname类似‘A%'dob=' 28/11/1981'。在这里,只考虑前两列用于索引,因为LIKE是一个范围条件。

  • 不要跳过索引中定义的任何列:这意味着您不允许使用lastnamedob来查找在WHERE条件中缺少firstname的员工。

  • 查找不是从索引列的最左侧开始的:例如,如果您查找名为Mohandob在特定日期的员工,则索引将不起作用。在此查询中,定义的列不是索引中最左侧的列。同样,如果您查找姓氏以某些内容结尾的员工,则索引也不起作用。

匹配最左侧前缀:查找所有有姓氏的员工。这些只使用索引中的第一列。

通过完整的树遍历多个级别来从大型数据库中找到单个值是非常困难的。为了克服这个问题,MySQL 提供了另一种索引类型,称为哈希索引。这个索引创建了一个哈希表,而不是 B-Tree 索引所具有的结构非常扁平。哈希主要使用哈希函数来生成数据的地址。与哈希相关的两个重要术语是:

  • 哈希函数:映射函数,用于将搜索键与存储实际记录的地址进行映射。

  • Bucket:桶是哈希索引存储数据的存储单元。一个桶表示一个完整的磁盘块,可以存储一个或多个记录。

除了哈希机制之外,哈希索引还具有一些特殊特性,如下所述:

  • 整个键用于搜索行。而在 B-Tree 的情况下,只使用键的最左前缀来查找行。

  • 优化器不会使用哈希索引来加速ORDER BY操作。换句话说,这个索引永远不会用于查找下一个条目。

  • 哈希索引用于使用=<=>运算符进行相等比较。它永远不会使用返回一系列值的比较运算符。例如,<(小于)运算符。

  • 范围优化器实际上无法估计两个值之间有多少行可用。而且,如果我们使用哈希索引的MEMORY表而不是InnoDBMyISAM,那么它也可能影响查询。

索引扩展

索引扩展是 MySQL 8 通过附加主键扩展次要索引的功能。如果需要,InnoDB引擎会自动扩展次要索引。为了控制索引扩展的行为,MySQL 8 在optimizer_switch系统变量中定义了一个use_index_extensions标志。默认情况下,此选项已启用,但用户可以使用以下命令在运行时更改它:

SET optimizer_switch = 'use_index_extensions=off';

让我们看一个例子,以深入了解索引扩展。让我们创建一个表,并插入以下值:

CREATE TABLE table1 (
 c1 INT NOT NULL DEFAULT 0,
 c2 INT NOT NULL DEFAULT 0,
 d1 DATE DEFAULT NULL,
 PRIMARY KEY (c1, c2),
 INDEX key1 (d1)
) ENGINE = InnoDB;

--Insert values into table
INSERT INTO table1 VALUES
(1, 1, '1990-01-01'), (1, 2, '1991-01-01'),
(1, 3, '1992-01-01'), (1, 4, '1993-01-01'),
(1, 5, '1994-01-01'), (2, 1, '1990-01-01'),
(2, 2, '1991-01-01'), (2, 3, '1992-01-01'),
(2, 4, '1993-01-01'), (2, 5, '1994-01-01'),
(3, 1, '1990-01-01'), (3, 2, '1991-01-01'),
(3, 3, '1992-01-01'), (3, 4, '1993-01-01'),
(3, 5, '1994-01-01'), (4, 1, '1990-01-01'),
(4, 2, '1991-01-01'), (4, 3, '1992-01-01'),
(4, 4, '1993-01-01'), (4, 5, '1994-01-01'),
(5, 1, '1990-01-01'), (5, 2, '1991-01-01'),
(5, 3, '1992-01-01'), (5, 4, '1993-01-01'),
(5, 5, '1994-01-01');

这个表在列c1c2上有一个主键,以及在列d1上有一个次要索引key_d1。现在,为了理解扩展效果,首先关闭它,然后执行以下带有解释命令的选择查询:

--Index extension is set as off
SET optimizer_switch = 'use_index_extensions=off';

--Execute select query with explain
EXPLAIN SELECT COUNT(*) FROM table1 WHERE c1 = 3 AND d1 = '1992-01-01';

--Output of explain query
*************************** 1\. row ***************************
 id: 1
 select_type: SIMPLE
 table: table1
 type: ref
possible_keys: PRIMARY,key1
 key: PRIMARY
 key_len: 4
 ref: const
 rows: 5
 Extra: Using where

同样,我们现在将打开扩展并再次执行解释计划查询以检查效果,使用以下代码:

--Index extension is set as on
SET optimizer_switch = 'use_index_extensions=on';

--Execute select query with explain
EXPLAIN SELECT COUNT(*) FROM table1 WHERE c1 = 3 AND d1 = '1992-01-01';

--Output of explain query
*************************** 1\. row ***************************
 id: 1
 select_type: SIMPLE
 table: table1
 type: ref
possible_keys: PRIMARY,key1
 key: key1
 key_len: 8
 ref: const,const
 rows: 1
 Extra: Using index

现在,我们将检查这两种方法之间的区别:

  • key_len值从 4 个字节变为 8 个字节,这表明键查找使用了列 d1 和 c1,而不仅仅是 d1。

  • ref值从(const)变为(const, const),这表明键查找使用了两个键部分而不是一个。

  • rows计数从 5 变为 1,这表明InnoDB需要比第一种方法更少的行来生成结果。

  • Extra值从Using where变为Using index。这表明行可以通过仅使用索引来读取,而不需要查询数据行中的任何其他列。

使用索引的优化器

MySQL 8 允许您在生成列上创建索引。生成列是其值从列定义中包含的表达式计算出来的列。考虑以下示例,我们定义了一个生成列c2,并在该列上创建了一个索引:

CREATE TABLE t1 (c1 INT, c2 INT AS (c1 + 1) STORED, INDEX (c2));

根据表的先前定义,优化器将在执行计划中考虑生成列的索引。此外,如果我们在查询中使用WHEREGROUP BYORDER BY子句中指定相同的表达式,那么优化器将使用生成列的索引。例如,如果我们执行以下查询,则优化器将使用生成列上定义的索引:

SELECT * FROM t1 WHERE c1 + 1 > 100;

在这里,优化器将识别表达式与列c2的定义相同。我们可以使用EXPLAIN命令来检查,如下所示:

mysql> explain SELECT * FROM t1 WHERE c1 + 1 > 100;
*************************** 1\. row ***************************
 id: 1
 select_type: SIMPLE
 table: t1
 partitions: NULL
 type: range
 possible_keys: c2
 key: c2
 key_len: 5
 ref: NULL
 rows: 1
 filtered: 100.00
 Extra: Using index condition

生成列索引有一些限制:

  • 查询表达式必须与生成的列定义完全匹配。例如,如果我们在列定义中将表达式定义为c1+1,那么在查询中使用相同的表达式,而不是应用1+c1

  • 在生成列定义中使用 JSON 字符串时,使用JSON_UNQUOTE()从值中删除额外的引号。例如,不要使用以下列定义:

 name TEXTAS(JSON_EXTRACT(emp,'$.name'))STORED
  • 我们将使用以下代码代替前面的代码:
 name TEXTAS(JSON_UNQUOTE(JSON_EXTRACT(emp,'$.name')))STORED
  • 优化适用于这些运算符:=<<=>>=BETWEENIN()

  • 在生成列表达式中不要仅使用其他列的引用。也就是说,不要使用以下代码:

 c2 INT AS (c1) STORED in column definition.
  • 如果优化器尝试使用错误的索引,请使用索引提示,这将禁用它并强制优化器使用不同的选择

不可见和降序索引

不可见索引是一个特殊功能,它将索引标记为优化器不可用。MySQL 8 将维护不可见索引,并在数据修改时保持其最新状态。这将适用于主键以外的索引。我们知道,默认情况下索引是可见的;我们必须在创建时或使用alter命令时显式地将它们设置为不可见。MySQL 8 提供了VISIBLEINVISIBLE关键字来维护索引的可见性。降序索引是按降序存储键值的方法。降序索引更有效,因为它可以按正向顺序扫描。让我们通过示例详细了解这些索引。

不可见索引

如前所述,优化器不使用不可见索引。那么这个索引有什么用呢?这个问题会出现在我们的脑海中,对吧?我们将向您解释一些不可见索引的用例:

  • 当定义了许多索引,但不确定哪个索引未被使用时。在这种情况下,您可以使一个索引不可见并检查性能影响。如果有影响,那么您可以立即使该索引可见。

  • 只有一个查询使用索引的特殊情况。在这种情况下,不可见索引是一个很好的解决方案。

在以下示例中,我们将使用CREATE TABLECREATE INDEXALTER TABLE命令创建一个不可见索引:

CREATE TABLE `employee` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `department_id` int(11),
 `salary` int(11),
 PRIMARY KEY (`id`)
 ) ENGINE=InnoDB;

CREATE INDEX idx1 ON employee (department_id) INVISIBLE;
ALTER TABLE employee ADD INDEX idx2 (salary) INVISIBLE;

要更改索引的可见性,请使用以下命令:

 ALTER TABLE employee ALTER INDEX idx1 VISIBLE;
 ALTER TABLE employee ALTER INDEX idx1 INVISIBLE;

要获取有关索引的信息,请以以下方式执行INFORMATION_SCHEMA.STATISTICStableSHOW INDEX命令:

mysql>SELECT * FROM information_schema.statistics WHERE is_visible='NO';
*************************** 1\. row ***************************
TABLE_CATALOG: def
TABLE_SCHEMA: db1
TABLE_NAME: employee
NON_UNIQUE: 1
INDEX_SCHEMA: db1
INDEX_NAME: idx1
SEQ_IN_INDEX: 1 
COLUMN_NAME: department_id
COLLATION: A 
CARDINALITY: 0 
SUB_PART: NULL 
PACKED: NULL 
NULLABLE: YES
INDEX_TYPE: BTREE 
COMMENT: 
INDEX_COMMENT: 
IS_VISIBLE: NO

mysql>SELECT INDEX_NAME, IS_VISIBLE FROM INFORMATION_SCHEMA.STATISTICS
 -> WHERE TABLE_SCHEMA = 'db1' AND TABLE_NAME = 'employee';
+------------+------------+
| INDEX_NAME | IS_VISIBLE |
+------------+------------+
| idx1 | NO |
| idx2 | NO |
| PRIMARY | YES |
+------------+------------+

mysql> SHOW INDEXES FROM employee;
*************************** 1\. row ***************************
Table:employee
Non_unique:1
Key_name:idx1
Seq_in_index:1
Column_name: department_id
Collation:A
Cardinality:0
Sub_part: NULL
Packed: NULL
Null:YES
Index_type: BTREE
Comment:
Index_comment:
Visible: NO

MySQL 8 在optimizer_switch系统变量中提供了一个use_invisible_indexes标志,用于控制查询优化器使用的不可见索引。如果此标志打开,则优化器在执行计划构建中使用不可见索引,而如果标志关闭,则优化器将忽略不可见索引。MySQL 8 提供了一个隐式主键的功能,如果您在NOT NULL列上定义了一个UNIQUE索引。一旦在此字段上定义了索引,MySQL 8 将不允许您将其设置为不可见。为了理解这种情况,让我们以以下表为例。让我们尝试执行以下命令,使idx1索引不可见:

CREATE TABLE table2 (
 field1 INT NOT NULL,
 field2 INT NOT NULL,
 UNIQUE idx1 (field1)
) ENGINE = InnoDB;

现在,服务器将会给出一个错误,如下所示的命令:

mysql> ALTER TABLE table2 ALTER INDEX idx1 INVISIBLE;
ERROR 3522 (HY000): A primary key index cannot be invisible

现在让我们使用以下命令将主键添加到表中:

ALTER TABLE table2 ADD PRIMARY KEY (field2);

现在,我们将尝试使idex1不可见。这次,服务器允许了,如下所示的命令:

mysql> ALTER TABLE table2 ALTER INDEX idx1 INVISIBLE;
Query OK, 0 rows affected (0.06 sec)
Records: 0 Duplicates: 0 Warnings: 0

降序索引

降序索引是按降序顺序存储键值的索引。这个索引按正向顺序扫描,与其他索引相比性能更好。降序索引允许用户定义组合升序和降序顺序的多列索引。实际知识总是比理论知识更容易理解,对吧?所以,让我们看一些例子,以深入了解降序索引。首先,创建一个具有以下定义的表:

CREATE TABLE t1 (
 a INT, b INT,
 INDEX idx1 (a ASC, b ASC),
 INDEX idx2 (a ASC, b DESC),
 INDEX idx3 (a DESC, b ASC),
 INDEX idx4 (a DESC, b DESC)
);

根据表定义,MySQL 8 将创建四个不同的索引,因此优化器对每个ORDER BY子句执行前向索引扫描。考虑以下不同版本的ORDER BY子句:

ORDER BY a ASC, b ASC -- optimizer can use idx1
ORDER BY a DESC, b DESC -- optimizer can use idx4
ORDER BY a ASC, b DESC -- optimizer can use idx2
ORDER BY a DESC, b ASC -- optimizer can use idx3

现在,让我们看一下相同表定义的第二种情况,它将描述与 MySQL 5.7.14 版本相比,降序索引对性能的影响。考虑以下选择查询以测量性能:

Query 1: SELECT * FROM t1 ORDER BY a DESC;
Query 2: SELECT * FROM t1 ORDER BY a ASC;
Query 3: SELECT * FROM t1 ORDER BY a DESC, b ASC;
Query 4: SELECT * FROM t1 ORDER BY a ASC, b DESC;
Query 5: SELECT * FROM t1 ORDER BY a DESC, b DESC;
Query 6: SELECT * FROM t1 ORDER BY a ASC, b ASC;

以下统计图是由 MySQL 8 提供的,针对先前提到的查询,有 1000 万行数据:

参考:mysqlserverteam.com/mysql-8-0-labs-descending-indexes-in-mysql/

在使用降序索引时,有一些重要的要点需要记住,如下所示:

  • 所有升序索引支持的数据类型也支持降序索引。

  • 降序索引支持BTREE,但不支持HASHFULLTEXTSPATIAL索引。如果您尝试为HASHFULLTEXTSPATIAL索引显式使用ASCDESC关键字,那么 MySQL 8 将生成错误。

  • 降序索引仅支持InnoDB存储引擎,但InnoDB SQL 解析器不使用降序索引。如果主键包括降序索引,则不支持对辅助索引的更改缓冲。

  • DISTINCT可以使用任何索引,包括降序键,但对于MIN()/MAX(),不使用降序键部分。

  • 非生成和生成列都允许使用降序索引。

总结

当你了解它是如何工作的时候,一切都变得非常有趣,对吧?我们希望您在本章中对索引有相同的看法。我们已经涵盖了非常有用的信息,这将帮助您在正确的列上定义索引以获得更好的性能。除此之外,我们还描述了各种类型的索引及其存储结构。

在下一章中,我们将为您提供有关复制的信息。我们将详细解释复制的配置和实现。

第八章:MySQL 8 中的复制

在上一章中,我们深入探讨了 MySQL 8 索引。索引是任何数据库管理系统的重要实体。它们通过限制要访问的记录数量来提高 SQL 查询性能。致力于性能改进的数据库管理员必须了解这一重要技术。本章详细解释了索引的类型及其优势。本章还解释了 MySQL 8 中索引的工作原理。这将是一个非常信息丰富的章节!

沿着同样的线路继续前进,在本章中,我们将讨论数据库复制。我们已经对数据库复制有多少了解并不重要。本章涵盖了有关数据库复制的深入细节。如果您已经了解数据库复制,本章将为您增加更多知识。如果您第一次听说它,您将在本章中找到使其正常工作所需的每一个细节。那么,我们准备好开始了吗?以下是本章将涵盖的主题列表:

  • 复制概述

  • 配置复制

  • 实施复制

  • 组复制与集群

  • 复制解决方案

复制概述

在本节中,我们将介绍数据库复制的基础知识。我们将了解复制是什么,它提供的优势以及复制可能有益的场景。

什么是 MySQL 复制?

假设您正在阅读本文有两个原因。您熟悉 MySQL 复制并愿意获取更多知识,也许您不熟悉 MySQL 复制并希望学习。

MySQL 复制对于服务许多不同的目的都是有用的。通常,当人们开始有更多的查询超过单个数据库服务器可以处理时,他们开始考虑 MySQL 复制。基于此,你对 MySQL 复制有什么猜测吗?复制是一种技术,可以设置多个数据库来为单个或多个客户端应用程序提供服务。客户端可以是最终用户或发送来自不同设备(如计算机、手机、平板电脑等)的读取数据或写入数据的任何查询请求的人。这些数据库是相同数据库的副本。这意味着参与数据库复制的所有数据库彼此完全相同。复制通过频繁地将数据从一个数据库复制到所有其他副本数据库来工作。这些数据库可以位于同一数据库服务器上、不同的数据库服务器上或完全不同的机器上。

如前所述,数据库复制有各种目的。这取决于为什么设置 MySQL 数据库复制。MySQL 复制是为了扩展数据库或由数据库支持的应用程序。它还用于维护数据库备份和报告目的。我们稍后将在本章中详细讨论这些问题。

MySQL 复制主要用于扩展读取。在任何 Web 应用程序中,读取操作的数量相对于写入数据库操作要高得多。大多数常见的 Web 应用程序都是读取密集型的。考虑一个社交网络网站的例子。如果我们导航到用户个人资料页面,我们会看到很多信息,如用户的个人信息、人口统计信息、社交关系、一些评分等等。如果仔细观察,我们会发现在数据库上执行的SELECT查询的数量要比INSERTUPDATEDELETE查询要高得多。通过 MySQL 数据库复制,我们可以将读取操作定向到特定的数据库上,以便实现更高的性能。

MySQL 复制看起来很简单,可以在几个小时内设置好,但很容易变得复杂。在新数据库上设置非常容易。相反,在生产数据库上设置它非常复杂。我们不应该将 MySQL 复制与分布式数据库系统混淆。在分布式数据库系统中,数据库保存不同的数据集。数据库操作根据一些关键信息路由到特定的数据库。

在传统的 MySQL 复制中,一个数据库充当主数据库,其余数据库充当从数据库。并不总是必须只有一个主数据库。我们可以在复制中有多个主数据库。这种技术称为多主复制。从服务器从主数据库复制数据。在传统的 MySQL 复制中,复制数据的过程是异步的。这意味着从数据库服务器与主数据库服务器并非永久连接。MySQL 支持不同级别的复制。我们可以将所有主数据库、选定的数据库或选定的主数据库中的表复制到从数据库中。

MySQL 8 提供了不同的数据库复制方法。MySQL 8 有一个二进制日志文件。文件的内容是描述数据库更改的事件。事件可以是“基于语句”的或“基于行”的类型。更改包括数据定义更改和数据操作更改,或者可能修改数据库的语句,如DELETE语句。二进制日志还包含每个 SQL 语句更新数据库所花费的时间的信息。传统的 MySQL 数据库复制方法基于主数据库服务器上的二进制日志文件同步数据库到从服务器。从服务器根据文件中日志记录的位置复制或复制主数据库服务器的二进制日志文件的内容。

MySQL 8 还支持基于二进制日志文件的数据库复制方法以及新的方法。在 MySQL 8 数据库服务器上提交的每个事务都被视为唯一的。每个在主数据库服务器上提交的事务都与唯一的全局事务标识符(GTID)相关联。正如其名称所示,全局标识符不仅仅是在创建它的主数据库服务器上唯一的,而且在参与 MySQL 8 复制的所有数据库中都是唯一的。因此,每个提交的事务和全局事务标识符之间存在一对一的映射。MySQL 复制的新方法基于 GTID。它极大地简化了复制过程,因为它不依赖于二进制日志文件及其位置的事件。GTID 表示为一对冒号(“:”)分隔的坐标,如下所示:

GTID = source_id:transaction_id

source_id是源自 GTID 的数据库服务器的标识符。通常,数据库服务器的server_uuid用作source_idtransaction_id是事务在数据库服务器上提交的顺序号。例如,以下示例显示了第一个提交事务的 GTID:

1A22AF74-17AC-E111-393E-80C49AB653A2:1

提交的事务的序列号从1开始。它永远不可能是0

基于 GTID 的 MySQL 复制方法是事务性的,这就是为什么它比基于二进制日志文件的复制方法更可靠。只要在主数据库服务器上提交的所有事务也在所有从数据库服务器上应用,GTID 就可以保证复制的准确性和一致性。

如前所述,MySQL 数据库复制通常是异步的。但是,MySQL 8 支持不同类型的复制同步。同步的常规方法是异步的。这意味着一个服务器充当主数据库服务器。它将所有事件写入二进制日志文件。其他数据库服务器充当从服务器。从服务器从主数据库服务器中读取和复制基于位置的事件记录。因此,它总是从主数据库服务器到从数据库服务器。MySQL 8 还支持半同步同步方法。在半同步复制方法中,任何在主数据库服务器上提交的事务都会被阻塞,直到主数据库服务器收到至少一个从数据库服务器已接收并记录了事务事件的确认。延迟复制是 MySQL 8 支持的另一种复制方法。在延迟复制中,从数据库服务器故意将事务事件记录在主数据库服务器之后一段时间。

MySQL 复制的优势

现在我们已经熟悉了 MySQL 数据库复制是什么,是时候评估维护多个数据库服务器的增加复杂性是否值得了。

MySQL 8 数据库复制的优势如下:

  1. 扩展解决方案:如前所述,通常 Web 应用程序是读密集型应用程序。读操作的数量远远高于写操作。这些应用程序提供需要在数据库服务器上执行复杂的 SQL 查询的功能。这些不是毫秒级执行的查询。这样复杂的查询可能需要几秒到几分钟的执行时间。执行这样的查询会给数据库服务器带来沉重的负载。在这种情况下,最好将这些读操作在主数据库服务器上执行而不是在主数据库服务器上执行。写数据库操作将始终在主数据库服务器上执行。你知道为什么吗?因为它触发数据库修改。这些修改的事件必须写入二进制日志文件,以便从从服务器进行复制同步。此外,同步是从主服务器到从服务器。因此,如果我们在从服务器上执行写数据库操作,这些操作将永远不会在主数据库服务器上可用。这种方法通过在多个从服务器上执行读操作来提高写操作的性能,并增加读操作的速度。

  2. 数据安全:安全性通常是每个 Web 应用程序都需要的重要功能。安全性可以在应用程序层或数据库层上进行。数据安全性可防止数据丢失。通过定期备份数据库来实现数据安全性。如果没有设置复制,备份生产数据库需要将应用程序置于维护模式。这是必需的,因为应用程序和备份过程同时访问数据库可能会损坏数据。有了复制,我们可以使用其中一个从服务器进行备份。由于从数据库服务器始终与主数据库服务器同步,我们可以备份从数据库服务器。为此,我们可以使从数据库服务器在备份过程运行时停止从主数据库服务器复制。这不需要 Web 应用程序停止使用主数据库服务器。事实上,它不会以任何方式影响主数据库服务器。另一个数据安全性方面是为生产或主数据库服务器提供基于角色的访问。我们只能让少数角色从后端访问主数据库服务器。其余用户或角色可以访问从数据库服务器。这减少了由于人为错误而导致的意外数据丢失的风险。

  3. 分析:分析和报告始终是数据库支持的应用程序的重要功能。这些功能需要频繁地从数据库中获取信息,以便对数据进行分析。如果设置了数据库复制,我们可以从从数据库服务器获取分析所需的数据,而不会影响主数据库服务器的性能。

  4. 远程数据分发:应用程序开发人员通常需要在本地开发环境中复制生产数据。在启用数据库复制的基础设施中,可以使用从数据库服务器在开发数据库服务器上准备数据库副本,而无需经常访问主数据库服务器。

配置复制

在本节中,我们将学习不同类型的 MySQL 8 复制方法的配置。它包括逐步设置和配置复制的说明。

基于二进制日志文件的复制

MySQL 数据库复制最常见的传统方法之一是二进制日志文件位置方法。本节重点介绍了二进制日志文件位置复制的配置。在我们进入配置部分之前,最好复习和了解基于二进制日志位置的复制的基础知识。

如前所述,MySQL 数据库服务器之一充当主服务器,其余的 MySQL 数据库服务器成为从服务器。主数据库服务器是数据库更改的起点。主数据库服务器根据数据库的更新或更改在二进制日志文件中写入事件。写入二进制日志文件的信息记录的格式根据记录的数据库更改而变化。MySQL REPLICATION SLAVE数据库服务器被配置为从主数据库服务器读取二进制日志事件。从服务器在本地数据库二进制日志文件上执行事件。这样从服务器就可以将数据库与主数据库同步。当从数据库服务器从主数据库服务器读取二进制日志文件时,从服务器会获得整个二进制日志文件的副本。一旦接收到二进制日志文件,就由从服务器决定在从服务器二进制日志文件上执行哪些语句。可以指定应在从数据库服务器的二进制日志文件上执行来自主数据库服务器二进制日志文件的所有语句。也可以处理特定数据库或表过滤的事件。

只有从数据库服务器可以配置过滤来自主数据库服务器日志文件的事件。无法配置主数据库服务器仅记录特定事件。

MySQL 8 提供了一个系统变量,可以帮助唯一标识数据库服务器。参与 MySQL 复制的所有数据库服务器都必须配置为具有唯一的 ID。每个从数据库服务器都必须配置主数据库服务器的主机名、日志文件名和日志文件中的位置。设置完成后,可以在从数据库服务器上使用CHANGE MASTER TO语句在 MySQL 会话中修改这些细节。

当从数据库服务器从主数据库二进制日志文件中读取信息时,它会跟踪二进制日志坐标的记录。 二进制日志坐标包括文件名和文件内的位置,从主数据库服务器读取和处理。 从数据库服务器读取主数据库服务器的二进制日志文件的效率非常高,因为可以将多个从数据库服务器连接到主数据库服务器,并从主数据库服务器处理二进制日志文件的不同部分。 主数据库服务器的操作保持不变,因为从主数据库服务器连接和断开从数据库服务器的控制由从服务器自己控制。 如前所述,每个从数据库服务器都会跟踪二进制日志文件中的当前位置。 因此,从数据库服务器可以断开连接并重新连接到主数据库服务器,并恢复二进制日志文件处理。

MySQL 中提供了多种设置数据库复制的方法。 复制的确切方法取决于数据库中是否已存在数据以及如何设置复制。 以下各节中的每个部分都是配置 MySQL 复制的步骤。

复制主配置

在设置复制主数据库服务器之前,必须确保数据库服务器已建立唯一 ID 并启用了二进制日志记录。 可能需要在进行这些配置后重新启动数据库服务器。 主数据库服务器二进制日志是 MySQL 8 数据库复制的基础。

要启用二进制日志记录,应将log_bin系统变量设置为ON。 默认情况下,MySQL 数据库服务器启用了二进制日志记录。 如果使用mysqld手动使用--initialize--initialize-insecure选项初始化数据目录,则默认情况下禁用了二进制日志记录。 必须通过指定--log-bin选项来启用它。 --log-bin选项指定要用于二进制日志文件的基本名称。

如果启动选项未指定文件名,则二进制日志文件名将基于数据库服务器主机名进行设置。 建议使用--log-bin选项指定二进制日志文件名。 如果使用--log_bin=old_host_name-bin指定日志文件名,则即使更改数据库服务器主机,日志文件名也将保留。

要设置主数据库服务器,请在主数据库服务器上打开 MySQL 配置文件:

sudo vim /etc/mysql/my.cnf

在配置文件中进行以下更改。

首先,找到将服务器绑定到 localhost 的部分:

bind-address = 127.0.0.1

用实际数据库服务器 IP 地址替换本地 IP 地址。 这一步很重要,因为从服务器可以使用主数据库服务器的公共 IP 地址访问主数据库服务器:

bind-address = 175.100.170.1

需要对主数据库服务器进行配置以配置唯一 ID。 还包括设置主二进制日志文件所需的配置:

[mysqld]
log-bin=/var/log/mysql/mysql-bin.log
server-id=1

现在,让我们配置数据库在从数据库服务器上进行复制。 如果需要在从数据库服务器上复制多个数据库,则多次重复以下行:

binlog_do_db = database_master_one
binlog_do_db = database_master_two

完成这些更改后,使用以下命令重新启动数据库服务器:

sudo service mysql restart

现在,我们已经设置好了主数据库服务器。 下一步是授予从用户权限如下:

mysql> mysql -u root -p
mysql> CREATE USER 'slaveone'@'%' IDENTIFIED BY 'password';
mysql> GRANT REPLICATION SLAVE ON *.* TO 'slaveone'@'%' IDENTIFIED BY 'password';

上述命令创建了从用户,在主数据库服务器上授予了权限,并刷新了数据库缓存的权限。

现在,我们必须备份要复制的数据库。 我们将使用mysqldump命令备份数据库。 此数据库将用于创建slave数据库。 主状态输出显示要复制的二进制日志文件名、当前位置和数据库名称:

mysql> USE database_master_one;
mysql> FLUSH TABLES WITH READ LOCK;
mysql> SHOW MASTER STATUS;
+------------------+----------+---------------------+------------------+ 
|       File       | Position |     Binlog_Do_DB    | Binlog_Ignore_DB | 
+------------------+----------+---------------------+------------------+ 
| mysql-bin.000001 |    102   | database_master_one |                  | 
+------------------+----------+---------------------+------------------+ 
1 row in set (0.00 sec)

mysqldump -u root -p database_master_one > database_master_one_dump.sql

在使用mysqldump命令进行数据库备份之前,我们必须锁定数据库以检查当前位置。稍后将使用此信息设置从数据库服务器。

数据库转储完成后,应使用以下命令解锁数据库:

mysql> UNLOCK TABLES;
mysql> QUIT;

我们已经完成了设置复制主数据库服务器所需的所有配置,并使其可以被REPLICATION SLAVE数据库服务器访问。

以下选项对主数据库服务器设置产生影响:

  1. innodb_flush_log_at_trx_commit=1sync_binlog=1选项应该设置为实现更高的耐久性和一致性。这些选项可以在my.cnf配置文件中设置。

  2. skip-networking选项不能被启用。如果启用了,从服务器就无法与主服务器通信,数据库复制将失败。

REPLICATION SLAVE 配置

与主数据库服务器类似,每个从数据库服务器必须有一个唯一的 ID。设置完成后,这将需要数据库服务器重启:

[mysqld]
server-id=2

要设置多个从数据库服务器,必须配置一个唯一的非零server-id,该 ID 与主服务器或任何其他从数据库服务器不同。不需要在从数据库服务器上启用二进制日志记录以设置复制。如果启用了,在从数据库服务器上的二进制日志文件可以用于数据库备份和崩溃恢复。

现在,创建一个新的数据库,它将成为主数据库的副本,并从主数据库的数据库转储中导入数据库如下:

mysql> CREATE DATABASE database_slave_one;
mysql> QUIT;

# mysql -u root -p database_slave_one &lt; /path/to/database_master_one_dump.sql

现在,我们必须在my.cnf文件中配置其他一些选项。与二进制日志类似,中继日志由带有数据库更改事件的编号文件组成。它还包含一个索引文件,其中包含所有已使用的中继日志文件的名称。以下配置设置了中继日志文件、二进制日志文件和从服务器数据库的名称,该名称是主数据库的副本,如下所示:

relay-log = /var/log/mysql/mysql-relay-bin.log
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = database_slave_one

在进行此配置更改后,需要重新启动数据库服务器。下一步是在 MySQL shell 提示符中启用从服务器复制。执行以下命令设置slave数据库服务器所需的master数据库信息:

mysql> CHANGE MASTER TO MASTER_HOST='12.34.56.789', MASTER_USER='slaveone', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS= 103;

作为最后一步,激活从服务器:

mysql> START SLAVE;

如果在slave数据库服务器上启用了二进制日志记录,从服务器可以参与复杂的复制策略。在这样的复制设置中,数据库服务器A充当数据库服务器B的主服务器。B充当Amaster数据库服务器的从服务器。现在,B反过来可以充当Cslave数据库服务器的主数据库服务器。类似的情况如下所示:

A -> B -> C

添加从服务器进行复制

可以向现有的复制配置中添加一个新的从数据库服务器。这不需要停止主数据库服务器。方法是复制现有的slave数据库服务器。复制后,我们必须修改server-id配置选项的值。

以下说明设置了一个新的从数据库服务器到现有的复制配置。首先,应该关闭现有的从数据库服务器如下:

mysql> mysqladmin shutdown

现在,应该将现有从服务器的数据目录复制到新的从数据库服务器。除了数据目录,还必须复制二进制日志和中继日志文件。建议为新的从数据库服务器使用与现有从数据库服务器相同的--relay-log值。

如果主信息和中继日志信息存储库使用文件,则必须从现有的从数据库服务器复制这些文件到新的从数据库服务器。这些文件保存了主服务器的当前二进制日志坐标和从服务器的中继日志。

现在,启动之前停止的现有从服务器。

现在,我们应该能够启动新的从服务器。如果尚未设置,必须在启动新的从服务器之前配置唯一的server-id

基于全局事务标识符的复制

本节重点介绍基于全局事务标识符的复制。它解释了在 MySQL 服务器中如何定义、创建和表示 GTID。它描述了设置和启动基于 GTID 的复制的过程。

使用基于 GTID 的复制,每个事务在提交到原始数据库服务器时都被分配一个唯一的事务 ID,称为GTID。这个唯一标识符是全局的,这意味着它在参与复制的所有数据库服务器中是唯一的。使用 GTID,更容易跟踪和处理每个事务在提交到master数据库服务器时。使用这种复制方法,不需要依赖日志文件来同步masterslave数据库。也更容易确定masterslave数据库是否一致,因为这种复制方法是基于事务的。只要在master数据库上提交的所有事务也在从服务器上应用,masterslave数据库之间的一致性就是有保证的。可以使用基于语句或基于行的复制与 GTID。如前所述,GTID 用由冒号(:)分隔的一对坐标表示,如下例所示:

GTID = source_id:transaction_id

使用基于 GTID 的复制方法的优点是:

  1. 通过这种复制方法,可以在服务器故障转移的情况下切换主数据库服务器。全局事务标识符在所有参与的数据库服务器上是唯一的。从服务器使用 GTID 跟踪最后执行的事务。这意味着如果主数据库服务器切换到新的数据库服务器,从服务器更容易继续使用新的主数据库服务器并恢复复制处理。

  2. 从服务器数据库服务器的状态以一种崩溃安全的方式维护。使用更新的复制技术,slave数据库服务器在名为mysql.gtid_slave_pos的系统表中跟踪当前位置。使用诸如InnoDB之类的事务存储引擎,状态的更新记录在与数据库操作相同的事务中。因此,如果从服务器崩溃,重新启动后,从服务器将启动崩溃恢复,并确保记录的复制位置与复制的更改匹配。这在传统的基于二进制日志文件的复制中是不可能的,因为中继日志文件独立于实际数据库更改而更新,如果从服务器崩溃,很容易出现不同步。

在深入了解基于 GTID 的复制配置之前,让我们先了解一些其他术语。

gtid_set是一组全局事务标识符。它在以下示例中表示:

gtid_set:
 uuid_set [, uuid_set] ...
 | ''

uuid_set:
 uuid:interval[:interval]...

uuid:
 hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh

h:
 [0-9|A-F]

interval:
 n[-n]
 (n >= 1)

GTID 集的使用方式有几种。系统变量gtid_executedgtid_purged用 GTID 集表示。MySQL 函数GTID_SUBSET()GTID_SUBTRACT()需要 GTID 集作为输入参数。

主数据库服务器和从数据库服务器都保留 GTID。一旦在一个服务器上使用一个 GTID 提交了一个事务,那个服务器就会忽略任何后续具有相似 GTID 的事务。这意味着在master数据库服务器上提交的事务只能在slave数据库服务器上提交或应用一次。这有助于保持masterslave数据库之间的一致性。

以下是 GTID 的生命周期摘要:

  1. 事务在主数据库服务器上执行并提交。使用主服务器的 UUID 为该事务分配一个 GTID。GTID 被写入主数据库服务器的二进制日志文件。

  2. 一旦二进制日志文件被从服务器接收并记录在从服务器的中继日志中,从服务器会将gtid_next系统变量的值设置为读取的 GTID。这表示给slave指示下一个要执行的事务是具有此 GTID 的事务。

  3. slave数据库服务器在二进制日志文件中维护其已处理事务的 GTID 集。在应用由gtid_next指示的事务之前,它会检查二进制日志文件中是否记录了或记录了该 GTID。如果在二进制日志文件中找不到 GTID,则从服务器会处理与 GTID 相关联的事务,并将 GTID 写入二进制日志文件。这样,从服务器可以保证同一个事务不会被执行多次。

现在,让我们转到基于 GTID 的 MySQL 复制的主配置。首先,打开my.cnf文件并进行以下更改:

[mysqld]
server-id = 1
log-bin = mysql-bin 
binlog_format = ROW 
gtid_mode = on 
enforce_gtid_consistency 
log_slave_updates

这些配置更改需要服务器重启。前面的配置是不言自明的。gtid_mode选项启用了基于 GTID 的数据库复制。

  1. 现在,在从服务器上为访问主数据库创建一个用户。同时,使用mysqldump命令进行数据库备份。数据库备份将用于设置从服务器。
 > CREATE USER 'slaveuser'@'%' IDENTIFIED BY 'password'; 
 > GRANT REPLICATION SLAVE ON *.* TO 'slaveuser'@'%' IDENTIFIED 
          BY 'password';
 > mysqldump -u root -p databaseName > databaseName.sql

这就是主数据库配置的全部内容。让我们继续进行从服务器端的配置。

  1. slave数据库服务器的 shell 提示符上,按照以下步骤从master数据库服务器备份中导入数据库:
 > mysql -u root -p databaseName &lt; /path/to/databaseName.sql
  1. 现在,在从服务器的my.cnf文件中添加以下配置:
 [mysqld]
 server_id = 2
 log_bin = mysql-bin
 binlog_format = ROW
 skip_slave_start
 gtid_mode = on
 enforce_gtid_consistency
 log_slave_updates
  1. 完成这些配置后,使用以下命令重新启动数据库服务器:
 sudo service mysql restart
  1. 下一步是在slave数据库服务器上使用CHANGE MASTER TO命令设置主数据库服务器信息:
 > CHANGE MASTER TO MASTER_HOST='170.110.117.12', MASTER_PORT=3306, 
 MASTER_USER='slaveuser', MASTER_PASSWORD='password', MASTER_AUTO_POSITION=1;
  1. 现在,启动slave服务器:
 START SLAVE;

在这种复制方法中,主数据库备份已经包含了 GTID 信息。因此,我们只需要提供从服务器应该开始同步的位置。

  1. 这是通过设置GTID_PURGED系统变量来完成的:
 -- -- GTID state at the beginning of the backup -- 
 mysql> SET @@GLOBAL.GTID_PURGED='b9b4712a-df64-11e3-b391-60672090eb04:1-7';

MySQL 多源复制

本节重点介绍了并行从多个主服务器复制的方法。这种方法称为多源复制。使用多源复制,REPLICATION SLAVE可以同时从多个源接收事务。每个master都会为REPLICATION SLAVE创建一个通道,从中接收事务。

多源复制配置需要至少配置两个主服务器和一个从服务器。主服务器可以使用基于二进制日志位置的复制或基于 GTID 的复制进行配置。复制存储库存储在FILETABLE存储库中。TABLE存储库是崩溃安全的。MySQL 多源复制需要TABLE存储库。设置TABLE存储库有两种方法。

一种是使用以下选项启动mysqld

mysqld —master-info-repostiory=TABLE && –relay-log-info-repository=TABLE

另一种做法是修改my.cnf文件如下:

[mysqld] 
master-info-repository = TABLE 
relay-log-info-repository = TABLE

可以修改正在使用FILE存储库的现有REPLICATION SLAVE以使用TABLE存储库。以下命令动态转换现有存储库:

STOP SLAVE; 
SET GLOBAL master_info_repository = 'TABLE'; 
SET GLOBAL relay_log_info_repository = 'TABLE';

以下命令可用于将基于 GTID 的新复制主服务器添加到现有的多源REPLICATION SLAVE。它将主服务器添加到现有的从服务器通道:

CHANGE MASTER TO MASTER_HOST='newmaster', MASTER_USER='masteruser', MASTER_PORT=3451, MASTER_PASSWORD='password', MASTER_AUTO_POSITION = 1 FOR CHANNEL 'master-1';

以下命令可用于将基于二进制日志文件位置的新复制主服务器添加到现有的多源REPLICATION SLAVE。它将主服务器添加到现有的从服务器通道:

CHANGE MASTER TO MASTER_HOST='newmaster', MASTER_USER='masteruser', MASTER_PORT=3451, MASTER_PASSWORD='password' MASTER_LOG_FILE='master1-bin.000006', MASTER_LOG_POS=628 FOR CHANNEL 'master-1';

以下命令START/STOP/RESET所有配置的复制通道:

START SLAVE thread_types; -- To start all channels
STOP SLAVE thread_types; -- To stop all channels
RESET SLAVE thread_types; -- To reset all channels

以下命令使用FOR CHANNEL子句START/STOP/RESET命名通道:

START SLAVE thread_types FOR CHANNEL channel;
STOP SLAVE thread_types FOR CHANNEL channel;
RESET SLAVE thread_types FOR CHANNEL channel;

复制管理任务

本节描述了一些常见的 MySQL 复制管理任务。通常情况下,一旦设置好,MySQL 复制就不需要定期监控。

最常见的任务之一是确保主数据库服务器和从数据库服务器之间的复制没有错误。使用SHOW SLAVE STATUS MySQL 语句进行如下检查:

mysql> SHOW SLAVE STATUS\G
*************************** 1\. row ***************************
 Slave_IO_State: Waiting for master to send event
 Master_Host: master1
 Master_User: root
 Master_Port: 3306
 Connect_Retry: 60
 Master_Log_File: mysql-bin.000004
 Read_Master_Log_Pos: 931
 Relay_Log_File: slave1-relay-bin.000056
 Relay_Log_Pos: 950
 Relay_Master_Log_File: mysql-bin.000004
 Slave_IO_Running: Yes
 Slave_SQL_Running: Yes
 Replicate_Do_DB:
 Replicate_Ignore_DB:
 Replicate_Do_Table:
 Replicate_Ignore_Table:
 Replicate_Wild_Do_Table:
 Replicate_Wild_Ignore_Table:
 Last_Errno: 0
 Last_Error:
 Skip_Counter: 0
 Exec_Master_Log_Pos: 931
 Relay_Log_Space: 1365
 Until_Condition: None
 Until_Log_File:
 Until_Log_Pos: 0
 Master_SSL_Allowed: No
 Master_SSL_CA_File:
 Master_SSL_CA_Path:
 Master_SSL_Cert:
 Master_SSL_Cipher:
 Master_SSL_Key:
 Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
 Last_IO_Errno: 0
 Last_IO_Error:
 Last_SQL_Errno: 0
 Last_SQL_Error:
 Replicate_Ignore_Server_Ids: 0

在前面的输出中,以下是一些关键字段的解释:

  • Slave_IO_State:从服务器的当前状态

  • Slave_IO_Running:指示读取主日志文件的 I/O 线程是否正在运行

  • Slave_SQL_Running:指示执行事件的 SQL 线程是否正在运行

  • Last_IO_Error, Last_SQL_Error:I/O 或 SQL 线程处理中继线程报告的最后错误

  • Seconds_Behind_Master指示从服务器 SQL 线程运行落后于主服务器处理主二进制日志的秒数

我们可以使用SHOW_PROCESSLIST语句来检查连接的从服务器的状态:

mysql> SHOW PROCESSLIST \G;
*************************** 4\. row ***************************
 Id: 10
 User: root
 Host: slave1:58371
 db: NULL
Command: Binlog Dump
 Time: 777
 State: Has sent all binlog to slave; waiting for binlog to be updated
 Info: NULL

当在主服务器上执行SHOW_SLAVE_HOSTS语句时,提供关于从服务器的信息如下:

mysql> SHOW SLAVE HOSTS;
+-----------+--------+------+-------------------+-----------+
| Server_id |  Host  | Port | Rpl_recovery_rank | Master_id |
+-----------+--------+------+-------------------+-----------+
|     10    | slave1 | 3306 |         0         |      1    |
+-----------+--------+------+-------------------+-----------+
1 row in set (0.00 sec)

另一个重要的复制管理任务是能够在slave数据库服务器上启动或停止复制。以下命令用于执行此操作:

mysql> STOP SLAVE;
mysql> START SLAVE;

还可以通过指定线程的类型来停止和启动单个线程,如下所示:

mysql> STOP SLAVE IO_THREAD; 
mysql> STOP SLAVE SQL_THREAD;

mysql> START SLAVE IO_THREAD; 
mysql> START SLAVE SQL_THREAD;

实施复制

复制的基础是主数据库服务器跟踪主数据库上发生的所有更改。这些更改以事件的形式在二进制日志文件中进行跟踪,自服务器启动以来。SELECT操作不会被记录,因为它们既不修改数据库也不修改内容。每个REPLICATION SLAVEmaster拉取二进制日志文件的副本,而不是主数据库推送日志文件到slave。从服务器依次执行从主二进制日志文件中读取的事件。这保持了masterslave服务器之间的一致性。在 MySQL 复制中,每个slave都独立于master和其他slave服务器。因此,从服务器可以在不影响masterslave功能的情况下,在方便的时候请求主的二进制日志文件。

本章节的重点是 MySQL 复制的详细信息。我们已经了解了基础知识,这将帮助我们理解深入的细节。

复制格式

正如我们现在已经知道的那样,MySQL 复制是基于从主服务器生成的二进制日志中的事件进行复制的。稍后,这些事件将被从服务器读取和处理。我们还不知道的是这些事件以何种格式记录在二进制日志文件中。复制格式是本节的重点。

当事件记录在主二进制日志文件中时,使用的复制格式取决于使用的二进制日志格式。基本上,存在两种二进制日志格式:基于语句和基于行。

使用基于语句的二进制日志记录,SQL 语句被写入主二进制日志文件。从服务器上的复制通过在slave数据库上执行 SQL 语句来工作。这种方法称为基于语句复制。它对应于 MySQL 基于语句的二进制日志格式。这是直到 MySQL 版本 5.1.4 和更早版本存在的唯一传统格式。

使用基于行的二进制日志记录,主二进制日志中写入的事件指示单个表行如何更改。在这种情况下,复制是通过从服务器复制表示表行更改的事件来工作的。这称为基于行的复制。基于行的日志记录是默认的 MySQL 复制方法。

MySQL 支持配置以混合基于语句和基于行的日志记录。使用日志格式的决定取决于被记录的更改。这被称为混合格式日志记录。当使用混合格式日志记录时,基于语句的日志记录是默认格式。根据使用的语句类型和存储引擎,日志会自动切换到基于行的格式。基于混合日志格式的复制被称为混合格式复制。

binlog_format系统变量控制着运行中的 MySQL 服务器中使用的日志格式。在会话或全局范围设置binlog_format系统变量需要SYSTEM_VARIABLES_ADMINSUPER权限。

基于语句与基于行的复制

在前面的部分中,我们学习了三种不同的日志格式。每种格式都有其自己的优点和缺点。通常情况下,混合格式应该提供最佳的完整性和性能组合。然而,要从基于语句或基于行的复制中获得最佳性能,本节中描述的优点和缺点是有帮助的。

与基于行的复制相比,基于语句的复制是一种传统且经过验证的技术。日志文件中记录的记录或事件数量较少。如果一个语句影响了许多行,只有一个语句将被写入二进制日志文件。在基于行的复制中,对于每个修改的表行,都将输入一条记录,尽管作为单个语句的一部分。实质上,这意味着基于语句的复制需要更少的存储空间用于日志文件。这也意味着备份、恢复或复制事件的速度更快。

除了前面描述的优点之外,基于语句的复制也有缺点。由于复制是基于 SQL 语句的,因此可能无法使用基于语句的复制复制修改数据的所有语句。以下是一些示例:

  • SQL 语句依赖于用户定义的函数,当这些用户定义的函数返回的值依赖于除了提供给它的参数之外的因素时,它是不确定的。

  • 带有LIMIT子句但没有ORDER BY子句的UPDATEDELETE语句是不确定的,因为在复制过程中可能已经改变了顺序。

  • 使用NOWAITSKIP LOCKED选项的FOR UPDATEFOR SHARE锁定读取语句。

  • 用户定义的函数必须应用于从数据库。

  • 使用函数的 SQL 语句,如LOAD_FILE()UUID()USER()UUID_SHORT()FOUND_ROWS()SYSDATE()GET_LOCK()等,无法使用基于语句的复制正确地进行复制。

  • INSERTSELECT语句需要更多的行级锁。

  • 使用表扫描的UPDATE需要锁定更多的行。

  • 复杂的 SQL 语句必须在从数据库服务器上执行和评估,然后再插入或更新行。

让我们看看基于行的复制提供的优势。基于行的复制是最安全的复制形式,因为它不依赖于 SQL 语句,而是依赖于表行中存储的值。因此,每个更改都可以被复制。在INSERT...SELECT语句中需要更少的行锁。不使用键的UPDATEDELETE语句需要更少的行级锁。

基于行的复制的主要缺点是它生成了更多必须记录的数据。使用基于语句的复制,一个 DML SQL 语句就足够记录,尽管它修改了许多行。在基于行的复制中,需要为每个更改的行记录日志。基于行的复制的二进制日志文件增长非常快。复制确定性用户定义的函数生成大型BLOB值需要更长的时间。

复制实现细节

在 MySQL 中,有三个线程参与实现复制。这三个线程中,一个在主服务器上,另外两个在slave数据库服务器上。让我们深入了解这些线程的细节:

  • Binlog 转储线程: 当从属数据库服务器请求二进制日志文件时,主服务器负责将内容发送到从属数据库服务器。为了实现这一点,当从属数据库服务器连接到主数据库服务器时,主数据库服务器会创建一个线程。binlog转储线程将二进制日志内容发送到从属数据库服务器。在主数据库服务器上执行SHOW PROCESSLIST命令的输出中,可以将此线程标识为Binlog Dump线程。binlog转储线程锁定主服务器上的二进制日志文件,以便读取要发送到从属数据库服务器的每个事件。一旦事件被读取,锁就会被释放,甚至在发送到从属数据库服务器之前。

  • 从属 I/O 线程: 从属 I/O 线程的主要责任是从主数据库服务器请求二进制日志更新。当执行START SLAVE命令时,从属数据库服务器会创建 I/O 线程。该线程连接到主数据库服务器,并请求从二进制日志发送更新。一旦主服务器的binlog转储线程发送内容,从属 I/O 线程读取内容并将其复制到本地文件,包括从属的中继日志。可以通过SHOW SLAVE STATUSSHOW STATUS命令获取此线程的状态。

  • 从属 SQL 线程: 从属 I/O 线程将事件写入从属的中继日志。从属 SQL 线程负责在从属数据库服务器上执行这些事件。从属 SQL 线程读取从属 I/O 线程写入的中继日志中的事件并执行它们。

根据前述描述,每个主从连接对都会创建三个线程。如果主服务器有多个slave数据库服务器,它会为每个当前连接的从属创建一个专用的二进制日志转储线程。另一方面,每个从属都会创建自己的 I/O 和 SQL 线程。从属数据库服务器为什么要创建两个单独的线程,一个用于写入事件,另一个用于执行事件?原因是通过这种方法,读取语句的任务不会被执行语句所减慢。考虑到从属服务器没有运行,其 I/O 线程在slave服务器启动时会快速从主数据库获取所有二进制日志,而不管 SQL 线程是否落后。此外,如果slave数据库服务器在 SQL 线程执行所有这些语句之前停止,这些语句将记录在从属中继日志中。因此,当从属再次启动时,SQL 线程可以执行这些语句。因此,中继日志作为从主数据库服务器读取的语句的安全副本。

SHOW PROCESSLIST语句提供了关于masterslave数据库服务器上发生的情况的信息。在master数据库服务器上执行该语句时的输出如下所示:

mysql> SHOW PROCESSLIST\G
*************************** 1\. row ***************************
 Id: 2
 User: root
 Host: localhost:32931
 db: NULL
Command: Binlog Dump
 Time: 94
 State: Has sent all binlog to slave; waiting for binlog to be updated
 Info: NULL

前述输出显示线程 2 是主服务器的binlog转储线程。状态表明所有最近的更新都已发送到从属。

当在从属数据库服务器上执行SHOW PROCESSLIST语句时,输出如下所示:

mysql> SHOW PROCESSLIST\G
*************************** 1\. row ***************************
 Id: 10
 User: system user
 Host:
 db: NULL
Command: Connect
 Time: 11
 State: Waiting for master to send event
 Info: NULL
*************************** 2\. row ***************************
 Id: 11
 User: system user
 Host:
 db: NULL
Command: Connect
 Time: 11
 State: Has read all relay log; waiting for the slave I/O thread to update it
 Info: NULL

在输出中,线程 10 是从属的 I/O 线程,线程 11 是从属的 SQL 线程。I/O 线程正在等待主服务器的binlog转储线程发送二进制日志内容。SQL 线程已经读取了在slave中继日志中记录的所有语句。从Time列可以确定slave落后于master的速度有多慢。

复制通道

复制通道是从主服务器到从服务器的事务流路径。本节解释了通道在复制中的使用方式。MySQL 服务器在启动时会自动创建一个名为""(空字符串)的默认通道。默认通道始终存在,用户无法创建或销毁。如果没有创建其他通道,则复制语句将在默认通道上执行。本节描述了在至少存在一个命名通道时应用于复制通道的语句。

在多源复制中,slave数据库服务器打开多个通道,每个通道对应一个主服务器。每个通道都有自己的中继日志和 SQL 线程。复制通道具有主机名和端口关联。多个通道可以分配给相同的主机名和端口组合。在 MySQL 8 中,多源复制拓扑结构中的一个从服务器最多可以添加 256 个通道。通道必须具有非空的唯一名称。

FOR CHANNEL子句与各种 MySQL 语句一起使用,用于在单个通道上执行复制操作。该子句可应用于以下语句:

  • CHANGE MASTER TO

  • START SLAVE

  • STOP SLAVE

  • RESET SLAVE

  • SHOW RELAYLOG EVENTS

  • 刷新中继日志

  • SHOW SLAVE STATUS

除此之外,以下函数具有额外的通道参数:

  • MASTER_POS_WAIT()

  • WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS()

为使多源复制正常工作,必须配置以下启动选项:

  • --relay-log-info-repository:如前所述,对于多源复制,必须设置为TABLE。在 MySQL 8 中,FILE选项已被弃用,TABLE是默认选项。

  • --master-info-repository:必须设置为TABLE

  • --log-slave-updates:从主服务器接收的事务写入二进制日志。

  • --relay-log-purge:每个通道自动清除自己的中继日志。

  • --slave-transaction-retries:所有通道的 SQL 线程重试事务。

  • --skip-slave-start:任何通道上都不启动复制线程。

  • --slave-skip-errors:所有通道继续执行并跳过错误。

  • --max-relay-log-size=size:中继日志文件在达到最大大小后进行轮换。

  • --relay-log-space-limit=size:每个单独通道的所有中继日志的总大小上限。

  • --slave-parallel-workers=value:每个通道的从服务器并行工作者数量。

  • --slave-checkpoint-group:I/O 线程的等待时间。

  • --relay-log-index=filename:每个通道的中继日志索引文件名。

  • --relay-log=filename:每个通道的中继日志文件名。

  • --slave-net-timeout=N:每个通道等待 N 秒以检查断开的连接。

  • --slave-skip-counter=N:每个通道从主服务器跳过 N 个事件。

复制中继和状态日志

REPLICATION SLAVE服务器创建保存从主数据库服务器发送的二进制日志事件的日志。记录有关中继日志的当前状态和位置的信息。在此过程中使用三种类型的日志:

  1. 中继日志:中继日志包含从主服务器二进制日志发送的事件。这些事件由从服务器的 I/O 线程写入。从服务器的中继日志中的事件由从服务器的 SQL 线程执行。

  2. 主服务器信息日志:主服务器信息日志包含有关从服务器连接到主数据库服务器的状态和当前配置的信息。主服务器信息日志中包含的信息包括主机名、登录凭据以及指示从服务器在读取主服务器二进制日志时的位置的坐标。这些日志写入mysql.slave_master_info表中。

  3. 中继日志信息日志:中继日志信息日志存储有关从服务器中继日志内执行点的信息。中继日志信息日志写入mysql.slave_relay_log_info表中。

不应尝试手动在slave_master_infoslave_relay_log_info表中插入或更新行。这可能会导致意外行为。在 MySQL 复制中不支持这样做。

从服务器中继日志由一个索引文件和一组编号的日志文件组成。索引文件包含所有中继日志文件的名称。MySQL 数据目录是中继日志文件的默认位置。中继日志文件指的是包含事件的单独编号文件。而中继日志指的是一组编号的中继日志文件和一个索引文件的集合。中继日志文件的格式与二进制日志文件相同。中继日志的索引文件名默认为host_name-relay-bin.index,用于默认通道,对于非默认复制通道,默认的中继日志文件和中继日志索引文件的位置可以通过--relay-log--relay-log-index服务器启动选项进行覆盖。如果在设置了复制后更改了从服务器的主机名,并且从服务器使用默认基于主机名的中继日志文件名,可能会在中继日志初始化期间抛出错误,例如无法打开中继日志找不到目标日志。这可能会导致复制失败。可以通过使用--relay-log--relay-log-index选项来明确指定中继日志文件名来避免此类错误。在从服务器设置上使用这些选项将使名称与服务器主机名无关。

评估复制过滤规则

本节重点介绍了过滤规则以及服务器如何评估这些规则。基本上,如果主服务器不记录语句,则从服务器不会复制该语句。如果主服务器在其二进制日志文件中记录了该语句,则从服务器会接收该语句。但是,从服务器数据库服务器是否处理该语句或忽略它取决于从服务器启动时使用的--replicate-*选项。一旦从服务器启动,CHANGE REPLICATION FILTER语句可以用于动态设置选项。

所有复制过滤选项都遵循与数据库和表的名称大小写敏感性相同的规则,包括lower_case_table_names系统变量。

组复制

本章节解释了组复制是什么,如何设置组复制,配置和监控组复制。基本上,MySQL 组复制是一个插件,使我们能够创建弹性、高可用、容错的复制拓扑。

组复制的目的是创建一个容错系统。为了创建一个容错系统,组件应该是冗余的。组件应该在不影响系统操作方式的情况下被移除。设置这样的系统存在挑战。这样一个系统的复杂性是不同层次的。复制的数据库需要维护和管理多个服务器,而不仅仅是一个。服务器合作创建一个组,这带来了与网络分区和脑裂场景相关的问题。因此,最终的挑战是让多个服务器就系统和数据的状态在每次对系统应用更改后达成一致意见。这意味着服务器需要作为分布式状态机运行。

MySQL 组复制可以提供具有服务器之间强协调的分布式状态机复制。属于同一组的服务器会自动协调。在一个组中,一次只有一个服务器接受更新。主服务器的选举是自动进行的。这种模式称为单主模式。

MySQL 提供了一个组成员服务,负责保持组的视图一致并对所有服务器可用。当服务器加入或离开组时,视图会得到更新。如果有任何服务器意外离开组,故障检测机制会通知组视图发生变化。这种行为是自动的。

大多数组成员必须就事务在全局事务序列中的顺序达成一致意见。决定是否提交或中止事务取决于各个服务器,但所有服务器都做出相同的决定。如果由于网络分区而导致无法达成一致意见,系统将不会继续进行。这意味着系统具有内置的自动分裂脑保护机制。所有这些都是由组通信系统GCS)协议完成的。它提供了故障检测机制、组成员服务、安全和完全有序的消息传递。Paxos 算法的实现是这项技术的核心,它充当了组通信引擎。

主从复制与组复制

这一部分侧重于复制工作的一些背景细节。这将有助于理解组复制的要求以及它与经典的异步 MySQL 复制有何不同。

以下图展示了传统的异步主从复制是如何工作的。主服务器是主服务器,从服务器是连接到主服务器的一个或多个从服务器,如下图所示:

图 1. MySQL 异步复制

MySQL 还支持半同步复制,其中主服务器等待至少一个从服务器确认事务接收:

图 2. MySQL 半同步复制

图中的蓝色箭头表示服务器和客户端应用程序之间传递的消息。

通过组复制,提供了一个通信层,保证了原子消息和总序消息传递。所有读写事务只有在组批准后才提交。只读事务立即提交,因为它不需要协调。因此,在组复制中,提交事务的决定并不是由发起服务器单方面做出的。当事务准备提交时,发起服务器广播写入值和相应的写入集。所有服务器以相同的顺序接收相同的事务集。因此,所有服务器以相同的顺序应用相同的事务。这样所有服务器在组内保持一致:

图 3. MySQL 组复制协议

组复制配置

这一部分侧重于配置组复制。

首先,打开my.cnf配置文件,并在mysqld部分中添加以下条目:

[mysqld] 
gtid_mode = ON 
enforce_gtid_consistency = ON 
master_info_repository = TABLE 
relay_log_info_repository = TABLE 
binlog_checksum = NONE 
log_slave_updates = ON 
log_bin = binlog 
binlog_format = ROW 
transaction_write_set_extraction = XXHASH64 
loose-group_replication_bootstrap_group = OFF 
loose-group_replication_start_on_boot = OFF 
loose-group_replication_ssl_mode = REQUIRED 
loose-group_replication_recovery_use_ssl = 1

这些是与组复制相关的全局事务 ID 和二进制日志记录所需的一般配置。

接下来的步骤是设置组复制配置。这些配置包括组 UUID、组成员白名单和指示种子成员:

# Shared replication group configuration 
loose-group_replication_group_name = "929ce641-538d-415d-8164-ca00181be227" 
loose-group_replication_ip_whitelist = "177.110.117.1,177.110.117.2,177.110.117.3"
loose-group_replication_group_seeds = "177.110.117.1:33061,177.110.117.2:33061,177.110.117.3:33061"
 . . . Choosing

为决定是设置单主组还是多主组,需要以下配置。要启用多主组,取消注释

loose-group_replication_single_primary_mode

loose-group_replication_enforce_update_everywhere_checks指令。它将设置多主或多主组:

. . . 
# Single or Multi-primary mode? Uncomment these two lines 
# for multi-primary mode, where any host can accept writes
loose-group_replication_single_primary_mode = OFF 
loose-group_replication_enforce_update_everywhere_checks = ON

必须确保所有服务器上的这些配置都相同。对这些配置的任何更改都需要重新启动 MySQL 组。

以下配置在组中的每台服务器上都不同:

. . . 
# Host specific replication configuration 
server_id = 1 
bind-address = "177.110.117.1" 
report_host = "177.110.117.1" 
loose-group_replication_local_address = "177.110.117.1:33061"

server-id必须在组中的所有服务器上是唯一的。端口 33061 是成员用于协调组复制的端口。在进行这些更改后需要重新启动 MySQL 服务器。

如果尚未完成,我们必须使用以下命令允许访问这些端口:

sudo ufw allow 33061 
sudo ufw allow 3306

下一步是创建复制用户并启用复制插件。每个服务器都需要复制用户来建立组复制。在复制用户创建过程中,我们需要关闭二进制日志记录,因为每个服务器的用户都不同,如下所示:

SET SQL_LOG_BIN=0; 
CREATE USER 'mysql_user'@'%' IDENTIFIED BY 'password' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'mysql_user'@'%'; 
FLUSH PRIVILEGES; 
SET SQL_LOG_BIN=1;

现在,使用CHANGE MASTER TO来配置服务器使用group_replication_recovery通道的凭据:

CHANGE MASTER TO MASTER_USER='mysql_user', MASTER_PASSWORD='password' FOR CHANNEL 'group_replication_recovery';

现在,我们已经准备好安装插件了。连接到服务器并执行以下命令:

INSTALL PLUGIN group_replication SONAME 'group_replication.so';

使用以下语句验证插件是否已激活:

SHOW PLUGINS;

下一步是启动组。在组的一个成员上执行以下语句:

SET GLOBAL group_replication_bootstrap_group=ON; 
START GROUP_REPLICATION; 
SET GLOBAL group_replication_bootstrap_group=OFF;

现在,我们可以在另一台服务器上启动组复制:

START GROUP_REPLICATION;

我们可以使用以下 SQL 查询检查组成员列表:

mysql> SELECT * FROM performance_schema.replication_group_members; 
+---------------------------+--------------------------------------+
|        CHANNEL_NAME       |                MEMBER_ID             |
+---------------------------+--------------------------------------+
| group_replication_applier | 13324ab7-1b01-11e7-9dd1-22b78adaa992 |
| group_replication_applier | 1ae4b211-1b01-11e7-9d89-ceb93e1d5494 |
| group_replication_applier | 157b597a-1b01-11e7-9d83-566a6de6dfef |
+---------------------------+--------------------------------------+
+---------------+-------------+--------------+ 
|   MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | 
+---------------+-------------+--------------+
| 177.110.117.1 |     3306    |     ONLINE   | 
| 177.110.117.2 |     3306    |     ONLINE   | 
| 177.110.117.3 |     3306    |     ONLINE   | 
+---------------+-------------+--------------+ 
3 rows in set (0.01 sec)

组复制用例

MySQL 组复制功能提供了一种通过在一组服务器上复制系统状态来构建容错系统的方法。只要大多数服务器正常运行,组复制系统就会保持可用,即使其中一些服务器出现故障。服务器故障由组成员服务跟踪。组成员服务依赖于分布式故障检测器,如果任何服务器离开组(自愿或由于意外停机),则会发出信号。分布式恢复过程确保服务器加入组时会自动更新。因此,使用 MySQL 组复制可以保证持续的数据库服务。不过,存在一个问题。尽管数据库服务可用,但连接到它的客户端在服务器崩溃时必须被重定向到另一台服务器。组复制不会尝试解决这个问题。这应该由连接器、负载均衡器、路由器或其他中间件来处理。

以下是 MySQL 组复制的典型用例:

  1. 弹性复制:组复制适用于服务器数量在流动环境中动态增长或缩减,且副作用最小的情况。例如,云数据库服务。

  2. 高度可用的分片:MySQL 组复制可用于实现高度可用的写扩展分片,其中每个复制组映射到一个分片。

  3. 主从复制的替代方案:组复制可以是单主服务器复制中出现争用问题的解决方案。

  4. 自主系统:MySQL 组复制可以用于复制协议内置的自动化。

复制解决方案

MySQL 复制在许多不同的场景中都很有用,以满足各种目的。本节重点介绍特定的用例,并提供有关如何使用复制的一般信息。

其中一个主要用例是将复制用于备份目的。master的数据可以在slave数据库服务器上复制,然后可以备份slave上的数据。slave数据库服务器可以关闭而不影响master数据库服务器上运行的操作。

另一个用例是处理REPLICATION SLAVE的意外停止。为了实现这一点,一旦slave重新启动,I/O 线程必须能够恢复有关已接收事务和 SQL 线程执行的事务的信息。这些信息存储在 InnoDB 表中。由于 InnoDB 存储引擎是事务性的,它总是可恢复的。正如前面提到的,为了使 MySQL 8 复制使用表,relay_log_info_repositorymaster_info_repository 必须设置为 TABLE

在基于行的复制中,可以通过性能模式工具阶段来监视从服务器的 SQL 线程的当前进度。要跟踪所有三种基于行的复制事件类型的进度,使用以下语句启用三个性能模式阶段:

mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'stage/sql/Applying batch of row changes%';

MySQL 8 复制过程可以工作,即使主服务器上的源表和从服务器上的目标表使用不同的引擎类型。default_storage_engine 系统变量不会被复制。这是复制中的一个巨大优势,不同的引擎类型可以用于不同的复制场景。一个例子是扩展场景,我们希望所有读操作在从数据库服务器上执行,而所有写操作应在主数据库服务器上执行。在这种情况下,我们可以在主服务器上使用事务性的 InnoDB 引擎,在从数据库服务器上使用非事务性的 MyISAM 引擎类型。

考虑一个想要将销售数据分发给不同部门以分担数据分析负载的组织的例子。MySQL 复制可以用于让单个主服务器将不同的数据库复制到不同的从服务器。这可以通过在每个从服务器上使用 --replicate-wild-do-table 配置选项来限制二进制日志语句来实现。

一旦设置了 MySQL 复制,随着连接到主服务器的从服务器数量的增加,负载也会增加。主服务器上的网络负载也会增加,因为每个从服务器都应该接收二进制日志的完整副本。主数据库服务器也忙于处理请求。在这种情况下,提高性能变得必要。提高性能的解决方案之一是创建更深层次的复制结构,使主服务器只复制到一个从服务器。其余的从服务器连接到主从服务器进行操作。

总结

在本章中,我们了解了关于 MySQL 8 复制的深刻细节,复制是什么,以及它如何帮助解决特定问题。我们还学习了如何设置基于语句和基于行的复制类型。在此过程中,我们还了解了复制的系统变量和服务器启动选项。在本章的后半部分,我们深入探讨了组复制以及它与传统的 MySQL 复制方法的不同之处。我们还学习了日志记录和复制格式。最后但并非最不重要的是,我们简要了解了不同的复制解决方案。我们涵盖了很多东西,是吧?

现在是时候进入我们的下一章了,在那里我们将设置几种类型的分区,并探索分区的选择和分区的修剪。它还解释了在分区时如何应对限制和限制。读者将能够根据需求了解哪种类型的分区适合某种情况。

第九章:MySQL 8 中的分区

在前一章中,解释了 MySQL 8 中的复制。这包括复制、配置和实现的详细解释。该章还解释了组复制与集群,并涵盖了复制方法作为解决方案。

在本章中,我们将在 MySQL 8 中进行分区。分区是管理和维护具有特定操作的数据的概念,具有多个运算符,并定义了控制分区的规则。基本上,它提供了一个配置钩子,以指定的方式管理底层数据文件。

我们将在分区上涵盖以下主题:

  • 分区概述

  • 分区类型

  • 分区管理

  • 分区选择

  • 分区修剪

  • 分区中的限制和限制

分区概述

分区的概念与数据库中数据存储的物理方面相关。如果查看SQL标准,它们并没有提供关于这个概念的太多信息,SQL语言本身意图独立于用于存储信息或特定于不同模式、表、行或列的数据的媒体或数据结构。先进的数据库管理系统已经添加了指定用于数据存储的物理位置的方法,如硬件、文件系统或两者兼而有之。在 MySQL 中,InnoDB存储引擎通过表空间的概念支持这些目的。

分区使我们能够将个别表的部分分布为在文件系统中的不同位置存储为单独的表。此外,分布是通过用户指定的规则提供的,例如模数、哈希函数或与简单值或范围匹配,并且用户提供的表达式充当通常称为分区函数的参数。

在 MySQL 8 中,目前InnoDB是唯一支持分区的存储引擎。在InnoDB存储引擎中,不需要额外的规范来启用分区。分区不能与存储引擎MyISAMCSVFEDERATEDMERGE一起使用。在本章中给出的所有示例中,我们假设默认存储引擎是InnoDB

创建分区表时,使用默认存储引擎,与创建表时相同,并且可以通过指定STORAGE ENGINE选项来覆盖,就像我们对任何表所做的那样。以下示例演示了创建一个分为四个分区的哈希表,所有分区都使用InnoDB存储引擎:

CREATE TABLE tp (tp_id INT, amt DECIMAL(5,2), trx_date DATE)
 ENGINE=INNODB
 PARTITION BY HASH ( MONTH (trx_date) )
 PARTITIONS 4;

分区适用于表的所有索引和所有数据。它不适用于索引或数据的任何一方,反之亦然。它可以同时适用于索引和数据,也不能应用于表的一部分。

上述表tp没有定义唯一键或主键,但在一般实践中,我们通常有主键、唯一键或两者作为表的一部分,分区列的选择取决于这些键是否存在。分区列的选择在分区键、主键和唯一键部分中有详细说明。为了简化分区的概念,所给出的示例可能不包括这些键。

分区类型

MySQL 8 支持多种分区类型,列举如下:

  • RANGE 分区:根据列值的范围将行分配到分区

  • LIST 分区:根据与给定一组值匹配的列值将行分配到分区

  • COLUMNS 分区:使用RANGELIST分区将行分配到具有多个列值的分区

  • HASH 分区:根据用户指定的表达式在列值上进行评估来分配分区

  • KEY 分区:除了HASH分区外,还允许使用多个列值

  • 子分区:除了分区外,还允许在分区表中进行进一步的划分,也称为复合分区

表的不同行可以分配到不同的物理分区;这被称为水平分区。表的不同列可以分配到不同的物理分区;这被称为垂直分区。MySQL 8 目前支持水平分区。

对于列表、范围和线性哈希类型的分区,分区列的值被传递给分区函数。分区函数返回一个整数值,即记录应存储在其中的分区号。分区函数必须是非随机和非常数的。分区函数不能包含查询,并且可以使用返回整数或 NULL 的 SQL 表达式,其中整数 intval 必须遵循表达式-MAXVALUE <= intval <= MAXVALUE。这里,-MAXVALUE 表示整数类型值的下限,MAXVALUE 是整数类型值的上限。

存储引擎必须对同一表的所有分区相同,但是在同一数据库或 MySQL 服务器中的不同分区表中使用不同的存储引擎没有限制。

分区管理

有不同的方法可以使用 SQL 语句修改分区表并执行操作,例如添加、重新定义、合并、删除或拆分现有的分区表。还可以使用 SQL 语句获取有关分区表和分区的信息。

MIN_ROWS 和 MAX_ROWS 可用于配置分区表中存储的最大和最小行数。

分区选择和修剪

还提供了分区和子分区的显式选择。它使得行匹配到 where 子句中给定的条件。在分区中,所描述的修剪概念不会扫描可能不存在匹配值的分区,并且使用查询应用,而分区选择适用于查询和许多 DML 语句。

分区的限制和限制

存储过程或函数、用户定义函数或插件以及用户变量或声明的变量在分区表达式中受到限制。在详细部分中还有几个适用于分区的限制和限制。

请参阅以下列表,了解分区的一些优点:

  • 分区有助于在一个表中存储比文件系统分区或单个磁盘能容纳的更多数据。

  • 通过删除仅包含无用数据的分区或分区,可以轻松删除已经变得无用的数据。在某些情况下,需要单独添加特定数据,可以根据指定的规则轻松地在单个或多个分区中进行分区。

  • 基于分区数据自动进行的查询优化,不会在不适用于 where 条件的分区中搜索数据。

  • 除了分区修剪外,还支持显式的分区选择,其中 where 子句应用于指定的分区或多个分区。

  • 通过将数据搜索分离到多个磁盘,可以实现更大的查询吞吐量。

分区类型

在本节中,您将了解不同类型的分区以及使用特定分区的目的。以下是 MySQL 8 中可用的分区类型列表:

  • 范围分区

  • 列表分区

  • 列分区

  • 哈希分区

  • 键分区

  • 子分区

除了上述列表,我们还将在 MySQL 8 分区的详细部分中看到对 NULL 的处理。

数据库分区的一个非常常见的用例是按日期对数据进行分隔。MySQL 8 不支持日期分区,一些数据库系统明确提供了日期分区,但可以使用日期、时间或日期时间列创建分区方案,或者基于日期/时间相关表达式创建分区方案,这些表达式评估这些列类型的值。

如果使用KEYLINEAR KEY分区,可以使用日期、时间或日期时间类型作为分区列的列值,而不需要进行任何修改,而在其他分区类型中,需要使用返回整数或NULL值的表达式。

无论您使用哪种类型的分区,分区始终会自动以整数顺序编号,按照创建顺序对每个分区进行编号。例如,如果表使用四个分区,它们将按照创建顺序分别编号为 0、1、2 和 3。

当您指定分区的数量时,它必须评估为正的、非零的整数,没有前导零。不允许使用小数分数作为分区号。

分区的名称不区分大小写,应遵循约定或规则,就像其他 MySQL 标识符(如表)一样。分区定义中使用的选项已经由CREATE TABLE语法提供。

现在,让我们详细查看分区,并检查每种类型,以了解它们之间的不同之处。

RANGE 分区

在这种类型的分区中,正如名称所示,RANGE是在一个表达式中给出的,该表达式评估值是否在给定范围内。范围是使用VALUES LESS THAN运算符定义的,它们不应该重叠且应该是连续的。

在接下来的几个示例中,假设我们正在创建一个表,用于保存 25 家食品店的员工个人记录。这些商店的编号从 1 到 25,是一家拥有 25 家食品店的连锁店,如下所示:

CREATE TABLE employee (
 employee_id INT NOT NULL,
 first_name VARCHAR(30),
    last_name VARCHAR(30),
    hired_date DATE NOT NULL DEFAULT '1990-01-01',
    termination_date DATE NOT NULL DEFAULT '9999-12-31',
 job_code INT NOT NULL,
 store_id INT NOT NULL
);

现在让我们对表进行分区,这样您就可以根据需要按范围对表进行分区。假设您考虑使用除法将数据分为五部分,以store_id范围进行分区。为此,表创建定义将如下所示:

CREATE TABLE employee (
 employee_id INT NOT NULL,
    first_name VARCHAR(30),
    last_name VARCHAR(30),
    hired_date DATE NOT NULL DEFAULT '1990-01-01',
 termination_date DATE NOT NULL DEFAULT '9999-12-31',
 job_code INT NOT NULL,
 store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (
    PARTITION p0 VALUES LESS THAN (6),
    PARTITION p1 VALUES LESS THAN (11),
    PARTITION p2 VALUES LESS THAN (16),
    PARTITION p3 VALUES LESS THAN (21),
 PARTITION p4 VALUES LESS THAN (26)
);

因此,根据上述分区方案,所有插入的行,其中包含在 1 到 5 号店工作的员工,都存储在p0分区中,1 到 10 号店工作的员工存储在p1分区中,依此类推。如果您查看分区定义,分区按照最低到最高的store_id列值排序,PARTITION BY RANGE语法看起来类似于编程语句if… elseif…语句,不是吗?

好吧,您可能会想知道如果一条记录带有store_id 26会发生什么;这将导致错误,因为服务器不知道在哪里放置记录。有两种方法可以防止发生此错误:

  1. 通过在INSERT语句中使用IGNORE关键字。

  2. 使用MAXVALUE而不是指定范围(26)。

当然,您也可以使用ALTER TABLE语句扩展限制,为 26-30 号店、30-35 号店等添加新的分区。

store_id类似,您还可以根据作业代码对表进行分区-基于列值的范围。假设管理职位使用 5 位代码,办公室和支持人员使用 4 位代码,普通工人使用 3 位代码,那么分区表创建定义将如下所示:

CREATE TABLE employee (
 employee_id INT NOT NULL,
    first_name VARCHAR(30),
    last_name VARCHAR(30),
    hired_date DATE NOT NULL DEFAULT '1990-01-01',
    termination_date DATE NOT NULL DEFAULT '9999-12-31',
    job_code INT NOT NULL,
    store_id INT NOT NULL
)
PARTITION BY RANGE (job_code) (
 PARTITION p0 VALUES LESS THAN (1000),
 PARTITION p1 VALUES LESS THAN (10000),
    PARTITION p2 VALUES LESS THAN (100000)
);

您还可以根据员工加入的年份进行分区,例如根据YEAR(hired_date)的值进行分区。现在表定义将如下所示:

CREATE TABLE employee (
 employee_id INT NOT NULL,
    first_name VARCHAR(30),
    last_name VARCHAR(30),
    hired_date DATE NOT NULL DEFAULT '1990-01-01',
    termination_date DATE NOT NULL DEFAULT '9999-12-31',
    job_code INT NOT NULL,
    store_id INT NOT NULL
)
PARTITION BY RANGE (YEAR(hired_date)) (
    PARTITION p0 VALUES LESS THAN (1996),
    PARTITION p1 VALUES LESS THAN (2001),
 PARTITION p2 VALUES LESS THAN (2006),
 PARTITION p3 VALUES LESS THAN MAXVALUE
);

根据这个方案,所有在1996年之前录用的员工记录将存储在分区p0中,然后在2001年之前录用的记录将存储在分区p1中,2001年到2006年之间的记录将存储在p2中,其余的记录将存储在分区p3中。

基于时间间隔的分区方案可以使用以下两个选项来实现:

  1. 通过RANGE对表进行分区,并使用在日期、时间或日期时间列值上操作的函数返回整数值作为分区表达式

  2. 通过RANGE COLUMN对表进行分区,并使用日期、时间或日期时间列作为分区列

RANGE COLUMN 在 MySQL 8 中得到支持,并在COLUMN PARTITIONING部分有详细描述。

LIST partitioning

正如其名称所示,LIST分区使用列表进行表分区。列表是在使用VALUES IN (value_list)进行分区时定义的逗号分隔的整数值;这里,value_list指的是逗号分隔的整数文字。

LIST分区在许多方面类似于RANGE分区,但也有不同之处。每个分区中使用的运算符是不同的。该运算符使用逗号分隔的值列表与列值或评估为整数值的分区表达式进行匹配。

考虑员工表作为一个例子,使用创建表语法的基本定义如下:

CREATE TABLE employee (
 employee_id INT NOT NULL,
 first_name VARCHAR(30),
 last_name VARCHAR(30),
 hired_date DATE NOT NULL DEFAULT '1990-01-01',
 termination_date DATE NOT NULL DEFAULT '9999-12-31',
 job_code INT NOT NULL,
 store_id INT NOT NULL
);

假设您希望将这 25 家食品店分配到五个区域-北、南、东、西和中央,分别使用店铺 ID 号(1,2,11,12,21,22)、(3,4,13,14,23,24)、(5,6,15,16,25)、(7,8,17,18)和(9,10,19,20)。

使用区域列表对表进行分区将为表分区提供以下定义:

CREATE TABLE employee (
 employee_id INT NOT NULL,
 first_name VARCHAR(30),
 last_name VARCHAR(30),
 hired_date DATE NOT NULL DEFAULT '1990-01-01',
 termination_date DATE NOT NULL DEFAULT '9999-12-31',
 job_code INT NOT NULL,
 store_id INT NOT NULL
)
PARTITION BY LIST (store_id) (
 PARTITION pNorth VALUES IN (1,2,11,12,21,22),
 PARTITION pSouth VALUES IN (3,4,13,14,23,24),
 PARTITION pEast VALUES IN (5,6,15,16,25),
 PARTITION pWest VALUES IN (7,8,17,18),
 PARTITION pCentral VALUES IN (9,10,19,20)
);

如前面的陈述所示,按区域进行分区意味着可以很容易地根据特定分区内的区域更新商店的记录。假设组织将西区卖给另一家公司;那么您可能需要使用查询中的pWest分区来删除西区的所有员工记录。执行ALTER TABLE employee TRUNCATE PARTITION pWestDELETE语句DELETE from employee where store_id IN (7,8,17,18)更容易和高效;此外,您还可以使用DROP语句来删除员工记录- ALTER TABLE employee DROP PARTITION pWest。除了前面的语句执行,您还将从表分区定义中删除pWest PARTITION,然后需要再次使用ALTER语句来添加pWest PARTITION并恢复先前的分区表方案。

RANGE分区类似,您还可以使用哈希或键来使用LIST分区生成复合分区,这也被称为子分区。随后的子分区专门部分将更详细地介绍子分区

LIST分区中,没有像MAXVALUE这样可以包含所有可能值的捕获机制。相反,您必须在values_list中管理预期的值列表,否则INSERT语句将导致错误,例如在以下示例中,表中没有值为 9 的分区:

CREATE TABLE tpl (
 cl1 INT,
 cl2 INT
)
PARTITION BY LIST (cl1) (
 PARTITION p0 VALUES IN (1,3,4,5),
 PARTITION p1 VALUES IN (2,6,7,8)
);

INSERT INTO tpl VALUES (9,5) ;

如前面的INSERT语句所示,值 9 不是在分区模式中给定的列表的一部分,因此会出现错误。如果使用多个值插入语句,同样的错误可能导致所有插入失败,不会插入任何记录;而是使用IGNORE关键字来避免这样的错误,如以下INSERT语句示例:

INSERT IGNORE INTO tpl VALUES (1,2), (3,4), (5,6), (7,8), (9,11);

COLUMNS partitioning

顾名思义,这种类型的分区使用列本身。我们可以使用两种版本的列分区。一种是RANGE COLUMN,另一种是LIST COLUMN。除了RANGE COLUMNLIST COLUMN分区之外,MySQL 8 还支持使用非整数类型的列来定义值范围或列表值。允许的数据类型列表如下:

  • 所有INTBIGINTMEDIUMINTSMALLINTTINYINT列类型都支持RANGELIST分区列,但不支持其他数值列类型,如FLOATDECIMAL

  • 支持DATEDATETIME,但不支持与日期和时间相关的其他列类型作为分区列

  • 支持字符串列类型BINARYVARBINARYCHARVARCHAR,但不支持TEXTBLOB列类型作为分区列

现在,让我们逐一详细查看RANGE COLUMN分区和LIST COLUMN分区。

RANGE COLUMN 分区

顾名思义,您可以使用RANGE分区和RANGE COLUMN分区使用列定义范围,但不同之处在于您可以定义多个列提供范围,并且还可以选择除整数之外的列类型。

因此,RANGE COLUMN分区与RANGE分区在以下列出的方式上有所不同:

  • RANGE COLUMNS可以使用一个或多个列,比较发生在列值列表之间,而不是标量值之间

  • RANGE COLUMNS只能使用列名,而不能使用任何表达式

  • RANGE COLUMNS分区列类型不仅限于INTEGER列类型,还可以使用字符串、日期和日期时间列类型作为分区列。

通过RANGE COLUMNS对表进行分区具有以下基本语法:

CREATE TABLE table_name
PARTITION BY RANGE COLUMNS (column_list) (
 PARTITION partition_name VALUES LESS THAN (value_list) [,
 PARTITION partition_name VALUES LESS THAN (value_list) ] [,
...]
)
column_list:
 column_name[, column_name] [, ...]
value_list :
 value[, value][, ...]

在前面的语法中,column_list代表分区列列表,value_list代表分区定义值列表,并且对于每个分区定义,value_list必须给出,并且与column_list中定义的相同数量的值一起给出。简而言之,COLUMNS子句中的列数(column_list)必须与VALUES LESS THAN子句中的值数(value_list)相同。

以下示例清楚地说明了它是什么以及如何与表定义一起使用:

CREATE TABLE trc (
 p INT,
    q INT,
    r CHAR(3),
    s INT
)
PARTITION BY RANGE COLUMNS (p,s,r) (
 PARTITION p0 VALUES LESS THAN (5,10,'ppp'),
 PARTITION p1 VALUES LESS THAN (10,20,'sss'),
 PARTITION p2 VALUES LESS THAN (15,30,'rrr'),
 PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
);

现在,您可以使用以下语句将记录插入到表trc中:

INSERT INTO trc VALUES (5,9,'aaa',2) , (5,10,'bbb',4) , (5,12,'ccc',6) ;

LIST COLUMN 分区

在这种类型的分区中,表分区定义中使用列列表,并且与RANGE COLUMN相似,必须提供相应列的值列表。与RANGE COLUMN类似,除了整数类型之外,还可以使用其他列类型,即字符串、日期和日期时间列类型。

假设您有这样的要求,即业务遍布 12 个城市,并且出于营销目的,您将它们分为三个城市的四个区域,如下所示:

  • Zone 1 with cities: Ahmedabad, Surat, Mumbai

  • Zone 2 with cities: Delhi, Gurgaon, Punjab

  • Zone 3 with cities: Kolkata, Mizoram, Hyderabad

  • Zone 4 with cities: Bangalore, Chennai, Kochi

现在,为客户数据创建一个表,该表有四个对应区域的分区,并用客户所居住的城市的名称列出它们。表分区定义如下:

CREATE TABLE customer_z (
 first_name VARCHAR(30),
    last_name VARCHAR(30),
    street_1 VARCHAR(35),
    street_2 VARCHAR(35),
    city VARCHAR(15),
    renewal DATE
)
PARTITION BY LIST COLUMNS (city) (
 PARTITION pZone_1 VALUES IN ('Ahmedabad', 'Surat', 'Mumbai'),
 PARTITION pZone_2 VALUES IN ('Delhi', 'Gurgaon', 'Punjab'),
 PARTITION pZone_3 VALUES IN ('Kolkata', 'Mizoram', 'Hyderabad'),
 PARTITION pZone_4 VALUES IN ('Bangalore', 'Chennai', 'Kochi')

);

RANGE COLUMN分区类似,不需要在COLUMNS()子句中提供任何将列值转换为整数字面值的表达式,除了列名列表本身之外,不允许提供任何其他内容。

HASH 分区

引入HASH分区的主要目的是确保在定义的分区数量之间均匀分布数据。因此,使用HASH分区需要指定要对其进行分区的表的列值或评估列值的表达式,以及要将分区表划分为的分区数。

要在表中定义HASH分区,需要在表定义中指定PARTITION BY HASH (expr)子句,其中expr是将返回整数的表达式,并且还需要使用PARTITIONS n指定分区的数量,其中n是一个正整数,表示分区的数量。

以下定义创建了一个在store_id列上使用HASH分区的表,分成了五个分区:

CREATE TABLE employee (
 employee_id INT NOT NULL,
 first_name VARCHAR(30),
 last_name VARCHAR(30),
 hired_date DATE NOT NULL DEFAULT '1990-01-01',
 termination_date DATE NOT NULL DEFAULT '9999-12-31',
 job_code INT NOT NULL,
 store_id INT NOT NULL
)
PARTITION BY HASH (store_id)
PARTITIONS 4;

在上面的语句中,如果排除PARTITIONS子句,则分区的数量将自动默认为 1。

线性哈希分区

MySQL 8 支持线性哈希,它基于线性二次幂算法,而不是基于哈希函数值的模数的常规哈希。LINEAR HASH分区需要在PARTITION BY子句中使用LINEAR关键字,如下所示:

CREATE TABLE employee (
 employee_id INT NOT NULL,
 first_name VARCHAR(30),
 last_name VARCHAR(30),
 hired_date DATE NOT NULL DEFAULT '1990-01-01',
 termination_date DATE NOT NULL DEFAULT '9999-12-31',
 job_code INT NOT NULL,
 store_id INT NOT NULL
)
PARTITION BY LINEAR HASH ( YEAR(hired_date))
PARTITIONS 4;

使用线性哈希的优势是更快的分区操作,劣势是与常规哈希分区相比数据分布不均匀。

KEY 分区

这种类型的分区与HASH分区类似,只是使用了用户定义的表达式而不是哈希函数。KEY PARTITIONING在分区定义的CREATE TABLE语句中使用PARTITION BY KEY子句。KEY分区的语法规则与HASH分区类似,因此让我们列出一些不同之处以便理解:

  • 在分区时使用KEY而不是HASH

KEY()中取一个或多个列名列表,如果在KEY中没有定义列,但表具有定义的主键或带有NOT NULL约束的唯一键,该列将自动作为KEY的分区列:

CREATE TABLE tk1 (
 tk1_id INT NOT NULL PRIMARY KEY,
    note VARCHAR(50)
)
PARTITION BY KEY ()
PARTITIONS 2;

与其他分区类型不同,列类型不仅限于NULL或整数值:

CREATE TABLE tk2 (
 cl1 INT NOT NULL,
 cl2 CHAR(10),
 cl3 DATE
)
PARTITION BY LINEAR KEY (cl1)
PARTITIONS 3;

如前面的示例语句所示,与HASH分区类似,KEY分区也支持LINEAR KEY分区,并且与LINEAR HASH分区具有相同的效果。

子分区

子分区也被称为复合分区,正如其名称所示,它只是将每个分区分成一个分区表本身。请参阅以下语句:

CREATE TABLE trs (trs_id INT, sold DATE)
PARTITION BY RANGE ( YEAR(sold) )
    SUBPARTITION BY HASH ( TO_DAYS(sold) )
    SUBPARTITIONS 2 (
        PARTITION p0 VALUES LESS THAN (1991),
        PARTITION p1 VALUES LESS THAN (2001),
        PARTITION p2 VALUES LESS THAN MAXVALUE
);

如前面的示例语句所示,表trs有三个RANGE分区,每个分区p0、p1、p2进一步分成两个子分区。有效地,整个表分成了六个分区。

使用RANGELIST分区的表可以进行子分区,并且子分区可以使用KEYHASH分区类型。子分区的语法规则与常规分区相同,唯一的例外是在KEY分区中指定默认列,因为它不会自动为子分区获取列。

在使用子分区时需要考虑以下几点:

  • 每个定义的分区的分区数量必须相同。

  • 必须在SUBPARTITIONING子句中指定名称,或者指定一个默认选项。

  • 指定的子分区名称必须在整个表中是唯一的。

分区中的 NULL 处理

MySQL 8 没有特定于禁止NULL作为分区的列值、分区表达式或用户定义表达式的内容。即使NULL被允许作为一个值,表达式返回的值也必须是整数,因此 MySQL 8 对分区的实现是将NULL视为小于ORDER BY子句中的任何非NULL值。

NULL处理的行为在不同类型的分区中有所不同:

  • RANGE分区中处理NULL:如果插入了包含NULL值的列,行将被插入到范围中指定的最低分区中。

  • 使用LIST分区处理NULL:如果表具有使用LIST分区定义的分区,并且其分区使用值列表明确指定NULL作为value_list中的值,则插入将成功;否则,将出现错误,因为表没有为NULL指定分区。

  • 使用HASHKEY分区处理NULL:当使用HASHKEY分区定义表分区时,NULL的处理方式不同,如果分区表达式返回NULL,它将被包装为零值。因此,根据分区插入操作将成功将记录插入到零分区。

分区管理

使用SQL语句修改分区表有很多方法——您可以使用ALTER TABLE语句删除、添加、合并、拆分或重新定义分区。还有一些方法可以检索分区表和分区信息。我们将在以下部分中看到每个方法:

  • RANGELIST分区管理

  • HASHKEY分区管理

  • 分区维护

  • 获取分区信息

RANGE 和 LIST 分区管理

对于RANGELIST分区类型,添加和删除分区的处理方式类似。可以使用ALTER TABLE语句的DROP PARTITION选项删除通过RANGELIST分区进行分区的表。

在执行ALTER TABLE ... DROP PARTITION语句之前,请确保您拥有DROP权限。DROP PARTITION将删除所有数据,并从表分区定义中删除分区。

以下示例说明了ALTER TABLE语句的DROP PARTITION选项:

SET @@SQL_MODE = '';
CREATE TABLE employee (
 id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 first_name VARCHAR(25) NOT NULL,
 last_name VARCHAR(25) NOT NULL,
 store_id INT NOT NULL,
 department_id INT NOT NULL
) 
 PARTITION BY RANGE(id) (
 PARTITION p0 VALUES LESS THAN (5),
 PARTITION p1 VALUES LESS THAN (10),
 PARTITION p2 VALUES LESS THAN (15),
 PARTITION p3 VALUES LESS THAN MAXVALUE
);
INSERT INTO employee VALUES
 ('', 'Chintan', 'Mehta', 3, 2), ('', 'Bhumil', 'Raval', 1, 2),
 ('', 'Subhash', 'Shah', 3, 4), ('', 'Siva', 'Stark', 2, 4),
 ('', 'Chintan', 'Gajjar', 1, 1), ('', 'Mansi', 'Panchal', 2, 3),
 ('', 'Hetal', 'Oza', 2, 1), ('', 'Parag', 'Patel', 3, 1),
 ('', 'Pooja', 'Shah', 1, 3), ('', 'Samir', 'Bhatt', 2, 4),
 ('', 'Pritesh', 'Shah', 1, 4), ('', 'Jaymin', 'Patel', 3, 2),
 ('', 'Ruchek', 'Shah', 1, 2), ('', 'Chandni', 'Patel', 3, 3),
 ('', 'Mittal', 'Patel', 2, 3), ('', 'Shailesh', 'Patel', 2, 2),
 ('', 'Krutika', 'Dave', 3, 3), ('', 'Dinesh', 'Patel', 3, 2);

ALTER TABLE employee DROP PARTITION p2;

在执行ALTER TABLE employee DROP PARTITION p2;语句后,您可以看到所有数据都从分区p2中删除。如果您想删除所有数据但又需要保留表定义和分区方案,可以使用TRUNCATE PARTITION选项来实现类似的结果。

要向现有分区表添加新的LISTRANGE分区,可以使用ALTER TABLE ... ADD PARTITION语句。

通过使用SHOW CREATE TABLE语句,您可以验证并查看ALTER TABLE语句对表定义和分区模式的影响是否符合预期。

HASH 和 KEY 分区管理

HASHKEY类型的表分区与RANGELIST类型的分区相似。如果表是通过HASHKEY类型的分区进行分区,那么无法删除分区,但可以使用ALTER TABLE ... COALESCE PARTITION选项合并HASHKEY分区。

假设您有一个客户表数据,通过HASH分区分割,分为十二个分区如下:

CREATE TABLE client (
 client_id INT,
 first_name VARCHAR(25),
 last_name VARCHAR(25),
 signed DATE
)
PARTITION BY HASH (MONTH (signed))
PARTITIONS 12;

在上述表分区模式中,如果您想将分区数量从十二个减少到八个,可以使用以下ALTER TABLE语句:

ALTER TABLE client COALESCE PARTITION 8;

在上述语句中,数字 8 表示要从表中删除的分区数量。您不能删除超过表分区模式中已存在的分区数量。同样,您可以使用ALTER TABLE... ADD PARTITION语句添加更多分区。

分区维护

有许多维护任务可以通过多个语句在多个表和分区上完成。可以使用ANALYSE TABLECHECK TABLEREPAIR TABLEOPTIMIZE TABLE等语句,这些语句专门用于支持分区表。

有许多ALTER TABLE的扩展可用于单个或多个分区表的操作,列举如下:

  • 重建分区:此选项会删除分区中的所有记录并重新插入,因此在碎片整理过程中很有帮助。以下是一个示例:
 ALTER TABLE trp REBUILD  PARTITION p0, p1, p2;
  • 优化分区:如果从表的一个或多个分区中删除了许多行,或者在可变长度列类型(如VARCHARBLOBTEXT等)的大量数据中有许多行更改,可以执行OPTIMIZE PARTITION来回收分区数据文件中未使用的空间。以下是一个例子:
 ALTER TABLE top OPTIMIZE PARTITION p0, p1, p2;

ALTER TABLE ... OPTIMIZE PARTITIONInnoDB存储引擎不兼容,因此应改用ALTER TABLE ... REBUILD PARTITIONALTER TABLE ... ANALYZE PARTITION

  • 分析分区:在此选项中,读取并存储分区的关键分布。以下是一个例子:
 ALTER TABLE tap ANALYZE  PARTITION p1, p2;
  • 修复分区:仅在发现损坏的分区需要修复时使用。以下是一个例子:
 ALTER TABLE trp REPAIR PARTITION p3;
  • 检查分区:此选项用于检查分区中的任何错误,例如在非分区表中使用的CHECK TABLE选项。以下是一个例子:
 ALTER TABLE tcp CHECK PARTITION p0;

在所有上述选项中,有一个选项可以使用ALL而不是特定分区,以便对所有分区执行操作。

获取分区信息

可以通过多种方式获取有关分区的信息,如下所示:

  • SHOW CREATE TABLE语句可用于查看包含分区表中所有分区子句的分区模式信息

  • SHOW TABLE STATUS语句可用于通过查看其状态检查表是否已分区

  • EXPLAIN SELECT语句可用于查看给定SELECT选项使用的分区

  • 使用INFORMATION_SCHEMA.PARTITIONS表查询分区表信息。

以下是使用SHOW CREATE TABLE语句选项查看分区信息的示例:

SHOW CREATE TABLE employee;

从前述语句的输出中,可以看到分区模式的单独信息,包括表模式的常见信息。

同样,您可以从INFORMATION_SCHEMA.PARTITIONS表中检索有关分区的信息。

EXPLAIN选项提供了许多有关分区的信息。例如,它提供了从特定于分区的查询中获取的行数。分区将根据查询语句进行搜索。它还提供有关键的信息。

EXPLAIN也用于从非分区表中获取信息。如果没有分区,则不会出现任何错误,但在分区列中会给出一个NULL值。

分区选择和修剪

在本节中,您将看到分区如何通过称为分区修剪的优化器来优化SQL语句的执行,并使用SQL语句有效地选择分区数据并对分区进行修改操作。

分区修剪

分区修剪与分区中的优化概念相关。在分区修剪中,基于查询语句应用了“不要扫描可能不存在匹配值的分区”的概念。

假设有一个分区表tp1,使用以下语句创建:

CREATE TABLE tp1 (
 first_name VARCHAR (30) NOT NULL,
 last_name VARCHAR (30) NOT NULL,
 zone_code TINYINT UNSIGNED NOT NULL,
 doj DATE NOT NULL
)
PARTITION BY RANGE (zone_code) (
 PARTITION p0 VALUES LESS THAN (65),
 PARTITION p1 VALUES LESS THAN (129),
 PARTITION p2 VALUES LESS THAN (193),
 PARTITION p3 VALUES LESS THAN MAXVALUE
);

在前面的示例表tp1中,假设您想从以下SELECT语句中检索结果:

SELECT first_name, last_name , doj from tp1 where zone_code > 126 AND zone_code < 131;

现在,您可以从前述语句中看到,根据该语句,没有数据存在于分区p0p3中,因此我们只需要在p1p2中搜索匹配的数据。因此,通过限制搜索,可以在表的所有分区中花费更少的时间和精力进行匹配和搜索数据。这种去除不匹配分区的操作称为修剪。

优化器可以利用分区修剪来执行查询执行,与具有相同模式、数据和查询语句的非分区表相比,速度更快。

优化器可以根据WHERE条件的减少在以下情况下进行修剪:

  • partition_column IN (constant1, constant2, ..., contantN)

  • partition_column = constant

在第一种情况下,优化器评估列表中每个值的分区表达式,并创建在评估期间匹配的分区列表,然后只在此分区列表中执行扫描或搜索。

在第二种情况下,优化器仅根据给定的常量或特定值评估分区表达式,并确定哪个分区包含该值,并且只在此分区上执行搜索或扫描。在这种情况下,可以使用另一个算术比较而不是等于。

目前,修剪不支持INSERT语句,但支持SELECTUPDATEDELETE语句。

修剪也适用于优化器可以将范围转换为等效值列表的短范围。当分区表达式由可以减少为相等集的相等性或范围组成,或者分区表达式表示递增或递减关系时,可以应用优化器。

修剪也适用于使用TO_DAYS()YEAR()函数进行分区的DATEDATETIME列类型,并且如果这些表在其分区表达式中使用TO_SECONDS()函数,也适用。

假设您有一个表tp2,如下语句所示:

CREATE TABLE tp2 (
 first_name VARCHAR (30) NOT NULL,
 last_name VARCHAR (30) NOT NULL,
 zone_code TINYINT UNSIGNED NOT NULL,
 doj DATE NOT NULL
)
PARTITION BY RANGE (YEAR(doj)) (
 PARTITION p0 VALUES LESS THAN (1971),
 PARTITION p1 VALUES LESS THAN (1976),
 PARTITION p2 VALUES LESS THAN (1981),
 PARTITION p3 VALUES LESS THAN (1986),
 PARTITION p4 VALUES LESS THAN (1991),
 PARTITION p5 VALUES LESS THAN (1996),
 PARTITION p6 VALUES LESS THAN (2001),
 PARTITION p7 VALUES LESS THAN (2006),
 PARTITION p8 VALUES LESS THAN MAXVALUE
);

现在,在前面的语句中,以下语句可以从分区修剪中受益:

SELECT * FROM tp2  WHERE doj = '1982-06-24';
UPDATE tp2  SET region_code = 8 WHERE doj BETWEEN '1991-02-16' AND '1997-04-26';
DELETE FROM tp2  WHERE doj >= '1984-06-22' AND doj <= '1999-06-22';

对于最后一条语句,优化器可以采取以下行动:

  1. 找到具有范围低端的分区为YEAR('1984-06-22'),得到值 1984,找到在p3分区中。

  2. 找到具有范围高端的分区为YEAR('1999-06-22'),得到值 1999,找到在p5分区中。

  3. 仅扫描上述两个确定的分区和它们之间的任何分区。

因此,在上述情况下,要扫描的分区仅为p3p4p5,而其余分区在匹配时可以忽略。

前面的示例使用了RANGE分区,但分区修剪也适用于其他类型的分区。假设您有表tp3的模式如下语句所示:

CREATE TABLE tp3 (
 first_name VARCHAR (30) NOT NULL,
 last_name VARCHAR (30) NOT NULL,
 zone_code TINYINT UNSIGNED NOT NULL,
 description VARCHAR (250),
 doj DATE NOT NULL
)
PARTITION BY LIST(zone_code) (
 PARTITION p0 VALUES IN (1, 3),
 PARTITION p1 VALUES IN (2, 5, 8),
 PARTITION p2 VALUES IN (4, 9),
 PARTITION p3 VALUES IN (6, 7, 10)
);

对于前面的表模式,请考虑是否要执行此语句SELECT * FROM tp3 WHERE zone_code BETWEEN 1 AND 3。优化器确定哪些分区可以具有值123,并找到p1p0,因此跳过其余的分区p3p2

具有常量的列值可以被修剪,如以下示例语句:

UPDATE tp3 set description = 'This is description for Zone 5' WHERE zone_code = 5;

只有当范围的大小小于分区数时才执行优化。

分区选择

还支持显式选择分区和子分区,这使得行匹配到 where 子句中给定的条件 - 这称为分区选择。它与分区修剪非常相似,因为只有特定的分区用于匹配,但在以下两个关键方面有所不同:

  • 要扫描的分区由发出语句的人指定,而不是像分区修剪那样自动进行。

  • 分区修剪仅限于查询,而分区选择支持查询和多个DML语句

支持显式分区选择的 SQL 语句如下所示:

  • INSERT

  • SELECT

  • UPDATE

  • REPLACE

  • LOAD DATA

  • LOAD XML

  • DELETE

用于显式分区选择的PARTITION选项的以下语法:

PARTITION (partition_names)
partition_names :
 partition_name, ...

上述选项总是跟随它所属的表结构或表模式。partition_names代表分区或子分区的逗号分隔名称列表,将用于分区。partition_names中的分区和子分区名称可以是任何顺序,甚至可以重叠,但列表中的每个名称必须是特定表的现有分区或子分区名称,否则语句将失败,并显示错误消息partition_name不存在。

如果使用PARTITION选项,只有列出的分区和子分区才会被检查匹配的行。PARTITION选项也可以用于SELECT语句,以检索属于任何给定分区的行。

假设你使用以下语句创建了表employee

SET @@SQL_MODE = '';
CREATE TABLE employee (
 id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 first_name VARCHAR(25) NOT NULL,
 last_name VARCHAR(25) NOT NULL,
 store_id INT NOT NULL,
 department_id INT NOT NULL
) 
 PARTITION BY RANGE(id) (
 PARTITION p0 VALUES LESS THAN (5),
 PARTITION p1 VALUES LESS THAN (10),
 PARTITION p2 VALUES LESS THAN (15),
 PARTITION p3 VALUES LESS THAN MAXVALUE
);
INSERT INTO employee VALUES
 ('', 'Chintan', 'Mehta', 3, 2), ('', 'Bhumil', 'Raval', 1, 2),
 ('', 'Subhash', 'Shah', 3, 4), ('', 'Siva', 'Stark', 2, 4),
 ('', 'Chintan', 'Gajjar', 1, 1), ('', 'Mansi', 'Panchal', 2, 3),
 ('', 'Hetal', 'Oza', 2, 1), ('', 'Parag', 'Patel', 3, 1),
 ('', 'Pooja', 'Shah', 1, 3), ('', 'Samir', 'Bhatt', 2, 4),
 ('', 'Pritesh', 'Shah', 1, 4), ('', 'Jaymin', 'Patel', 3, 2),
 ('', 'Ruchek', 'Shah', 1, 2), ('', 'Chandni', 'Patel', 3, 3),
 ('', 'Mittal', 'Patel', 2, 3), ('', 'Shailesh', 'Patel', 2, 2),
 ('', 'Krutika', 'Dave', 3, 3), ('', 'Dinesh', 'Patel', 3, 2);

现在,如果你检查分区p1,你会看到以下输出,因为行添加到分区p1中:

mysql> SELECT * FROM employee PARTITION (p1);
+----+-----------+------------+----------+---------------+
| id | last_name | last_name | store_id | department_id |
+----+-----------+------------+----------+---------------+
| 5 | Chintan | Gajjar | 1 | 1 |
| 6 | Mansi | Panchal | 2 | 3 |
| 7 | Hetal | Oza | 2 | 1 |
| 8 | Parag | Patel | 3 | 1 |
| 9 | Pooja | Shah | 1 | 3 |
+----+-----------+------------+----------+---------------+
5 rows in set (0.00 sec) 

如果使用这个语句SELECT * FROM employee WHERE id BETWEEN 5 AND 9;,将得到相同的输出。

为了从多个分区中检索行,可以使用逗号分隔的分区名称列表。例如,SELECT * FROM employee PARTITION (p1,p2),将得到来自分区p1p2的所有行,并排除其余分区。

可以使用任何支持的分区类型来使用分区选择语句。当使用LINEAR HASHLINEAR KEY分区类型创建表时,MySQL 8 会自动添加分区名称,而且这也适用于子分区。在对这个表执行SELECT语句时,可以指定 MySQL 8 生成的分区名称来检索特定分区的数据。

PARTITION选项也适用于SELECT语句,用于INSERT ... SELECT语句,可以插入从特定分区或子分区检索的数据。

PARTITION选项也适用于具有特定分区或子分区数据的表的连接查询的SELECT语句。

分区中的限制和限制

在本节中,您将看到 MySQL 8 分区中的限制和限制,涵盖了禁止的结构、性能考虑和与存储引擎和函数相关的限制方面的详细信息,以便从表分区中获得最佳效益。

分区键、主键和唯一键

分区键与主键和唯一键之间的关系对于分区模式结构设计非常重要。简而言之,规则是分区表中用于分区的所有列必须包括表的每个唯一键。因此,包括表上的主键列在内的每个唯一键都必须是分区表达式的一部分。看一下以下使用不符合规则的唯一键的CREATE TABLE语句的例子:

CREATE TABLE tk1 (
 cl1 INT NOT NULL,
 cl2 DATE NOT NULL,
 cl3 INT NOT NULL,
 cl4 INT NOT NULL,
 UNIQUE KEY (cl1, cl2)
)
PARTITION BY HASH(cl3)
PARTITIONS 4;

CREATE TABLE tk2 (
 cl1 INT NOT NULL,
 cl2 DATE NOT NULL,
 cl3 INT NOT NULL,
 cl4 INT NOT NULL,
 UNIQUE KEY (cl1),
 UNIQUE KEY (cl3)
)
PARTITION BY HASH(cl1 + cl3)
PARTITIONS 4;

在上述每个用于创建表tk1tk2的语句中,建议的表可以至少有一个唯一键,该键不包括分区表达式中的所有列。

现在看一下以下修改后的表创建语句,这些语句已经可以工作,并且从无效变为有效:

CREATE TABLE tk1 (
 cl1 INT NOT NULL,
 cl2 DATE NOT NULL,
 cl3 INT NOT NULL,
 cl4 INT NOT NULL,
 UNIQUE KEY (cl1, cl2, cl3)
)
PARTITION BY HASH(cl3)
PARTITIONS 4;

CREATE TABLE tk2 (
 cl1 INT NOT NULL,
 cl2 DATE NOT NULL,
 cl3 INT NOT NULL,
 cl4 INT NOT NULL,
 UNIQUE KEY (cl1, cl3)
)
PARTITION BY HASH(cl1 + cl3)
PARTITIONS 4;

如果你看一下以下表结构,它根本无法分区,因为没有办法包含可以成为分区键列的唯一键列:

CREATE TABLE tk4 (
 cl1 INT NOT NULL,
 cl2 INT NOT NULL,
 cl3 INT NOT NULL,
 cl4 INT NOT NULL,
 UNIQUE KEY (cl1, cl3),
 UNIQUE KEY (cl2, cl4)
);

根据定义,每个主键都是唯一键。这个限制也适用于表的主键。以下是表tk5tk6的两个无效语句的例子:

CREATE TABLE tk5 (
 cl1 INT NOT NULL,
 cl2 DATE NOT NULL,
 cl3 INT NOT NULL,
 cl4 INT NOT NULL,
 PRIMARY KEY(cl1, cl2)
)
PARTITION BY HASH(cl3)
PARTITIONS 4;

CREATE TABLE tk6 (
 cl1 INT NOT NULL,
 cl2 DATE NOT NULL,
 cl3 INT NOT NULL,
 cl4 INT NOT NULL,
 PRIMARY KEY(cl1, cl3),
 UNIQUE KEY(cl2)
)
PARTITION BY HASH( YEAR(cl2) )
PARTITIONS 4;

在上述两个语句中,所有引用的列都不包括相应的主键在分区表达式中。以下语句是有效的:

CREATE TABLE tk7 (
 cl1 INT NOT NULL,
 cl2 DATE NOT NULL,
 cl3 INT NOT NULL,
 cl4 INT NOT NULL,
 PRIMARY KEY(cl1, cl2)
)
PARTITION BY HASH(cl1 + YEAR(cl2))
PARTITIONS 4;

CREATE TABLE tk8 (
 cl1 INT NOT NULL,
 cl2 DATE NOT NULL,
 cl3 INT NOT NULL,
 cl4 INT NOT NULL,
 PRIMARY KEY(cl1, cl2, cl4),
 UNIQUE KEY(cl2, cl1)
)
PARTITION BY HASH(cl1 + YEAR(cl2))
PARTITIONS 4;

如果表没有唯一键或主键,则该限制不适用,并且可以根据分区类型的兼容列类型在分区表达式中使用任何列。所有上述限制也适用于ALTER TABLE语句。

与存储引擎相关的分区限制

分区支持不是由 MySQL 服务器提供的,而是来自 MySQL 8 中的存储引擎自己或本机分区处理程序。在 MySQL 8 中,InnoDB存储引擎只提供本机分区处理程序,因此分区表的创建不适用于任何其他存储引擎。

ALTER TABLE ... OPTIMIZE PARTITIONInnoDB存储引擎中无法正确工作,因此请改用ALTER TABLE ... REBUILD PARTITIONALTER TABLE ... ANALYZE PARTITION操作。

与函数相关的分区限制

在分区表达式中,只有以下列出的 MySQL 8 函数是允许的:

  • ABS(): 它为给定参数提供了绝对值

  • CEILING(): 它为给定参数提供可能的最小整数

  • DAY(): 它为给定日期提供月份中的日期

  • DAYOFMONTH(): 它提供给定日期的月份中的日期,与DAY()相同

  • DAYOFWEEK(): 它为给定日期提供星期几的编号

  • DAYOFYEAR(): 它为给定日期提供一年中的日期

  • DATEDIFF(): 它提供两个给定日期之间的天数

  • EXTRACT(): 它提供给定参数的一部分

  • FLOOR(): 它为给定参数提供了可能的最大整数值

  • HOUR(): 它从给定参数中提供小时数

  • MICROSECOND(): 它从给定参数中提供微秒数

  • MINUTE(): 它从给定参数中提供分钟数

  • MOD(): 它执行模运算并提供N除以M的余数,其中MOD(N,M)

  • MONTH(): 它从给定参数中提供月份

  • QUARTER(): 它从给定参数中提供季度

  • SECOND(): 它从给定参数中提供秒数

  • TIME_TO_SEC(): 它从给定时间值参数中提供秒数

  • TO_DAYS(): 它为给定参数提供了从公元 0 年开始的天数

  • TO_SECONDS(): 它为给定参数提供从公元 0 年开始的秒数

  • UNIX_TIMESTAMP() (with TIMESTAMP columns): 它为给定参数提供自'1970-01-01 00:00:00' UTC 以来的秒数

  • WEEKDAY(): 它为给定参数提供星期几的索引

  • YEAR(): 它为给定参数提供年份

  • YEARWEEK(): 它为给定参数提供年份和周数

分区修剪支持 MySQL 8 中的TO_DAYS()TO_SECONDS()TO_YEAR()UNIX_TIMESTAMP()函数。

总结

在本章中,我们学习了不同类型的分区和分区的需求。我们还详细介绍了管理所有类型的分区的信息。我们学习了分区修剪和选择分区,这是优化器使用的。我们还讨论了在使用分区时需要考虑的适用限制和限制。

在下一章中,您将学习如何在 MySQL 8 中进行扩展,并了解在提供 MySQL 8 可扩展性时面临的常见挑战。您还将学习如何使 MySQL 服务器高度可用并实现高可用性。

第十章:MySQL 8 – 可扩展性和高可用性

在本章中,我们将涵盖 MySQL 8 可扩展性和高可用性的以下重要主题:

  • MySQL 8 中可扩展性和高可用性的概述

  • 扩展 MySQL 8

  • MySQL 8 的扩展性挑战

  • 实现高可用性

在我们继续详细讨论之前,让我们先来了解一下 MySQL 8 中的可扩展性和高可用性

MySQL 8 中可扩展性和高可用性的概述

在任何类型的应用程序中,无论是移动、Web 门户、网站、社交、电子商务、企业还是云应用程序,数据都是业务的核心部分。数据可用性被认为是任何企业或组织的最重要关注点。数据丢失或应用程序的任何停机都可能导致严重的财务损失,也会影响公司在市场上的信誉。

如果我们考虑一个在线购物网站的例子,它在特定区域有一个良好覆盖的市场,有客户和良好的商业信誉。如果这家企业面临数据丢失或任何应用程序服务器或数据库服务器的停机问题,将影响整个业务。许多客户会失去对企业的信任,企业也会在财务和信用方面遭受损失。

没有一个单一的公式可以提供解决方案。不同的企业有他们自己的应用程序需求、业务需求、不同的流程、不同地点的不同基础设施和运营能力。在这些情况下,技术在实现高可用性方面起着重要作用。

根据可扩展性和高可用性的要求,MySQL 可以用于各种应用程序,并且根据需要能够克服故障,包括 MySQL 的故障、操作系统的故障或可能影响可用性的任何计划维护活动。简单来说,可扩展性具有在 MySQL 服务器之间分配数据库负载和应用程序查询的能力。

选择正确的高可用性解决方案时,重要的属性取决于系统可以被称为高可用性的程度,因此这些要求因系统而异。对于较小的应用程序,用户负载预计不会很高,设置复制或集群环境可能会导致非常高的成本。在这种情况下,提供正确的 MySQL 配置也足以减少应用程序负载。

以下部分简要描述了 MySQL 8 支持的主要高可用性解决方案。

MySQL 复制

MySQL 复制允许将一个服务器上的数据复制到多个 MySQL 服务器上。MySQL 复制提供主从设计,因此组中的一个服务器充当主服务器,应用程序执行写操作,然后主服务器将数据复制到多个从服务器。复制是高可用性的一个成熟解决方案,被 Facebook、Twitter 等社交巨头使用。

MySQL 集群

这是 MySQL 的另一个流行的高可用性解决方案。集群使数据能够自动共享到多个 MySQL 服务器上进行复制。它旨在提供更好的可用性和吞吐量。

Oracle MySQL 云服务

Oracle MySQL 云服务提供了一种有效的方式来构建一个安全、具有成本效益的 MySQL 数据库服务,用于现代应用程序。与本地部署相比,它被证明是可扩展和成本效益的,资源利用率较低。

具有 Solaris 集群的 MySQL

MySQL 数据服务提供的 Sun Solaris 集群提供了有序启动和关闭、故障监控和 MySQL 服务的自动故障转移机制。Sun 集群 HA 保护的 MySQL 数据服务的以下 MySQL 组件。

使用第三方解决方案可以获得更多选项。用于实现高可用数据库服务的每种架构都因其提供的可用性水平而有所不同。这些架构可以分为三个主要类别:

  • 数据复制

  • 集群化和虚拟化系统

  • 地理复制集群

根据问题的最佳答案,您可以选择适合您的应用程序的正确选项,以实现最低成本和高可用性解决方案。这次讨论为我们提供了 MySQL 8 高可用性的公平概述。

MySQL 8 的扩展

可伸缩性是将任何应用程序查询的负载分布到各种 MySQL 实例的能力。对于某些情况,数据不能超过某个限制或用户数量不会超出范围是不可预测的。可扩展的数据库将是一个更可取的解决方案,以便在任何时候我们都能满足规模的意外需求。MySQL 是一个有回报的数据库系统,因为它具有可扩展性,可以在水平和垂直方面进行扩展;在数据方面,将客户端查询分布到各种 MySQL 实例是相当可行的。向 MySQL 集群添加性能非常容易,以处理负载。

实现高可用性(HA)和可伸缩性的要求可能因系统而异。每个系统都需要不同的配置才能实现这些能力。当我们考虑在 MySQL 中进行扩展时,会有许多问题,而在我们在 MySQL 中执行扩展操作时:

  • 为什么需要扩展?

  • 在 MySQL 中扩展的优势是什么?

  • 在我们在 MySQL 中进行扩展时,需要牢记哪些要点?

  • 如何进行扩展工作?

  • 数据安全吗-它是否提供数据安全的保证?

  • 还有很多...

让我们举一个实时例子来理解为什么我们需要在 MySQL 中进行扩展。我们有一个在线电子商务网站,它已经覆盖了一个小市场,用户和网站的点击量有限,只有一个数据库服务器。业务正在不断增长;业务的性能不断提高,用户数量也在增加,但我们的单个数据库服务器并不能始终满足所有请求和性能的扩展。这可能导致服务器崩溃,业务可能会在利润和市场信用方面遭受损失。为了避免这种情况,可伸缩性将发挥重要作用。如果由于任何原因客户的请求失败,或者节点宕机,其他节点将迅速处理并向客户提供适当的响应。

扩展是为了持续提高数据库响应时间的性能和提高产品的生产力。它将有助于最终产品在数据可伸缩性、性能和更好结果方面。集群和复制都是 MySQL 中可以用于扩展的关键功能。

使用集群进行扩展

基本集群架构分为四个不同的层:

  • 客户端节点

  • 应用节点

  • 管理节点

  • 数据节点

这些显示在以下图像中:

客户端节点

客户端节点是发送来自不同设备(如计算机、手机、平板电脑等)的读取数据或写入数据的查询请求的最终用户或应用程序。

应用节点

应用节点旨在提供应用程序逻辑和包含 MySQL 数据的节点之间的桥梁。应用程序可以通过 SQL 访问存储在 MySQL 集群中的数据,使用 SQL 的一个或多个 MySQL 服务器。在应用程序中,我们有多种技术可以连接到 MySQL 服务器。我们使用标准的 MySQL 连接器连接 MySQL 服务器,这使我们能够与各种访问技术连接。

作为另一种选择,我们有 NDB API;一个高性能接口,可用于控制实时用户体验并提供更好的吞吐量。在 MySQL 中,我们有 NDB API,它在 NoSQL 接口之外添加了一层,具有直接访问集群的能力。应用节点可以从所有数据节点获取数据,因此故障的唯一原因可能是应用服务不可用,因为应用可以使用所有数据节点来执行数据操作。

管理节点

管理节点在其集群中发布相关的集群信息,以及节点管理。管理节点在所有节点希望加入 MySQL 集群以及需要重新配置系统时启动时起作用。管理节点可以停止并重新启动所有服务,而不会损害或影响正在进行的操作、执行或数据和应用节点的处理。

数据节点

数据节点存储数据。表在数据节点之间共享,这也有助于处理负载平衡、复制和高可用性故障转移。

数据节点是 MySQL 集群解决方案的主要节点。它提供以下功能和好处:

磁盘和内存数据的数据存储和管理

在共享无情景况下,数据存储在至少一个副本中,而无需使用共享磁盘空间。MySQL 创建数据库的一个副本,进行同步复制过程。如果任何数据节点由于任何特定原因失败,复制的数据将处理并提供相应的输出。它对节点进行同步复制,因此它包含与主节点数据相同的数据。

根据需求,我们可以将数据存储在内存中或部分存储在磁盘上。频繁更改的数据建议存储在内存中。内存中的数据会定期与本地磁盘进行检查,并协调将数据更新到其余数据节点。

表的自动和用户定义的分区或分片

MySQL 集群提供低延迟、高吞吐量、可伸缩性和高可用性。它采用水平扩展和自动分片来通过不同的 NoSQL 查询提供重载读/写操作。NDB 集群是一组不同的节点,每个任务在其自己的处理器上运行。

数据节点之间的同步数据复制

当我们为数据节点进行数据复制时,它遵循同步复制,因此任何时候所有节点数据都将同步。如果任何节点由于任何原因失败,其他节点具有相同的数据,因此将能够为查询提供数据。因此,MySQL 提供了完美的无数据响应停机时间的解决方案。

数据检索和事务

MySQL 支持可以映射的每个事务,因为它在主服务器上提交并应用于从服务器上。这种方法不是指binlog文件或binlog文件中的相关位置。GTID复制仅基于事务工作;很容易确定主服务器和从服务器是否同步。

自动故障转移

如果任何数据节点由于任何原因失败,其他节点将负责并响应请求。数据库的复制在停机或任何节点发生故障的关键情况下非常有帮助。

故障后自动重新同步进行自我修复。

如果任何节点失败,它将自动启动并再次对其余节点执行数据同步,并在节点中复制所有最新数据。在这种情况下,它对故障进行自我修复。

在 MySQL 8 中使用 memcached 进行扩展

在 MySQL 8 中,使用 memcached 是实现可伸缩性的一种方式。Memcached 是一种简单且高度可伸缩的解决方案,可以在内存可用时以键值形式将数据存储在缓存中。Memcached 通常用于快速访问数据。存储在内存中的数据不需要执行 I/O 操作来获取数据。

由于所有信息都存储在内存中,因此数据的访问速度比每次从磁盘加载要快得多,并且可以在数据库服务器上获得更好的查询执行时间。该插件还具有序列化功能,可以将二进制文件、代码块或任何其他对象转换为可以存储的字符串,并提供了检索这些对象的简单方法。在指定内存分配时,不应大于服务器可用的物理内存。

如果指定的值过大,那么为 memcached 分配的一些内存将使用交换空间而不是物理内存。这可能会导致存储和检索值时出现延迟,因为数据被交换到磁盘而不是直接存储在内存中。

前面的图片描述了 memcached 架构,显示了数据从 memcached 流向客户端或最终用户,或者从应用程序请求数据的流程。

在 memcached 中的数据永远不会存储在数据库中。它始终在内存中可用。如果其中一个 memcached 服务器失败,数据将从数据库中获取,因此不会影响最终用户的数据检索,也不会对应用程序产生重大性能影响。在使用 memcached 服务器时唯一需要牢记的是,与任何重要信息相关的数据,例如财务交易,不应放置在 memcached 中。在这种情况下,如果 memcached 发生故障,可能无法检索数据。在 memcached 服务器中,数据完整性不健康,因为它存储在内存中,因此在发生故障时最好不要将重要数据保存在 memcached 中。在配置 memcached 服务器时,内存大小是关键因素。如果配置不当,就可能会出现糟糕的情况。

通过这种方式,我们可以使用 memcached 来扩展 MySQL 服务器,以提高数据响应时间,并提供更快的性能。这将减轻 MySQL 服务器和多个服务器的负载作为缓存组的一部分,并为多种语言提供接口。建议在有大量读取操作时使用。

NoSQL API

MySQL 集群提供了许多方法来帮助访问数据存储。最通用的方法之一是利用 SQL;然而,在实际用例中,我们也可以依赖于本机 API,它允许从数据库内部获取数据,而不会影响性能或增加进一步的复杂性,因为需要开发应用程序来转换 SQL。

使用复制进行扩展

复制是 MySQL 数据库的复制。MySQL 提供了不同的复制方法。MySQL 具有复制功能,提供了扩展解决方案、数据安全、远程数据分发等许多好处。我们在第八章中详细讨论了这一点,MySQL 8 中的复制。以下图片解释了 MySQL 中复制的基本架构:

复制是 MySQL 的最佳功能之一。它简单地将数据复制到新服务器或另一台物理机器上,该服务器将从主服务器导入数据。每当需要数据时,它将填充准确的结果。它遵循主从复制的方法。主数据库是应用程序的实际数据库,从数据库是由 MySQL 在另一台物理服务器的数据库服务器中创建的,其中包含来自主服务器的复制数据。我们可以为特定操作配置从数据库,例如当查询涉及从数据库读取数据时,我们可以在从服务器上执行此操作。在这种情况下,主数据的负载将比以前少。假设我们有 40%的写入数据查询和 60%的读取数据查询比例;在这种情况下,如果我们有一台单独的服务器,它将处理与读写操作相关的所有操作。但是,如前图所定义,我们已经将数据库复制到两个不同的服务器中,并且读操作是在从服务器上执行的,因此我们可以利用其中一个从服务器来执行复杂的读查询。这使得在 MySQL 8 上进行数据分析报告成为可能,因为执行复杂的读查询不会影响整体应用程序性能。

在标准的 MySQL 复制中,主服务器创建二进制日志文件并维护日志文件的索引以维护和跟踪日志轮换。二进制日志文件用于记录更新,并发送到从服务器。当从服务器连接到主数据库服务器时,它会考虑它在日志文件中读取的最后位置,之后从服务器将接收自那时以来发生的任何更新。从服务器随后会阻塞并等待主服务器通知其进行进一步更新。

心中的问题是为什么我们需要复制?或者,复制的目的是什么?如果复制需要另一个数据库服务器、复杂性和额外的配置,它会增加维护和监控时间。尽管如此,对于企业和数据库管理员来说,我们仍然有许多额外的好处。

单服务器依赖

在任何情况下,如果主数据库服务器失败,我们可以轻松地将数据库连接切换到复制的从服务器,以在关键情况下提供稳定性。这包括网络故障、服务器故障、硬件问题等失败原因。

性能

性能是数据库的主要部分。当我们在多个服务器上拥有分布式数据库时,我们可以将不同的应用程序连接到不同的数据库服务器以提高性能。此功能减少了查询的响应时间。

备份和恢复

复制有助于备份主数据库。它比将数据库存储在磁盘上更有效。用户可以使用复制的数据库将数据库存储在主数据库中,作为备份,而不是挖掘备份文件。当需要恢复主服务器的数据时,用户可以轻松地从从服务器获取,而无需处理备份文件并寻找最后的更新和其他操作。

负载分布

通过使用数据库的复制负载,可以减少查询执行;我们可以将读写操作分割到不同的数据库中。如果我们在主数据库中执行写操作,并在从数据库中执行读操作,这将改善应用程序的响应时间。我们可以在 MySQL 中创建负载平衡的环境,以分享对数据库服务器的所有请求的负载。负载平衡器随后进一步将请求发送到可以处理每个事务的数据库,从而获得更好的吞吐量。

异步数据复制

异步数据复制意味着数据从一台机器复制到另一台机器,会有一定的延迟。这种延迟基于网络带宽、资源可用性或管理员在配置中设置的时间间隔。正确的配置和时间设置可以提供准确的响应结果。这是基于网络管理员的配置。同步数据复制意味着数据同时提交到一个或多个机器。

地理数据分布

组复制使得可以将主服务器的数据复制到位于远程位置的从服务器,并为一个独立的客户组执行读操作,而不会影响主服务器的操作。

GTID 复制

全局事务标识符GTID)使用基于事务的数据复制,而不是基于二进制日志文件的复制。除非在所有从服务器上存在在主服务器上操作和提交的事务,否则 GTID 不会考虑复制处于一致状态。

在 MySQL 8 中,复制可以在异步模式或半同步模式下进行。在异步模式下,写操作立即在主服务器上执行,而在从服务器上的复制是根据配置定期进行的。

在复制的半同步模式中,如果在主节点和至少一个从节点上启用了半同步配置,则主节点上的事务在获得事务超时之前会等待,直到半同步启用的节点确认已接收到所需的数据或更新。超时后,主节点再次寻找半同步从节点并执行复制。

MySQL 8 提供了一种新的复制方法,GTID,其中为在主服务器上保存或提交的每个事务创建并连接了一个唯一标识符。这些标识符的唯一性在创建它的服务器中的所有服务器以及复制的服务器中都是如此。GTID 在所有事务之间有一对一的映射。在启动新的从服务器创建或故障转移到新的 MySQL 主服务器时,不需要使用日志文件引用文件中的位置的概念。您可以在 GTID 中使用基于行或基于语句的复制。

使用全局事务 ID 主要提供了两个主要的好处:

  • 在故障转移期间很容易将主服务器更改为连接从服务器:GTID 在复制组中的所有服务器中是唯一的。从服务器记住了来自旧主服务器的最后一个事件的全局事务 ID。这意味着很容易确定在新的 MySQL 主服务器上重新初始化复制的位置,因为全局事务 ID 在整个复制层次结构中都是已知的。

  • 从服务器的状态提供了一种崩溃安全的方法:从服务器在mysql.gtid_slave_pos系统表中保存当前位置信息。如果该表使用事务性存储引擎(例如默认的InnoDB),则进一步的更新将在同一事务中进行。

GTID 是在主服务器上提交的每个事务(插入和更新操作)上创建并关联的唯一键。该键不仅对主服务器是唯一的,而且在复制的所有服务器中也是唯一的。

ZFS 复制

ZFS 文件系统具有提供服务器文件的快照、将快照传输到另一台机器并提取快照以在不同服务器上重新创建文件系统的能力。用户可以随时创建快照,并可以根据需要创建多个快照。通过不断创建、传输和恢复快照,可以在一个或多个机器之间提供类似于 DRBD 的同步。

我们已经看到了使用不同技术在 MySQL 中扩展数据库的所有可能方法。根据业务需求和灵活性,我们可以通过数据库备份来进行扩展。扩展并不是一项容易的任务,但在 MySQL 8 中是可能的,只要具备对业务需求的正确理解和 MySQL 8 提供的配置。对于数据库扩展,我们必须对数据库的整个工作流程和通信方法有适当的理解。

MySQL 8 扩展中的挑战

我们已经看到了扩展的工作原理以及扩展的优势和目的。当我们开始在 MySQL 8 中进行扩展时,我们将面临哪些挑战,以及在我们朝着扩展的方向努力时需要记住哪些步骤?我们必须考虑如果我们正在进行扩展并且主服务器失败,达到了限制,读写操作无法处理应用程序的请求,或者在重新平台化数据库时。扩展并不是一项容易的任务;它需要确保能够处理增加的交易而不会出现任何困难。在进行扩展时,我们需要记住许多要点,例如主服务器和从服务器中的写入和读取操作限制。数据库负载平衡是帮助减少交易流量的方法之一,但它需要完美,需要正确理解负载平衡配置。以下是我们进行扩展时面临的主要挑战。

业务类型和灵活性

这是在进行扩展时需要记住的第一点。业务类型或业务行为是核心部分;如果业务是电子商务,我们已经知道电子商务业务具有许多功能和关于客户的非常关键的数据,例如产品细节,业务的优惠和折扣的垄断。最重要的是客户细节和付款信息,如信用卡细节,借记卡细节和客户反馈。

在这种情况下,当我们在 MySQL 8 中进行扩展时,需要记住所有参数,例如数据库备份、安全性、数据库的角色/权限和扩展的向后兼容性。在通过集群进行扩展时,所有数据节点都需要保持一致。如果应用程序使用多种技术开发,并且我们为每个堆栈进行扩展,我们可以有不同的数据节点可用;在这种情况下,在进行扩展时需要确保数据库同步是最重要的事情之一。在设计扩展之前,应明确哪些类型的数据应该驻留在 memcached 的缓存内,哪些类型的数据应该驻留在磁盘上。

应用程序的行为从共享数据节点访问数据。如果我们有一个电子商务网站,并且我们对其进行分片,并且在某个特定级别上,客户端无法使用其他分片服务器的数据,那么在那时将需要跨节点事务。这完全取决于业务行为,并取决于业务在接受有关数据库扩展的变化时有多灵活。

了解服务器工作负载

为了灵活性、规模和性能的提高,在 MySQL 8 中有许多选项和操作。许多人在执行此类活动时会遇到问题,因为他们没有足够的理解或知识来处理各种技术堆栈和配置选项选择,这些选项可以改善应用程序和部署活动的可扩展性、性能、安全性和灵活性。这些配置选项包括集群、复制、分片、内存缓存、存储引擎等,可以很好地设计来处理应用程序的整个工作负载。数据库工作负载和业务行为有助于决定 MySQL 的配置。

读写操作限制

如果主数据库服务器的读写限制达到并且事务在增加,会发生什么情况。MySQL 有容量限制;例如,如果许多客户在进行读写操作的同时访问网站,而服务器或节点未同步,那么这将给最终用户带来困惑或误解。或者,在电子商务网站上,如果一个客户购买了最后一件库存中剩下的产品,并且同时另一个客户搜索相同的产品并且它仍然可用,那么在数据库的读写操作方面,这两个操作都不同步。

最后,其他客户可能会购买我们仓库中没有的同一产品。这会影响库存计算,并且客户对购买周期的过程产生疑虑。在这种情况下,我们将失去客户对业务的信任,业务的信用也会受到影响。

另一种方法是进行数据库分片。分片可以简单地理解为将数据库分成多个服务器的分区。分片有助于减轻单个数据库或主数据库的负载。如果我们在地理上进行数据库分片,并且对于不同的国家或地区,我们有不同的数据库服务器,我们可以解决 MySQL 服务器上读取和写入操作的限制问题。但是,我们用于分片的技术也决定了数据库的性能。我们已经在《第九章》MySQL 8 中的分区中详细了解了这一点。

维护

在 MySQL 8 中进行了扩展时,我们必须知道如何管理主服务器和从服务器,以及在执行扩展时需要哪些配置。在服务器处于关键阶段时需要注意哪些步骤?在分片、集群或数据库服务器复制时需要执行哪些步骤?

扩展是可能的,但并不是一项容易的操作。如果我们想要进行扩展,我们应该知道数据库可以处理更多的事务而不会出现任何问题。我们应该知道适当的配置来克服主服务器上写入和读取操作的默认限制。完成后,我们需要执行类似的步骤来配置从数据库服务器,该服务器应该只为最终用户提供读取操作,并且应始终与主数据库同步。

如果我们有多个服务器,那么服务器的维护也将成为一项昂贵的开销。所有服务器都需要保持一致,配置应该合理,服务器的成本也会影响业务。如果数据量不断增加,那么服务器空间也需要以适当的方式进行管理。

主服务器故障

如果主服务器失败并且在那时数据对客户不可用,最终用户会感到沮丧,业务将在市场信用和失去客户方面受到影响。业务将不得不承受损失。

同步

无论我们是通过集群还是复制来执行扩展,都需要确保同步。所有从服务器应该有与主服务器相同的数据库。如果在主服务器上执行写操作并在从服务器上执行读操作,那么所有数据都需要同步。所有结果应该是相同的,如果在数据未同步的情况下某个服务器在某个时间段内宕机,将会导致数据丢失的问题。

数据库安全。

如果我们有不同的服务器并且进行了分片,我们如何保护数据库?如果在不同地点有不同的数据库服务器,并且在那个时候数据库的访问不是特定用户专用的,那么数据泄漏是一个很有可能的问题。我们必须完全了解数据库服务器的 IP 配置的数据访问点,以及对执行各种活动的数据库用户的适当角色和权限。哪些 IP 有访问权限,哪些 IP 需要限制从服务器传输数据?在进行数据库的跨节点事务时,应该有准确的数据;不应该允许从服务器访问受限数据的权限。

跨节点事务

在进行扩展后,如果有多个节点并且一个节点需要其他节点的数据作为输入的一部分,则需要跨节点事务。例如,如果我们在不同地点有不同的节点,并且在那个时候所有地点都有单一的库存,那么当一个用户请求任何一个在那个时候不在数据节点上的产品时,就必须根据用户的请求与其他数据节点通信以获取产品的信息。

发展团队以进行开发

当应用程序可能有积极的响应并且其持续的成功增加了业务团队时,数据库管理员的扩展也将是必要的。当我们在 MySQL 8 中进行分片、扩展或复制时,需要具有适当知识和经验的团队成员来处理持续扩展和数据库服务器的管理。这不仅仅局限于设置数据库服务器;我们还需要关注服务器的维护并且持续监视服务器活动。

管理变更请求

当数据库结构发生变化并且我们已经进行了扩展或复制时,需要在变更请求的一部分中注意一些事项,或者如果我们添加了新功能或增强了功能。这包括更新分片键、修改节点的数据分布、更新查询以考虑复制延迟以避免正在管理分片时的陈旧数据、数据平衡,并确保新更新的数据可用。

扩展和扩展

扩展描述了最大化单个 MySQL 节点处理能力的过程。扩展的过程可能涉及优化调整数据库软件和选择正确的存储引擎,正如之前在第六章中讨论的MySQL 8 存储引擎,并选择适当的硬件。单个节点的扩展有一定的限制,这些限制由数据大小、模式复杂性、CPU 周期、系统内存和磁盘 IO 操作的某种组合确定。尽管由于需要处理日益庞大的数据集,扩展一直备受关注,但重要的是要记住,我们扩展得越好,我们就需要越少的扩展节点,因此我们在硬件上的开销就越少。

扩展可以用于提供涵盖几种不同用例的解决方案。其中一些最常见的用例是通过复制来增加读取容量,或者使用数据库分片来增加总数据库大小和整体事务吞吐量。

在扩展 MySQL 8 时面临的关键挑战。在进行 MySQL 8 数据库扩展时,需要考虑这些挑战。一个错误可能会让业务陷入我们都不想面对的境地。扩展是改善数据库性能的更好方式。

实现高可用性

高可用性指的是系统具有耐用性,并且可以在移动、网络门户、网站、社交、电子商务、企业和云应用程序的任何请求或响应所需的数据上执行操作而不受任何干扰。数据可用性被认为是任何企业或组织的最关注的问题。任何停机问题可能会影响业务信用,并且在某些情况下,企业可能会遭受财务损失。

例如,如果我们有一个单个数据库服务器的电子商务应用程序,如果由于硬件故障、网络问题、病毒或操作系统问题等原因,该服务器宕机,也会影响数据。电子商务应用程序可能会在同一时间有大量的客户访问,任何服务器无法为用户请求提供响应的故障都会影响用户;他们会寻找其他购买商品的选择。

MySQL 8 具有提供应用程序后端的能力,帮助实现高可用性和准备一个完全可扩展的系统。系统保持连接持久性的能力,以防止基础设施的一部分失败,以及系统从此类故障中恢复的能力被认为是高可用性。系统的故障可能是由系统的一部分进行维护活动,如硬件或软件升级,或者由安装的软件的故障引起的。

高可用性的目的

实现高可用性和可扩展性的要求可能因系统而异。每个系统都需要不同的配置才能实现这些能力。MySQL 8 还支持不同的方法,例如在多个 MySQL 服务器之间复制数据,或者根据地理位置准备多个数据中心,并从最接近客户位置的数据中心为客户请求提供服务。这样的解决方案可以用来实现 MySQL 的最高运行时间。

如今,在竞争激烈的市场中,组织的关键点是使其系统保持运行。任何故障或停机都会直接影响业务和收入。因此,高可用性是一个不容忽视的因素。MySQL 非常可靠,并且使用集群和复制配置始终可用。集群服务器可以立即处理故障,并管理故障转移部分,以使系统几乎始终可用。如果一个服务器宕机,它将重定向用户的请求到另一个节点,并执行所请求的操作。

数据可用性

数据在任何情况下都是可用的。在任何应用程序中,数据都是核心部分,实际上是应用程序所有者的财富。如果我们有一个医疗保健系统,在任何患者的医疗检查时,由于服务器宕机或其他原因,他们的数据不可用,可能会阻碍医生的进一步处理,在这种情况下会影响患者的生命。

数据安全

首先想到的是保护数据,因为如今数据变得非常宝贵,如果不满足法律义务,可能会影响业务的连续性;事实上,情况可能会很糟糕,甚至可能会迅速关闭您的业务。MySQL 是最安全可靠的数据库管理系统,被许多知名企业使用,如 Facebook、Twitter 和 Wikipedia。它确实提供了一个良好的安全层,可以保护敏感信息免受入侵者的侵害。MySQL 提供访问控制管理,因此在用户身上授予和撤销所需的访问权限很容易。还可以定义角色,并为用户授予或撤销权限列表。所有用户密码都以加密格式存储,使用特定的插件算法。

数据同步

当我们只有一个数据库服务器时,如果由于任何原因它宕机,我们将丢失整个数据库,如果我们备份数据库直到当天,我们可以恢复数据库直到那一天,但在这种情况下,所有当前事务也将丢失。那时最后的交易数据将不可用。

数据备份

当一个企业有任何基于服务器的应用程序,其中单个数据库服务器执行所有任务时,应该在计划中备份数据库直到最后一个事务。在进行高可用性时,需要在架构中包括备份和恢复操作的所有场景。

竞争激烈的市场

在市场上有许多竞争对手提供相同性质的业务。在这种情况下,如果一个企业在数据可用性方面出现问题,客户可能会选择另一个提供商而不是继续与该企业合作。这是业务连续性的一个重要部分。

性能

高可用性在数据操作的性能方面也很重要。如果我们只有一个服务器,并且所有操作都在该服务器上执行,那么在某个阶段它将达到其极限,服务器容量将耗尽。因此,在这种情况下,如果我们实施了高可用性架构,它将提供一种平衡事务和数据操作性能的手段。复制和集群使并发性更好,并管理工作负载。

系统更新

当任何在线站点或应用程序需要更新或计划进行任何新的生产发布时,它直接影响最终用户。如果一个应用程序在那个时候只有有限的用户,我们可以通过电子邮件或应用程序内的消息管理所有最终用户的更新。但是,在一个应用程序中有大量用户的情况下,这将影响业务。它将同时停止所有用户,由于这个正在运行的事务会受到影响。

选择解决方案

再次,我们必须考虑选择可用性的正确解决方案。在我们计划在 MySQL 中实现高可用性时,需要牢记许多事情。实现 HA 和可伸缩性的要求可能因系统而异。每个系统都需要不同的配置才能实现这些能力。

这样的解决方案可以用来实现 MySQL 在以下方面的最高运行时间:

  • 所需的可用性级别

  • 部署的应用程序类型

  • 自己环境中的最佳实践

在 MySQL 中,复制和集群是实现高可用性的最佳选择。所有应用程序都有自己的架构,当我们选择任何技术来实现 MySQL 8 的高可用性时,需要考虑其业务性质。

高可用性的优势

当我们在 MySQL 中执行高可用性时,我们拥有以下优势:

  • MySQL 非常可靠,并且使用集群和复制配置具有持续的可用性。

  • 集群服务器可以立即处理故障并管理故障转移部分,以使系统几乎始终可用。如果一个服务器宕机,它将重定向用户的请求到另一个节点并执行请求的操作。

  • 系统保持连接持久的能力,即使基础设施的一部分失败,系统从此类故障中恢复的能力被视为高可用性。

  • MySQL 8 还支持不同的方法,例如在多个 MySQL 服务器之间复制数据,或者基于地理位置准备多个数据中心,并从最接近客户位置的数据中心提供客户请求。

  • MySQL 以最佳速度进行事务处理。它可以缓存结果,提高读取性能。

  • 复制和集群使并发性更好,并管理工作负载。组复制基本上负责在大多数组复制成员已经同时确认事务已被接收时提交事务。如果写入的总数不超过组复制成员的容量,则可以创建更好的吞吐量。

  • 集群使数据能够复制到多个 MySQL 服务器并进行自动共享。它旨在提供更好的可用性和吞吐量。

  • Memcached 去除了 SQL 层,直接访问 InnoDB 数据库表。因此,诸如 SQL 解析之类的开销操作将不再执行,这确实会影响性能。

  • Memcached 与 MySQL 还为您提供了一种使内存中的数据持久化的方法,以便我们可以在不丢失数据的情况下使用各种数据类型。

  • Memcached API 可用于不同的编程语言,如 Perl、Python、Java、PHP、C 和 Ruby。借助 Memcached API,应用程序可以与 Memcached 接口交互,存储和检索信息。

总结

在本章中,我们从 MySQL 8 的可伸缩性和高可用性概述开始,涵盖了各种可伸缩性需求、优势、方法以及在设计可伸缩性 MySQL 8 时需要注意的关键点。我们还讨论了在进行可伸缩性时通常遇到的缺点以及如何通过适当的解决方案克服挑战。我们学习了 MySQL 8 的扩展和扩展 MySQL 8 的故障排除挑战。我们还学习了在 MySQL 8 中实现高可用性的许多不同方法。

在接下来的章节中,我们将学习如何确保 MySQL 8 的安全性。我们将了解影响安全性的一般因素,核心 MySQL 8 文件的安全性,访问控制以及保护数据库系统本身。我们还将学习安全插件的详细信息,并深入了解关系数据库的数据库安全性。

第十一章:MySQL 8 - 安全性

在之前的章节中,我们学习了 MySQL 8 的可伸缩性以及在扩展 MySQL 8 时如何解决挑战。除此之外,我们还学习了如何使 MySQL 8 具有高可用性。现在,安全对于任何应用程序都很重要,对吧?当我们谈论安全时,它包括帐户管理、角色、权限等。考虑到这些方面,我们将在本章中涵盖所有这些主题。本章主要关注 MySQL 8 数据库安全及其相关功能。本章涵盖以下主题:

  • MySQL 8 的安全概述

  • 常见安全问题

  • MySQL 8 中的访问控制

  • MySQL 8 中的帐户管理

  • MySQL 8 中的加密

  • 安全插件

MySQL 8 的安全概述

安全这个术语不局限于特定主题;它涵盖了与 MySQL 8 相关的各种主题。在开始对其进行详细讨论之前,让我们提到与安全相关的一些重要要点:

  • 考虑数据库中的安全性,需要管理用户及其与各种数据库对象相关的权限。

  • 用户的密码安全。

  • 在安装过程中进行安全配置,包括各种类型的文件,如日志文件、数据文件等。这些文件必须受到保护,以防止读/写操作。

  • 为处理系统级故障场景,您必须拥有备份和恢复计划。这包括所有必需的文件,如数据库文件、配置文件等。

  • 管理安装了 MySQL 8 的系统的网络安全,允许有限数量的主机进行连接。

现在,您的旅程将开始另一个重要且非常有趣的主题。我们开始吧。

常见安全问题

在深入讨论复杂问题之前,您必须首先了解一些基本要点,这将有助于防止滥用或攻击。

一般指南

在 MySQL 8 中,用户执行的所有连接、查询和操作都基于访问控制列表ACLs)安全。以下是与安全相关的一些一般指南:

  • 不要允许任何用户访问user表,除了 root 帐户。使用GRANTREVOKE语句管理用户权限。

  • 在进行互联网数据传输时,使用加密协议,如 SSH 或 SSL。MySQL 8 支持 SSL 连接。

  • 在客户端使用应用程序将数据输入 MySQL 时,使用适当的防御性编程技术。

  • 使用哈希函数将密码存储到 MySQL 8 数据库中;不要将明文存储为密码。对于密码恢复,考虑一些字符串作为盐,并使用hash(hash(password)+salt)值。

  • 使用适当的密码策略来防止密码被破解。这意味着您的系统应该只接受符合您规则/约定的密码。

  • 使用防火墙可以减少 50%的故障几率,并为您的系统提供更多保护。将 MySQL 定义在一个非军事区或防火墙后面,以防止来自不信任主机的攻击。

  • 基于 Linux 的系统提供了tcpdump命令,以更安全的方式执行传输任务。该命令在网络层上提供安全性。例如,使用以下命令,您可以检查 MySQL 数据流是否加密:

        shell> tcpdump -l -i eth0 -w - src or dst port 3306 | strings

安全密码的指南

在本节中,我们描述了关于不同用户的密码安全的指南,并介绍了如何在登录过程中进行管理。MySQL 8 提供了validate_password插件来定义可接受密码的策略。

最终用户的指南

本节描述了定义密码的各种方法,作为最终用户,以最安全的方式。它解释了如何使您的密码更安全。最安全的方法是在受保护的选项文件中定义密码,或在客户端程序中提示输入密码。请参阅以下不同的定义密码的方式:

  • 使用以下选项在命令行中提供密码:
 cmd>mysql -u root --password=your_pwd
 --OR
 cmd> 
  • 在前两个命令中,您必须在命令行中指定密码,这是不可取的。MySQL 8 提供了另一种安全的连接方式。执行以下命令,它将提示您输入密码。一旦输入密码,MySQL 会为每个密码字符显示星号(*):
 cmd>mysql -u root -p
 Enter password: *********

这比前两种方法更安全,前两种方法中,您在命令行参数中定义密码:

  • 使用MYSQL_PWD环境变量来定义您的密码。与其他方法相比,这种方法是不安全的,因为环境变量可能被其他用户访问。

  • 使用mysql_config_editor实用程序定义密码,这是一种提供的选项,用于将密码存储到加密的登录路径文件中,命名为named.mylogin.cnf。 MySQL 8 稍后将使用此文件与 MySQL 服务器连接。

  • 使用选项文件存储密码。在将凭据定义到文件中时,请确保其他用户无法访问该文件。例如,在基于 UNIX 的系统中,您可以在客户端部分的选项文件中定义密码,如下所示:

 [client]
 password=your_pass

要使文件安全或设置其访问模式,请执行以下命令:

shell> chmod 600 .my.cnf

管理员指南

对于数据库管理员,应遵循以下准则来保护密码:

  • 使用validate_password来对接受的密码应用策略

  • MySQL 8 使用mysql.user表来存储用户密码,因此配置系统以使只有管理员用户可以访问此表

  • 用户应该被允许在密码过期的情况下重置帐户密码

  • 如果日志文件包含密码,请对其进行保护

  • 管理对插件目录和my.cnf文件的访问,因为它可以修改插件提供的功能

密码和日志记录

MySQL 8 允许您在 SQL 语句中以纯文本形式编写密码,例如CREATE USERSET PASSWORDGRANT。如果我们执行这些语句,MySQL 8 将密码以文本形式写入日志文件,并且所有可以访问日志文件的用户都可以看到。为了解决这个问题,避免使用上述 SQL 语句直接更新授权表。

保护 MYSQL 8 免受攻击

为了保护 MySQL 8 免受攻击,请强烈考虑以下几点:

  • 为所有 MySQL 帐户设置密码。永远不要定义没有密码的帐户,因为这允许任何用户访问您的帐户。

  • 与 MySQL 8 建立连接时,使用安全协议/通道,例如压缩协议,MySQL 8 内部 SSL 连接或用于加密 TCP/IP 连接的 SSH。

  • 对于基于 Unix 的系统,为运行mysqld的 Unix 帐户设置数据目录的读/写权限。不要使用 root 用户启动 MySQL 8 服务器。

  • 使用secure_file_priv变量指定读写权限的目录。使用此变量,您可以限制非管理员用户访问重要目录。使用此变量设置plugin_dir的权限非常重要。同样,不要向所有用户提供FILE权限,因为这允许用户在系统中的任何位置写文件。

  • 使用max_user_connections变量限制每个帐户的连接数。

  • 在创建授权表条目时,正确使用通配符。最好使用 IP 而不是 DNS。

  • 在存储过程和视图创建期间遵循安全准则。

MySQL 8 提供的安全选项和变量

MySQL 8 提供了以下选项和变量以确保安全:

名称 命令行 选项文件 系统变量 状态变量 变量范围 动态
allow-suspicious-udfs
automatic_sp_privileges 全局
- chroot
- des-key-file
- local_infile 全局
- old_passwords 两者
- safe-user-create
- secure-auth 全局
- - 变量:secure_auth 全局
- secure-file-priv 全局
- - 变量:secure_file_priv 全局
- skip-grant-tables
- skip-name-resolve 全局
- - 变量:skip_name_resolve 全局
- skip-networking 全局
- - 变量:skip_networking 全局
- skip-show-database 全局
- - 变量:skip_show_database 全局

参考:dev.mysql.com/doc/refman/8.0/en/security-options.html

客户端编程的安全指南

不要相信应用程序用户输入的任何数据,因为用户有可能输入了针对 MySQL 数据库的dropdelete语句。因此,存在安全漏洞和数据丢失的风险。作为 MySQL 数据库的管理员,应遵循以下检查表:

  • 在将数据传递给 MySQL 8 之前,必须检查数据的大小。

  • 为使 MySQL 8 更加严格,启用严格的 MySQL 模式。

  • 对于数字字段,应输入字符、特殊字符和空格,而不是数字本身。在将字段值发送到 MySQL 8 服务器之前,通过应用程序将其更改为原始形式。

  • 使用两个不同的用户进行应用程序连接到数据库和数据库管理。

  • 通过在动态 URL 和 Web 表单的情况下将数据类型从数字更改为字符类型并添加引号来修改数据类型。还在动态 URL 中添加%22(")、%23(#)和%27(')。

先前定义的功能内置于所有编程接口中。例如,Java JDBC 提供带占位符的预编译语句,Ruby DBI 提供quote()方法。

MySQL 8 中的访问控制

特权主要用于验证用户并验证用户凭据,检查用户是否被允许进行请求的操作。当我们连接到 MySQL 8 服务器时,它将首先通过提供的主机和用户名检查用户的身份。连接后,当请求到来时,系统将根据用户的身份授予特权。基于这一理解,我们可以说在使用客户端程序连接到 MySQL 8 服务器时,访问控制包含两个阶段:

  • 阶段 1:MySQL 服务器将根据提供的身份接受或拒绝连接

  • 阶段 2:从 MySQL 服务器获取连接后,当用户发送执行任何操作的请求时,服务器将检查用户是否具有足够的权限

MySQL 8 特权系统存在一些限制:

  • 不允许用户在特定对象(如表或例程)上设置密码。MySQL 8 允许在账户级别全局设置密码。

  • 作为管理员用户,我们不能以允许创建/删除表但不允许创建/删除该表的数据库的方式指定权限。

不允许显式限制用户访问,这意味着无法显式匹配用户并拒绝其连接。MySQL 8 在内存中管理授予表的内容,因此在INSERTUPDATEDELETE语句的情况下,对授予表的执行需要服务器重新启动才能生效。为了避免服务器重新启动,MySQL 提供了一个刷新权限的命令。我们可以以三种不同的方式执行此命令:

  1. 通过发出FLUSH PRIVILEGES

  2. 使用mysqladmin reload

  3. 使用mysqladmin flush-privileges

当我们重新加载授予表时,它将按照以下提到的要点工作:

  • 表和列特权:这些特权的更改将在下一个客户端请求中生效

  • 数据库特权:这些特权的更改将在客户端执行USE dbname语句的下一次生效

  • 全局特权和密码:这些特权的更改对连接的客户端不受影响;它将适用于随后的连接

MySQL 8 提供的特权

特权定义了用户帐户可以执行哪些操作。根据操作的级别和应用的上下文,它将起作用。它主要分为以下几类:

  • 数据库特权:应用于数据库及其内的所有对象。它可以授予单个数据库,也可以全局定义以应用于所有数据库。

  • 管理特权:它在全局级别定义,因此不限于单个数据库。它使用户能够管理 MySQL 8 服务器的操作。

  • 数据库对象的特权:用于定义对数据库对象(如表、视图、索引和存储例程)的特权。它可以应用于数据库的特定对象,可以应用于数据库中给定类型的所有对象,也可以全局应用于所有数据库中给定类型的所有对象。

MySQL 8 将帐户特权相关信息存储到授予表中,并在服务器启动时将这些表的内容存储到内存中,以提高性能。特权进一步分为静态和动态特权:

  • 静态特权:这些特权内置于服务器中,无法注销。这些特权始终可供用户授予。

  • 动态特权:这些特权可以在运行时注册或注销。如果特权未注册,则不可供用户帐户授予。

授予表

授予表包含与用户帐户和授予的特权相关的信息。当我们在数据库中执行任何帐户管理语句时,如CREATE USERGRANTREVOKE,MySQL 8 会自动将数据插入这些表中。MySQL 允许管理员用户在授予表上进行插入、更新或删除操作,但这并不是一个理想的方法。MySQL 8 数据库的以下表包含授予信息:

  • user:它包含与用户帐户、全局特权和其他非特权列相关的详细信息

  • password_history:它包含密码更改的历史记录

  • columns_priv:它包含列级特权

  • procs_priv:它包含与存储过程和函数相关的特权

  • proxies_priv:它包含代理用户的特权

  • tables_priv:它包含表级特权

  • global_grants:它包含与动态全局特权分配相关的详细信息

  • role_edges:它包含角色子图的边缘

  • db:它包含数据库级别的特权

  • default_roles:它包含与默认用户角色相关的详细信息

授予表包含范围和特权列:

  • 范围列:此列定义表中行的范围,即行适用的上下文。

  • 特权列:此列指示用户被允许执行哪些操作。MySQL 服务器从各种授予表中合并信息,以构建用户特权的完整详细信息。

从 MySQL 8.0 开始,授予表使用InnoDB存储引擎管理事务状态,但在此之前,MySQL 使用MyISAM引擎管理非事务状态。这种改变使用户能够以事务模式管理所有帐户管理语句,因此在多个语句的情况下,要么全部成功执行,要么全部不执行。

访问控制阶段的验证

MySQL 8 在两个不同的阶段执行访问控制检查。

第 1 阶段 - 连接验证

这是连接验证阶段,因此在验证后,MySQL 8 将接受或拒绝您的连接请求。将根据以下条件执行验证:

  1. 基于用户的身份,以及其密码。

  2. 用户账户是否被锁定。

如果这两种情况中的任何一种失败,服务器将拒绝访问。在这里,身份包含请求来源的用户名和主机名。MySQL 对用户表的account_locked列进行锁定检查,并对用户表范围的三列HostUserauthentication_string进行凭据检查。

第 2 阶段 - 请求验证

一旦与 MySQL 服务器建立连接,第 2 阶段就会出现,MySQL 服务器会检查您要执行的操作以及您是否有权限执行。为了进行此验证,MySQL 使用授权表的特权列;它可能来自userdbtables_privcolumns_privprocs_priv表。

MySQL 8 中的账户管理

顾名思义,本主题描述了如何在 MySQL 8 中管理用户账户。我们将描述如何添加新账户,如何删除账户,如何为账户定义用户名和密码,以及更多。

添加和删除用户账户

MySQL 8 提供了创建账户的两种不同方式:

  • 使用账户管理语句:这些语句用于创建用户并设置其特权;例如,使用CREATE USERGRANT语句,通知服务器对授权表进行修改

  • 使用授权表的操作:使用INSERTUPDATEDELETE语句,我们可以操作授权表

在这两种方法中,账户管理语句更可取,因为它们更简洁,更不容易出错。现在,让我们看一个使用命令的例子:

#1 mysql> CREATE USER 'user1'@'localhost' IDENTIFIED BY 'user1_password';
#2 mysql> GRANT ALL PRIVILEGES ON *.* TO 'user1'@'localhost' WITH GRANT OPTION;

#3 mysql> CREATE USER 'user2'@'%' IDENTIFIED BY 'user2_password';
#4 mysql> GRANT ALL PRIVILEGES ON *.* TO 'user2'@'%' WITH GRANT OPTION;

#5 mysql> CREATE USER 'adminuser'@'localhost' IDENTIFIED BY 'password';
#6 mysql> GRANT RELOAD,PROCESS ON *.* TO 'adminuser'@'localhost';

#7 mysql> CREATE USER 'tempuser'@'localhost';

#8 mysql> CREATE USER 'user4'@'host4.mycompany.com' IDENTIFIED BY 'password';
#9 mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON db1.* TO 'user4'@'host4\. mycompany.com';

前面的命令执行以下操作:

  • #1命令创建'user1',命令#2'user1'分配了完整的特权。但是'user1'@'localhost'表示'user1'只允许与localhost连接。

  • #3命令创建'user2',命令#4'user2'分配了完整的特权,与'user1'相同。但在#4 中,提到了'user2'@'%',这表示'user2'可以与任何主机连接。

  • #5创建'adminuser'并允许它仅与localhost连接。在#6中,我们可以看到仅为'adminuser'提供了RELOADPROCESS特权。它允许'adminuser'执行mysqladmin reloadmysqladmin refreshmysqladmin flush-xxx命令和mysqladmin processlist命令,但它无法访问任何数据库。

  • #7创建了没有密码的'tempuser'账户,并允许用户仅与localhost连接。但是没有为'tempuser'指定授权,因此此用户无法访问数据库,也无法执行任何管理命令。

  • #8创建'user4'并允许用户仅使用'host4'访问数据库。#10表示'user4''db1'上对所有提及的操作具有授权。

要删除用户账户,请执行DROP USER命令如下:

mysql> DROP USER 'user1'@'localhost';

此命令将从系统中删除'user1'账户。

使用角色的安全性

与用户账户角色具有特权一样,我们也可以说角色是特权的集合。作为管理员用户,我们可以向角色授予和撤销特权。MySQL 8 提供了以下与角色配置相关的命令、函数和变量。

设置角色

SET ROLE在当前会话中更改活动角色。参考以下与SET ROLE相关的命令:

mysql> SET ROLE NONE; SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE |
+----------------+
mysql> SET ROLE 'developer_read'; SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| `developer_read`@`%` |
+----------------+

第一个命令将在当前会话中取消用户的所有角色。您可以使用CURRENT_ROLE();函数查看效果。在第二个命令中,我们将'developer_read'角色设置为默认角色,然后再次使用预定义函数检查当前角色。

创建角色

CREATE ROLE用于创建角色;参考以下命令,它将创建一个名为'developer_role'的角色:

CREATE ROLE 'developer_role';

删除角色

DROP ROLE用于删除角色。参考以下命令,它将删除'developer_role'角色:

DROP ROLE 'developer_role';

授予权限

GRANT分配权限给角色,并将角色分配给帐户。例如,以下命令将所有权限分配给开发人员角色:

GRANT ALL ON my_db.* TO 'developer_role';

同样,要将角色分配给用户帐户,请执行以下命令:

GRANT 'developer_role' TO 'developer1'@'localhost';

此命令将'developer_role'角色分配给developer1帐户。MySQL 8 还提供了从用户到用户和从角色到角色的GRANT分配功能。考虑以下示例:

CREATE USER 'user1';
CREATE ROLE 'role1';
GRANT SELECT ON mydb.* TO 'user1';
GRANT SELECT ON mydb.* TO 'role1';
CREATE USER 'user2';
CREATE ROLE 'role2';
GRANT 'user1', 'role1'TO 'user2';
GRANT 'user1', 'role1'TO 'role2';

在此示例中,通过使用GRANT命令以简单的方式在user1role1上应用了GRANT。现在,对于user2role2,我们已经分别从user1role1应用了GRANT

撤销

REVOKE用于从角色中删除权限,并从用户帐户中删除角色分配。参考以下命令:

REVOKE developer_role FROM user1;
REVOKE INSERT, UPDATE ON app_db.* FROM 'role1';

第一个命令用于删除user1'developer_role',第二个命令用于从'app_db'上的'role1'中删除插入和更新权限。

设置默认角色

SET DEFAULT ROLE指示默认情况下活动的角色,每当用户登录时,默认角色对用户可用。要设置默认根角色,请执行以下命令:

mysql>SET DEFAULT ROLE app_developer TO root@localhost;

mysql> SELECT CURRENT_ROLE();
+---------------------+
| CURRENT_ROLE() |
+---------------------+
| `app_developer`@`%` |
+---------------------+
1 row in set (0.04 sec)

设置默认角色后,重新启动服务器并执行current_role()函数,以检查是否分配了角色。

显示授予权限

SHOW GRANTS列出与帐户和角色相关的权限和角色分配。对于一个角色,执行以下命令:

mysql> show grants for app_developer;
+-------------------------------------------+
| Grants for app_developer@% |
+-------------------------------------------+
| GRANT USAGE ON *.* TO `app_developer`@`%` |
+-------------------------------------------+
1 row in set (0.05 sec)

此命令显示了'app_developer'角色上可用的授予权限。同样,要检查用户的授予权限,请执行以下命令:

mysql> show grants for root@localhost;

前面的命令列出了用户 root 拥有的所有访问权限:

  • CURRENT_ROLE():此函数用于列出当前会话中的当前角色。如默认角色命令中所述,它显示用户当前分配的角色。

  • activate_all_roles_on_login:这是一个系统变量,用于在用户登录时自动激活所有授予的角色。默认情况下,角色的自动激活是禁用的。

  • mandatory_roles:这是一个系统变量,用于定义强制角色。请记住,定义为强制角色的角色不能使用drop命令删除。在服务器文件my.cnf中定义您的强制角色如下:

 [mysqld]
 mandatory_roles='app_developer'

要在运行时持久化和设置这些角色,请使用以下语句:

SET PERSIST mandatory_roles = 'app_developer';

此语句应用于运行中的 MySQL 8 实例的更改,并保存以供后续重新启动。如果要应用运行实例的更改而不是其他重新启动的更改,则使用关键字GLOBAL而不是PERSIST

密码管理

MySQL 8 提供了以下与密码管理相关的功能:

  • 密码过期:用于定义密码过期的时间段,以便用户可以定期更改密码。MySQL 8 允许为帐户手动设置密码过期,以及设置过期策略。对于过期策略,可以使用mysql_native_passwordsha256_passwordcaching_sha2_password插件。要手动设置密码,请执行以下命令:
 ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE;

这将标记指定用户的密码已过期。对于密码策略,您必须以天数为单位定义持续时间。MySQL 使用系统变量default_password_lifetime,其中包含一个正整数来定义天数。我们可以在my.cnf文件中定义它,也可以使用PERSIST选项在运行时定义它:

  • 密码重用限制:用于防止再次使用旧密码。MySQL 8 基于两个参数定义此限制-更改次数和经过的时间;它们可以单独或结合使用。MySQL 8 分别定义了password_historypassword_reuse_interval系统变量来应用限制。我们可以在my.cnf文件中定义这些变量,也可以使其持久化。

  • password_history:此变量表示新密码不能从旧密码设置/复制。在这里,根据指定的次数考虑最近的旧密码。

  • password_reuse_interval: 此变量表示密码不能从旧密码设置/复制。在这里,间隔定义了特定的时间段,MySQL 8 将检查用户在该时间段内所有密码与新密码是否匹配。例如,如果间隔设置为 20 天,则新密码在过去 20 天内的更改数据中不应存在。

  • 密码强度评估:用于定义强密码。它使用validate_password插件实现。

MySQL 8 中的加密

当需要在网络上传输数据时,必须使用加密进行连接。如果使用未加密数据,则可以轻松观察网络访问权限的人员查看客户端和服务器之间传输的所有流量,并查看传输的数据。为了保护您在网络上传输的数据,请使用加密。确保所使用的加密算法包含安全元素,以保护连接免受已知攻击,如更改消息顺序或在数据上重复两次。根据应用程序要求,可以选择加密或未加密类型的连接。MySQL 8 使用传输层安全性TLS)协议对每个连接执行加密。

配置 MySQL 8 以使用加密连接

本节描述了如何配置服务器和客户端以进行加密连接。

服务器端配置加密连接

在服务器端,MySQL 8 使用-ssl选项来指定与加密相关的属性。以下选项用于在服务器端配置加密:

  • --ssl-ca:此选项指定证书颁发机构CA)证书文件的路径名

  • --ssl-cert:此选项指定服务器公钥证书文件的路径名

  • --ssl-key:此选项指定服务器私钥文件的路径名

您可以通过在my.cnf文件中指定上述选项来使用这些选项:

[mysqld]
ssl-ca=ca.pem
ssl-cert=server-cert.pem
ssl-key=server-key.pem

--ssl选项默认启用,因此在服务器启动时,MySQL 8 将尝试在数据目录下查找证书和密钥文件,即使您没有在my.cnf文件中定义它。如果找到这些文件,MySQL 8 将提供加密连接,否则将继续不加密连接。

客户端端配置加密连接

在客户端,MySQL 使用与服务器端相同的-ssl选项来指定证书和密钥文件,但除此之外,还有-ssl-mode选项。默认情况下,如果服务器允许,客户端可以与服务器建立加密连接。为了进一步控制,客户端程序使用以下-ssl-mode选项:

  • --ssl-mode=REQUIRED:此选项表示必须建立加密连接,如果未建立则失败

  • --ssl-mode=PREFFERED:此选项表示客户端程序可以建立加密连接,如果服务器允许,否则建立未加密连接而不会失败

  • --ssl-mode=DISABLED:此选项表示客户端程序无法使用加密连接,只允许未加密连接

  • --ssl-mode=VERIFY_CA:此选项与REQUIRED相同,但除此之外,它还会验证 CA 证书与配置的 CA 证书匹配,并在找不到匹配项时返回失败

  • --ssl-mode=VERIFY_IDENTITY:与VERIFY_CA选项相同,但除此之外,它还将执行主机名身份验证

加密连接的命令选项

MySQL 8 提供了用于加密连接的几个选项。您可以在命令行上使用这些选项,也可以在选项文件中定义它们:

格式 描述
--skip-ssl 不使用加密连接
--ssl 启用加密连接
--ssl-ca 包含受信任的 SSL 证书颁发机构列表的文件
--ssl-capath 包含受信任的 SSL 证书颁发机构证书文件的目录
--ssl-cert 包含 X509 证书的文件
--ssl-cipher 连接加密的允许密码列表
--ssl-crl 包含证书吊销列表的文件
--ssl-crlpath 包含证书吊销列表文件的目录
--ssl-key 包含 X509 密钥的文件
--ssl-mode 与服务器连接的安全状态
--tls-version 允许加密连接的协议

参考:dev.mysql.com/doc/refman/8.0/en/encrypted-connection-options.html

从 Windows 远程连接到 MySQL 8 并使用 SSH

要从 Microsoft Windows 系统远程连接到 MYSQL 8 并使用 SSH,执行以下步骤:

  1. 在本地系统上安装 SSH 客户端。

  2. 启动 SSH 客户端后,通过要连接到服务器的主机名和用户 ID 进行设置。

  3. 配置端口转发如下并保存信息:

  • 对于远程转发配置local_port:3306remote_host:mysqlservername_or_ipremote_port:3306

  • 对于本地转发配置local_port:3306remote_host:localhostremote_port:3306

  1. 使用创建的 SSH 会话登录服务器。

  2. 在本地的 Microsoft Windows 机器上,启动任何 ODBC 应用程序,如 Microsoft Access。

  3. 在本地系统中,创建新文件并尝试使用 ODBC 驱动程序链接到 MySQL 服务器。确保在连接中定义了localhost而不是mysqlservername

安全插件

MySQL 8 提供了几个插件来实现安全性。这些插件提供了与身份验证协议、密码验证、安全存储等相关的各种功能。让我们详细讨论各种类型的插件。

认证插件

以下是认证插件的列表及其详细信息:

  • 本机可插拔身份验证:为了实现本机身份验证,MySQL 8 使用mysql_native_password插件。此插件在服务器和客户端两侧都使用一个通用名称,并由 MySQL 8 为服务器和客户端程序提供内置支持。

  • SHA-256 可插拔身份验证

为了实现 SHA-256 哈希,MySQL 8 提供了两种不同的插件:

  1. sha256_password:此插件用于实现基本的 SHA-256 身份验证。

  2. caching_sha2_password:此插件实现了 SHA-256 身份验证,并具有缓存功能以提高性能,与基本插件相比具有一些附加功能。

此插件与 MySQL 8 服务器和客户端程序内置提供,名称相同为sha256_password。在客户端中,它位于libmysqlclient库下。要为帐户使用此插件,请执行以下命令:

CREATE USER 'testsha256user'@'localhost'
IDENTIFIED WITH sha256_password BY 'userpassword';

SHA-2 可插拔身份验证

SHA-2 可插拔身份验证与 SHA-256 可插拔插件相同,只是其插件名称为caching_sha2_passwordsha256_password相比,此插件具有以下优点:

  1. 如果使用 Unix 套接字文件和共享内存协议,则为客户端连接提供支持。

  2. SHA-2 插件中提供了内存缓存,为以前连接过的用户提供更快的重新认证。

  3. 该插件提供了基于 RSA 的密码交换,可以在 MySQL 8 提供的 SSL 库无关的情况下工作。

客户端明文可插拔认证

该插件用于将密码发送到服务器而不进行哈希或加密。在客户端库中以mysql_clear_password的名称提供。MySQL 8 在客户端库中内置了它。

无登录可插拔认证

这是一个服务器端插件,用于阻止使用它的任何帐户的所有客户端连接。插件名称是'mysql_no_login',它不是 MySQL 的内置插件,因此我们必须使用mysql_no_login.so库。要使其可用,首先将库文件放在插件目录下,然后执行以下步骤之一:

  1. 通过在my.cnf文件中添加--plugin-load-add参数在服务器启动时加载插件:
 [mysqld]
 plugin-load-add=mysql_no_login.so
  1. 要在运行时注册插件,请执行以下命令:
 INSTALL PLUGIN mysql_no_login SONAME 'mysql_no_login.so';

要卸载此插件,请执行以下命令:

  1. 如果使用--plugin-load-adoption在服务器启动时安装了插件,则通过删除该选项重启服务器来卸载插件。

  2. 如果使用INSTALL PLUGIN命令安装了插件,则使用卸载命令将其移除:

UNINSTALL PLUGIN mysql_no_login;

套接字对等凭证可插拔认证

名为auth_socket的服务器端插件用于对从本地主机使用 Unix 套接字文件连接的客户端进行身份验证。它仅用于支持SO_PEERCRED选项的系统。SO_PEERCRED用于获取有关运行客户端程序的用户的信息。这不是一个内置插件;我们必须使用auth_socket.so库来使用这个插件。要使其可用,首先将库文件放在插件目录下,然后执行以下步骤之一:

  1. 通过在my.cnf文件中添加--plugin-load-add参数在服务器启动时加载插件:
 [mysqld]
 plugin-load-add=auth_socket.so
  1. 通过执行以下命令在运行时注册插件:
 INSTALL PLUGIN auth_socket SONAME 'auth_socket.so';

要卸载此插件,请执行以下命令:

  1. 如果使用--plugin-load-addoption在服务器启动时安装了插件,则通过删除该选项重启服务器来卸载插件。

  2. 如果使用INSTALL PLUGIN命令安装了插件,则使用UNINSTALL命令将其移除:

 UNINSTALL PLUGIN auth_socket;

测试可插拔认证

MySQL 8 提供了一个测试插件,用于检查帐户凭据并在服务器日志中记录成功或失败。这不是一个内置插件,需要在使用之前安装。它适用于服务器端和客户端,分别命名为test_plugin_serverauth_test_plugin。MySQL 8 使用auth_test_plugin.so库来提供此插件。要安装和卸载此插件,请执行与前面插件中提到的相同的步骤。

连接控制插件

MySQL 8 使用这些插件在特定数量的连接尝试失败后向客户端的服务器响应中引入逐渐增加的延迟。MySQL 为连接控制提供了两个插件。

CONNECTION_CONTROL

这个插件将检查所有传入连接的请求,并根据需要在服务器响应中添加延迟。该插件使用一些系统变量进行配置,并使用状态变量进行监视。它还使用其他一些插件、事件类和进程,比如审计插件、MYSQL_AUDIT_CONNECTION_CLASSMASK事件类、MYSQL_AUDIT_CONNECTION_CONNECTMYSQL_AUDIT_CONNECTION_CHANGE_USER进程,以检查服务器是否应该在处理任何客户端连接之前添加延迟:

CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS

该插件实现了对INFORMATION_SCHEMA表的使用,以提供有关失败连接监视的详细信息。

插件安装

我们必须使用connection_control.so库来使用这个插件。要使其可用,首先将库文件放在插件目录下,然后执行以下步骤之一:

  1. my.cnf文件中添加--plugin-load-add参数,以在服务器启动时加载插件:
 [mysqld]
 plugin-load-add= connection_control.so
  1. my.cnf文件中添加--plugin-load-add参数,以在服务器启动时加载插件:
 INSTALL PLUGIN CONNECTION_CONTROL SONAME 
          'connection_control.so';
 INSTALL PLUGIN CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS SONAME 
          'connection_control.so';

与连接控制相关的变量

以下变量由CONNECTION-CONTROL插件提供:

  • Connection_control_delay_generated: 这是一个状态变量,主要用于管理计数器。它指示服务器在连接失败尝试时添加延迟的次数。它还取决于connection_control_failed_connections_threshold系统变量,因为除非尝试次数达到阈值变量定义的限制,否则此状态变量不会增加计数。

  • connection_control_failed_connections_threshold: 这是一个系统变量,指示在服务器对每次尝试添加延迟之前,客户端允许连续失败的尝试次数。

  • connection_control_max_connection_delay: 这是一个系统变量,定义了服务器在连接失败尝试时的最大延迟时间(以毫秒为单位)。一旦阈值变量包含一个大于零的值,MySQL 8 将考虑这个变量。

  • connection_control_min_connection_delay: 该系统变量定义了服务器对连接失败尝试的最小延迟时间(以毫秒为单位)。一旦阈值变量包含一个大于零的值,MySQL 8 将考虑这个变量。

密码验证插件

对于密码验证,MySQL 提供了一个名为validate_password的插件。它主要用于测试密码并提高安全性。以下是该插件的两个主要功能:

  • VALIDATE_PASSWORD_STRENGTH(): 一个 SQL 函数,用于查找密码的强度。它以密码作为参数,并返回一个介于 0 和 100 之间的整数值。这里,0 表示弱密码,100 表示强密码。

  • 按照 SQL 语句中的策略检查密码:对于所有使用明文密码的 SQL 语句,插件将检查提供的密码是否符合密码策略,并根据此返回响应。对于弱密码,插件将返回一个ER_NOT_VALID_PASSWORD错误。如果密码在参数中以明文形式定义,ALTER USERCREATE USERGRANTSET PASSWORD语句和PASSWORD()函数始终由该插件检查。

安装密码验证插件

我们必须使用validate_password.so库与该插件。要使其可用,首先将库文件放在插件目录下,然后执行以下步骤之一:

  1. 在服务器启动时加载插件,通过在my.cnf文件中添加--plugin-load-add参数:
 [mysqld]
 plugin-load-add=validate_password.so
  1. 在运行时注册插件,执行以下命令:
 INSTALL PLUGIN validate_password SONAME 'validate_password.so';

与密码验证插件相关的变量和选项

MySQL 8 提供了以下与密码验证插件相关的系统变量、状态变量和选项。

  • validate_password_check_user_name: 这是一个系统变量,在 MySQL 8 中默认启用。顾名思义,它用于将密码与当前有效用户的用户名进行比较。如果密码与用户名或其反转匹配,MySQL 8 将拒绝密码,而不管VALIDATE_PASSWORD_STRENGTH()函数的值如何。

  • validate_password_dictionary_file: 该系统变量包含了validate_password插件使用的目录的路径名。您可以在运行时设置它,无需重新启动服务器,并且一旦安装了插件,它就可用。如果您定义了用于密码检查的目录,将密码策略值设置为 2(强)。密码策略的可能值在validate_password_policy系统变量下描述。

  • validate_password_length: 一旦安装了插件,该系统变量可用于定义密码与validate_password插件进行检查所需的最小字符数。

  • validate_password_mixed_case_count:一旦安装了插件,此系统变量可用于定义密码检查中小写和大写字符的最小数量。

  • validate_password_number_count:一旦安装了插件,此系统变量可用于定义密码检查中数字的最小数量。

  • validate_password_special_char_count:一旦安装了插件,此系统变量可用于定义密码检查中非字母数字字符的最小数量。

  • validate_password_policy:一旦安装了插件,此系统变量可用,并指示插件在其他系统变量情况下应如何行为。此变量的以下值描述了validate_password插件的行为:

策略 执行的测试
0 或 LOW 长度
1 或 MEDIUM 长度;数字,小写/大写和特殊字符
2 或 STRONG 长度;数字,小写/大写和特殊字符;字典文件

参考:dev.mysql.com/doc/refman/8.0/en/validate-password-options-variables.html

  • validate_password_dictionary_file_last_parsed:这是一个状态变量,用于指示上次解析目录文件的时间。

  • validate_password_dictionary_file_words_count:这是一个状态变量,用于指示从目录文件中读取的单词数量。

  • --validate-password[=value]:此选项用于定义服务器在启动时如何加载validate_password插件。此选项仅在插件使用INSTALL PLUGIN注册或使用--plugin-load-add功能加载时可用。

MySQL 8 keyring

MySQL 8 提供了一个 keyring 服务,允许 MySQL 服务器的内部组件和插件存储它们的敏感信息以供以后使用。对于此功能,MySQL 8 使用keyring_file插件,该插件将数据存储在服务器主机上的文件中。此插件在所有 MySQL 的发行版中都可用,如社区版和企业版。

安装 keyring 插件

我们必须使用keyring_file.so库与此插件。为了使其可用,首先将库文件放在插件目录下,然后执行以下步骤之一:

  • 通过在my.cnf文件中添加--plugin-load-add参数,在服务器启动时加载插件:
 mysqld]
 plugin-load-add=keyring_file.so
  • 通过执行以下命令在运行时注册插件:
 INSTALL PLUGIN keyring_file SONAME 'keyring_file.so';

与 keyring 插件相关的系统变量

MySQL 8 提供了以下与 keyring 插件相关的系统变量:

  • keyring_file_data:一旦安装了插件,此系统变量可用于定义keyring_file插件用于存储安全数据的数据文件的路径。Keyring 操作是事务性的,因此此插件在写操作期间使用备份文件来处理回滚情况。在这种情况下,备份文件的命名约定与keyring_file_data系统变量中定义的相同,并带有.backup后缀。

总结

在本章中,我们首先概述了安全性,然后介绍了 MySQL 8 安全相关的功能。首先我们讨论了一些常见的安全问题,然后展示了如何分配权限以及如何在 MySQL 8 中管理访问控制。本章还涵盖了加密,以保护您的敏感数据。最后,我们介绍了一些重要的安全插件,这些插件对于在 MySQL 8 中实现安全性非常有用。

现在是时候转到我们的下一章了,在那里我们将为优化配置 MySQL 8。对于优化,我们将涵盖数据库的不同领域,如优化查询,优化表,优化缓冲和缓存等等。除了服务器配置,它还涵盖了如何为优化配置客户端。

第十二章:优化 MySQL 8

在上一章中,我们了解了安全性,这是任何生产级应用程序的重要方面。该章节以安全性介绍和识别常见安全问题开始。随后,该章节涵盖了 MySQL 8 中的访问控制机制、账户管理和加密。我们在该章节的后部分了解了各种 MySQL 8 安全插件。安全性是每个生产级应用程序的重要基准。这就是为什么上一章是重要的。

沿着类似的线路,为了开发高度优化的数据库,本章重点介绍了优化方法。它从 MySQL 8 优化的概述开始。它带领读者了解 MySQL 8 服务器和客户端的优化,优化数据库结构,以及优化常见查询和数据库表。在本章的后面部分,重点放在了缓冲和缓存技术上。

以下是要涵盖的主题列表:

  • MySQL 8 优化概述

  • 优化 MySQL 8 服务器和客户端

  • 优化数据库结构

  • 优化查询

  • 优化表

  • 利用缓冲和缓存

MySQL 8 优化概述

让我们从理解 MySQL 8 优化开始。优化是识别性能瓶颈并实施优化解决方案以克服这些问题的过程。MySQL 8 中的优化涉及在多个不同级别进行性能测量、配置和调整。对于管理员来说,在不同级别优化性能是一项重要任务,比如单个 SQL 查询、整个数据库应用程序、数据库服务器或分布式数据库服务器。在 CPU 和内存级别进行性能优化可以提高可伸缩性。它还允许数据库处理更复杂的查询而不会使数据库服务器变慢。

数据库的性能取决于多个因素。在数据库级别,这些因素可以是表、查询和配置。数据库服务器的启动和数据库查询执行是这些构造影响 CPU 或在硬件级别执行 I/O(输入/输出)操作的事件之一。这是 MySQL 8 数据库管理员的责任:确保硬件性能达到最佳水平。需要确保硬件以最大效率使用。在软件级别上,性能优化从学习通用规则和指南以及用时性能测量开始。逐渐地,我们了解各种数据库操作的内部。我们可以用 CPU 周期和 I/O 操作来衡量性能。为了获得最佳的数据库性能,我们可以在基本级别优化软件和硬件配置。在高级水平上,我们可以通过开发自定义存储引擎和硬件设备来改进 MySQL 本身,从而扩展 MySQL 生态系统。

优化数据库

使数据库以最佳速度运行的最重要因素是什么?答案是,基本的数据库设计。以下是数据库设计需要注意的检查表:

  • 数据库列必须是正确的数据类型。表必须为所需的目的具有适当的列。对数据库执行频繁操作的应用程序具有较少的列的许多表,而分析大量数据的应用程序具有许多列的有限表。

  • 正如我们在之前的章节中学到的,数据库索引在增强查询性能方面起着重要作用。因此,为了提高查询执行效率,正确的索引放置非常重要。

  • 我们在早期章节中讨论了数据库存储引擎,比如 MyISAM 或 InnoDB。对于每个单独的表使用适当的存储引擎是重要的。InnoDB 对于事务性数据库表更为合适,而 MyISAM 对于定义非事务性数据库表更为合适。存储引擎的选择在定义数据库的性能和可伸缩性方面起着至关重要的作用。

  • 在 MySQL 8 数据类型章节中,我们详细了解了行格式。对于每个表来说,拥有适当的行格式是非常重要的。行格式的选择取决于所选择的存储引擎。压缩表占用更少的磁盘空间,并且需要更少的磁盘 I/O 操作。对于 InnoDB 表,压缩适用于所有读写操作。相反,只有对于只读的 MyISAM 表才适用压缩。

  • MySQL 数据库支持多种锁定策略。锁定可以是表级别的,也可以是行级别的。应用程序必须使用适当的锁定策略。通过在适当的地方授予共享访问权限,可以实现并发运行数据库操作。此外,应该可以请求独占访问,以便可以执行关键的数据库操作,并保持数据完整性和优先级。在这种情况下,存储引擎的选择再次变得重要。InnoDB 存储引擎处理大多数锁定问题而无需用户参与。它允许更好的并发性,并减少了对代码的实验和调整的数量。

  • 内存区域必须使用正确的缓存大小。它应该足够大,以容纳频繁访问的数据,同时又不会过载物理内存并导致分页。InnoDB 缓冲池和 MyISAM 关键缓存是需要配置的主要内存区域。

对于新创建的表,MyISAM 是默认的存储引擎。在实际使用中,InnoDB 的高级性能特性意味着使用 InnoDB 存储引擎的表在操作繁重的数据库中表现优于 MyISAM 表。

优化硬件

增长是每个软件应用的本质。随着应用程序的增长,数据库也会增长。数据库在执行操作时变得越来越繁忙。在某一点上,数据库应用程序最终会达到硬件限制。管理员必须评估调整应用程序或重新配置服务器以避免这些问题的可能性。还应该评估是否增加更多的硬件资源会有所帮助。系统瓶颈通常来自以下来源:

  • 磁盘搜索:作为磁盘读取操作的一部分,查找数据需要时间。现代磁盘的平均查找数据时间通常低于 10 毫秒。因此,理论上应该是每秒 100 次搜索。随着技术的进步,新的磁盘在磁盘时间上有所改进,但很难为单个表进行优化。要优化搜索时间,需要将数据分布在多个磁盘上。

  • 磁盘读写:要从磁盘读取或写入数据,需要磁盘处于正确的位置。一个磁盘至少提供每秒 10 到 20MB 的吞吐量(吞吐量是每秒读取或写入的数据量)。因此,读取和写入吞吐量比搜索时间更容易优化,因为我们可以从多个磁盘并行读取。

  • CPU 周期:我们必须在主内存中处理数据以获得所需的结果。对于大表来说,内存的数量是最常见的限制因素。然而,对于小表来说,速度通常不是问题。

  • 内存带宽:在罕见的情况下,当 CPU 需要的数据超过 CPU 缓存内存的容量时,主内存带宽成为瓶颈。

优化 MySQL 8 服务器和客户端

本节重点介绍了 MySQL 8 数据库服务器和客户端的优化,从优化服务器开始,然后优化 MySQL 8 客户端实体。本节更适合数据库管理员,以确保多个服务器的性能和可伸缩性。这也将帮助准备脚本的开发人员(包括设置数据库)和运行 MySQL 进行开发和测试的用户,以最大限度地提高生产力。

优化磁盘 I/O

在本节中,我们将学习如何配置存储设备,以将更多和更快的存储硬件专用于数据库服务器。磁盘寻址(查找磁盘上正确位置以读取或写入内容)是一个主要的性能瓶颈。当数据量足够大以至于无法进行缓存时,磁盘寻址的问题就会显现出来。在大型数据库中,数据访问基本上是随机进行的,因此我们需要至少进行一次磁盘寻址操作来读取数据,以及进行多次磁盘寻址操作来写入数据。我们应该使用适当的磁盘来调节或最小化磁盘寻址时间。

为了解决磁盘寻址性能问题,可以增加可用磁盘轴承的数量,将文件符号链接到不同的磁盘,或者进行磁盘分区。以下是详细信息:

  • 使用符号链接:在使用符号链接时,我们可以为索引和数据文件创建 Unix 符号链接。在MyISAM表的情况下,符号链接从数据目录中的默认位置指向另一个磁盘。这些链接也可以进行条带化。这将改善寻址和读取时间。假设磁盘没有同时用于其他目的。符号链接不支持InnoDB表。但是,我们可以将InnoDB数据和日志文件放在不同的物理磁盘上。

  • 条带化:在条带化中,我们有许多磁盘。我们将第一个块放在第一个磁盘上,第二个块放在第二个磁盘上,依此类推。第N块放在(N % 磁盘数)磁盘上。如果条带大小完全对齐,正常数据大小将小于条带大小。这将有助于提高性能。条带化取决于条带大小和操作系统。在理想情况下,我们会使用不同的条带大小对应用程序进行基准测试。条带化的速度差异取决于我们使用的参数,如条带大小。性能差异还取决于磁盘数量。我们必须选择是要优化随机访问还是顺序访问。为了获得可靠性,我们可能决定设置条带化和镜像(RAID 0+1)。RAID代表独立驱动器冗余阵列。这种方法需要 2 x N驱动器来容纳N驱动器的数据。通过良好的卷管理软件,我们可以有效地管理这种设置。

  • 还有另一种方法。根据数据类型的重要性,我们可以改变 RAID 级别。例如,我们可以将非常重要的数据,如主机信息和日志,存储在 RAID 0+1 或 RAID N 磁盘上,而将较重要的数据存储在 RAID 0 磁盘上。在 RAID 的情况下,奇偶校验位用于确保存储在每个驱动器上的数据的完整性。因此,如果要执行太多写操作,RAID N 将成为一个问题。在这种情况下,更新奇偶校验位所需的时间较长。

  • 如果维护文件上次访问时间不重要,我们可以使用-o noatime选项挂载文件系统。此选项跳过文件系统上的更新,从而减少磁盘寻址时间。我们还可以使文件系统异步更新。根据文件系统是否支持,我们可以设置-o async选项。

使用 NFS 与 MySQL

在使用网络文件系统NFS)时,可能会出现各种问题,这取决于操作系统和 NFS 版本。以下是详细信息:

  • NFS 系统存在数据不一致性的问题。这可能是因为接收到的消息顺序不正确或网络流量丢失。我们可以使用带有hardintr挂载选项的 TCP 来避免这些问题。

  • MySQL 数据和日志文件如果放在 NFS 驱动器上,可能会被锁定并变得无法使用。如果多个 MySQL 实例访问相同的数据目录,可能会导致锁定问题。MySQL 的不正确关闭或断电是文件系统锁定问题的其他原因。最新版本的 NFS 支持咨询和基于租约的锁定,有助于解决锁定问题。但是,不建议在多个 MySQL 实例之间共享数据目录。

  • 必须了解最大文件大小限制,以避免任何问题。使用 NFS 2,客户端只能访问文件的较低 2 GB。NFS 3 客户端支持更大的文件。最大文件大小取决于 NFS 服务器的本地文件系统。

优化内存的使用

为了提高数据库操作的性能,MySQL 分配缓冲区和缓存内存。默认情况下,MySQL 服务器在具有 512 MB RAM 的虚拟机(VM)上启动。我们可以修改 MySQL 的默认配置,使其在有限内存系统上运行。

以下列表描述了优化 MySQL 内存的方法:

  • 用于缓存InnoDB数据的表、索引和其他辅助缓冲区的内存区域称为InnoDB缓冲池。缓冲池分为页面。页面包含多行。缓冲池实现为页面的链表,以实现高效的缓存管理。使用算法从缓存中删除很少使用的数据。缓冲池大小是系统性能的重要因素。innodb__buffer_pool_size系统变量定义了缓冲池大小。InnoDB在服务器启动时分配整个缓冲池大小。建议将系统内存的 50%至 75%用于缓冲池大小。

  • 使用MyISAM,所有线程共享关键缓冲区。key_buffer_size系统变量定义了关键缓冲区的大小。索引文件为服务器打开的每个MyISAM表打开一次。对于访问表的每个并发线程,数据文件只打开一次。为每个并发线程分配一个表结构,每个列的列结构以及一个 3 x N大小的缓冲区。MyISAM存储引擎为内部使用维护了一个额外的行缓冲区。

  • 优化器通过扫描来估计多行的读取。存储引擎接口使优化器能够提供有关记录缓冲区大小的信息。缓冲区的大小可以根据估计的大小而变化。为了利用行预取功能,InnoDB使用可变大小的缓冲能力。它减少了锁定和 B 树导航的开销。

  • 通过将myisam_use_mmap系统变量设置为 1,可以为所有MyISAM表启用内存映射。

  • 内存临时表的大小可以由tmp_table_size系统变量定义。堆表的最大大小可以使用max_heap_table_size系统变量定义。如果内存表变得太大,MySQL 会自动将表从内存转换为磁盘上。磁盘上临时表的存储引擎由internal_tmp_disk_storage_engine系统变量定义。

  • MySQL 配备了 MySQL 性能模式。这是一个监视 MySQL 低级执行的功能。性能模式通过根据实际服务器负载调整其内存使用来动态分配内存,而不是在服务器启动时分配内存。一旦分配了内存,直到服务器重新启动才会释放。

  • 服务器用于管理客户端连接的每个线程都需要特定的空间。堆栈大小由thread_stack系统变量控制。连接缓冲区由net_buffer_length系统变量控制。结果缓冲区由net_buffer_length控制。连接缓冲区和结果缓冲区以net_buffer_length字节开始,但根据需要扩大到max_allowed_packets字节。

  • 所有线程共享相同的基本内存。

  • 所有连接子句都在单次执行中执行。大多数连接可以在不使用临时表的情况下执行。临时表是基于内存的哈希表。包含BLOB数据和行长度较大的表存储在磁盘上。

  • 为每个请求分配一个读取缓冲区,该请求对表进行顺序扫描。读取缓冲区的大小由read_buffer_size系统变量确定。

  • 在以任意方式读取行时分配了一个随机读取缓冲区,以避免磁盘寻道。缓冲区大小由read_rnd_buffer_size系统变量确定。

  • 线程分配的内存在线程不再需要时被释放。释放的内存会被返回给系统,除非线程被放入线程缓存中。

  • 当执行FLUSH TABLESmysqladmin flush-table 命令时,MySQL 会立即关闭所有未使用的表。当当前线程执行完成时,它标记所有正在使用的表将被关闭。这会释放正在使用的内存。FLUSH TABLES仅在所有表关闭后才返回。

可以监视 MySQL 性能模式和内存使用情况的 sys 模式。在执行此操作之前,我们必须在 MySQL 性能模式上启用内存工具。可以通过更新性能模式setup_instruments表的ENABLED列来完成。以下是查询 MySQL 中可用内存工具的查询:

mysql> SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE '%memory%';

如果在启动时启用了内存工具,则可以确保在启动时对内存分配进行计数。

此查询将返回数百个内存工具。我们可以通过指定代码区域来缩小范围。以下是将结果限制为InnoDB内存工具的示例:

mysql> SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE '%memory/innodb%'; 
+-------------------------------------------+---------+-------+ 
|                    NAME                   | ENABLED | TIMED | 
+-------------------------------------------+---------+-------+ 
|     memory/innodb/adaptive hash index     |    NO   |   NO  | 
|     memory/innodb/buf_buf_pool            |    NO   |   NO  | 
| memory/innodb/dict_stats_bg_recalc_pool_t |    NO   |   NO  | 
|   memory/innodb/dict_stats_index_map_t    |    NO   |   NO  | 
| memory/innodb/dict_stats_n_diff_on_level  |    NO   |   NO  | 
|         memory/innodb/other               |    NO   |   NO  | 
|         memory/innodb/row_log_buf         |    NO   |   NO  | 
|          memory/innodb/row_merge_sort     |    NO   |   NO  | 
|             memory/innodb/std             |    NO   |   NO  | 
|      memory/innodb/trx_sys_t::rw_trx_ids  |    NO   |   NO  |
+-------------------------------------------+---------+-------+ 

以下是启用内存工具的配置:

performance-schema-instrument='memory/%=COUNTED'

以下是在性能模式中的memory_summary_global_by_event_name表中查询内存工具数据的示例:

mysql> SELECT * FROM performance_schema.memory_summary_global_by_event_name WHERE EVENT_NAME LIKE 'memory/innodb/buf_buf_pool'\G;

EVENT_NAME: memory/innodb/buf_buf_pool
COUNT_ALLOC: 1
COUNT_FREE: 0
SUM_NUMBER_OF_BYTES_ALLOC: 137428992
SUM_NUMBER_OF_BYTES_FREE: 0
LOW_COUNT_USED: 0
CURRENT_COUNT_USED: 1
HIGH_COUNT_USED: 1
LOW_NUMBER_OF_BYTES_USED: 0
CURRENT_NUMBER_OF_BYTES_USED: 137428992
HIGH_NUMBER_OF_BYTES_USED: 137428992

它通过EVENT_NAME对数据进行汇总。

以下是查询 sys 模式以按代码区域聚合当前分配的内存的示例:

mysql> SELECT SUBSTRING_INDEX(event_name,'/',2) AS        
  code_area, sys.format_bytes(SUM(current_alloc))        
  AS current_alloc        
  FROM sys.x$memory_global_by_current_bytes        
  GROUP BY SUBSTRING_INDEX(event_name,'/',2)        
  ORDER BY SUM(current_alloc) DESC; 
+---------------------------+---------------+ 
| code_area                 | current_alloc | 
+---------------------------+---------------+ 
| memory/innodb             | 843.24 MiB    | 
| memory/performance_schema | 81.29 MiB     | 
| memory/mysys              | 8.20 MiB      | 
| memory/sql                | 2.47 MiB      | 
| memory/memory             | 174.01 KiB    | 
| memory/myisam             | 46.53 KiB     | 
| memory/blackhole          | 512 bytes     | 
| memory/federated          | 512 bytes     | 
| memory/csv                | 512 bytes     | 
| memory/vio                | 496 bytes     | 
+---------------------------+---------------+

优化网络的使用

MySQL 数据库服务器打开网络接口以与客户端连接,并开始监听这些接口。连接管理器线程负责处理客户端连接请求。连接管理器线程还处理 Unix 平台上的套接字文件。连接管理器线程处理共享内存连接请求,另一个线程处理 Windows 系统上的命名管道连接请求。不会为服务器不监听的接口创建线程。

连接管理器线程为每个客户端连接分配一个线程。该线程对该客户端连接进行身份验证并处理请求处理。管理器线程首先在线程缓存中检查是否有可用于客户端连接的线程。如果缓存中没有可用线程,则创建一个新线程。一旦客户端请求被处理并且连接结束,为服务客户端连接创建的线程将被返回到线程缓存,除非缓存已满。

在这种线程连接模型中,线程的数量与当前连接的客户端数量相同。它也有缺点。当服务器需要扩展以处理比当前处理的连接数更多的连接时,线程的创建和处理变得昂贵。在这种线程连接模型中,每个线程都需要服务器和内核资源。

有些服务器变量可用于设置服务器以实现优化的网络使用。thread_cache_size是定义线程缓存大小的系统变量。线程缓存大小的默认值为 0。这意味着对于每个新连接,都需要设置一个线程,并在连接终止时进行处理。如果我们将thread_cache_size设置为 10,它将启用 10 个非活动连接线程进行缓存。当与其关联的客户端的连接终止时,线程连接变为非活动状态。

服务器可以处理的 SQL 语句的复杂性受线程堆栈大小的限制。MySQL 8 服务器可以使用--thread_stack=N启动,为每个线程设置N字节的堆栈大小。

设置线程缓存大小后,监视其影响变得至关重要。Threads_cachedThreads_created是用于查找线程缓存中的线程数以及因无法从缓存中获取而创建的线程数的状态变量。以下是查找服务器状态变量值的示例命令:

mysql> show global status;
+-----------------------------+--------+
| Variable_name               |  Value |
+-----------------------------+--------+
| Aborted_clients             |     0  |
| Aborted_connects            |     1  |
| Acl_cache_items_count       |     0  |
| Binlog_cache_disk_use       |     0  |
| Binlog_cache_use            |     0  |
| Binlog_stmt_cache_disk_use  |     0  |
| Binlog_stmt_cache_use       |     0  |
| Bytes_received              |    443 |
| Bytes_sent                  |    346 |
| Threads_cached              |     0  |
| Threads_connected           |     1  |
| Threads_created             |     1  |
| Threads_running             |     2  |
+-----------------------------+--------+

下面是过滤status变量的示例:

mysql> show status like '%Thread%';
+------------------------------------------+-------+
| Variable_name                            | Value |
+------------------------------------------+-------+
| Delayed_insert_threads                   |     0 |
| Performance_schema_thread_classes_lost   |     0 |
| Performance_schema_thread_instances_lost |     0 |
| Slow_launch_threads                      |     0 |
| Threads_cached                           |     0 |
| Threads_connected                        |     1 |
| Threads_created                          |     1 |
| Threads_running                          |     2 |
+------------------------------------------+-------+

优化锁定操作

如前几章所讨论的,MySQL 8 使用锁定机制来管理争用。当多个线程中同时执行查询尝试同时获取一个表时,就会发生争用。如果这些查询同时在表上执行,表数据将处于不一致状态。MySQL 8 支持两种类型的锁定:内部锁定和外部锁定。

MySQL 服务器内部由多个线程执行内部锁定,以管理对表内容的争用。这种类型的锁定完全由 MySQL 服务器执行,不涉及任何其他程序。那么,为什么它被称为内部锁定?在外部锁定的情况下,MySQL 服务器和其他程序锁定表文件,以决定哪些程序可以同时访问表。

以下是内部锁定的两种方法:

  • 行级锁定。

  • 表级锁定。

MySQL 中的行级锁定支持多个会话的同时写访问。这使得多用户和高并发应用程序成为可能。在单个表上执行多个并发写操作时,很可能会发生死锁。

为了避免这种死锁情况,锁定机制在事务开始时使用SELECT ... FOR UPDATE语句获取每组要修改的行的锁定。如果事务锁定多个表,MySQL 会在每个事务内以相同的顺序应用语句。InnoDB数据库引擎会自动检测死锁条件并回滚受影响的事务。考虑到这一点,死锁会影响性能。

在高度并发的系统中,死锁检测可能会导致减速。在这种情况下,禁用死锁检测会更有效。当发生死锁时,我们可以依靠innodb_lock_wait_timeout设置来进行事务回滚。使用innodb_deadlock_detect配置选项,我们可以禁用死锁检测。

以下是行级锁定的优点:

  • 当不同会话访问表中的不同行时,锁冲突的数量较少

  • 要回滚的更改数量较少

  • 可以长时间锁定单个表行

表级锁定由 MySQL 用于MyISAMMEMORYMERGE表。在表级锁定的情况下,MySQL 一次只允许一个会话更新这些表。通过表级锁定,这些存储引擎适用于只读或单用户应用程序。这些存储引擎在查询开始时一次性请求所有所需的锁,以避免任何死锁。它总是以相同的顺序锁定表。表级锁定的主要缺点是影响并发性。如果其他会话需要修改表,则必须等到并发数据更改语句完成。

表级锁定的优点如下:

  • 与行级锁定相比,它需要更少的内存

  • 当用于表的大部分时,速度很快,因为只需要一个锁

  • 如果频繁执行GROUP BY操作,速度很快

MySQL 授予表写锁的策略如下:

  1. 如果表上没有写锁,则在表上放置写锁

  2. 如果表已经有写锁,则将锁请求放入写锁队列

MySQL 授予表读锁的策略如下:

  1. 如果表上没有读锁,则在表上放置读锁

  2. 如果表已经有读锁,则将锁请求放入读锁队列

对表的更新比表的检索给予更高的优先级。当锁被释放时,写锁请求首先可用,然后是读锁请求。

以下是分析表锁争用的示例:

mysql> SHOW STATUS LIKE 'Table_locks%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Table_locks_immediate |     5 |
| Table_locks_waited    |     0 |
+-----------------------+-------+

MyISAM存储引擎天然支持多个并发插入,以减少读者和写者对表的争用。它允许MyISAM表在数据文件中间插入行。如果表在数据文件中间没有空闲块,则行将插入到文件末尾。这使得 MySQL 能够同时在同一表上执行INSERTSELECT查询。concurrent_insert是全局系统变量,控制MyISAM存储引擎允许执行并发INSERTSELECT语句的行为。如果将此系统变量设置为AUTO,则允许并发INSERTSELECT

如果无法进行并发插入,并且我们想在表tab1上执行多个INSERTSELECT操作,可以使用临时表temp_tab1来保存tab1表数据,并使用temp_tab1表中的行更新tab1表。以下是演示此场景的示例:

mysql> LOCK TABLES tab1 WRITE, temp_tab1 WRITE;
mysql> INSERT INTO tab1 SELECT * FROM temp_tab1;
mysql> DELETE FROM temp_tab1;
mysql> UNLOCK TABLES;

性能基准测试

在衡量性能时,我们必须考虑以下因素:

  • 在衡量单个操作或一组操作的速度时,重要的是在数据库工作负载繁重的情况下模拟场景以进行基准测试

  • 在不同的环境中,测试结果可能不同

  • 根据工作负载,某些 MySQL 功能可能无法提高性能

MySQL 8 支持衡量单个语句的性能。如果要衡量任何 SQL 表达式或函数的速度,则使用BENCHMARK()函数。以下是该函数的语法:

BENCHMARK(loop_count, expression)

BENCHMARK函数的输出始终为零。速度可以通过 MySQL 在输出中打印的行来衡量。以下是一个例子:

mysql> select benchmark(1000000, 1+1);
+-------------------------+
| benchmark(1000000, 1+1) |
+-------------------------+
|                       0 |
+-------------------------+
1 row in set (0.15 sec)

从上面的例子中,我们可以发现计算1+11000000 次所需的时间为0.15 秒

检查线程信息

有时,我们可能需要弄清楚 MySQL 服务器在做什么。因此,有必要找出进程列表。进程列表是 MySQL 服务器内当前正在执行的线程集合。

获取进程列表信息的来源如下:

  • SHOW [FULL] PROCESSLIST语句。以下是进程列表信息的示例:
mysql> show processlist;
+----+-----------------+-----------------+------+---------+--------+
| Id |       User      |       Host      |   db | Command |  Time  |
+----+-----------------+-----------------+------+---------+--------+
+------------------------+-----------------------+
|         State          |         Info          |
+------------------------+-----------------------+
+----+-----------------+-----------------+------+---------+--------+
| 4  | event_scheduler |      localhost  | NULL | Daemon  | 214901 |
+----+-----------------+-----------------+------+---------+--------+
|  8 |  root           | localhost:58629 | NULL |  Query  |     0  |
+----+-----------------+-----------------+------+---------+--------+
+------------------------+-----------------------+
| Waiting on empty queue |         NULL          |
+------------------------+-----------------------+
| starting               | show full processlist |
+------------------------+-----------------------+
  • SHOW PROFILE语句。

  • INFORMATION_SCHEMA PROCESSLIST表:

mysql> select * from information_schema.processlist;
+----+-----------------+-----------------+------+---------+--------+
| ID |       USER      |       HOST      |  DB  | COMMAND |  TIME  |
+----+-----------------+-----------------+------+---------+--------+
+------------------------+----------------------------------------------+
|         STATE          |                         INFO                 |
+------------------------+----------------------------------------------+
+----+-----------------+-----------------+------+---------+--------+
|  8 | root            | localhost:58629 | NULL | Query   |      0 |
+----+-----------------+-----------------+------+---------+--------+
|  4 | event_scheduler | localhost       | NULL | Daemon  | 215640 |
+----+-----------------+-----------------+------+---------+--------+
+------------------------+----------------------------------------------+
| executing              | select * from information_schema.processlist |
+------------------------+----------------------------------------------+
| Waiting on empty queue | NULL                                         |
+------------------------+----------------------------------------------+
  • mysqladmin processlist命令。

  • 性能模式线程表,阶段表和锁表。

我们必须能够查看用户线程的信息。需要PROCESS权限才能查看正在执行的线程的信息。要访问线程,不需要互斥访问。对 MySQL 服务器性能的影响较小。访问INFORMATION_SCHEMA.PROCESSLISTSHOW PROCESSLIST需要互斥访问,并会影响性能。线程还提供后台线程的详细信息。INFORMATION_SCHEMA.PROCESSLISTSHOW PROCESSLIST不提供有关后台线程的信息。

以下表格显示了每个进程列表条目中包含的信息:

信息 详情
Id 与线程关联的客户端连接标识符。
用户,主机 与线程相关的帐户。
db 线程的默认数据库或NULL
命令,状态 表示线程当前正在做什么。
时间 表示线程在当前状态下已经多久。
信息 包含线程执行的语句的信息。

以下是与一般查询处理相关的线程状态值:

  • 创建后:当线程创建表时发生,包括内部临时表

  • 分析:当线程正在计算MyISAM键分布时发生

  • 检查权限:当检查服务器是否具有执行 SQL 语句所需的权限时发生

  • 检查表:当线程执行表检查操作时发生

  • 清理:当线程处理完一个命令并释放内存时发生

  • 关闭表:当线程刷新更改的表数据到磁盘并关闭已使用的表时发生

  • 修改表:当服务器处理ALTER TABLE语句时发生

  • 创建索引:当线程处理MyISAM表的ALTER TABLE ... ENABLE KEYS时发生

  • 创建表:当线程正在创建表时发生

  • end:在结束之前发生,但在清理ALTER TABLECREATE VIEWDELETEINSERTSELECTUPDATE语句之前

  • 执行:当线程开始执行语句时发生

  • init:在ALTER TABLEDELETEINSERTSELECTUPDATE语句初始化之前发生

以下是复制主服务器线程的binlog转储线程中的常见状态列表:

  • 完成读取一个binlog;切换到下一个binlog

  • 主服务器已将所有binlog发送到从属服务器;等待更多更新

  • 向从属服务器发送binlog事件

  • 等待最终终止

以下是从属服务器 I/O 线程的常见状态列表:

  • 检查主服务器版本

  • 连接到主服务器

  • 将主事件排队到中继日志

  • 在失败的binlog转储请求后重新连接

  • 在失败的主服务器事件读取后重新连接

  • 在主服务器上注册从属服务器

  • 请求binlog转储

  • 等待轮到自己提交

  • 等待主服务器发送事件

  • 等待主服务器更新

  • 等待从属服务器退出的互斥

  • 等待从属服务器 SQL 线程释放足够的中继日志空间

  • 在失败的binlog转储请求后等待重新连接

  • 在失败的主服务器事件读取后等待重新连接

以下是从属服务器 SQL 线程的常见状态列表:

  • 杀死从属服务器

  • 在重放LOAD DATA INFILE之前制作临时文件(追加)

  • 在重放LOAD DATA INFILE之前制作临时文件(创建)

  • 从中继日志读取事件

  • 从属服务器已读取所有中继日志;等待更多更新

  • 等待来自协调器的事件

  • 等待从属服务器退出的互斥

  • 等待从属服务器工作线程释放挂起事件

  • 等待中继日志中的下一个事件

  • 等待MASTER_DELAY秒,直到主服务器执行事件

优化数据库结构

作为数据库管理员,我们必须寻找有效的方法来组织表模式、表和列。我们最小化 I/O,提前规划,并将相关项目放在一起,以调整应用程序代码,以保持性能高并增加数据量。通常从高效的数据库设计开始,这样可以更容易地编写高性能的应用程序代码。这也使得数据库在应用程序发展或重写时能够自我维持。

优化数据大小

为了最小化磁盘上的空间,我们应该开始设计数据库表。这会带来巨大的性能改进,因为它减少了要写入和从磁盘读取的数据量。较小的表通常需要较少的主内存,而在查询执行期间活动处理内容。表数据空间的任何减少都会导致需要更小的索引,这样可以更快地处理。

正如在 MySQL 8 数据类型章节中讨论的那样,MySQL 支持许多不同的存储引擎和行格式。我们可以决定每个表要使用的存储和索引方法。选择适当的表格式是一个很大的性能提升。

表列

我们应该为表列使用尽可能小的数据类型。这是最有效的方法。MySQL 支持专门的数据类型来节省内存和磁盘空间。例如,我们应该尽可能使用整数类型来获得较小的表。比较MEDIUMINTINTMEDIUMINT是一个更好的选择,因为它使用的空间比INT少 25%。

我们必须尽可能声明列为NOT NULL。这样可以更好地使用索引,并消除测试每个值是否为NULL的开销。这会导致更快的 SQL 操作。我们也可以节省每列一个位的存储空间。如果我们真的需要,应该使用NULLNULL值不应该作为每列的默认设置而被允许。

通过以下技术可以获得表的巨大性能提升并最小化存储空间需求:

行格式

默认情况下,在创建InnoDB表时使用DYNAMIC行格式。我们可以配置innodb_default_row_format以使用除DYNAMIC之外的行格式。我们还可以在CREATE TABLEALTER TABLE语句中明确指定ROW_FORMAT选项。

行格式包括COMPACTDYNAMICCOMPRESSED。它们减少了行存储空间,但在某些操作上增加了 CPU 使用。对于平均工作负载,受到缓存命中率和磁盘速度的限制,它会更快。如果受到 CPU 速度的限制,它会更慢。

行格式还优化了使用可变长度字符集时CHAR数据类型列的存储。使用REDUNDANT行格式,CHAR(N)列值占用字符集中最大字节长度的N倍。InnoDB存储引擎在NN倍字符集中最大字节长度范围内分配可变数量的存储空间。

如果在MyISAM表的情况下没有可变长度列,例如VARCHARTEXTBLOB,则使用固定大小的行格式。

索引

表的主索引必须尽可能短。这样可以轻松识别每一行。这也很有效。在InnoDB表的情况下,主键列在每个次要索引条目中都会被复制。如果我们有一个较短的主键,那么在有许多次要索引的情况下可以节省空间。

我们应该只创建那些提高查询性能的索引。索引改善信息检索,但会减慢插入和更新操作。必须要注意索引的性能影响来创建索引。如果需要通过组合列进行搜索来访问表,最好在组合列上创建复合索引,而不是在每个列上单独创建索引。最常用的列应该是索引的第一部分。如果在表的选定操作中经常使用许多列,建议将具有最多重复项的列作为索引中的第一列。这样可以更好地压缩索引。

如果一个长字符串列应该有一个唯一的前缀作为前几个字符,建议只索引前缀,使用 MySQL 对列的最左边部分进行索引的支持。更短的索引更受青睐,不仅因为它们需要更少的空间,而且因为它们在索引缓存中提供更多的命中,并且需要更少的磁盘查找。

连接

如果一个表经常被扫描,如果可行的话,将表拆分成两个表是有益的。这尤其适用于动态格式表。还可以使用较小的静态格式表,用于在扫描表时搜索相关行。

具有相同信息的列应该在不同的表中声明,具有相同的数据类型。这加快了基于匹配列的连接。

列名必须保持简单,以便在表之间使用相同的名称。这简化了连接查询。例如,在客户表中,我们应该使用name作为列名,而不是使用customer_name。为了使名称可移植到其他 SQL 服务器,我们应该保持列名短于 18 个字符。

规范化

表列中的数据必须保持非冗余,考虑规范化理论中的第三范式。如果列包含重复的长数值,例如名称或地址,最好分配唯一的 ID,并在多个较小的表中重复这些 ID。在搜索时,应该通过在连接子句中引用 ID 来使用连接查询。

在应用程序中,如果偏好速度而不是磁盘空间或使用多个数据副本的维护成本,建议复制信息或创建摘要表以获得更快的速度。一个例子是商业智能系统,从大型表中分析数据。在这种情况下,规范化规则并不严格遵循。

优化 MySQL 数据类型

以下是优化数字数据类型的指南:

  • 数字列必须优先于字符串列,用于存储唯一 ID 或其他可以表示为字符串或数字的值。它更快,占用更少的内存来传输和比较,因为与字符串相比,大的数字值存储在较少的字节中。

  • 从数据库中访问信息比从文本文件中访问信息更快。当使用数字数据时,这一点尤为真实。数据库中的信息以比文本文件更紧凑的格式存储。因此,它需要更少的磁盘访问。

以下是优化字符和字符串数据类型的指南:

  • 二进制排序顺序(逻辑顺序)应该用于更快的比较和排序操作。二进制运算符也可以在查询中使用二进制排序顺序。

  • 对于InnoDB表,当我们使用随机生成的值作为主键时,如果可行的话,应该以升序值作为前缀,例如日期和时间。在这种情况下,主键值在物理上更接近。InnoDB可以更快地插入或检索这些值。

  • 对于预计保存少于 8KB 数据的列值,应使用二进制 VARCHAR 数据类型而不是 BLOB。如果原始表没有任何 BLOB 列,GROUP BY 和 ORDER BY 子句会生成临时表。这些临时表可以使用 MEMORY 存储引擎。

  • 为了在运行查询时避免字符串转换,应尽可能在比较来自不同列的值时使用相同的字符集和排序声明列。

  • 如果表包含不经常在检索操作中使用的字符串列,应考虑将字符串列拆分为单独的表。在检索操作中,应根据需要使用外键进行连接查询。当检索任何行的值时,MySQL 读取包含该行的所有列的数据块。当我们保持行较小,仅包含经常使用的列时,可以使更多的行适应每个数据块。这些紧凑的表减少了内存使用和磁盘 I/O。

以下是优化 BLOB 数据类型的指南:

  • 检索和显示信息时,BLOB 列的性能要求可能会有所不同。因此,应考虑将 BLOB 特定表存储在不同的存储设备或单独的数据库实例中。例如,需要在大型顺序磁盘读取中检索 BLOB。因此,传统硬盘驱动器或 SSD 设备可能更适合需求。

  • 为了减少不使用 BLOB 列的查询的内存需求,对于具有多个列的表,应考虑将 BLOB 拆分为单独的表,并根据需要使用连接查询进行引用。

  • 如果表列是一个包含文本数据的大型 blob,应首先考虑压缩。如果整个表由存储引擎(如 InnoDB 或 MyISAM)压缩,就不应该使用这种技术。

优化多个表

我们学习了在某些情况下将表拆分为多个表以加快查询执行的技术。这种技术并不适用于所有情况,因为如果表的数量达到数千个,管理所有这些表的开销将成为另一个性能噩梦。

在本节中,我们将看到 MySQL 如何打开和关闭表。以下显示了如何在 MySQL 服务器上发现打开的文件:

> mysqladmin status
Uptime: 262200 Threads: 2 Questions: 16 Slow queries: 0 Opens: 111 Flush tables: 2 Open tables: 87 Queries per second avg: 0.000

MySQL 8 服务器是多线程的。可能会有许多客户端同时为一个表发出查询。MySQL 为每个并发会话独立打开表,以最小化同一表上具有不同状态的多个客户端会话的问题。这提高了性能,尽管需要额外的内存。每个打开 MyISAM 表的客户端需要一个额外的文件描述符。

table_open_cache 系统变量确定所有线程的打开表的数量。通过增加这个值可以增加 mysqld 需要的文件描述符的数量。max_connections 系统变量确定允许的最大同时客户端连接数。在某种程度上,这两个系统变量影响 MySQL 服务器可以保持打开的文件的最大数量。如果我们增加这两个值,可能会受到操作系统对每个进程打开文件数量的限制。

以下是 MySQL 关闭未使用表的情况:

  • 当表缓存已满且有线程尝试打开不在表缓存中的表时。

  • 当表缓存包含的条目多于 table_open_cache 系统变量中指定的条目,并且缓存中的表不再被任何线程使用时。

  • 当有人发出 FLUSH TABLES 语句或执行 mysqladmin flush-tables 或 mysqladmin refresh 命令时,表刷新操作会发生。MySQL 在此事件上关闭表。

MySQL 8 服务器在表缓存已满时使用以下过程来定位缓存条目:

  • 从最近最少使用的表开始释放未使用的表。

  • 如果需要打开一个新表,表缓存已满且无法释放表,则根据需要临时扩展缓存。如果在表缓存处于临时扩展状态时,表从已使用状态转换为未使用状态,则关闭该表并从表缓存中释放。

以下是查找打开表数量的示例:

mysql> SHOW GLOBAL STATUS LIKE '%Opened_Tables%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Opened_tables | 112   |
+---------------+-------+

在 MySQL 中使用内部临时表

在处理 SQL 语句时,MySQL 8 服务器在某些情况下创建临时内部表。以下是服务器创建临时表的条件:

  • UNION 语句

  • 使用TEMPTABLE算法、UNION聚合的视图

  • 派生表

  • 公共表达式

  • 为子查询或半连接材料化创建的表

  • 包含ORDER BYGROUP BY子句的语句

  • 带有DISTINCTORDER BY的语句

  • 使用SQL_SMALL_RESULT修饰符的查询

  • 从相同表中选择并插入的INSERT ... SELECT语句

  • 多表UPDATE语句

  • GROUP_CONCAT()COUNT(DISTINCT)表达式

EXPLAIN语句可用于确定语句是否需要临时表。EXPLAIN语句有限制。它不会指示语句是否需要为派生或材料化临时表创建临时表。

Created_tmp_tables状态变量跟踪在内部内存中创建的临时表的数量。当 MySQL 服务器创建临时表时,它会增加Created_tmp_tables状态变量中的值。Created_tmp_disk_tables是另一个状态变量,用于跟踪在磁盘上创建的表的数量。

根据查询条件,服务器阻止在内存中使用临时表。在这种情况下,服务器在磁盘上创建表。以下是一些实例:

  • 如果表具有BLOBTEXT

  • 如果SELECT列表中的字符串列的最大长度大于 512 字节,并且使用了UNIONUNION ALL

  • 如果SHOW COLUMNSDESCRIBE语句使用BLOB作为列的类型

在以下条件下,UNION会在不创建临时表的情况下进行评估:

  • 联合是UNION ALL而不是UNIONUNION DISTINCT

  • 没有全局ORDER BY子句

  • SELECT查询中,联合不在顶层查询块

优化查询

与表类似,数据库查询是任何数据库的最关键元素。应用程序使用查询与数据库交互。查询也称为可执行的 SQL 语句。本节重点介绍了改进查询执行性能的技术。

优化 SQL 语句

SQL 语句用于执行任何数据库应用程序的核心逻辑。无论语句是直接通过解释器发出还是通过 API 在后台提交,都无关紧要。本节概述了改进数据库中读写数据的 SQL 操作性能的准则。

SELECT语句在数据库中执行所有查找操作。考虑到SELECT语句的频率,调整这些语句变得至关重要。调整技术必须应用于像CREATE TABLE...AS SELECTINSERT INTO...SELECTDELETE语句中的WHERE子句等构造。

以下是优化查询的主要考虑因素:

  • 为了优化SELECT ... WHERE查询,首先要检查的是是否可以添加索引。我们应该在SELECT查询的WHERE子句中使用的列上添加索引。这将加快评估、过滤和检索结果。策略应该是构建一小组可以加速应用程序中许多相关查询的索引。这也避免了浪费的磁盘空间。

  • 索引对于引用使用连接和外键的不同表的查询非常重要。EXPLAIN语句可用于确定在SELECT语句执行中使用了哪些索引。

  • 下一步应该是隔离和调整查询的部分;例如,需要大量时间的函数调用。根据查询的结构,函数调用可以针对表中的每一行或结果集中的每一行进行。

  • 查询中全表扫描的次数必须最小化,特别是对于大表。

  • 应定期使用ANALYZE TABLE语句来保持表统计信息的最新。优化器提供了构建高效查询执行计划所需的信息。

  • 如果基本指导方针不能解决性能问题,应该通过阅读EXPLAIN计划并调整索引、WHERE子句、连接子句等内部细节来调查查询。

  • 应避免将查询转换为难以理解的方式,特别是当优化器自动执行一些相同的转换时。

  • InnoDB缓冲池、MyISAM键缓存和 MySQL 查询缓存必须有效地用于重复查询,以便在第一次后从内存中检索结果后更快地运行。内存区域的大小和属性必须进行调整,因为 MySQL 用于缓存。

  • 如果查询使用缓存内存区域运行得更快,我们仍然应该进一步优化它,以便需要更少的缓存内存。这使应用程序更具可扩展性,使应用程序能够处理更多的同时用户、更大的请求等,而不会出现性能下降。

  • 如果查询的速度受其他会话同时访问表的影响,我们应该处理锁定问题。

以下是优化WHERE子句的指导方针。这些优化适用于SELECTDELETEUPDATE查询中的WHERE子句:

  • 不必要的括号应该被移除。以下是一个括号移除的例子:
 ((a AND b) AND c OR (((a AND b) AND (c AND d)))) 
        -> (a AND b AND c) OR (a AND b AND c AND d)
  • 常量折叠是在编译时而不是运行时评估值的过程。如果我们已经将一个常量值赋给一个变量,然后在表达式中使用该变量,我们应该使用常量值。以下是一个常量折叠的例子:
 (a<b AND b=c) AND a=5 
        -> b>5 AND b=c AND a=5
  • 由于常量折叠,我们应该移除常量条件。以下是一个常量条件移除的例子:
 (B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6) 
        -> B=5 OR B=6

优化索引

索引的基本用途是快速查找具有特定列值的行。如果索引不存在,MySQL 将从第一行开始并读取整个表以查找所有匹配的行。这需要更多时间,取决于表有多大。如果索引存在于适当的列中,MySQL 能够快速确定在数据文件中寻找的位置,而不必查看整个表数据。

以下是 MySQL 使用索引的操作列表:

  • 基于WHERE子句快速查找匹配行。

  • 在选择多个索引以消除考虑的行时,MySQL 使用行数最少的索引(最具选择性的索引)。

  • 如果表具有复合索引,优化器使用索引的最左前缀来查找行。例如,在一个有三列索引的表中(在 col1、col2、col3 上),优化器可以查找具有索引搜索能力的行(col1)、(col1,col2)和(col1,col2,col3)。

  • MySQL 在使用连接从其他表中提取行时使用索引。如果索引声明为相同类型和大小,MySQL 可以在列上有效地使用它们。当声明为相同大小时,VARCHARCHAR被视为相同。

  • MySQL 还使用索引查找索引列key_col最小值(MIN())最大值(MAX())。预处理器检查是否在所有关键部分上使用WHERE key_part_N = constant来优化它。

  • 还可以优化查询以检索值而不需要查询数据行。(覆盖索引是为查询提供所有结果的索引。)如果查询仅使用某个索引中包含的表中的那些列,所选值将从索引树中获取。这将更快地检索值。

查询执行计划

MySQL 优化器考虑优化技术,以有效地执行查询中涉及的查找,具体取决于表、列和索引的细节,以及WHERE子句中的条件。查询也可以在不读取大表上的所有行的情况下执行。SQL 连接也可以在不比较每个行的组合的情况下执行。查询执行计划是 MySQL 优化器选择执行最有效查询的一组操作。它也被称为EXPLAIN计划。作为管理员,目标是识别查询执行计划的方面,以确定查询是否经过优化。

EXPLAIN语句用于确定查询执行计划。以下是EXPLAIN语句提供的信息集:

  • EXPLAIN语句与SELECTDELETEINSERTUPDATEREPLACE语句一起工作。

  • EXPLAIN与 SQL 语句一起使用时,MySQL 显示有关查询执行计划的 MySQL 优化器的信息。这意味着 MySQL 解释了语句执行的过程。它包括有关表如何连接以及连接顺序的信息。

  • 如果EXPLAIN显示了命名连接中语句执行的执行计划,而不是可解释的 SQL 语句,则使用FOR CONNECTION连接 ID。

  • EXPLAINSELECT语句显示了额外的执行计划信息。

  • EXPLAIN还可用于检查涉及分区表的查询。

  • EXPLAIN支持FORMAT选项,可用于选择输出格式。TRADITIONAL格式以表格格式显示输出。这是默认的格式选项。JavaScript 对象表示JSON)格式选项以 JSON 格式生成信息。

根据EXPLAIN语句的输出,可以确定在表中添加索引的位置,以便语句执行更快。还可以确定优化器是否按优化顺序连接表。使用SELECT STRAIGHT_JOIN开始语句,而不仅仅是SELECT,以向优化器提供使用与SELECT语句中命名表的顺序相对应的连接顺序的提示。由于STRAIGHT_JOIN禁用半连接转换,它可能会阻止索引的使用。

优化器跟踪是另一个工具,用于查找有关查询执行的信息。优化器跟踪可能提供与EXPLAIN不同的信息。优化器跟踪的格式和内容会根据版本而有所不同。

以下表格显示了EXPLAIN语句的输出格式:

JSON 名称 细节
id select_id SELECT标识符
select_type None SELECT类型
table table_name 输出行的表
partitions partitions 匹配的分区
type access_type 连接类型
possible_keys possible_keys 可能选择的索引
key key 实际选择的索引
key_len key_length 所选键的长度
ref ref 与索引进行比较的列
rows rows 预计要检查的行数
filtered filtered 表条件过滤的行的百分比
Extra None 附加信息

参考:dev.mysql.com/doc/refman/8.0/en/explain-output.html#explain-output-column-table

优化表

数据库表是任何数据库的最基本构建块。在本章节的这一部分,我们将专注于优化表。本节提供了通过表优化技术来改善性能的详细指南。

InnoDB 表的优化

在生产环境中,InnoDB存储引擎是首选,特别是在可靠性和并发性很重要的情况下。它是 MySQL 表的默认存储引擎。本节重点介绍了优化InnoDB表的数据库操作。

以下是优化InnoDB表的指南:

  • 应考虑使用OPTIMIZE TABLE语句来重新组织表并压缩浪费空间,一旦数据达到稳定大小或表增加了数十兆字节。对于重新组织的表来说,执行完整的表扫描需要更少的磁盘 I/O。

  • OPTIMIZE TABLE语句会复制表中的数据并重建索引。这是有益的,因为它可以改善索引内数据的打包,并减少磁盘上表空间的碎片。收益可能会有所不同,取决于每个表中的数据。在某些情况下,收益可能是显著的,而在其他情况下则不是。收益也可能随着时间的推移而减少,直到进行下一次表优化。如果表很大或正在重建的索引不适合缓冲池,操作可能会很慢。

  • InnoDB表中,长主键会浪费大量磁盘空间,应该避免使用。

  • InnoDB表中,应优先选择VARCHAR数据类型,而不是CHAR数据类型来存储可变长度字符串,或者用于预期包含NULL值的列。CHAR(N)列始终占用N个字符来存储数据,即使值为NULL。较小的表更适合适应缓冲池并减少磁盘 I/O。

  • 考虑为大表或包含大量重复文本或数字数据的表使用COMPRESSED行格式。

MyISAM 表的优化

对于只读或读取频率较高的数据,或低并发操作,MyISAM存储引擎最适合。这是因为表锁限制了同时进行更新的能力。在本节中,重点将放在优化要在MyISAM表上执行的查询上。

以下是加快MyISAM表查询的指南:

  • 避免在频繁更新的MyISAM表上执行复杂的SELECT查询。这样可以避免由于写入者和读取者之间的争用而导致的表锁定问题。

  • MyISAM存储引擎支持并发插入。如果表数据文件中间没有空闲块,我们可以在其他线程从表中读取数据的同时向其中INSERT新行。如果重要的是能够进行并发读写操作,可以考虑使用该表来避免删除行。另一个选择是在删除行后执行OPTIMIZE TABLE来对表进行碎片整理。这种行为可以通过设置concurrent_insert系统变量来进行控制或修改。

  • 对于频繁更改的MyISAM表,应避免所有可变长度列。如果表中包含至少一个可变长度列,则使用动态行格式。

  • myisamchk --sort-index --sort-records=1命令可用于对索引进行排序。它还根据索引对数据进行排序。如果我们有唯一索引,并且希望按照索引的顺序读取所有行,这样可以使查询运行更快。第一次对大表进行这种排序会花费很长时间。

  • 如果我们通常按照expression1expression2等顺序检索行,可以使用ALTER TABLE ... ORDER BY expression1, expression2,..,这样会提高性能,如果在对表进行大量更改后使用此选项。

内存表的优化

MySQL 的MEMORY表应该只用于经常访问的非关键数据,而且是只读且很少更新的数据。应该对应用程序进行基准测试,以确认额外的性能是否值得冒失去数据的风险。

我们应该检查针对每个表的查询类型,以获得MEMORY表的最佳性能。我们还应该指定每个相关索引的使用类型。它可以是 B 树索引或哈希索引。在CREATE INDEX语句上使用USING BTREEUSING HASH子句。

利用缓冲和缓存

这一部分关注使用缓冲和缓存技术来提高性能。

InnoDB 缓冲池优化

InnoDB存储引擎维护一个称为缓冲池的存储区域,用于在内存中缓存数据和索引。了解InnoDB缓冲池的工作原理非常重要,以便利用它将经常访问的数据保留在内存中。这是 MySQL 调优的一个重要方面。

以下是改进InnoDB缓冲池性能的一般指导方针:

  • 在理想情况下,缓冲池的大小应该足够大,同时留出足够的内存供服务器上的其他进程运行,而不会过度分页。有了更大的缓冲池,InnoDB的功能也更多,比如内存数据库。在这种情况下,它只需从磁盘读取数据一次,然后在后续读取中从内存中访问数据。

  • 我们可以考虑将缓冲池分成多个部分,用于具有大内存的 64 位系统。这样可以在并发操作期间最大程度地减少内存争用。

  • 经常访问的数据应该保留在内存中。

  • 可以控制InnoDB何时以及如何执行预读请求,以异步方式将页面预取到缓冲池中。InnoDB使用两种预读算法来提高 I/O 性能。线性预读可以根据顺序访问缓冲池中的页面来预测可能很快需要的页面。随机预读可以根据缓冲池中的页面来预测可能需要的页面,而不考虑页面读取的顺序。innodb_read_ahead_threshold配置参数控制线性预读的灵敏度。我们可以通过将innodb_random_read_ahead设置为ON来启用随机预读。

  • innodb_buffer_pool_read_ahead确定读入InnoDB缓冲池的页面数。innodb_buffer_pool_read_ahead_evicted确定后台预读线程读入缓冲池的页面数,随后被查询访问。innodb_buffer_pool_read_ahead_rnd确定InnoDB发起的随机预读次数。

MyISAM 键缓存

MyISAM存储引擎采用了一种被许多数据库管理系统支持的策略,以最小化磁盘 I/O。MyISAM使用缓存机制将最常访问的表块保留在内存中。

  • 为索引块维护了一个称为键缓存的特殊结构。最常用的索引块被放置在包含多个块缓冲区的结构中。

  • MySQL 依赖于本机操作系统文件系统缓存来存储数据块。

key_buffer_size系统变量确定关键缓存的大小。如果设置为零,则不使用关键缓存。如果key_buffer_size值太小而无法分配最小顺序的块缓冲区,则也不使用关键缓存。关键缓存结构中的所有块缓冲区大小相同。这个大小可以等于、大于或小于表索引块的大小。通常情况下,这两个值中的一个是另一个的倍数。

当需要从任何表索引块访问数据时,服务器首先检查它是否在关键缓存的某个块缓冲区中可用。如果数据可用,服务器将从关键缓存中访问数据,而不是从磁盘上访问。如果数据不可用,服务器选择包含不同表索引块的缓存块缓冲区,并通过复制所需的表索引块来替换其中的数据。一旦新的索引块在缓存中可用,就可以访问索引数据。

MySQL 服务器遵循最近最少使用(LRU)策略。根据这个策略,在选择要替换的块时,它选择最近最少使用的索引块。关键缓存模块包含 LRU 链中的所有使用的块(一个特殊列表)。列表按使用时间排序。当访问块时,它是最近使用的。块被放置在列表的末尾。当需要替换块时,列表开头的块是最近最少使用的。因此,顶部的块成为首选的驱逐候选。

如果选择用于替换的块已被修改,则该块被视为脏块。在替换之前,块内容被刷新到它们来自的表索引中。

根据以下条件,线程可以同时访问关键缓存缓冲区:

  • 未被更新的缓冲区可以被多个会话访问。

  • 正在更新的缓冲区会导致需要等待更新完成的会话使用它

  • 只要会话是独立的并且不相互干扰,多个会话可以发起请求,导致缓存块替换。

通过这种方式,对关键缓存的共享访问显著提高了性能。

总结

在本章中,我们详细学习了优化 MySQL 8 组件的技术。该章节从优化的基础知识开始,包括硬件和软件优化指南。我们还讨论了 MySQL 8 服务器和客户端、数据库结构、查询和表的优化指南。我们还涵盖了属于不同存储引擎的表的优化,如MyISAMInnoDBMEMORY。我们学习了工具,如EXPLAINEXPLAIN ANALYZE,以了解查询执行计划。在本章的后部分,我们学习了缓冲和缓存技术以提高性能。

现在是时候转到下一章了。下一章将重点介绍扩展 MySQL 8 的技术。该章节将详细介绍 MySQL 8 插件,这些插件有助于扩展默认的 MySQL 8 功能。它还将解释调用这些插件的服务。该章节将讨论添加新功能、调试和移植方法。这对数据库管理员来说将是一个重要的章节。

第十三章:扩展 MySQL 8

在上一章中,我们学习了如何优化 MySQL 8。我们还了解了需要进行哪些配置才能实现优化,以及如何利用缓存和缓冲进行优化。我们逐步进行了用例研究,以实现以下组件的优化:

  • 优化 MySQL 8 服务器和客户端

  • 优化数据结构

  • 优化查询

  • 优化表

在本章中,我们将学习如何扩展 MySQL 8。我们将检查允许扩展的 MySQL 8 组件,并了解如何根据特定业务需求自定义 MySQL 8。在扩展 MySQL 8 之前,您将了解基本组件以及用于扩展 MySQL 8 的 MySQL 插件 API 的功能。以下是本章涵盖的主题列表:

  • 扩展 MySQL 8 的概述

  • 扩展插件并使用服务来调用它们

  • 添加新功能

  • 调试和移植

扩展 MySQL 8 的概述

在这一部分,您将学习如何根据自己的需求扩展 MySQL 8 中最令人兴奋的主题之一。在尝试扩展 MySQL 8 之前,您应该充分了解 MySQL 8 的几个组件。以下是扩展 MySQL 8 重要的组件列表:

  • MySQL 8 内部

  • MySQL 8 插件 API

  • MySQL 8 组件和插件的服务

  • 向 MySQL 8 添加新功能

  • 调试和移植 MySQL 8

MySQL 8 内部

在开始处理 MySQL 代码之前,您应该了解一些事项。要贡献或跟踪 MySQL 开发,您应该按照系统或操作系统平台的源代码分发安装说明。源代码包括内部文档,这对于从开发人员的角度了解 MySQL 的内部工作非常重要。您还可以订阅 internals 邮件列表,网址为lists.mysql.com/internals,其中包括从事 MySQL 代码工作的人员,您还可以讨论与 MySQL 开发相关的主题或发布补丁:

  • MySQL 8 线程:MySQL 服务器创建线程,例如连接管理器线程、信号线程、读写线程(如果使用 InnoDB 存储引擎)、调度器线程来处理连接以及复制和事件处理。

  • MySQL 8 测试套件:MySQL 8 提供了包含在 Unix 源代码分发中的测试系统,以帮助用户和开发人员对 MySQL 代码进行回归测试。您还可以使用测试框架编写自己的测试用例。

MySQL 8 插件 API

MySQL 8 通过插件 API 提供对服务器组件本身的支持。插件可以在服务器启动期间加载,也可以在运行时加载和卸载;无需重新启动服务器。该 API 非常通用,不限制插件在限制方面可以做什么,而是允许它们做的比内置组件更多。该 API 支持用于组件的接口,例如存储引擎插件、全文解析器插件、服务器扩展等。

插件接口利用 MySQL 8 数据库中的plugin表来存储有关已安装插件的信息,通过使用INSTALL PLUGIN语句来永久安装插件。在 MySQL 8 安装过程中,将创建plugin表。对于单个服务器调用,也可以使用--plugin--load选项安装插件,但使用此选项不会将已安装的插件记录到plugin表中。

MySQL 8 还为客户端插件提供支持 API,用于特定目的,例如通过不同的身份验证方法启用客户端的服务器连接。

MySQL 8 组件和插件的服务

MySQL 8 服务器插件可以访问和启动服务器插件服务;同样,服务器组件也可以访问和请求组件服务。MySQL 8 插件服务接口通过公开服务器功能来补充 API 插件,插件可以调用这些功能。以下是插件服务的特点:

  • 这些服务使插件能够使用普通函数调用访问服务器代码,并且还可以调用用户定义的函数。

  • 这些服务是可移植的,可以在多个平台上运行

  • 服务提供版本支持,防止插件和服务之间的不兼容性

  • 这些服务还支持测试插件服务

MySQL 提供了两种插件和组件的服务类型,如下所示:

  1. 锁定服务:提供两个级别的锁定服务接口,即 C 级别和 SQL 级别。接口在锁定命名空间、锁定名称和锁定模式属性上工作。

  2. 密钥环服务:密钥环服务提供了一个接口,用于安全存储内部服务器组件和插件以便以后检索的敏感信息。

向 MySQL 8 添加新函数

您可以向 MySQL 8 添加自己的函数,可以使用三种支持的函数类型之一来完成。新函数可以像调用内置函数ABS()一样调用,而不管您新增了哪种函数类型都是如此。以下是 MySQL 8 中支持的三种新函数类型的列表:

  1. 通过用户定义函数UDF)接口添加函数。

  2. 将函数添加为本机(内置)MySQL 函数。

  3. 通过创建存储函数添加函数。

调试和移植 MySQL 8

将 MySQL 8 移植到其他操作系统目前受到许多操作系统的支持;支持的操作系统列表提供在www.mysql.com/support/supportedplatforms/database.html。如果您添加了新的端口并且在新的端口上遇到问题,您可以使用 MySQL 8 的调试。

根据您遇到问题的位置,可以以不同的方式开始调试——可能是在 MySQL 服务器中或在 MySQL 客户端中。根据问题的位置,您可以分别在 MySQL 服务器或客户端开始调试,并从DBUG包中获取帮助来跟踪程序的活动。

扩展插件并使用服务调用它们

在本节中,您将了解插件 API、其接口和 MySQL 服务如何相互交互,并在 MySQL 8 中提供扩展。插件在 MySQL 8 架构中也被视为组件,因此您可以使用它们提供可插拔的功能。插件 API 和插件服务接口有以下区别:

  • 插件 API 使服务器能够使用插件。服务器启动和调用插件,因此插件可以扩展服务器的功能或注册自己以接收服务器处理通知。

  • 插件服务接口允许插件调用服务器代码。插件启动和调用服务函数,以便许多插件可以利用相同的服务器功能,而无需为功能单独实现。

编写插件

要创建一个插件库,必须提供所需的描述符信息,因为它指定了库文件包含哪些插件。还必须为指定的每个插件编写接口函数。

每个服务器插件必须具有通用描述符,提供信息给插件 API,并具有特定类型的描述符,提供指定插件类型的接口信息。用于指定通用描述符的结构对所有插件类型都是相同的,而特定类型的描述符可以根据插件行为或功能的要求而变化。服务器插件接口允许插件公开系统变量和状态。

客户端插件的架构与服务器端插件略有不同。例如,每个插件必须具有描述符信息,但通用和特定类型的描述符之间没有单独的区分。

插件可以用 C 或 C++或任何其他可以使用 C 调用约定的语言编写。插件是动态加载和卸载的,因此操作系统必须动态支持您动态编译调用应用程序的地方。特别是对于服务器插件,这意味着mysqld必须动态链接。

由于我们无法确定哪个应用程序将使用插件,因此客户端插件编写者应避免对调用应用程序的符号的依赖。

以下是支持的插件创建类型,可以实现多种功能:

  • 身份验证

  • 密码验证和强度检查

  • 协议跟踪

  • 查询重写

  • 安全钥匙存储和检索

  • 存储引擎

  • 全文解析器

  • 守护进程

  • INFORMATION_SCHEMA

  • 半同步复制

  • 审计

组件和插件服务

您可以通过查看 MySQL 8 源代码分发的include/mysql/components和相应的services目录来识别 MySQL 提供的组件服务和函数。

同样,您可以通过查看 MySQL 8 源代码分发的include/mysql目录和相关文件来识别 MySQL 提供的插件服务和函数,如下所示:

  • plugin.h文件包括services.h文件,services.h文件包含其中所有可用的特定服务头文件

  • 特定服务头文件的名称将以service_xxx.h的形式命名

以下是 MySQL 8 中可用的组件服务列表:

  • component_sys_variable_registercomponent_sys_variable_unregister:用于注册和注销系统变量

  • log_builtinslog_builtins_string:用于日志组件服务

  • mysql_service_udf_registrationmysql_service_udf_registration_aggregate:用于在组件和插件中启用标量和聚合用户定义函数的注册和注销

  • mysql_string:用于字符串服务 API

  • pfs_plugin_table:用于动态性能模式表操作

以下是 MySQL 8 中可用的插件服务列表:

  • get_sysvar_source:用于检索系统变量设置

  • locking_service:用于使用 C 语言和 SQL 级接口实现锁定,具有命名空间、名称和模式属性

  • my_plugin_log_service:用于将错误消息写入日志

  • my_snprintf:用于字符串格式化,以保持输出在各个平台上的一致性

  • status_variable_registration:用于注册状态变量

  • my_thd_scheduler:用于线程调度器选择

  • mysql_keyring:用于钥匙存储服务

  • mysql_password_policy:用于密码强度和验证检查

  • plugin_registry_service:用于访问组件注册表和相关服务

  • security_context:用于管理线程安全上下文

  • thd_alloc:用于内存分配

  • thd_wait:用于报告休眠或停滞

现在,您对插件服务和组件服务有了清楚的了解。MySQL 8 提供以下类型的服务来支持插件和组件服务:

  1. 锁定服务

  2. 钥匙存储服务

以下部分详细介绍了两种类型的服务。

锁定服务

锁定服务接口提供两个级别:C 级别和 SQL 级别。该接口在锁命名空间、锁名称和锁模式属性上工作。C 语言接口可作为插件服务从用户定义的函数或服务器插件调用,SQL 级别接口用作一组用户定义的函数,映射到调用服务例程。

以下是锁定接口的特征:

  • 锁命名空间、锁名称和锁模式是锁的三个属性。

  • 通过形成锁命名空间和锁名称的组合来识别锁。

  • 锁模式可以是读或写。读锁是共享的,而写锁是排他的。

  • 锁名称和命名空间最多可以有 64 个字符,并且必须是非 NULL 和非空字符串。

  • 锁名称和命名空间被视为二进制字符串,因此比较将区分大小写。

  • 提供了获取和释放锁的函数,不需要特殊权限来调用这些函数。

  • 在不同会话中的锁获取调用期间检测死锁;选择一个调用者并终止其锁获取请求,优先选择持有读锁的调用者会话,而不是持有写锁的会话。

  • 典型的会话可以通过单个锁获取调用请求多个锁获取。它为请求提供原子行为,并且如果所有锁都被获取则成功,如果任何锁获取失败则失败。

  • 同一锁标识符的多个锁可以由会话获取,其中锁实例可以是写锁、读锁或读写锁的混合。

  • 通过显式调用释放锁函数或者如果会话终止则隐式释放会话中获取的锁。

  • 在给定命名空间中释放的所有锁都在会话内一起释放。

密钥环服务

密钥环服务提供了一个接口,用于安全地存储内部服务器组件和插件以便以后检索的敏感信息。在密钥环服务中,来自密钥库本身的记录由数据组成——密钥和可以访问密钥的唯一标识符。标识符由以下两部分组成:

  1. key_id:名称。以mysql_开头的key_id或密钥 ID 值由 MySQL 服务器保留。

  2. user_iduser_id代表每个会话的有效user_id。如果没有用户上下文,则可以为NULL,并且该值不一定需要是实际的user,而是取决于应用程序。

以下是密钥环服务函数的常见特征:

  • 每个函数返回 1 表示失败,返回 0 表示成功

  • user_idkey_id参数的唯一组合指示在密钥环中使用哪个密钥

  • 通过key_type参数值提供有关密钥的附加信息,例如其预期用途、加密方法或其他信息

  • 在密钥环服务函数中,用户名称、密钥 ID、类型和值被视为二进制字符串,因此比较是区分大小写的

以下是可用的密钥环服务函数列表:

  • my_key_generate(): 正如其名称所示,它生成给定类型和长度的新随机密钥,并存储在密钥环中。该函数由参数key_iduser_idkey_typekey_len组成,以及以下函数语法:
 bool my_key_generate(const char *key_id, const char*key_type, 
          const char *user_id, size_t key_len)
  • my_key_fetch(): 对参数值进行解密并从密钥环中检索密钥及其类型。该函数由参数key_iduser_idkey_typekeykey_len组成,以及以下函数语法:
 bool my_key_fetch(const char *key_id, const char **key_type, 
          const char* user_id, void **key, size_t *key_len)
  • my_key_remove(): 从密钥环中删除关联的密钥。该函数由参数key_iduser_id组成,以及以下函数语法:
 bool my_key_remove(const char *key_id, const char* user_id)
  • my_key_store(): 对参数值进行混淆并将密钥存储在密钥环中。该函数包括参数key_iduser_idkey_typekeykey_len,以及以下函数语法:
 bool my_key_store(const char *key_id, const char *key_type, 
          const char* user_id, void *key, size_t key_len)

添加新函数

可以在 MySQL 8 中添加任何三种支持的类型的新函数。每种类型都有其自己的优点和缺点。应该根据函数的要求决定在何处以及应该添加或实现哪种类型的函数。

以下是 MySQL 8 中支持的三种新函数类型的列表,我们将在下一节中进行讨论:

  1. 通过用户定义函数接口添加函数。

  2. 将函数添加为本机(内置)MySQL 函数。

  3. 通过创建存储函数添加函数。

用户定义函数接口的特点

用户定义的函数接口为用户目的函数提供独立的功能。

MySQL 用户定义函数接口提供以下功能和能力:

  • 函数可以接受整数、字符串或实数值的参数,并且可以返回相同类型的值

  • 可以定义简单函数以一次操作一行,也可以是聚合函数以操作行组

  • 函数被提供信息以使它们能够检查传递的参数的类型、名称和数量

  • 在将参数传递给给定函数之前,还可以要求 MySQL 强制参数

  • 如果函数导致任何错误或返回NULL,可以进行指示

添加新的用户定义函数

UDF 函数必须用 C 或 C++编写,并且底层操作系统必须支持动态加载行为。有一个文件sql/udf_example.cc,定义了五个 UDF 函数,并包含在 MySQL 源分发中。分析该文件将让您了解 UDF 的调用约定如何工作。用户定义的函数相关符号和数据结构在include/mysql_com.h文件中定义,并且该文件包含在mysql.h头文件中。

UDF 中包含的典型代码在运行服务器中执行,因此在编写 UDF 代码时适用所有约束-服务器代码。当前适用的约束可能在服务器升级时得到修订,并且这可能导致需要重写 UDF 代码,因此在编写 UDF 代码时要小心。

为了使用 UDF,必须动态链接mysqld。对于在 SQL 语句中使用的任何函数,必须有底层的 C 或 C++函数。遵循将 SQL 和 C/C++代码分开的约定,其中大写的xxx()表示 SQL 函数调用,而小写的xxx()表示 C/C++函数调用。

当您使用 C++时,将您的 C 函数封装如下所示:extern "C" { ... },以确保您的 C++函数名称在完成的用户定义函数中可读。

要编写和实现接口函数名XXX(),必须有主函数xxx(),并且还需要从以下功能中实现一个或多个功能:

  • xxx(): 生成函数结果的主函数

  • xxx_init(): 主函数xxx()的初始化函数,可用于以下任何目的:

  • 检查要传递给XXX()的参数数量

  • 在调用主函数时,使用声明验证参数类型

  • 在需要时为主函数分配内存

  • 结果的最大长度验证

  • 为结果设置最大的十进制数限制

  • 指定结果是否可以为NULL

  • xxx_deinit(): 代表主函数的去初始化,并在需要时释放主函数的初始化函数分配的内存

在 MySQL 8 中,聚合 UDF 按以下顺序处理:

  1. 调用xxx_init()以便它分配所需的内存来存储结果信息。

  2. 按照GROUP BY函数指定的表/结果进行排序。

  3. 调用xxx_clear()来重置每个新组中第一行的当前聚合值。

  4. 调用xxx_add()来将参数添加到当前的聚合值。

  5. 调用xxx()来获取按组更改或在处理最后一行后的聚合数据结果。

  6. 重复步骤 3-5,直到处理完所有指定/结果行。

  7. 调用xxx_deinit()来释放为 UDF 分配的任何内存。

所有函数必须是线程安全的,包括主要函数以及其他所需的附加函数,以及初始化和去初始化函数。

与上述顺序类似,以下是在添加新的用户定义函数时需要注意的重要方面:

  • UDF 参数处理

  • UDF 返回值和错误处理

  • UDF 编译和安装

  • UDF 安全预防措施

添加新的本机函数

为了使用包含新本机函数的修改源代码进行编译,需要源分发文件。当迁移到另一个 MySQL 版本时,也需要重复这一过程。

如果在语句中引用新的本机函数,并且还要在从服务器上复制,确保每个从服务器都有新的本机函数可用,否则当尝试调用新的本机函数时,从服务器上的复制将失败。

以下是在sql目录的源分发文件中添加新本机函数的步骤:

  1. 函数的子类需要在item_create.cc中添加:
  • 在参数数量固定的情况下,子类应该从Create_func_arg0Create_func_arg1Create_func_arg2Create_func_arg3中创建,具体取决于您的本机函数需要的参数数量。您可以参考Create_func_absCreate_func_uuidCreate_func_pow类。

  • 在参数数量可变的情况下,子类应该从Create_native_func中创建。您可以参考Creat_func_concat类。

  1. 在 SQL 语句中引用的函数名称需要在item_create.cc中注册,通过向数组添加以下行:static Native_func_registry func_array[]
  • 如果需要,可以为同一个函数注册多个名称。您可以参考LOWERLCASE的行,它们是Create_func_lcase的别名。
  1. 根据您的函数返回类型是字符串还是数字,在item_func.h文件中,需要声明从Item_str_funcItem_num_func继承的类。

  2. 根据您的函数在item_func.cc文件中定义为字符串还是数字函数,需要添加以下声明之一:

 double Item_func_newname::val()
 longlong Item_func_newname::val_int() 
        String *Item_func_newname::Str(String *str)
    • 如果您的对象是从任何标准项继承的,那么您可能只需要定义前面的函数之一,因为父对象将处理其他函数。您可以参考定义了val()函数的Item_str_func类,该函数在::str()函数的返回值上执行atof()函数。
  1. 如果函数是非确定性的 - 也就是说,如果对于固定给定的参数,返回的结果在不同的调用中会有所不同 - 那么需要在项目构造函数中包含以下语句,表示函数结果不应被缓存:current_thd->lex->safe_to_cache_query=0;

  2. 您可能还需要为您的本机函数定义以下对象函数:

  • void Item_func_newname::fix_length_and_dec()

  • 函数至少应包括对给定参数的max_length计算。

  • 如果您的主要函数不能返回任何NULL值,还应该设置maybenull = 0

  • 您可以参考Item_func_mod::fix_length_and_dec

所有函数都必须具有线程安全性。在没有受到互斥保护的情况下,您不应该在函数中使用任何静态或全局变量。

调试和移植

将 MySQL 8 移植到其他操作系统目前受到许多操作系统的支持。最新支持的操作系统列表提供在www.mysql.com/support/supportedplatforms/database.html。如果您已添加或尝试添加新的端口(受支持的平台)并遇到问题,您可以使用 MySQL 8 的调试来查找并解决问题。

首先,在调试mysqld之前,您应该让测试程序mysys/thr_lock工作。这可以确保您的线程安装有远程工作的可能性!

根据您遇到问题的位置,可以在 MySQL 服务器或 MySQL 客户端中开始调试。根据问题的位置,您可以分别在 MySQL 服务器或 MySQL 客户端中开始调试,并且可以从DEBUG包中获得程序活动的跟踪帮助。

MySQL 源代码包括使用Doxygen编写的内部文档,这对于理解 MySQL 的开发者视角非常有帮助。

在本节中,您将看到以下主题的详细信息:

  • 调试 MySQL 服务器

  • 调试 MySQL 客户端

  • DBUG

调试 MySQL 服务器

如果您在 MySQL 中使用了一些非常新的功能并遇到了一些问题——比如服务器崩溃——您可以尝试使用--skip-new选项运行mysqld。此选项告诉 MySQL 服务器禁用所有新的可能不安全的功能。

mysqld无法启动的情况下,请验证my.cnf文件,因为它们可能会干扰设置!您可以使用mysqld --print-defaults选项检查my.cnf中的参数,然后使用--no-defaults选项启动mysqld以避免使用它们。

如果mysqld开始占用内存或 CPU 或挂起,您可以检查mysqladmin processlist status,并查找是否有某个查询执行时间过长。如果您遇到性能问题或问题,并且新客户端无法连接,您可以使用mysqladmin -i10 process list status。

您还可以使用调试命令mysqladmin,它会将有关查询使用、内存使用和正在使用的锁的信息转储到 MySQL 日志文件中,并且可以为您解决一些问题。如果您没有为调试编译 MySQL,此命令也可以提供一些有用的信息。

如果您遇到表变慢的问题,您应该尝试使用myisamchkOPTIMIZE_TABLE来优化表。如果有任何慢查询,您应该使用EXPLAIN来查找并修复查询中的问题。

在调试 MySQL 8 时,以下是需要考虑的重要领域:

  • 为调试编译 MySQL:在出现非常特定的问题时,您可以尝试调试 MySQL。为此,您必须使用-DWITH_DEBUG=1选项配置 MySQL。调试配置会自动启用大量额外的安全检查功能,以监视mysqld的健康状况。

  • 创建跟踪文件:您可以尝试通过创建跟踪文件来找到问题。为此,您必须使用带有调试支持的mysqld编译。然后,您可以使用--debug选项,在 Unix 上将在/tmp/mysqld.trace中添加跟踪日志,在 Windows 上将在\mysqld.trace中添加跟踪日志。

  • 使用 PDB 和 WER 创建 Windows 崩溃转储:程序数据库文件包含在 ZIP 存档调试二进制文件和测试套件中,作为 MySQL 的单独分发。这些文件提供了有关 MySQL 安装问题的调试信息。它们可以与 WinDbg 或 Visual Studio 一起使用来调试mysqld

  • 在 gdb 下调试 mysqld:当您遇到线程问题或mysqld服务器在ready for connections之前挂起时,可以使用此选项。

  • 使用堆栈跟踪:当mysqld意外死机时,您也可以使用此选项找出问题。

  • 使用服务器日志查找mysqld错误原因:您可以通过启用常规查询日志来使用此选项 - 在此之前,您应该使用myisamchk实用程序检查所有表,并从日志中验证是否存在任何问题。

  • 创建一个测试用例,如果您遇到表损坏:当您遇到表损坏问题时使用此选项,仅适用于MyISAM表。

调试 MySQL 客户端

在您在 MySQL 客户端遇到问题的情况下,您也可以在 MySQL 客户端内部进行调试,但是为了这样做,您必须具有集成的调试包。您需要配置 MySQL 以启用 MySQL 客户端中的调试,使用-DWITH_DEBUG=1

在运行 MySQL 客户端之前,您应该设置环境变量MYSQL_DEBUG如下:

shell> MYSQL_DEBUG=d:t:O,/tmp/client.trace 
shell> export MYSQL_DEBUG

这将使 MySQL 客户端在 Unix 的/tmp/client.trace或 Windows 的\client.trace中生成一个跟踪文件。

在您自己的客户端代码出现问题的情况下,您可以尝试使用已知可用的客户端运行您的查询来连接服务器。为此,您应该以调试模式运行mysqld

shell> mysql --debug=d:t:O,/tmp/client.trace

如果您想要为问题发送错误报告,此跟踪将提供有用的信息。

在某些情况下,如果您的客户端在一些合法的代码处崩溃,您可以检查您的mysql.h头文件是否与您的 MySQL 库文件匹配。这是一个非常常见的错误,使用旧的mysql.h文件从旧的 MySQL 安装中与新的 MySQL 库一起,导致此问题。

DBUG 包

Fred Fish 最初创建了 MySQL 服务器和大多数 MySQL 客户端的DBUG包。如果 MySQL 配置为调试模式,此包使得生成有关程序正在执行的信息的跟踪文件成为可能。

有调试选项可供指定,以便使用DBUG包将特定信息写入跟踪文件。它可以在程序调用中使用-#[debug_options]选项或--debug[=debug_options]选项。

大多数 MySQL 程序将使用默认值,如果指定了--debug-#选项,而没有指定debug_options值。服务器默认值是 Windows 上的d:t:i:O,\mysqld.trace和 Unix 上的d:t:i:o,/tmp/mysqld.trace。此默认值的效果如下所列:

  • d:启用所有调试宏的输出

  • t: 跟踪函数调用和退出

  • i:在跟踪文件中添加PID到输出行

  • o,/tmp/mysqld.traceO,\mysqld.trace:在 Unix 和 Windows 中分别设置调试输出文件

在大多数情况下,对于大多数客户端程序,无论平台如何,都应使用默认的debug_optionsd:t:o,/tmp/myprogram_name.trace。对于 Windows,请使用\myprogram_name.trace

以下是一些在 shell 命令行上指定的调试控制字符串的示例:

--debug=d:t 
--debug=d:f,main,subr1:F:L:t,20 
--debug=d,input,output,files:n 
--debug=d:t:i:O,\\mysqld.trace

摘要

在本章中,您学习了如何通过自定义函数和 API 扩展 MySQL 8。您还了解了编写函数以及插件服务和 API 的相关特性。您现在可以创建自己的函数或插件,满足特定的业务需求,并在函数不符合预期时进行调试和测试。

在下一章中,您将学习 MySQL 8 的最佳实践和基准测试。您将了解基准测试和用于基准测试的工具。您还将学习 MySQL 8 一些非常重要功能的最佳实践,例如 memcached、复制、数据分区和索引。

第十四章:MySQL 8 最佳实践和基准测试

在上一章中,你学习了如何扩展 MySQL 8。它涵盖了许多有趣的方面,比如扩展插件并通过 MySQL 8 中的服务调用它们,向 MySQL 8 添加和调试新功能等等。在本章中,我们将介绍 MySQL 8 的最佳实践,这是一个备受期待的版本,承诺解决之前版本的许多不足之处,并具有令人兴奋的新功能。MySQL 8 承诺不仅是一个独立的数据库,而且还将在包括大数据解决方案在内的各个领域发挥重要作用。我们将学习如何在 MySQL 8 中实施最佳实践以最大程度地利用其功能。基准测试将进一步增进我们的理解。

我们将在本章中涵盖以下主题:

  • MySQL 基准测试和工具

  • memcached 的最佳实践

  • 复制的最佳实践

  • 数据分区的最佳实践

  • 查询和索引的最佳实践

由于突出的优化和改变,MySQL 8 直接从 MySQL 5.7 的发布中提升了版本。MySQL 8 将不再有文件限制,这之前限制了你可以拥有的数据库数量。还有许多令人兴奋的功能,我们在《MySQL 8 简介》第一章中已经介绍了。MySQL 8 现在可以在一个数据库中存储数百万个表。它还可以快速修改表。

我很兴奋地阅读这一章,因为 MySQL 8 的最佳实践不仅影响着你的数据库性能、可伸缩性、安全性和可用性,而且整体上还会暴露出你的系统对最终用户的表现。这是我们的最终目标,不是吗?让我们看看在我们的测试实验室中得出的一些基准测试结果,这些结果肯定会让你眼前一亮:

MySQL 基准测试和工具

我们已经研究了 MySQL 8 中的各种新功能和改进。这让我们更加兴奋,因为性能一直是我们渴望的。由于 MySQL 8 尚未普遍可用,Oracle 还没有发布其基准测试结果。我们没有等待它这样做,而是在一些领域进行了自己的分析。

MySQL 的最佳配置实践是锦上添花;没有樱桃,蛋糕看起来就不完整。除了配置,基准测试还帮助我们验证并找到瓶颈并解决它们。让我们看看一些特定领域,这将帮助我们了解配置和性能基准测试的最佳实践。

资源利用

IO 活动、CPU 和内存使用是你不应该错过的东西。这些指标帮助我们了解系统在进行基准测试和扩展时的表现。它还帮助我们推导每个事务的影响。

延长基准测试时间

我们可能经常希望快速查看性能指标;然而,确保 MySQL 在较长时间的测试中表现一致也是一个关键因素。有一些基本的东西可能会影响性能,比如内存碎片化、IO 的退化、数据积累后的影响、缓存管理等等。

我们不希望我们的数据库因为清理垃圾而重新启动,对吧?因此,建议长时间运行基准测试以验证稳定性和性能。

复制生产设置

让我们在一个生产复制环境中进行基准测试。等等!在复制环境中禁用数据库复制,直到我们完成基准测试。搞定!我们得到了一些不错的数字!

经常发生的情况是,我们并没有完全模拟我们将在生产环境中配置的所有内容。这可能会证明是代价高昂的,因为我们可能无意中在一个可能在生产中产生不利影响的环境中进行基准测试。在进行基准测试时,在你的复制环境中复制生产设置、数据、工作负载等。

吞吐量和延迟的一致性

吞吐量和延迟是相辅相成的。重点应该放在吞吐量上;然而,随着时间的推移,延迟可能是需要注意的。在InnoDB的早期版本中注意到了性能下降、缓慢或停顿。自那时以来它已经有了很大的改进,但由于可能有其他情况取决于你的工作负载,所以始终关注吞吐量和延迟是很好的。

Sysbench 可以做更多

Sysbench 是一个很好的工具,可以模拟你的工作负载,无论是成千上万的表、事务密集型、内存中的数据等等。它是一个很好的模拟工具,并为你提供了很好的表示。

虚拟化世界

我想保持简单;裸金属与虚拟化并不相同。因此,在进行基准测试时,根据你的环境来衡量你的资源。如果你进行比较,你可能会惊讶地看到结果的差异。

并发

大数据坐落在沉重的数据工作负载上;高并发是重要的。MySQL 8 在每个新版本中都在扩展其最大 CPU 核心支持,根据你的需求和硬件资源优化并发应该得到关注。

隐藏的工作负载

不要错过在后台运行的因素,比如用于大数据分析的报告、备份以及在基准测试时进行的即时操作。这些隐藏的工作负载或过时的基准测试工作负载的影响可能会让你的日子(和夜晚)变得痛苦。

你的查询的神经

哎呀!我们错过了优化器吗?还没有。优化器是一个强大的工具,它会读取你的查询的神经并提供建议。这是我在对查询进行更改之前在生产环境中使用的工具。当你需要优化复杂的查询时,它就是救星。

这些是我们应该注意的几个领域。现在让我们看一下我们在 MySQL 8 上进行的一些基准测试,并将它们与 MySQL 5.7 上的基准测试进行比较。

基准测试

首先,让我们从所有的InnoDB表中获取所有列名。以下是我们执行的查询:

SELECT t.table_schema, t.table_name, c.column_name
FROM information_schema.tables t,
information_schema.columns c
WHERE t.table_schema = c.table_schema
AND t.table_name = c.table_name
AND t.engine='InnoDB';

以下图表显示了当有四个实例时,MySQL 8 的性能比 MySQL 5.7 快一千倍:

接着,我们还进行了基准测试,以查找静态表元数据。以下是我们执行的查询:

SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT 
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA LIKE 'chintan%';

以下图表显示了 MySQL 8 的性能比 MySQL 5.7 快大约 30 倍:

这让我们渴望更详细地了解一下。因此,我们想做最后一次测试,以找到动态表元数据。

以下是我们执行的查询:

SELECT TABLE_ROWS
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA LIKE 'chintan%';

以下图表显示了 MySQL 8 的性能比 MySQL 5.7 快大约 30 倍:

MySQL 8.0 为表带来了巨大的性能改进。扩展到一百万个表,这是许多大数据需求的需要,现在是可以实现的。一旦 MySQL 8 可用于一般用途,我们期待着更多的基准测试被正式发布。

现在让我们看看我们的下一个主题,这将让你的生活更轻松。这一切都是关于考虑 memcached 的最佳实践。

memcached 的最佳实践

现在可以使用InnoDB memcached 插件进行多个get操作,这将真正有助于提高读取性能。现在可以在单个 memcached 查询中获取多个键值对。频繁的通信流量也已经最小化,因为我们可以一次获取多个数据。

对于 memcached 配置最佳实践,您应该考虑的关键要点现在将要介绍。

资源分配

对于 memcached 的内存分配不应超过可用物理内存,也不应忽视其他可能使用内存的资源。如果我们过度分配内存,memcached 有很高的机会从交换空间中分配内存。这可能导致在插入或提取值时出现延迟,因为交换空间存储在磁盘上,比内存慢。

操作系统架构

由于操作系统架构为 32 位,需要谨慎。我们知道,在 32 位操作系统架构中,资源分配存在限制。

同样,具有 32 位操作系统架构的 4 GB RAM 的 memcached 不应设置超过 3.5 GB RAM,因为这可能会导致性能异常和崩溃。

默认配置

一些关键的默认配置参数应该根据您的需求进行调整:

  • 内存分配:默认情况下,这是 64 MB;而应该根据您的需求和测试重新配置

  • 连接:默认情况下,这是 1,024 个并发连接;而应该根据您的需求和测试重新配置

  • 端口:默认情况下,这在端口11211上监听;而应该出于安全考虑监听另一个端口

  • 网络接口:默认情况下,这接受来自所有网络接口的连接;为了安全起见,应该限制

最大对象大小

您应该考虑配置最大对象大小,默认情况下为 1 MB。但是,它可以增加到 128 MB。这纯粹取决于您要存储的数据类型,因此应允许其最大对象大小。允许将开销数据存储在 memcached 中可能会产生不利影响,因为可能有更多的数据需要检索,这可能会导致失败。

积压队列限制

积压队列限制是关于如果达到允许的连接限制,应该保留在 memcached 队列中的连接数。理想情况下,您允许的连接数应该配置得足够满足大部分需求。当 memcached 出现意外的高负载时,积压队列限制可能会有所帮助。理想情况下,它不应超过总连接数的 20%,否则可能会影响系统从 memcached 中获取信息的体验,因为会出现严重延迟。

大页支持

在支持大内存页的系统上,您应该启用 memcached 来利用它们。大页支持有助于分配大数据块来存储数据,并且还减少了使用这种方法的缓存未命中调用的数量。

敏感数据

在 memcached 中存储敏感数据可能构成安全威胁,因为可以访问 memcached 的人可以查看敏感信息。您显然应该采取预防措施来限制 memcached 的曝光。您还可以在将敏感信息存储在 memcached 之前对其进行加密。

限制曝光

Memcached 没有许多内置的安全功能。其中一项措施涉及在所需边界内暴露 memcached 访问。如果您的应用服务器需要与 memcached 通信,它只允许从该服务器访问 memcached,借助系统防火墙规则,如 IP Tables 或类似技术。

故障转移

Memcached 没有良好的故障转移技术。建议您配置应用程序以使其能够故障转移到不可用节点,并在另一个实例中重新生成数据。最好至少配置两个 memcached 以避免由于实例不可用而导致的故障。

命名空间

您可以利用 memcached 提供的命名空间,在将数据存储在 memcached 中之前基本上添加前缀。当您有多个应用程序与 memcached 通信时,这将会有所帮助。这是有帮助的,使用一些基本的命名约定原则,您可以得出一个解决方案。如果有存储名和姓的数据,您可以分别使用前缀,例如 FN 和 LN。这将帮助您轻松地从应用程序中识别和检索数据。

缓存机制

开始利用 memcached 中的缓存的最简单方法之一是使用两列表;您可以利用 memcached 提供的命名空间,基本上添加前缀。第一列将是主键,并且数据库架构应该是通过主键映射以及唯一约束的地址要求的唯一标识符。如果您想通过组合多个列值来获得单个项目值,您应该确保选择适当的数据类型。

具有单个WHERE子句的查询可以轻松映射到 memcached 查找,同时在查询本身中使用=IN运算符。在使用多个WHERE子句或解析复杂操作(如<>LIKEBETWEEN)的情况下,memcached 可以帮助您解决挑战。建议您使用传统的 SQL 查询将这些复杂操作添加到数据库中。

将整个对象缓存在 memcached 中,而不是选择缓存来自 MySQL 8 的单个行,这将是有益的。例如,对于博客网站,应该在 memcached 中缓存博客端的整个对象。

Memcached 常规统计信息

为了帮助您更好地了解 memcached 的统计信息,我们将提供健康和性能的概述。下表显示了 memcached 返回的统计信息及其含义:

用于定义每个统计信息值的术语是:

  • 32u:32 位无符号整数

  • 64u:64 位无符号整数

  • 32u:32u:由冒号分隔的两个 32 位无符号整数

  • 字符串:字符字符串

统计 数据类型 描述
pid 32u memcached 实例的进程 ID。
uptime 32u 该 memcached 实例的正常运行时间(以秒为单位)。
time 32u 当前时间(作为时期)。
version 字符串 该实例的版本字符串。
pointer_size 字符串 该主机指定的指针大小(32 位或 64 位)。
rusage_user 32u:32u 该实例的总用户时间(秒:微秒)。
rusage_system 32u:32u 该实例的总系统时间(秒:微秒)。
curr_items 32u 该实例存储的当前项目数量。
total_items 32u 在该实例的生命周期内存储的项目总数。
bytes 64u 该服务器用于存储项目的当前字节数。
curr_connections 32u 当前打开连接的数量。
total_connections 32u 服务器运行以来打开的连接总数。
connection_structures 32u 服务器分配的连接结构的数量。
cmd_get 64u 检索请求(get操作)的总数。
cmd_set 64u 存储请求(set操作)的总数。
get_hits 64u 请求并找到的键的数量。
get_misses 64u 请求但未找到的项目数。
delete_hits 64u 已删除并找到的键的数量。
delete_misses 64u 删除但未找到的项目数。
incr_hits 64u 增加并找到的键的数量。
incr_misses 64u 增加但未找到的项目数。
decr_hits 64u 已减少并找到的键的数量。
decr_misses 64u 减少但未找到的项目数。
cas_hits 64u 已比较并交换并找到的键的数量。
cas_misses 64u 已比较并交换但未找到的项目数量。
cas_badvalue 64u 已比较并交换,但比较(原始)值与提供的值不匹配的键的数量。
驱逐 64u 从缓存中移除的有效项目数量,以释放内存供新项目使用。
bytes_read 64u 该服务器从网络中读取的总字节数。
bytes_written 64u 该服务器发送到网络的总字节数。
limit_maxbytes 32u 该服务器允许用于存储的字节数。
threads 32u 请求的工作线程数量。
conn_yields 64u 连接的让步次数(与-R 选项相关)。

参考:dev.mysql.com/doc/refman/8.0/en/ha-memcached-stats-general.html

这些是一些有用的项目,应该随时掌握 memcached 的最佳实践。现在是时候继续前进,看看复制的最佳实践了。

复制的最佳实践

MySQL 8 在复制方面取得了一些重大进展。MySQL 8 主要关注的是扩展性、性能和安全性,以及数据的最高完整性,这有望成为大数据领域的一个改变者。

组复制中的吞吐量

组复制基本上负责在大多数组复制成员同时确认接收事务后提交事务。如果写入的总数不超过组复制成员的容量,这将导致更好的吞吐量。如果容量规划不合适,受影响的成员会出现滞后,与组内其他成员相比。

基础设施规模

基础设施规模是性能和最佳实践清单的常见成功因素。如果基础设施规模不合适或在组复制的节点之间不均匀,可能会对复制基本拓扑产生不利影响。在考虑所需的组件吞吐量时,应考虑每个组件。

恒定的吞吐量

实现恒定的吞吐量是一个很好的成功因素。如果您开始经历影响组复制中其他成员的工作负载,可能是您的主服务器不断接受额外的工作负载并滞后,然后在耗尽所有资源之前可能返回到可接受的水平。此外,您可以实施一个排队方法,可以防止资源耗尽,并且只允许您将工作负载传递给根据容量预定义的成员。

在考虑排队方法时,您不能允许队列呈指数增长。这会影响最终用户,因为数据更新会有延迟。但是,您需要根据自己的需求和业务需求来决定,以实现系统的恒定吞吐量。

矛盾的工作负载

基本上,组复制旨在允许来自组中任何成员的更新。基于行重叠的事务回滚会检查每个事务;其余的将被提交并发送以更新到组中的其他成员。如果同一行上频繁发生多次更新,可能会导致多次回滚。您可能会遇到循环情况,其中一个服务器更新,请求其他服务器更新,并且同时另一个服务器已经为同一行更新。这将导致回滚。

为了防止这种情况,您可以让组中的最后一个成员应用更新,然后再进行下一个成员。您可以从先前执行更新的相同节点路由类似的更新,以防止循环回滚条件的发生。

写扩展性

通过分享写操作来分配您的写入工作负载,这可能会导致更好的吞吐量和更好的写入性能可伸缩性。这将取决于您在系统中预期的矛盾工作负载。当您的高峰工作负载是可以共享负载的情况下,这是有帮助的。通常情况下,如果您对写入可伸缩性进行了良好的容量规划,您将看到微不足道的改进。

请参考下面的图表:

您会注意到,通过多主分发,您的负载具有更好的吞吐量。它还考虑了多主配置中的组大小。

数据分区的最佳实践

一般来说,分区是将任何东西逻辑上分成多个子组,以便每个子组可以独立识别并组合成一个单一分区。

现在让我们学习不同的分区方法以及分区如何在存在大型数据表的情况下提供帮助。

对于任何组织来说,以一种能够提供可伸缩性、性能、可用性和安全性的方式存储数据非常重要。例如,在一个访问量很高的电子商务商店中,经常会有成千上万的订单。因此,为了维护日常订单交付并显示当前订单的仪表板,需要查询显示过去五年的订单表;使用当前数据执行这个过程将需要很长时间。在这里,历史订单数据用于分析用户行为或趋势,但这将需要在有限的数据集上执行。

有各种方法可以实现高可用性、可伸缩性和高性能架构的最佳解决方案;关键因素是分区。在数据库中,每个表中的数据存储在物理文件组中。因此,将这些数据表从单个文件组分割为多个文件组可以减少文件大小,并帮助我们创建一个可伸缩和高性能的数据库。

以下是在数据库中使用分区的关键好处:

  • 可伸缩性:由于数据将在多个分区之间共享,服务器可以配置为使用多个节点,并且可以在多个节点之间配置分区。这样做将消除任何硬件限制,并允许数据库大规模扩展以容纳大量数据。

  • 高性能:由于数据存储在多个分区中,每个查询将在数据的一小部分上执行。例如,在一个订单历史超过两年的电子商务商店中,要获取本月下的订单列表只需要检查一个分区而不是整个订单历史,从而减少查询执行时间。为了在多个分区上获取查询,我们也可以并行运行,从而减少从数据库获取数据的总时间。

  • 高可用性:在分区中,数据被分割到多个文件组中。每个文件组在逻辑上连接在一起,但可以独立访问和处理。因此,如果一个文件组或分区损坏,或者服务器中的一个节点失败,那么我们不会失去对整个表的访问权限,而只是数据库的一部分不可用,从而消除了系统故障的可能性,使您的系统高度可用。

  • 安全性:可能有些表中的数据需要高安全措施以避免数据窃取或数据泄漏。通过分区,您可以为一个或多个分区提供额外的安全性,以避免任何安全问题,从而提高数据的可访问性和安全性。

一般来说,分区是将任何东西逻辑上分成多个子组,以便每个子组可以独立识别并组合成一个单一分区。让我们了解在关系型数据库管理系统中分区意味着什么。

分区通常用于将数据分成多个逻辑文件组,以提高性能、可用性和可管理性。在处理大数据时,数据通常以数十亿条记录的形式存在。因此,为了提高数据库的性能,最好将数据分成多个文件组。这些文件组可以在单台机器上或跨多台机器共享,并由一个键标识。这些文件组被称为分区数据。

表中的数据可以通过两种方式进行分区:

  • 水平分区

  • 垂直分区

水平分区

当表中的行数非常大时,表可以被分成多个分区;这被称为水平分区。使用水平分区时,表的每个分区包含相同数量的列。可以同时访问所有分区,也可以单独访问每个分区。

垂直分区

在垂直分区中,表的列被分区以实现性能和更好地管理数据库。垂直分区可以通过两种方式实现。第一种是通过规范化表。可以将表中的列分成多个表,通过数据的划分。第二种是通过为表中定义的列创建单独的物理文件组。MySQL 8 目前不支持垂直分区。

让我们看看与分区相关的一些好处:

  • 如果表包含历史数据,比如应用程序的日志,六个月前的数据对于应用程序的活跃性没有任何意义。如果基于月份创建分区,可以轻松地删除其中一个分区。

  • 在前面的日志案例中,如果我们想要在两个日期之间过滤数据,MySQL 优化器可以识别特定的分区,从中找到过滤记录,这可能会导致查询结果更快,因为要检查的行数大大减少。

  • MySQL 8 还支持在特定分区上查询数据。当您知道需要查询所需数据的分区时,可以减少要检查的记录数。

在 MySQL 中修剪分区

修剪是数据的选择性提取。由于我们有多个分区,在检索过程中将遍历每个分区,这是耗时的并且会影响性能。在搜索时,一些分区也会被包括在内,而请求的数据在该分区内并不可用,这是一个额外的过程。修剪在这里有助于仅搜索具有相关数据的分区,这将避免在检索过程中不必要地包括这些分区。

这种优化避免了扫描可能没有匹配值的分区,被称为分区修剪。在分区修剪中,优化器分析 SQL 语句中的FROMWHERE子句,以消除不必要的分区,并扫描与 SQL 语句相关的数据库分区。

查询和索引的最佳实践

很难为参考和重用编写最佳查询。它将始终根据您的应用程序性质、架构、设计、表结构等而变化。但是,在编写 MySQL 查询时可以采取预防措施,以获得更好的性能、可扩展性和完整性。

让我们来看看在设计或编写 MySQL 查询时应该牢记的一些最佳实践。

数据类型

数据库表可能包含多个具有数字或字符串等数据类型的列。MySQL 8 提供了各种数据类型,而不仅仅限于数字或字符串:

  • 小尺寸是好的。由于 MySQL 将数据加载到内存中,大数据量会对其性能产生不利影响。较小的数据集可以在内存中容纳更多数据,并减少资源利用的开销。

  • 修复您的长度。如果不修复数据类型长度,每次需要时它都必须去获取所需的信息。因此,无论在哪里,您都可以使用 char 数据类型来限制数据长度。

非空

MySQL 不太喜欢非空数据。非空列使用更多存储空间,影响性能,并需要在 MySQL 中进行额外处理。

优化引用空数据的查询也很困难。当空数据列被索引时,它会为每个条目使用额外的字节。

索引

索引很重要,因为它可以改善您设计不良的查询和表结构的性能,甚至可以将一个设计良好的查询变成一个性能不佳的查询,这也会影响性能。

搜索字段索引

通常,我们在 MySQL 查询中用作过滤器的字段上进行索引。这显然有助于更快地读取,但可能会对写入/更新产生不利影响,因此只索引您需要的内容将是一个明智的决定。

数据类型和连接

MySQL 可以对不同数据类型进行连接,但如果要求 MySQL 使用不同的数据类型进行连接字段,性能可能会受到影响,因为它必须将每一行从一种类型转换为另一种类型。

复合索引

如果一个查询要引用表的多个列,那么为这些列创建一个复合索引可能会有所帮助。复合索引按照第一列、第二列等结果集中的列进行引用。

列的顺序在查询的性能中起着重要作用,因此在设计表结构和索引时,您需要有效地使用它。

缩短主键

对于主键来说,小尺寸也是好的。缩短主键会像我们讨论数据类型那样有益。由于主键较小,您的索引大小将更小,因此缓存的使用量将更少,因此可以在内存中容纳更多数据。

最好使用数字类型,因为这些比字符要小得多,以实现缩短主键的目标。在进行连接时可能会有所帮助,因为通常会引用主键进行连接。

索引一切

索引一切是一个好主意;然而,MySQL 不会这样做。您知道吗,如果 MySQL 要扫描的索引高于 30%,它将执行全表扫描吗?不要对不需要索引的值进行索引。

我们需要记住,索引在正确使用时有助于获取数据;然而,在编写/更新数据时,它是一种负担。

获取所有数据

select *... - 啊!除非真的需要,否则不要使用这个。到目前为止,我的经验还没有需要这个。获取所有数据会减慢执行时间,并且严重影响 MySQL 服务器的资源利用率。您需要提供一个特定的列名或适当的条件。

让应用程序来完成工作

让应用程序也为 MySQL 完成工作。您可以通过让应用程序进行排序来避免使用order by等子句。在 MySQL 中进行排序比在应用程序中慢得多。您可以确定应该由应用程序处理的查询。

数据的存在

使用EXISTS子句检查数据的存在要快得多。EXISTS子句将在从获取的数据中获取第一行后立即返回输出。

限制自己

限制自己只获取需要的数据。在获取数据时,始终确保使用适当的限制,因为获取不需要的数据将毫无用处,并影响性能。在你的 SQL 查询中使用LIMIT子句。

分析慢查询

这是一个要遵循的良好实践。随着数据的增长,我们可能会错过要么优化要么意识到对查询产生不利影响的查询。你可能会对需要获取的数据的需求发生变化,而我们可能会忽视查询的影响。始终密切关注可以在 MySQL 中配置并优化的慢查询是很好的。

查询成本

你的查询成本是多少?解释是对这个问题的正确回答。使用explain查询参数来了解你的查询受到了什么影响——无论是全表扫描、索引扫描、范围访问等等。明智地利用explain提供的信息,进一步优化查询。这是 MySQL 的一个奇妙、快速、方便的工具。如果你知道你已经尽力了,索引将成为一个救世主,根据你的需求进一步优化它。

编写查询的最佳实践始于需求、设计、实施和持续维护。这是一个我们无法分散的完整生命周期。理解模式、索引和分析起着重要作用。对我们来说,响应时间和最佳资源利用率至关重要。

我个人喜欢深入研究这个领域,远远超出我们在这里所能提及的范围——这是一个关系的世界!你的查询将会遇到表的行或列,或者与另一个表连接。除此之外,如果你没有做对,你会试图从一个不需要的子集中找到一个关系。我们怎么能忘记适当使用的索引是救世主呢?所有这些加在一起将展示我们的关系,并迅速响应请求的查询。

总结

我相信在阅读本章时,你已经记住了需要注意的事项,或者在回忆这些事项,如果你的 MySQL 8 实现中有任何遗漏。在这些章节中,我们讨论了 MySQL 8 的最佳实践,这些最佳实践在实施、使用、管理和故障排除等各个阶段都会有所帮助,并且会成为 MySQL 8 最佳实践的指引;这些可能会根据不同的用例而有所不同。适当的测试和验证将有助于确认实施最佳实践的好处。

我们广泛涵盖了一些关于 MySQL 8 基准测试和一些配置参数以及 memcached 最佳实践的令人兴奋的主题。我们讨论了 MySQL 复制的最佳实践,其中我们经历了一些关键要点。最后,还讨论了 MySQL 查询和索引的指针,以及数据分区的最佳实践。这一章中的任何内容都不足以涵盖所有内容,但提供的指针是必要的。

到目前为止,我们应该对 MySQL 8 有了很好的理解;现在是解决问题的时候了。

现在让我们转到下一章,看看我们如何可能遇到许多常见问题,识别错误代码以及用于排除 MySQL 8 故障的真实场景。

第十五章:MySQL 8 故障排除

在上一章中,我们学习了 MySQL 8 数据库、基准测试和最佳实践的一个重要方面。基准测试有助于比较当前数据库性能与预期性能矩阵。我们了解了什么是基准测试,以及可以用来查找 MySQL 8 服务器基准性能的工具。在本章的后面部分,我们学习了关于 memcached、复制、分区和索引的最佳实践。最佳实践有助于确保 MySQL 8 数据库的最佳配置。

在本章中,重点将放在理解在使用 MySQL 8 数据库时可能遇到的常见错误上。错误可能是服务器错误或客户端错误。我们将探讨一种确定问题发生的方法。我们还将学习错误的故障排除和解决技术。在本章的后面部分,我们将探讨这些技术适用的真实场景。以下是要涵盖的主题列表:

  • MySQL 8 常见问题

  • MySQL 8 服务器错误

  • MySQL 8 客户端错误

  • MySQL 8 故障排除方法

  • 真实场景

MySQL 8 常见问题

在故障排除时,首先要做的是找出导致问题的程序或设备。

以下是表明硬件或内核问题的症状:

  • 键盘无法正常工作。可以通过按下大写锁定键来检查。如果大写锁定键上的灯不亮,那么键盘有问题。同样,鼠标不动表示鼠标有问题。

  • ping是一个操作系统命令,用于检查一台计算机从另一台计算机的可访问性。执行 ping 命令的计算机称为本地计算机,而被 ping 的计算机称为远程计算机。如果远程计算机不响应本地计算机的 ping,表示存在硬件或网络相关的问题。

  • 如果除了 MySQL 之外的程序无法正常工作,可能表明操作系统内核程序有问题。

  • 如果系统意外重启,可能表明操作系统或硬件有问题。在典型情况下,用户级程序不应该能够使系统崩溃。

要排除问题,可以执行以下一项或多项操作:

  • 运行诊断工具检查硬件

  • 确保相关的库文件是最新的

  • 检查操作系统的更新、补丁或服务包的可用性

  • 检查所有连接

ECC 内存是纠错码内存。它可以检测和纠正大多数常见的内部数据损坏问题。建议使用 ECC 内存以便在早期检测内存问题。

以下说明可能有助于进一步确定问题:

  • 检查系统日志文件可能有助于发现问题的原因。如果 MySQL 出现问题,还必须检查 MySQL 日志文件。

  • 可以使用特定于操作系统的命令来检查内存、文件描述符、磁盘空间或其他关键资源的问题。

  • 如果一个有问题的运行进程即使我们执行了杀死它的命令仍不会死掉,那么操作系统内核中可能存在一个 bug。

  • 如果硬件似乎没有问题,应该尝试确定可能导致问题的程序。使用特定于操作系统的命令,如 Windows 上的任务管理器,Linux 上的pstop,或类似的程序,我们可以识别占用 CPU 或阻塞系统进程的程序。

  • 即使键盘被锁定,也可以恢复对计算机的访问。可以通过从另一台计算机登录系统来实现。成功登录后执行kbd_mode -a命令。

MySQL 用户可以通过使用 MySQL 提供的多个渠道之一来报告问题。在检查了所有可能的替代方案之后,如果可以确定是 MySQL 服务器或 MySQL 客户端引起了问题,用户可以为邮件列表创建错误报告或联系 MySQL 支持团队。报告人必须提供有关错误、系统信息和行为以及预期行为的详细信息。报告人必须根据为什么似乎是 MySQL 错误的原因描述原因。如果程序失败,了解以下信息很有用:

  • 使用top命令,检查所讨论的程序是否占用了所有的 CPU 时间。在这种情况下,我们应该允许程序运行一段时间,因为可能程序正在执行密集的计算指令。

  • 观察 MySQL 服务器对客户端程序尝试连接时的响应。它停止响应了吗?服务器提供了任何输出吗?

  • 如果发现 MySQL 服务器在mysqld程序中引起问题,请尝试使用mysqladmin程序连接以检查mysqld是否有响应。可以使用mysqladmin -u root pingmysqladmin -u root processlist命令。

  • 失败的程序是否发生了分段错误?

最常见的 MySQL 错误

本节提供了用户经常遇到的最常见的 MySQL 错误列表。

访问被拒绝

MySQL 提供了一个特权系统,用于验证从主机连接的用户,并将用户与数据库上的访问权限关联起来。权限包括SELECTINSERTUPDATEDELETE,并能够识别匿名用户并授予 MySQL 特定功能的权限,例如LOAD DATA INFILE和管理操作。

访问被拒绝的错误可能是由许多原因引起的。在许多情况下,问题是由于客户端程序使用的 MySQL 帐户与 MySQL 服务器连接时获得了服务器的许可。

无法连接到[local] MySQL 服务器

在本节中,我们将重点关注遇到无法连接到 MySQL 服务器错误的情况。但在我们跳到特定错误的细节之前,有必要了解 MySQL 客户端如何连接到 MySQL 服务器。

在 Unix 系统上,MySQL 客户端连接到mysqld服务器进程有两种不同的方式。以下是这两种方法的详细信息:

  • TCP/IP 连接mysqld服务器进程在特定端口上监听客户端连接。MySQL 客户端使用指定的 TCP/IP 端口连接服务器。

  • Unix 套接字文件:在这种连接模式下,Unix 套接字文件用于通过文件系统(/tmp/mysql.sock)进行连接。

与 TCP/IP 相比,套接字文件连接速度更快,但只能在连接到同一台计算机上的服务器时使用。要使用 Unix 套接字文件,我们不指定主机名或应指定特殊主机名 localhost。

以下是 MySQL 客户端在 Windows 上连接到 MySQL 服务器的方式:

  • TCP/IP 连接:与 Unix 系统之前描述的一样,TCP/IP 连接在指定的端口号上运行。MySQL 客户端连接到 MySQL 服务器正在监听的端口。

  • 命名管道连接:MySQL 服务器可以使用--enable-named-pipe选项启动。如果客户端在运行服务器的主机上运行,则客户端可以使用命名管道连接。MySQL是命名管道的默认名称。如果在连接到mysqld服务器进程时未提供主机名,则 MySQL 首先尝试连接到默认命名管道。如果无法连接到命名管道,则尝试连接到 TCP/IP 端口。在 Windows 上可以通过使用.作为主机名来强制使用命名管道。

MySQL 错误由预定义的唯一错误代码标识。相同的错误可能与不同的错误代码相关联。具有错误代码2002的无法连接到 MySQL 服务器错误表示三个问题之一。可能是 MySQL 服务器没有在系统上运行,或者提供的 Unix 套接字文件名不正确,或者提供的用于连接到服务器的 TCP/IP 端口号不正确。TCP/IP 端口可能被防火墙或端口阻止服务阻止。

错误代码2003也与无法连接到 MySQL 服务器相关联。它表示服务器拒绝了网络连接。应该检查 MySQL 服务器是否启用了网络连接,MySQL 服务器是否正在运行,并且服务器上是否配置了指定的网络端口。

以下命令可用于确保mysqld服务器进程正在运行:

> ps xa | grep mysqld

如果mysqld服务器进程没有运行,我们应该启动服务器。如果服务器已经运行,应使用以下命令:

> mysqladmin version
> mysqladmin variables
> mysqladmin -h `hostname` version variables
> mysqladmin -h `hostname` --port=3306 version 
> mysqladmin -h host_ip version
> mysqladmin --protocol=SOCKET --socket=/tmp/mysql.sock version

在上述命令中,hostname是运行 MySQL 服务器的计算机的主机名。host_ip是服务器机器的 IP 地址。

与 MySQL 服务器的连接丢失

与 MySQL 服务器的连接丢失错误可能是由本节中解释的三种可能原因之一引起的。

错误的一个潜在原因是网络连接出现问题。如果这是一个频繁出现的错误,应该检查网络条件。如果错误消息中包含during query,那么可以肯定是由于网络连接问题导致了错误。

connection_timeout系统变量定义了mysqld服务器在连接超时响应之前等待连接数据包的秒数。很少情况下,当客户端尝试与服务器进行初始连接并且connection_timeout值设置为几秒时,可能会发生此错误。在这种情况下,可以通过根据距离和连接速度增加connection_timeout值来解决问题。SHOW GLOBAL STATUS LIKEAborted_connects可用于确定我们是否更频繁地遇到此问题。可以肯定地说,如果错误消息包含reading authorization packet,增加connection_timeout值就是解决方案。

可能会因为Binary Large OBjectBLOB)值大于max_allowed_packet而出现问题。这可能会导致客户端与 MySQL 服务器的连接丢失错误。如果观察到ER_NET_PACKET_TOO_LARGE错误,则确认应增加max_allowed_packet值。

密码输入错误时失败

当客户端程序在没有密码值的情况下使用--password-p选项调用时,MySQL 客户端会要求密码。以下是命令:

> mysql -u user_name -p
Enter password:

在一些系统上,当在选项文件或命令行中指定密码时,密码可以正常工作。但是,在Enter password:提示符处交互输入时,密码无法正常工作。这是因为系统提供的用于读取密码的库将密码值限制为少量字符(通常为八个)。这是系统库的问题,而不是 MySQL 的问题。作为解决方法,将 MySQL 密码更改为八个或更少字符的值,或将密码存储在选项文件中。

主机 host_name 被阻止

如果mysqld服务器从中断的主机接收了太多连接请求,将出现以下错误:

Host 'host_name' is blocked because of many connection errors.
Unblock with 'mysqladmin flush-hosts'

max_connect_errors系统变量确定允许的连续中断连接请求的次数。一旦有max_connect_errors次失败的请求而没有成功的连接,mysqld就会认为出现了问题,并阻止主机进一步连接,直到发出FLUSH HOSTS语句或mysqladmin flush-hosts命令。

默认情况下,mysqld在 100 个连接错误后会阻止主机。可以通过在服务器启动时设置max_connect_errors值来进行调整,如下所示:

> mysqld_safe --max_connect_errors=10000

此值也可以在运行时设置,如下所示:

mysql> SET GLOBAL max_connect_errors=10000;

如果针对特定主机收到host_name被阻止的错误,首先应检查主机的 TCP/IP 连接是否存在问题。如果网络存在问题,则增加max_connect_errors变量的值是无济于事的。

连接过多

此错误表示所有可用连接都用于其他客户端连接。max_connections是控制与服务器连接数的系统变量。最大连接数的默认值为 151。我们可以为max_connections系统变量设置大于 151 的值,以支持超过 151 个连接。

mysqld服务器进程实际上允许比max_connectionsmax_connections + 1)值多一个连接。额外的一个连接被保留给具有CONNECTION_ADMINSUPER特权的帐户。管理员可以通过具有PROCESS特权的访问来授予该特权。有了这个访问权限,管理员可以使用保留的连接连接到服务器。他们可以执行SHOW PROCESSLIST命令来诊断问题,即使最大客户端连接数已用完。

内存不足

如果mysql没有足够的内存来存储 MySQL 客户端程序发出的查询的整个请求,服务器会抛出以下错误:

mysql: Out of memory at line 42, 'malloc.c'
mysql: needed 8136 byte (8k), memory in use: 12481367 bytes (12189k)
ERROR 2008: MySQL client ran out of memory

为了解决问题,我们必须首先检查查询是否正确。我们是否期望查询返回这么多行?如果不是,我们应该纠正查询并再次执行。如果查询是正确的且不需要更正,我们可以使用--quick选项连接mysql。使用--quick选项会导致mysql_use_result() C API 函数用于获取结果集。该函数会增加服务器的负载,减少客户端的负载。

数据包太大

通信数据包是以下内容之一:

  • MySQL 客户端发送到 MySQL 服务器的单个 SQL 语句

  • 从 MySQL 服务器发送到 MySQL 客户端的单行

  • 从复制主服务器发送到复制从服务器的二进制日志事件

1 GB 数据包大小是可以传输到 MySQL 8 服务器或客户端的最大可能数据包大小。如果接收到大于max_allowed_packet字节的数据包,MySQL 服务器或客户端会发出ER_NET_PACKET_TOO_LARGE错误并关闭连接。

MySQL 客户端程序的默认max_allowed_packet大小为 16 MB。可以使用以下命令来设置更大的值:

> mysql --max_allowed_packet=32M

MySQL 服务器的默认值为 64 MB。值得注意的是,为此系统变量设置更大的值是没有害处的,因为额外的内存会根据需要分配。

表已满

表已满错误发生在以下条件之一:

  • 磁盘已满

  • 表已达到最大大小

MySQL 数据库中的实际最大表大小可以通过操作系统对文件大小的限制确定。

无法创建/写入文件

如果在执行查询时出现以下错误,则表示 MySQL 无法在结果集的临时目录中创建临时文件:

Can't create/write to file '\\sqla3fe_0.ism'.

错误的可能解决方法是使用--tmpdir选项启动mysqld服务器。以下是命令:

> mysqld --tmpdir C:/temp

作为替代,可以在 MySQL 配置文件的[mysqld]部分中指定如下:

[mysqld]
tmpdir=C:/temp

命令不同步

如果客户端函数的调用顺序错误,则会收到命令不同步的错误。这意味着命令无法在客户端代码中执行。例如,如果我们执行mysql_use_result()并在执行mysql_free_result()之前尝试执行另一个查询,则可能会出现此错误。如果我们在调用mysql_use_result()mysql_store_result()函数之间执行两个返回结果集的查询,也可能会发生这种情况。

忽略用户

mysqld服务器启动时或服务器重新加载授权表时,如果在用户表中找到具有无效密码的帐户,则会收到以下错误:

Found wrong password for user 'some_user'@'some_host'; ignoring user

由于 MySQL 权限系统忽略了该帐户,因此会出现问题。为了解决问题,我们应该为该帐户分配一个新的有效密码。

表 tbl_name 不存在

以下错误表示默认数据库中不存在指定的表:

Table 'tbl_name' doesn't exist
Can't find file: 'tbl_name' (errno: 2)

在某些情况下,用户可能会错误地引用表。这是可能的,因为 MySQL 服务器使用目录和文件来存储数据库表。根据操作系统文件管理的不同,数据库和表名可能区分大小写。

对于不区分大小写的文件系统,例如 Windows,在查询中使用的指定表的引用必须使用相同的字母大小写。

MySQL 8 服务器错误

本节重点介绍 MySQL 8 服务器错误。该部分描述了与 MySQL 服务器管理、表定义和 MySQL 8 服务器中已知问题相关的错误。

文件权限问题

如果在服务器启动时设置了UMASKUMASK_DIR环境变量,则可能会出现文件权限问题。在表创建时,MySQL 服务器可能会发出以下错误消息:

ERROR: Can't find file: 'path/with/file_name' (Errcode: 13)

UMASKUMASK_DIR系统变量的默认值分别为 0640 和 0750。如果这些环境变量的值以零开头,则表示 MySQL 服务器的值是八进制的。例如,八进制中的默认值 0640 和 0750 分别等于十进制的 415 和 488。

为了更改默认的UMASK值,我们应该启动mysqld_safe,如下所示:

> UMASK=384 # = 600 in octal 
> export UMASK 
> mysqld_safe

MySQL 服务器创建数据库目录时的默认访问权限值为0750。我们可以设置UMASK_DIR变量来修改这种行为。如果设置了此值,新目录将以UMASKUMASK_DIR值的组合作为访问权限值创建。

以下是提供所有新目录组访问权限的示例:

> UMASK_DIR=504 # = 770 in octal 
> export UMASK_DIR 
> mysqld_safe &

重置根密码

如果在 MySQL 中从未设置根密码,则 MySQL 服务器连接为根用户时不需要密码。如果之前分配的密码被遗忘,可以进行重置。

以下是在 Windows 系统上重置root @ localhost帐户密码的说明:

  1. 使用系统管理员凭据登录系统。

  2. 如果 MySQL 服务器已经在运行,请停止服务器。如果 MySQL 服务器作为 Windows 服务运行,请按照开始菜单|控制面板|管理工具|服务找到服务。在服务中,找到 MySQL 服务并停止它。如果 MySQL 服务器没有作为 Windows 服务运行,请使用 Windows 任务管理器杀死 MySQL 服务器进程。

  3. 一旦 MySQL 服务器停止,创建一个包含密码分配语句的单行文本文件,如下所示:

 ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewPassword';
  1. 保存文件。例如,将文件保存为C:\mysql-root-reset.txt

  2. 按照开始菜单|运行|cmd 打开 Windows 命令提示符。

  3. 在命令提示符中,使用--init-file选项启动 MySQL 服务器,如下所示:

 C:\> cd "C:\Program Files\MySQL\MySQL Server 8.0\bin" 
        C:\> mysqld --init-file=C:\\mysql-root-reset.txt
  1. 一旦 MySQL 服务器重新启动,删除C:\mysql-root-reset.txt文件。

以下是在类 Unix 系统上重置根用户密码的说明:

  1. 使用与 MySQL 服务器运行的相同用户登录系统。通常是mysql用户。

  2. 如果 MySQL 服务器已经运行,请停止服务器。为此,找到包含 MySQL 服务器进程 ID 的.pid文件。根据 Unix 发行版的不同,文件的实际位置和名称可能不同。通常的位置是/var/lib/mysql//var/run/mysqld//usr/local/mysql/data/。通常,文件名以mysqld或系统主机名开头,并具有.pid扩展名。可以通过向mysqld服务器进程发送正常的 kill 命令来停止 MySQL 服务器。可以使用以下命令和.pid文件的实际路径名:

 > kill 'cat /mysql-data-directory/host_name.pid'
  1. 一旦 MySQL 服务器停止,创建一个包含密码赋值语句的文本文件,如下所示:
 ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewPassword';
  1. 保存文件。假设文件存储在/home/me/mysql-reset-root。由于文件包含 root 用户的密码,应确保其他用户无法读取它。如果我们没有使用适当的用户登录,我们应该确保用户有权限读取该文件。

  2. 使用--init-file选项启动 MySQL 服务器,如下所示:

 > mysqld --init-file=/home/me/mysql-reset-root &
  1. 一旦服务器启动,删除/home/me/mysql-reset-root中的文件。

以下是重置 root 用户密码的通用说明:

  1. 如果 MySQL 服务器正在运行,请停止服务器。一旦停止,使用--skip-grant-tables特权重新启动 MySQL 服务器。除了--skip-grant-tables--skip-networking选项会自动启用,以防止远程连接。

  2. 使用mysql客户端程序连接到 MySQL 服务器。由于服务器是使用--skip-grant-tables启动的,因此不需要密码:

 > mysql
  1. 在 MySQL 客户端本身中,要求服务器重新加载授予表。这将启用帐户管理语句:
 mysql> FLUSH PRIVILEGES;
  1. 使用以下命令更改root @ localhost帐户密码:
 mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 
          'NewPassword';
  1. 重新启动服务器并使用 root 用户和新设置的密码登录。

MySQL 崩溃预防

作为标准发布实践,每个 MySQL 版本在发布之前都会在不同的平台上进行验证。假设 MySQL 可能有一些难以发现的错误。当我们遇到 MySQL 的问题时,如果我们尝试找出系统崩溃的原因,这将是有帮助的。首先要确定的是mysqld服务器进程是否崩溃,或者问题出现在 MySQL 客户端程序上。可以通过执行mysqladmin version命令来检查 MySQL 服务器运行了多长时间。以下是一个示例输出:

C:\Program Files\MySQL\MySQL Server 8.0\bin>mysqladmin version -u root -p
Enter password: *****
mysqladmin Ver 8.0.3-rc for Win64 on x86_64 (MySQL Community Server (GPL))
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Server version 8.0.3-rc-log
Protocol version 10
Connection localhost via TCP/IP
TCP port 3306
Uptime: 9 days 4 hours 4 min 52 sec

Threads: 2 Questions: 4 Slow queries: 0 Opens: 93 Flush tables: 2 Open tables: 69 Queries per second avg: 0.000

resolve_stack_dump是一个实用程序,用于将数字堆栈转储解析为符号。为了分析mysqld服务器进程死机的根本原因,我们在堆栈跟踪错误日志中找到。可以使用resolve_stack_dump程序解决这个问题。必须注意的是,错误日志中找到的变量值可能不准确。

损坏的数据或索引文件可能会导致 MySQL 服务器崩溃。这些文件在执行每个 SQL 语句之前和客户端被通知结果之前使用write()系统在磁盘上进行更新。这意味着即使在mysqld崩溃的情况下,数据文件中的内容也是安全的。未刷新的数据在磁盘上的写入由操作系统负责。--flush选项可以与mysqld一起使用,以强制 MySQL 在每个 SQL 语句执行后将所有内容刷新到磁盘上。

以下是 MySQL 损坏表的原因之一:

  • 如果数据文件或索引文件崩溃,则其中包含损坏的数据。

  • MySQL 服务器进程中的一个错误导致服务器在更新过程中崩溃。

  • 外部程序在没有表锁定的情况下与mysqld同时操纵数据和索引文件。

  • 在更新过程中,MySQL 服务器进程被终止。

  • 许多mysqld服务器正在系统上运行。这些服务器使用相同的数据目录。系统没有良好的文件系统锁定,或者外部锁定被禁用。

  • 可能会发现数据存储代码中存在错误。我们可以尝试通过在已修复的表的副本上使用ALTER TABLE来更改存储引擎。

处理 MySQL 磁盘满

本节重点介绍 MySQL 对磁盘满错误和超出配额错误的响应。它更相关于MyISAM表中的写入。它可以应用于二进制日志文件和索引文件的写入。它排除了应被视为事件的行和记录的引用。

当磁盘满时,MySQL 执行以下操作:

  • MySQL 确保有足够的空间可用来写入当前行。

  • MySQL 服务器每 10 分钟在日志文件中写入一条条目。它会警告磁盘满的情况。

应采取以下措施来解决问题:

  • 应该释放磁盘空间,以确保有足够的空间来插入所有记录。

  • 我们可以执行mysqladmin kill命令来中止线程。下次检查磁盘时,线程将被中止。

  • 可能会有一些线程在等待导致磁盘满的表。在多个被锁定的线程中,杀死等待磁盘满条件的线程将使其他线程继续运行。

  • REPAIR TABLEOPTIMIZE TABLE语句是前述条件的例外。其他例外包括在LOAD DATA INFILEALTER TABLE语句之后批量创建的索引。这些 SQL 语句可能会创建大量的临时文件。这可能会给系统的其余部分带来大问题。

MySQL 临时文件存储

TMPDIR环境变量的值在 Unix 上被 MySQL 用作存储临时文件的目录路径名。如果未设置TMPDIR,MySQL 会使用系统默认值,如/tmp/var/tmp/usr/tmp

MySQL 在 Windows 上会检查TMPDIRTEMPTMP环境变量的值。如果 MySQL 找到已设置的变量,它将使用该值,并不会检查剩余的值。如果这三个变量都未设置,MySQL 将使用系统默认值,即C:\windows\temp\

如果文件系统中的临时文件目录太小,我们可以使用mysqld --tmpdir选项来指定具有足够空间的文件系统上的目录。对于复制,在从服务器上,我们可以使用--slave-load-tmpdir并指定在复制LOAD DATA INFILE语句期间保存临时文件的目录。可以使用--tmpdir选项以轮询方式设置多个路径的列表。在 Unix 系统上,路径可以用冒号字符(:)分隔,而在 Windows 上,可以用分号字符(;)分隔路径。

为了有效地分配负载,多个临时目录路径应该属于不同的物理磁盘,而不是同一磁盘的不同分区。

对于作为复制从服务器工作的 MySQL 服务器,我们必须注意设置--slave-load-tmpdir选项,以免指向基于内存的文件系统中的目录,或者指向在服务器或服务器主机重新启动时清除的目录。为了复制临时表或LOAD DATA INFILE操作,复制从服务器需要在机器重新启动时其临时文件。如果临时文件目录中的文件丢失,复制将失败。

mysqld服务器进程终止时,MySQL 会负责删除临时文件。在类 Unix 平台上,可以在打开文件后取消链接文件。这样做的一个主要缺点是该文件名不会出现在目录列表中。还有可能我们看不到占用文件系统的大文件。

ibtmp1InnoDB存储引擎用来存储临时表的表空间文件的名称。该文件位于 MySQL 的数据目录中。如果我们想要指定不同的文件名和位置,可以在服务器启动时使用innodb_temp_data_file_path选项。

如果ALTER TABLE操作在InnoDB表上使用ALGORITHM=COPY技术,存储引擎会在相同目录中创建原始表的临时副本。临时表的文件名以#sql-前缀开头。它们只在执行ALTER TABLE操作时短暂出现。

如果使用ALGORITHM=INPLACE方法通过ALTER TABLE SQL 语句重建InnoDB表,则InnoDB存储引擎会在与原始表相同的目录中创建原始表的中间副本。中间表的文件名以#sql-ib前缀开头。它们只在执行ALTER TABLE操作时短暂出现。

innodb_tmpdir选项不能应用于中间表文件。这些中间文件始终在与原始表相同的目录中创建和存储。

使用ALGORITHM=INPLACE方法重建InnoDB表的ALTER TABLE SQL 语句会在默认的 MySQL 临时目录中创建临时排序文件。默认临时目录由 Unix 上的$TMPDIR,Windows 上的%TEMP%--tmpdir选项指定的目录表示。如果临时目录不足以存储这样的文件,可能需要重新配置tmpdir。作为替代方案,可以使用innodb_tmpdir选项为在线InnoDB ALTER TABLE语句定义另一个临时目录。innodb_tmpdir选项可以在运行时使用SET GLOBALSET SESSION语句进行配置。

在复制环境中,如果所有服务器具有相同的操作系统环境,则应考虑复制innodb_tmpdir配置。在其他情况下,innodb_tmpdir设置复制可能会导致在线ALTER TABLE操作执行失败。如果操作环境不同,建议为每台服务器单独配置innodb_tmpdir

MySQL Unix 套接字文件

MySQL 服务器使用/tmp/mysql.sock作为与本地客户端通信的 Unix 套接字文件的默认位置。根据不同的发行格式,如 RPMs 的/var/lib/mysql,可能会有所不同。

在几个 Unix 版本上,可以删除存储在/tmp目录和其他类似目录中的文件。如果套接字文件存储在文件系统上的这种目录中,可能会导致问题。

可以保护/tmp目录,以确保文件只能由所有者或 root 超级用户删除。这在几乎每个版本的 Unix 上都是可能的。可以在以 root 用户登录时设置/tmp目录的粘滞位。以下是执行相同操作的命令:

chmod +t /tmp

使用ls -ld /tmp命令,还可以检查粘滞位是否已设置。如果最后一个权限字符是t,则设置了该位。粘滞位用于定义 Unix 系统中的文件权限。

还有一种替代方法,即更改 Unix 套接字文件的位置。如果更改 Unix 套接字文件的位置,必须确保客户端程序也知道文件的新位置。以下是实现这一点的方法:

  • 可以在全局或本地选项文件中设置路径,如下所示:
 [mysqld]
 socket=/path/to/socket

 [client]
 socket=/path/to/socket
    • 我们还可以在命令行上为mysqld_safe指定--socket选项,并在运行客户端程序时也可以这样做。
  • MYSQL_UNIX_PORT环境变量可以设置为 Unix 套接字文件的路径。

  • MySQL 也可以重新从源代码编译,以便将不同的 Unix 套接字文件位置作为默认值使用。

使用以下命令,可以确保新的套接字位置有效:

mysqladmin --socket=/path/to/socket version

时区问题

MySQL 服务器必须告知用户当前的时区,如果我们在使用SELECT NOW()时返回的是 UTC 时间而不是用户当前的时区。如果UNIX_TIMESTAMP()返回错误的数值也适用。这应该针对运行服务器的环境进行设置,例如mysqld_safemysql.server

我们还可以使用--timezone=timezone_name选项与mysqld_safe一起设置服务器时区。也可以在启动mysqld之前将值分配给TZ环境变量来设置时区。

--timezoneTZ的允许值列表取决于系统。

MySQL 8 客户端错误

本节重点介绍 MySQL 8 客户端出现的错误。MySQL 客户端的工作是连接到 MySQL 服务器,以执行 SQL 查询并从 MySQL 8 数据库获取结果。本节列出了与查询执行相关的错误。

字符串搜索中的区分大小写

字符串搜索使用非二进制字符串的比较操作数的逻辑顺序,例如CHARVARCHARTEXT。二进制字符串的比较,如BINARYVARBINARYBLOB使用操作数中字节的数值。这基本上意味着对于字母字符,比较将区分大小写。

将非二进制字符串与二进制字符串进行比较将被视为二进制字符串之间的比较。

比较操作,如>=, >, =, <, <=, sortinggrouping取决于每个字符的排序值。具有相似排序值的字符被视为相同字符。以 e 和é为例。这些字符在提供的逻辑顺序中具有相同的排序值。这些被视为相等。

utf8mb4utf8mb4_0900_ai_ci分别是默认的字符集和排序规则。默认情况下,非二进制字符串比较是不区分大小写的。这意味着如果我们使用col_name LIKE 'a%'搜索,我们将得到所有以 A 或 a 开头的列值。要使其区分大小写,我们必须确保其中一个操作数具有二进制或区分大小写的排序规则。例如,如果将列与字符串进行比较,并且两者都具有utf8mb4字符集,则可以使用COLLATE运算符来使其中一个操作数具有utf8mb4_0900_as_csutf8mb4_bin排序规则。以下是一个示例:

col_name COLLATE utf8mb4_0900_as_cs LIKE 'a%' 
col_name LIKE 'a%' COLLATE utf8mb4_0900_as_cs 
col_name COLLATE utf8mb4_bin LIKE 'a%' 
col_name LIKE 'a%' COLLATE utf8mb4_bin

为了将非二进制区分大小写字符串比较更改为不区分大小写,我们应该使用COLLATE来命名一个不区分大小写的排序规则。以下是COLLATE如何将比较更改为区分大小写的示例:

mysql> SET NAMES 'utf8mb4'; 
mysql> SET @s1 = 'MySQL' COLLATE utf8mb4_bin, @s2 = 'mysql' COLLATE utf8mb4_bin; mysql> SELECT @s1 = @s2;
+-----------+ 
| @s1 = @s2 | 
+-----------+ 
|         0 | 
+-----------+ 
mysql> SELECT @s1 COLLATE utf8mb4_0900_ai_ci = @s2; 
+--------------------------------------+ 
| @s1 COLLATE utf8mb4_0900_ai_ci = @s2 | 
+--------------------------------------+ 
|                                    1 | 
+--------------------------------------+

DATE 列的问题

在 MySQL 中,DATE值的默认格式是YYYY-MM-DD。标准 SQL 不允许任何其他格式。这是在UPDATE表达式和SELECT语句的WHERE子句中应该使用的格式。以下是日期格式的示例:

SELECT * FROM table_name WHERE date_col >= '2011-06-02';

当将常量字符串与DATETIMEDATETIMETIMESTAMP使用<, <=, =, >=, >,或BETWEEN运算符进行比较时,MySQL 将字符串转换为内部长整数值。MySQL 这样做是为了实现更快的比较。然而,以下例外情况适用于此转换:

  • 比较两列

  • DATETIMEDATETIMETIMESTAMP列与表达式进行比较

  • 使用除列出的方法之外的比较方法,如INSTRCMP()

在这些例外情况下,通过将对象转换为字符串值并执行字符串比较来进行比较。

NULL 值的问题

NULL值经常让新程序员感到困惑。NULL值在字符串的情况下被错误地解释为空字符串''。这是不正确的。以下是完全不同的语句的示例:

mysql> INSERT INTO my_table (phone) VALUES (NULL); 
mysql> INSERT INTO my_table (phone) VALUES ('');

在上面的例子中,两个语句都将值插入到同一列(phone 列)中。第一个语句插入一个NULL值,而第二个语句插入一个空字符串。第一个值可以被认为是电话号码未知,而第二个值表示该人已知没有电话,因此没有电话号码。

NULL值与任何其他值进行比较时,它总是评估为假。包含NULL值的表达式总是返回NULL值。以下示例返回一个NULL值:

mysql> SELECT NULL, 1+NULL, CONCAT('Invisible',NULL);

如果 SQL 语句的目的是搜索NULL列值,我们不能使用expression = NULL。以下是一个示例,返回零行,因为expression = NULL始终为假:

mysql> SELECT * FROM my_table WHERE phone = NULL;

要进行NULL值比较,应该使用IS NULL。以下示例演示了IS NULL的使用:

mysql> SELECT * FROM my_table WHERE phone IS NULL; 
mysql> SELECT * FROM my_table WHERE phone = '';

MySQL 8 故障排除方法

在本章的这一部分,我们将专注于 MySQL 8 的故障排除方法。我们为什么需要排除 MySQL 8 的故障?排除故障的原因如下:

  • 更快地执行 SQL 查询

  • 性能增强

  • 资源的有效利用

主要的资源集包括 CPU、磁盘 IO、内存和网络。有两种方法来衡量 MySQL 的性能:

  • 在查询集中的方法中,重要的是要衡量查询的执行速度。

  • 在资源集中的方法中,查询使用更少的资源是很重要的。

让我们深入了解如何排除 MySQL 问题。

分析查询

EXPLAIN是提供 MySQL 执行 SQL 语句信息的 SQL 语句。EXPLAIN语句与INSERTUPDATEREPLACEDELETESELECT语句一起使用。EXPLAIN语句的输出是对SELECT语句中提到或使用的每个表的信息行。输出按照 MySQL 在执行语句时读取这些表的顺序列出。所有连接都使用嵌套循环连接方法解析。在嵌套循环连接方法中,MySQL 从列表中的第一个表中读取一行,然后在列表中的第二个表中找到匹配的行,然后是第三个表,依此类推。一旦处理完列表中的所有表,MySQL 处理所选列的结果,并通过表的列表回溯,直到找到具有更多匹配行的表。它从这个表中读取下一行。这样的过程继续进行。

以下是来自EXPLAIN输出的列:

  • id**:这表示查询中的SELECT的顺序号。它也被称为SELECT标识符。当行属于其他行的联合结果时,该值可能为NULL。输出在表列中显示<unionM, N>。这意味着该行是 ID 值MN的联合。

  • select_type:此输出列指示SELECT语句的类型。可能的值列表包括SIMPLEPRIMARYUNIONDEPENDENT UNIONUNION RESULTSUBQUERYDEPENDENT SUBQUERYDERIVEDMATERIALIZEDUNCACHEABLE SUBQUERYUNCACHEABLE UNION

  • table:此列指示输出中提到的表的名称。它可以具有诸如<unionM, N><derivedN><subqueryN>之类的值。

  • partitions:这标识查询匹配记录的分区。对于非分区表,该值为NULL

  • 类型:这表示JOIN的类型。

  • possible_keys:此输出列指示 MySQL 可能选择用于获取表中行的索引。如果没有匹配的索引,返回值将为NULL

  • key:此输出列指示 MySQL 实际用于从表中获取行的关键索引。

  • refref输出列指示用于与键输出列中提到的索引进行比较以选择表行的列或常量。

  • rows:行输出列指示为成功执行查询需要检查的行数。

EXPLAIN中有以下类型的连接:

  • system:这意味着表只有一行。这是const连接类型的特殊情况。

  • const:这意味着表至少有一行匹配。这一行在查询开始时被读取。由于只找到一行匹配,优化器的其余部分将这一行中的列值视为常量。由于 const 表只被读取一次,所以非常快。当PRIMARY KEYUNIQUE索引的所有部分与常量值进行比较时,使用 const。以下是一个使用tbl_name作为 const 表的示例:

 mysql> SELECT * FROM tbl_name WHERE primary_key=1; 
 mysql> SELECT * FROM tbl_name WHERE primary_key_part1=1 AND primary_key_part2=2;
  • ref:对于前面表的每个行组合,从ref表中读取所有具有匹配索引值的行。如果连接只使用了键的最左前缀,则使用ref

现实世界的场景

MySQL 查询优化是指提高查询执行时间。例如,当一个查询性能不佳时,意味着查询执行时间比预期的时间长。查询执行时间很重要,但还有其他指标用于衡量性能。本节解释了应该测量什么以及如何尽可能精确地进行测量。

以下问题出现了:为什么我们应该优化查询?如果只需要百分之一秒,真的需要优化吗?是的,除非查询很少执行,否则确实需要优化。我们应该优化最昂贵的查询。

让我们讨论一个实时的例子。在某个应用程序中,我们有一个基于复杂查询生成的报告,花费了太多时间。执行时间是以分钟计算的。为了优化这样一个复杂的查询,我们考虑了以下方法:

  1. 使用EXPLAIN分析查询计划:MySQL 提供了两种分析查询性能的方法。一种是EXPLAIN方法,我们已经在本章的前一部分学习过。另一个工具是SHOW STATUS。通常,我们应该优先使用EXPLAIN来理解SELECT查询的查询计划。在报告查询的情况下,我们将一些非SELECT查询转换为SELECT查询。这有助于我们理解非SELECT查询的查询执行计划。例如,通过在UPDATE查询中使用WHERE子句,我们可以将其转换为SELECT查询。我们还可以找到表上缺少的索引。

  2. SHOW STATUSSHOW STATUS语句输出 MySQL 的内部计数器。这些计数器在每次查询执行时由 MySQL 递增。借助这些计数器,我们可以了解服务器的聚合操作类型。它还有助于指示每个单独查询所做的工作。

以下是对 MySQL 服务器变量执行的测量:

  • Select_:每次执行SELECT查询时,此计数器会递增。此计数器还可用于确定是否执行了表扫描。

  • Key_read:此变量提供了关于键索引使用情况的额外信息。

  • Last_query_cost:这个值表示上次执行的查询有多昂贵。

以下是执行查询优化的步骤:

  1. 多次执行查询以确保返回相同的结果。

  2. 执行SHOW STATUS。保存输出。

  3. 执行查询。

  4. 执行SHOW STATUS以观察与上一次执行的差异。

  5. 如果需要,执行EXPLAIN

应该分析以下参数以优化查询性能:

  • 表索引

  • 排序

  • 整体性能

  • 行级操作

  • 磁盘 I/O 操作

总结

在这本书的最后一章中,我们学习了数据库的一个重要方面:解决我们在使用 MySQL 服务器或客户端时可能遇到的错误。我们从理解故障排除开始讨论。我们讨论了初步诊断错误的不同方法。我们了解了常见的 MySQL 错误以及错误消息的含义。我们还学习了如何修复这些错误。我们还了解了 MySQL 服务器和客户端的错误以及这些错误的修复方法。在本章的后半部分,我们学习了 MySQL 故障排除方法,并看了一个真实的案例。对于最后一章来说,这是相当重要的内容,是吧?这本书就到这里了。

posted @ 2024-05-21 12:36  绝不原创的飞龙  阅读(90)  评论(0)    收藏  举报