MySQL8-中文参考-十一-
MySQL8 中文参考(十一)
译文:
dev.mysql.com/doc/refman/8.0/en/clone-plugin-concurrent-ddl.html
7.6.7.4 克隆和并发 DDL
在 MySQL 8.0.27 之前,在克隆操作期间,包括TRUNCATE TABLE
在内的捐赠者和接收者 MySQL 服务器实例上的 DDL 操作是不允许的。在选择数据源时应考虑此限制。一种解决方法是使用专用的捐赠者实例,可以在克隆数据时阻止 DDL 操作。
为防止在克隆操作期间进行并发 DDL,捐赠者和接收者上会获取独占的备份锁。clone_ddl_timeout
变量定义了在捐赠者和接收者上,克隆操作等待备份锁的时间(以秒为单位)。默认设置为 300 秒。如果在指定的时间限制内未获得备份锁,则克隆操作将因错误而失败。
从 MySQL 8.0.27 开始,默认情况下允许在捐赠者上进行并发 DDL。捐赠者上的并发 DDL 支持由clone_block_ddl
变量控制。可以使用SET
语句动态启用和禁用捐赠者上的并发 DDL 支持。
SET GLOBAL clone_block_ddl={OFF|ON}
默认设置为clone_block_ddl=OFF
,允许在捐赠者上进行并发 DDL。
并发 DDL 操作的效果是否被克隆取决于 DDL 操作是否在克隆操作获取动态快照之前完成。
不允许在克隆操作期间进行的 DDL 操作,无论clone_block_ddl
设置如何,包括:
-
ALTER TABLE *
tbl_name* DISCARD TABLESPACE;
-
ALTER TABLE *
tbl_name* IMPORT TABLESPACE;
-
ALTER INSTANCE DISABLE INNODB REDO_LOG;
原文:
dev.mysql.com/doc/refman/8.0/en/clone-plugin-encrypted-data.html
7.6.7.5 克隆加密数据
支持对加密数据进行克隆。以下要求适用:
-
在将远程数据克隆时,需要安全连接以确保未加密表空间密钥在网络上传输时的安全性。表空间密钥在捐赠者处解密后传输,并在接收者处使用接收者主密钥重新加密。如果没有加密连接可用或在
CLONE INSTANCE
语句中使用REQUIRE NO SSL
子句,则会报告错误。有关为克隆配置加密连接的信息,请参见 为克隆配置加密连接。 -
当将数据克隆到使用本地管理的密钥环的本地数据目录时,启动克隆目录上的 MySQL 服务器时必须使用相同的密钥环。
-
当将数据克隆到使用本地管理的密钥环的远程数据目录(接收者目录)时,启动克隆目录上的 MySQL 服务器时必须使用接收者密钥环。
注意
innodb_redo_log_encrypt
和 innodb_undo_log_encrypt
变量设置在克隆操作进行时无法修改。
有关数据加密功能的信息,请参见 第 17.13 节,“InnoDB 数据静态加密”。
原文:
dev.mysql.com/doc/refman/8.0/en/clone-plugin-compressed-data.html
7.6.7.6 克隆压缩数据
支持对页面压缩数据进行克隆。在克隆远程数据时,需要满足以下要求:
-
接收方文件系统必须支持稀疏文件和空洞打孔,以便在接收方上进行空洞打孔。
-
捐赠方和接收方文件系统必须具有相同的块大小。如果文件系统块大小不同,则会报告类似以下错误:ERROR 3868 (HY000): Clone Configuration FS Block Size: Donor value: 114688 is different from Recipient value: 4096.
关于页面压缩功能的信息,请参见第 17.9.2 节,“InnoDB 页面压缩”。
原文:
dev.mysql.com/doc/refman/8.0/en/clone-plugin-replication.html
7.6.7.7 复制用的克隆
克隆插件支持复制。除了克隆数据外,克隆操作还会从捐赠者提取复制坐标并将其传输给接收者,这使得可以使用克隆插件为配置组复制成员和副本提供服务。使用克隆插件进行配置比复制大量事务要快得多且更有效率。
配置组复制成员也可以配置为使用克隆插件作为分布式恢复的选项,这样加入成员会自动选择从现有组成员检索组数据的最有效方式。有关更多信息,请参见 Section 20.5.4.2, “Cloning for Distributed Recovery”。
在克隆操作期间,二进制日志位置(文件名、偏移量)和gtid_executed
GTID 集都会从捐赠者的 MySQL 服务器实例中提取并传输到接收者。这些数据允许在复制流中的一致位置启动复制。二进制日志和中继日志(保存在文件中)不会从捐赠者复制到接收者。为了启动复制,接收者需要的二进制日志必须在数据克隆和启动复制之间不被清除。如果所需的二进制日志不可用,则会报告复制握手错误。因此,克隆实例应尽快添加到复制组中,以避免所需的二进制日志被清除或新成员明显滞后,需要更多的恢复时间。
-
在克隆的 MySQL 服务器实例上执行此查询,以检查已传输给接收者的二进制日志位置:
mysql> SELECT BINLOG_FILE, BINLOG_POSITION FROM performance_schema.clone_status;
-
在克隆的 MySQL 服务器实例上执行此查询,以检查已传输给接收者的
gtid_executed
GTID 集:mysql> SELECT @@GLOBAL.GTID_EXECUTED;
在 MySQL 8.0 中,默认情况下,复制元数据存储库保存在在克隆操作期间从捐赠者复制到接收者的表中。复制元数据存储库保存了可以在克隆操作后正确恢复复制的与复制相关的配置设置。
-
在 MySQL 8.0.17 和 8.0.18 中,只会复制表
mysql.slave_master_info
(连接元数据存储库)。 -
从 MySQL 8.0.19 开始,表
mysql.slave_relay_log_info
(应用程序元数据存储库)和mysql.slave_worker_info
(应用程序工作程序元数据存储库)也会被复制。
要查看每个表中包含的内容列表,请参阅 Section 19.2.4.2, “Replication Metadata Repositories”。请注意,如果服务器上使用了设置 master_info_repository=FILE
和 relay_log_info_repository=FILE
(这在 MySQL 8.0 中不是默认设置且已被弃用),则不会克隆复制元数据存储库;只有在设置为 TABLE
时才会克隆。
要进行复制克隆,请执行以下步骤:
-
对于 Group Replication 的新成员,首先按照 Section 20.2.1.6, “Adding Instances to the Group” 中的说明配置 MySQL Server 实例以进行 Group Replication。同时,设置克隆的先决条件,详见 Section 20.5.4.2, “Cloning for Distributed Recovery”。当在加入成员上发出
START GROUP_REPLICATION
命令时,克隆操作将由 Group Replication 自动管理,因此您无需手动执行操作,也无需在加入成员上执行任何进一步的设置步骤。 -
对于源/复制 MySQL 复制拓扑中的副本,首先手动将数据从捐赠方 MySQL 服务器实例克隆到接收方。捐赠方必须是复制拓扑中的源或副本。有关克隆说明,请参阅 Section 7.6.7.3, “Cloning Remote Data”。
-
克隆操作成功完成后,如果您希望在接收方 MySQL 服务器实例上使用与捐赠方相同的复制通道,请验证哪些通道可以在源/复制 MySQL 复制拓扑中自动恢复复制,哪些需要手动设置。
-
对于基于 GTID 的复制,如果接收方配置为
gtid_mode=ON
并且从配置为gtid_mode=ON
、ON_PERMISSIVE
或OFF_PERMISSIVE
的捐赠方克隆,那么从捐赠方应用gtid_executed
GTID 集到接收方。如果接收方是从已在拓扑中的副本克隆而来,那么在克隆操作后,使用 GTID 自动定位的复制通道可以在启动通道后自动恢复复制。如果您只想使用这些相同通道,则无需执行任何手动设置。 -
对于基于二进制日志文件位置的复制,如果接收端是 MySQL 8.0.17 或 8.0.18,则从提供端的二进制日志位置不会应用到接收端,只会记录在性能模式
clone_status
表中。因此,接收端上使用基于二进制日志文件位置的复制通道必须手动设置以在克隆操作后恢复复制。确保这些通道未配置为在服务器启动时自动开始复制,因为它们尚未具有二进制日志位置,并尝试从头开始复制。 -
对于基于二进制日志文件位置的复制,如果接收端是 MySQL 8.0.19 或更高版本,则从提供端应用二进制日志位置到接收端。接收端上使用基于二进制日志文件位置的复制通道会自动尝试执行中继日志恢复过程,使用克隆的中继日志信息,在重新启动复制之前。对于单线程副本(
replica_parallel_workers
或slave_parallel_workers
设置为 0),在没有其他问题的情况下,中继日志恢复应该成功,使通道能够在没有进一步设置的情况下恢复复制。对于多线程副本(replica_parallel_workers
或slave_parallel_workers
大于 0),中继日志恢复可能会失败,因为通常无法自动完成。在这种情况下,会发出错误消息,您必须手动设置通道。
-
-
如果您需要手动设置克隆复制通道,或者希望在接收端使用不同的复制通道,以下说明提供了一个摘要和简化示例,用于将接收端 MySQL 服务器实例添加到复制拓扑中。还请参考适用于您的复制设置的详细说明。
-
要将一个接收方 MySQL 服务器实例添加到使用基于 GTID 的事务作为复制数据源的 MySQL 复制拓扑中,请根据需要配置实例,并按照 Section 19.1.3.4,“使用 GTID 设置复制”中的说明操作。按照以下简化示例为实例添加复制通道。
CHANGE REPLICATION SOURCE TO
语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO
语句(在 MySQL 8.0.23 之前)必须定义源的主机地址和端口号,并且应启用SOURCE_AUTO_POSITION
|MASTER_AUTO_POSITION
选项,如下所示:mysql> CHANGE MASTER TO MASTER_HOST = '*source_host_name*', MASTER_PORT = *source_port_num*, ... MASTER_AUTO_POSITION = 1, FOR CHANNEL '*setup_channel*'; mysql> START SLAVE USER = '*user_name*' PASSWORD = '*password*' FOR CHANNEL '*setup_channel*'; Or from MySQL 8.0.22 and 8.0.23: mysql> CHANGE SOURCE TO SOURCE_HOST = '*source_host_name*', SOURCE_PORT = *source_port_num*, ... SOURCE_AUTO_POSITION = 1, FOR CHANNEL '*setup_channel*'; mysql> START REPLICA USER = '*user_name*' PASSWORD = '*password*' FOR CHANNEL '*setup_channel*';
-
要将一个接收方 MySQL 服务器实例添加到使用基于二进制日志文件位置的复制的 MySQL 复制拓扑中,请根据需要配置实例,并按照 Section 19.1.2,“设置基于二进制日志文件位置的复制”中的说明操作。按照以下简化示例为实例添加复制通道,使用在克隆操作期间传输给接收方的二进制日志位置:
mysql> SELECT BINLOG_FILE, BINLOG_POSITION FROM performance_schema.clone_status; mysql> CHANGE MASTER TO MASTER_HOST = '*source_host_name*', MASTER_PORT = *source_port_num*, ... MASTER_LOG_FILE = '*source_log_name*', MASTER_LOG_POS = *source_log_pos*, FOR CHANNEL '*setup_channel*'; mysql> START SLAVE USER = '*user_name*' PASSWORD = '*password*' FOR CHANNEL '*setup_channel*'; Or from MySQL 8.0.22 and 8.0.23: mysql> SELECT BINLOG_FILE, BINLOG_POSITION FROM performance_schema.clone_status; mysql> CHANGE SOURCE TO SOURCE_HOST = '*source_host_name*', SOURCE_PORT = *source_port_num*, ... SOURCE_LOG_FILE = '*source_log_name*', SOURCE_LOG_POS = *source_log_pos*, FOR CHANNEL '*setup_channel*'; mysql> START REPLICA USER = '*user_name*' PASSWORD = '*password*' FOR CHANNEL '*setup_channel*';
-
原文:
dev.mysql.com/doc/refman/8.0/en/clone-plugin-directories.html
7.6.7.8 克隆操作期间创建的目录和文件
当数据被克隆时,以下目录和文件会被创建用于内部使用。不应该被修改。
-
#clone
:包含克隆操作使用的内部克隆文件。在数据被克隆到的目录中创建。 -
#ib_archive
:包含在克隆操作期间在捐赠者上归档的内部归档日志文件。 -
*.#clone
文件:在接收端创建的临时数据文件,当数据从接收端数据目录中移除并在远程克隆操作期间克隆新数据时创建。
原文:
dev.mysql.com/doc/refman/8.0/en/clone-plugin-failure-handling.html
7.6.7.9 远程克隆操作失败处理
本节描述了克隆操作在不同阶段的失败处理。
-
先决条件被检查(参见远程克隆先决条件)。
- 如果在先决条件检查期间发生故障,则
CLONE INSTANCE
操作会报告错误。
- 如果在先决条件检查期间发生故障,则
-
在 MySQL 8.0.27 之前,在提供方和接收方上备份锁会阻止并发的 DDL 操作。从 MySQL 8.0.27 开始,仅当
clone_block_ddl
变量设置为ON
时(默认设置为OFF
)才会阻止提供方上的并发 DDL。参见 Section 7.6.7.4, “Cloning and Concurrent DDL”。- 如果克隆操作无法在
clone_ddl_timeout
变量指定的时间限制内获得 DDL 锁,则会报告错误。
- 如果克隆操作无法在
-
用户创建的数据(模式、表、表空间)和接收方上的二进制日志在将数据克隆到接收方数据目录之前被移除。
-
在远程克隆操作期间从接收方数据目录中移除用户创建的数据和二进制日志时,数据不会被保存,如果发生故障可能会丢失。如果数据很重要,应在启动远程克隆操作之前进行备份。
为了信息目的,警告会被打印到服务器错误日志中,以指定数据移除何时开始和结束:
[Warning] [MY-013453] [InnoDB] Clone removing all user data for provisioning: Started... [Warning] [MY-013453] [InnoDB] Clone removing all user data for provisioning: Finished
如果在移除数据时发生故障,接收方可能会留下部分在克隆操作之前存在的模式、表和表空间。在执行克隆操作期间或发生故障后的任何时候,服务器始终处于一致状态。
-
-
数据从提供方克隆。用户创建的数据、字典元数据和其他系统数据都会被克隆。
-
如果在克隆数据时发生故障,克隆操作将被回滚并移除所有克隆数据。在此阶段,接收方上先前存在的用户创建的数据和二进制日志也已被移除。
如果发生这种情况,您可以纠正故障原因并重新执行克隆操作,或放弃克隆操作并从在克隆操作之前进行的备份中恢复接收方数据。
-
-
服务器会自动重启(适用于不克隆到命名目录的远程克隆操作)。在启动过程中,会执行典型的服务器启动任务。
- 如果自动服务器重启失败,您可以手动重启服务器以完成克隆操作。
在 MySQL 8.0.24 之前,如果在克隆操作期间发生网络错误,且在五分钟内解决了错误,则操作会恢复。从 MySQL 8.0.24 开始,如果在捐赠实例上定义的clone_donor_timeout_after_network_failure
变量指定的时间内解决了错误,则操作会恢复。clone_donor_timeout_after_network_failure
的默认设置为 5 分钟,但支持 0 到 30 分钟的范围。如果操作未在分配的时间内恢复,则会中止并返回错误,捐赠者会删除快照。将设置为零会导致在发生网络错误时捐赠者立即删除快照。配置更长的超时时间允许更多时间解决网络问题,但也会增加捐赠实例上的增量大小,从而增加克隆恢复时间以及在克隆旨在作为副本或复制组成员时的复制延迟。
在 MySQL 8.0.24 之前,捐赠者线程在监听克隆协议命令时使用 MySQL Server wait_timeout
设置。因此,低wait_timeout
设置可能导致长时间运行的远程克隆操作超时。从 MySQL 8.0.24 开始,克隆空闲超时设置为默认的wait_timeout
设置,即 28800 秒(8 小时)。
原文:
dev.mysql.com/doc/refman/8.0/en/clone-plugin-monitoring.html
7.6.7.10 监控克隆操作
本节描述了监控克隆操作的选项。
-
使用性能模式克隆表监控克隆操作
-
使用性能模式阶段事件监控克隆操作
-
使用性能模式克隆工具监控克隆操作
-
Com_clone 状态变量
使用性能模式克隆表监控克隆操作
克隆操作可能需要一些时间才能完成,这取决于数据量和与数据传输相关的其他因素。您可以使用clone_status
和 clone_progress
性能模式表在接收 MySQL 服务器实例上监视克隆操作的状态和进度。
注意
clone_status
和 clone_progress
性能模式表仅可用于监视接收 MySQL 服务器实例上的克隆操作。要监视捐赠 MySQL 服务器实例上的克隆操作,请使用克隆阶段事件,如使用性能模式阶段事件监控克隆操作中所述。
-
clone_status
表提供当前或最近执行的克隆操作的状态。克隆操作有四种可能的状态:未开始
、进行中
、已完成
和失败
。 -
clone_progress
表提供当前或最近执行的克隆操作的进度信息,按阶段划分。克隆操作的阶段包括DROP DATA
、FILE COPY
、PAGE_COPY
、REDO_COPY
、FILE_SYNC
、RESTART
和RECOVERY
。
访问性能模式克隆表需要在性能模式上具有SELECT
和EXECUTE
权限。
要检查克隆操作的状态:
-
连接到接收方 MySQL 服务器实例。
-
查询
clone_status
表:mysql> SELECT STATE FROM performance_schema.clone_status; +-----------+ | STATE | +-----------+ | Completed | +-----------+
如果在克隆操作期间发生故障,您可以查询clone_status
表以获取错误信息:
mysql> SELECT STATE, ERROR_NO, ERROR_MESSAGE FROM performance_schema.clone_status;
+-----------+----------+---------------+
| STATE | ERROR_NO | ERROR_MESSAGE |
+-----------+----------+---------------+
| Failed | xxx | "xxxxxxxxxxx" |
+-----------+----------+---------------+
要查看克隆操作的每个阶段的详细信息:
-
连接到接收方 MySQL 服务器实例。
-
查询
clone_progress
表。例如,以下查询提供了克隆操作每个阶段的状态和结束时间数据:mysql> SELECT STAGE, STATE, END_TIME FROM performance_schema.clone_progress; +-----------+-----------+----------------------------+ | stage | state | end_time | +-----------+-----------+----------------------------+ | DROP DATA | Completed | 2019-01-27 22:45:43.141261 | | FILE COPY | Completed | 2019-01-27 22:45:44.457572 | | PAGE COPY | Completed | 2019-01-27 22:45:44.577330 | | REDO COPY | Completed | 2019-01-27 22:45:44.679570 | | FILE SYNC | Completed | 2019-01-27 22:45:44.918547 | | RESTART | Completed | 2019-01-27 22:45:48.583565 | | RECOVERY | Completed | 2019-01-27 22:45:49.626595 | +-----------+-----------+----------------------------+
要监视其他克隆状态和进度数据点,请参考第 29.12.19 节,“性能模式克隆表”。
使用性能模式阶段事件监视克隆操作
克隆操作可能需要一些时间才能完成,这取决于数据量和与数据传输相关的其他因素。有三个阶段事件用于监视克隆操作的进度。每个阶段事件报告 WORK_COMPLETED
和 WORK_ESTIMATED
值。随着操作的进行,报告的值会进行修订。
可以在捐赠方或接收方 MySQL 服务器实例上使用此方法监视克隆操作。
按发生顺序,克隆操作阶段事件包括:
-
stage/innodb/clone (file copy)
: 表示克隆操作的文件复制阶段的进度。WORK_ESTIMATED
和WORK_COMPLETED
单位为文件块。在文件复制阶段开始时,就已知要传输的文件数量,并且根据文件数量估算出块的数量。WORK_ESTIMATED
设置为估计文件块的数量。每发送一个块后,WORK_COMPLETED
都会更新。 -
stage/innodb/clone (page copy)
: 表示克隆操作的页面复制阶段的进度。WORK_ESTIMATED
和WORK_COMPLETED
单位为页面。一旦文件复制阶段完成,就会知道要传输的页面数量,并且将WORK_ESTIMATED
设置为此值。每发送一个页面后,WORK_COMPLETED
都会更新。 -
stage/innodb/clone (redo copy)
: 表示克隆操作的重做复制阶段的进度。WORK_ESTIMATED
和WORK_COMPLETED
单位为重做块。一旦页面复制阶段完成,就会知道要传输的重做块数量,并且WORK_ESTIMATED
设置为此值。每发送一个块后,WORK_COMPLETED
都会更新。
以下示例演示了如何启用stage/innodb/clone%
事件仪器和相关的消费者表来监视克隆操作。有关性能模式阶段事件仪器和相关消费者的信息,请参见第 29.12.5 节,“性能模式阶段事件表”。
-
启用
stage/innodb/clone%
仪器:mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'stage/innodb/clone%';
-
启用阶段事件消费者表,包括
events_stages_current
、events_stages_history
和events_stages_history_long
。mysql> UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE '%stages%';
-
运行克隆操作。在此示例中,将本地数据目录克隆到名为
cloned_dir
的目录。mysql> CLONE LOCAL DATA DIRECTORY = '/path/to/cloned_dir';
-
通过查询性能模式
events_stages_current
表来检查克隆操作的进度。显示的阶段事件取决于正在进行的克隆阶段。WORK_COMPLETED
列显示已完成的工作。WORK_ESTIMATED
列显示总共需要的工作量。mysql> SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED FROM performance_schema.events_stages_current WHERE EVENT_NAME LIKE 'stage/innodb/clone%'; +--------------------------------+----------------+----------------+ | EVENT_NAME | WORK_COMPLETED | WORK_ESTIMATED | +--------------------------------+----------------+----------------+ | stage/innodb/clone (redo copy) | 1 | 1 | +--------------------------------+----------------+----------------+
如果克隆操作已经完成,
events_stages_current
表将返回一个空集。在这种情况下,您可以检查events_stages_history
表以查看已完成操作的事件数据。例如:mysql> SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED FROM events_stages_history WHERE EVENT_NAME LIKE 'stage/innodb/clone%'; +--------------------------------+----------------+----------------+ | EVENT_NAME | WORK_COMPLETED | WORK_ESTIMATED | +--------------------------------+----------------+----------------+ | stage/innodb/clone (file copy) | 301 | 301 | | stage/innodb/clone (page copy) | 0 | 0 | | stage/innodb/clone (redo copy) | 1 | 1 | +--------------------------------+----------------+----------------+
使用性能模式克隆仪器监视克隆操作
性能模式提供了用于高级性能监控克隆操作的仪器。要查看可用的克隆仪器,并发出以下查询:
mysql> SELECT NAME,ENABLED FROM performance_schema.setup_instruments
WHERE NAME LIKE '%clone%';
+---------------------------------------------------+---------+
| NAME | ENABLED |
+---------------------------------------------------+---------+
| wait/synch/mutex/innodb/clone_snapshot_mutex | NO |
| wait/synch/mutex/innodb/clone_sys_mutex | NO |
| wait/synch/mutex/innodb/clone_task_mutex | NO |
| wait/synch/mutex/group_rpl/LOCK_clone_donor_list | NO |
| wait/synch/mutex/group_rpl/LOCK_clone_handler_run | NO |
| wait/synch/mutex/group_rpl/LOCK_clone_query | NO |
| wait/synch/mutex/group_rpl/LOCK_clone_read_mode | NO |
| wait/synch/cond/group_rpl/COND_clone_handler_run | NO |
| wait/io/file/innodb/innodb_clone_file | YES |
| stage/innodb/clone (file copy) | YES |
| stage/innodb/clone (redo copy) | YES |
| stage/innodb/clone (page copy) | YES |
| statement/abstract/clone | YES |
| statement/clone/local | YES |
| statement/clone/client | YES |
| statement/clone/server | YES |
| memory/innodb/clone | YES |
| memory/clone/data | YES |
+---------------------------------------------------+---------+
等待仪器
性能模式等待仪器跟踪需要时间的事件。克隆等待事件仪器包括:
-
wait/synch/mutex/innodb/clone_snapshot_mutex
:跟踪克隆快照互斥锁的等待事件,该互斥锁在多个克隆线程之间同步访问动态快照对象(在捐赠者和接收者之间)。 -
wait/synch/mutex/innodb/clone_sys_mutex
:跟踪克隆系统互斥锁的等待事件。在 MySQL 服务器实例中有一个克隆系统对象。此互斥锁在捐赠者和接收者之间同步访问克隆系统对象。它由克隆线程和其他前台和后台线程获取。 -
wait/synch/mutex/innodb/clone_task_mutex
:跟踪克隆任务互斥锁的等待事件,用于克隆任务管理。clone_task_mutex
由克隆线程获取。 -
wait/io/file/innodb/innodb_clone_file
: 跟踪克隆操作的文件上的所有 I/O 等待操作。
关于监控InnoDB
互斥等待的信息,请参阅第 17.16.2 节,“使用性能模式监控 InnoDB 互斥等待”。关于一般监控等待事件的信息,请参阅第 29.12.4 节,“性能模式等待事件表”。
阶段工具
性能模式阶段事件跟踪语句执行过程中发生的步骤。克隆阶段事件工具包括:
-
stage/innodb/clone (file copy)
: 表示克隆操作的文件复制阶段的进度。 -
stage/innodb/clone (redo copy)
: 表示克隆操作的重做复制阶段的进度。 -
stage/innodb/clone (page copy)
: 表示克隆操作的页面复制阶段的进度。
关于使用阶段事件监控克隆操作的信息,请参阅使用性能模式阶段事件监控克隆操作。关于一般监控阶段事件的信息,请参阅第 29.12.5 节,“性能模式阶段事件表”。
语句工具
性能模式语句事件跟踪语句执行。当启动克隆操作时,由克隆语句工具跟踪的不同语句类型可能并行执行。您可以在性能模式语句事件表中观察这些语句事件。执行的语句数量取决于clone_max_concurrency
和clone_autotune_concurrency
设置。
克隆语句事件工具包括:
-
statement/abstract/clone
: 跟踪在被分类为本地、客户端或服务器操作类型之前的任何克隆操作的语句事件。 -
statement/clone/local
: 跟踪本地克隆操作的克隆语句事件;在执行CLONE LOCAL
语句时生成。 -
statement/clone/client
: 跟踪发生在接收端 MySQL 服务器实例上的远程克隆语句事件;在接收端执行CLONE INSTANCE
语句时生成。 -
statement/clone/server
: 跟踪发生在捐赠 MySQL 服务器实例上的远程克隆语句事件;在接收端执行CLONE INSTANCE
语句时生成。
欲了解有关监控性能模式语句事件的更多信息,请参见 第 29.12.6 节,“性能模式语句事件表”。
内存工具
性能模式内存工具跟踪内存使用情况。克隆内存使用工具包括:
-
memory/innodb/clone
: 跟踪InnoDB
为动态快照分配的内存。 -
memory/clone/data
: 跟踪克隆操作期间克隆插件分配的内存。
欲了解使用性能模式监控内存使用情况的更多信息,请参见 第 29.12.20.10 节,“内存摘要表”。
Com_clone 状态变量
Com_clone
状态变量提供了 CLONE
语句执行次数的计数。
欲了解更多信息,请参考关于 Com_xxx
语句计数变量的讨论,见 第 7.1.10 节,“服务器状态变量”。
7.6.7.11 停止克隆操作
如有必要,您可以使用KILL QUERY *
processlist_id*
语句停止克隆操作。
在接收端的 MySQL 服务器实例中,您可以从clone_status
表的PID
列中检索克隆操作的进程列表标识符(PID)。
mysql> SELECT * FROM performance_schema.clone_status\G
*************************** 1\. row ***************************
ID: 1
PID: 8
STATE: In Progress
BEGIN_TIME: 2019-07-15 11:58:36.767
END_TIME: NULL
SOURCE: LOCAL INSTANCE
DESTINATION: /*path/to/clone_dir*/
ERROR_NO: 0
ERROR_MESSAGE:
BINLOG_FILE:
BINLOG_POSITION: 0
GTID_EXECUTED:
您还可以从INFORMATION_SCHEMA
PROCESSLIST
表的ID
列,SHOW PROCESSLIST
输出的Id
列,或性能模式threads
表的PROCESSLIST_ID
列中检索进程列表标识符。这些获取 PID 信息的方法可用于捐赠者或接收者的 MySQL 服务器实例。
dev.mysql.com/doc/refman/8.0/en/clone-plugin-option-variable-reference.html
7.6.7.12 克隆系统变量参考
表 7.7 克隆系统变量参考
名称 | 命令行 | 选项文件 | 系统变量 | 状态变量 | 变量范围 | 动态 |
---|---|---|---|---|---|---|
自动调整并发性 | 是 | 是 | 是 | 全局 | 是 | |
克隆块 DDL | 是 | 是 | 是 | 全局 | 是 | |
克隆缓冲区大小 | 是 | 是 | 是 | 全局 | 是 | |
DDL 超时 | 是 | 是 | 是 | 全局 | 是 | |
数据丢弃后的延迟 | 是 | 是 | 是 | 全局 | 是 | |
网络故障后的捐赠者超时 | 是 | 是 | 是 | 全局 | 是 | |
启用压缩 | 是 | 是 | 是 | 全局 | 是 | |
最大并发数 | 是 | 是 | 是 | 全局 | 是 | |
最大数据带宽 | 是 | 是 | 是 | 全局 | 是 | |
克隆最大网络带宽 | 是 | 是 | 是 | 全局 | 是 | |
SSL CA | 是 | 是 | 是 | 全局 | 是 | |
SSL 证书 | 是 | 是 | 是 | 全局 | 是 | |
SSL 密钥 | 是 | 是 | 是 | 全局 | 是 | |
克隆有效捐赠者列表 | 是 | 是 | 是 | 全局 | 是 | |
名称 | 命令行 | 选项文件 | 系统变量 | 状态变量 | 变量范围 | 动态 |
原文:
dev.mysql.com/doc/refman/8.0/en/clone-plugin-options-variables.html
7.6.7.13 克隆系统变量
本节描述了控制克隆插件操作的系统变量。如果在启动时指定的值不正确,则克隆插件可能无法正确初始化,服务器也不会加载它。在这种情况下,服务器可能还会因为不识别它们而为其��克隆设置产生错误消息。
每个系统变量都有一个默认值。系统变量可以在服务器启动时使用命令行选项或选项文件进行设置。它们可以使用SET
语句在运行时动态更改,这使您能够修改服务器的操作而无需停止和重新启动它。
通常,设置全局系统变量的运行时值需要SYSTEM_VARIABLES_ADMIN
权限(或已弃用的SUPER
权限)。有关更多信息,请参见第 7.1.9.1 节,“系统变量权限”。
克隆变量在执行克隆操作的接收方 MySQL 服务器实例上进行配置。
-
clone_autotune_concurrency
命令行格式 --clone-autotune-concurrency
引入 8.0.17 系统变量 clone_autotune_concurrency
范围 全局 动态 是 SET_VAR
提示适用否 类型 布尔 默认值 ON
当启用
clone_autotune_concurrency
(默认情况下),为了优化数据传输速度,为远程克隆操作动态生成额外的线程。此设置仅适用于接收方 MySQL 服务器实例。在克隆操作期间,线程数逐渐增加,直至达到当前线程数的两倍目标。在每个增量评估数据传输速度的影响。根据以下规则,该过程要么继续,要么停止:
-
如果数据传输速度在增量增加时下降超过 5%,则该过程停止。
-
如果在达到目标的 25%后有至少 5%的改进,则该过程继续。否则,该过程停止。
-
如果在达到目标的 50%后有至少 10%的改进,则该过程继续。否则,该过程停止。
-
如果在达到目标后有至少 25%的改进,则该过程继续,朝着当前线程数的两倍新目标前进。否则,该过程停止。
自动调整过程不支持减少线程数。
clone_max_concurrency
变量定义了可以生成的最大线程数。如果禁用
clone_autotune_concurrency
,clone_max_concurrency
定义了远程克隆操作生成的线程数。 -
-
clone_buffer_size
命令行格式 --clone-buffer-size
引入版本 8.0.17 系统变量 clone_buffer_size
作用域 全局 动态 是 SET_VAR
提示适用否 类型 整数 默认值 4194304
最小值 1048576
最大值 268435456
单位 字节 定义本地克隆操作期间传输数据时使用的中间缓冲区的大小。默认值为 4 兆字节(MiB)。较大的缓冲区大小可以允许 I/O 设备驱动程序并行获取数据,从而提高克隆性能。
-
clone_block_ddl
命令行格式 --clone-block-ddl
引入版本 8.0.27 系统变量 clone_block_ddl
作用域 全局 动态 是 SET_VAR
提示适用否 类型 布尔 默认值 OFF
在克隆操作期间在捐赠者 MySQL 服务器实例上启用独占备份锁,这会阻止捐赠者上的并发 DDL 操作。参见第 7.6.7.4 节,“克隆和并发 DDL”。
-
clone_delay_after_data_drop
命令行格式 --clone-delay-after-data-drop
引入版本 8.0.29 系统变量 clone_delay_after_data_drop
作用域 全局 动态 是 SET_VAR
提示适用否 类型 整数 默认值 0
最小值 0
最大值 3600
单位 字节 指定在远程克隆操作开始时,在接收 MySQL 服务器实例上删除现有数据后立即延迟的时间段。延迟旨在在从捐赠者 MySQL 服务器实例克隆数据之前为接收主机上的文件系统释放足够的空间。某些文件系统(如 VxFS)会在后台进程中异步释放空间。在这些文件系统上,在删除现有数据后太快克隆数据可能导致由于空间不足而克隆操作失败。最大延迟时间为 3600 秒(1 小时)。默认设置为 0(无延迟)。
此变量仅适用于远程克隆操作,并配置在接收 MySQL 服务器实例上。
-
clone_ddl_timeout
命令行格式 --clone-ddl-timeout
引入版本 8.0.17 系统变量 clone_ddl_timeout
范围 全局 动态 是 SET_VAR
提示适用否 类型 整数 默认值 300
最小值 0
最大值 2592000
单位 秒 克隆操作等待备份锁的时间(以秒为单位)。备份锁在执行克隆操作时阻止并发 DDL。此设置应用于捐赠者和接收者 MySQL 服务器实例。
设置为 0 意味着克隆操作不会等待备份锁。在这种情况下,执行并发的 DDL 操作可能会导致克隆操作失败。
在 MySQL 8.0.27 之前,在克隆操作期间,备份锁会阻止捐赠者和接收者上的并发 DDL 操作,并且在当前 DDL 操作完成之前,克隆操作无法继续进行。从 MySQL 8.0.27 开始,如果
clone_block_ddl
变量设置为OFF
(默认值),则在克隆操作期间允许在捐赠者上进行并发 DDL 操作。在这种情况下,克隆操作不必等待捐赠者上的备份锁。参见第 7.6.7.4 节,“克隆和并发 DDL”。 -
clone_donor_timeout_after_network_failure
命令行格式 --clone-donor-timeout-after-network-failure
引入版本 8.0.24 系统变量 clone_donor_timeout_after_network_failure
范围 全局 动态 是 SET_VAR
提示适用否 类型 整数 默认值 5
最小值 0
最大值 30
单位 分钟 定义了在网络故障后,捐赠方允许接收方重新连接并重新启动克隆操作的时间长度(以分钟为单位)。更多信息请参见第 7.6.7.9 节,“远程克隆操作失败处理”。
此变量设置在捐赠 MySQL 服务器实例上。在接收 MySQL 服务器实例上设置它没有任何效果。
-
clone_enable_compression
命令行格式 --clone-enable-compression
引入版本 8.0.17 系统变量 clone_enable_compression
范围 全局 动态 是 SET_VAR
提示适用否 类型 布尔 默认值 OFF
启用在远程克隆操作期间在网络层对数据进行压缩。压缩可以节省网络带宽,但会消耗 CPU。启用压缩可能会提高数据传输速率。此设置仅应用于接收 MySQL 服务器实例。
-
clone_max_concurrency
命令行格式 --clone-max-concurrency
引入版本 8.0.17 系统变量 clone_max_concurrency
范围 全局 动态 是 SET_VAR
提示适用否 类型 整数 默认值 16
最小值 1
最大值 128
单位 线程 定义了远程克隆操作的最大并发线程数。默认值为 16。更多的线程数可以提高克隆性能,但也会减少允许的同时客户端连接数,这可能会影响现有客户端连接的性能。此设置仅应用于接收 MySQL 服务器实例。
如果启用了
clone_autotune_concurrency
(默认情况下),clone_max_concurrency
是可以为远程克隆操作动态生成的最大线程数。如果禁用了clone_autotune_concurrency
,clone_max_concurrency
定义了为远程克隆操作生成的线程数。推荐远程克隆操作每个线程的最小数据传输速率为 1 Mebibyte(MiB)每秒。远程克隆操作的数据传输速率由
clone_max_data_bandwidth
变量控制。 -
clone_max_data_bandwidth
命令行格式 --clone-max-data-bandwidth
引入版本 8.0.17 系统变量 clone_max_data_bandwidth
作用范围 全局 动态 是 SET_VAR
Hint Applies否 类型 整数 默认值 0
最小值 0
最大值 1048576
单位 MiB/秒 定义了远程克隆操作每秒的最大数据传输速率(以 Mebibytes(MiB)为单位)。此变量有助于管理克隆操作的性能影响。仅当捐赠端磁盘 I/O 带宽饱和,影响性能时,才应设置限制。值为 0 表示“无限制”,允许克隆操作以最高可能的数据传输速率运行。此设置仅适用于接收端 MySQL 服务器实例。
每个线程的最小数据传输速率为每秒 1 MiB。例如,如果有 8 个线程,则最小传输速率为每秒 8 MiB。
clone_max_concurrency
变量控制了远程克隆操作生成的最大线程数。由
clone_max_data_bandwidth
指定的请求数据传输速率可能与performance_schema.clone_progress
表中的DATA_SPEED
列报告的实际数据传输速率不同。如果您的克隆操作未达到所需的数据传输速率,并且有可用带宽,请检查接收端和捐赠端的 I/O 使用情况。如果有未充分利用的带宽,则 I/O 是下一个最可能成为瓶颈的地方。 -
clone_max_network_bandwidth
命令行格式 --clone-max-network-bandwidth
引入版本 8.0.17 系统变量 clone_max_network_bandwidth
作用范围 全局 动态 是 SET_VAR
Hint Applies否 类型 整数 默认值 0
最小值 0
最大值 1048576
单位 MiB/秒 指定远程克隆操作每秒的最大近似网络传输速率(以 mebibytes(MiB)为单位)。此变量可用于管理克隆操作对网络带宽的性能影响。仅当网络带宽饱和,影响捐赠��实例上的性能时才应设置该值。值为 0 表示“无限制”,允许以网络上最高可能的数据传输速率进行克隆,提供最佳性能。此设置仅适用于接收端 MySQL 服务器实例。
-
clone_ssl_ca
命令行格式 --clone-ssl-ca=file_name
引入版本 8.0.14 系统变量 clone_ssl_ca
作用范围 全局 动态 是 SET_VAR
提示适用否 类型 文件名 默认值 空字符串
指定证书颁发机构(CA)文件的路径。用于配置远程克隆操作的加密连接。此设置在接收端配置,并在连接到捐赠者时使用。
-
clone_ssl_cert
命令行格式 --clone-ssl-cert=file_name
引入版本 8.0.14 系统变量 clone_ssl_cert
作用范围 全局 动态 是 SET_VAR
提示适用否 类型 文件名 默认值 空字符串
指定公钥证书的路径。用于配置远程克隆操作的加密连接。此设置在接收端配置,并在连接到捐赠者时使用。
-
clone_ssl_key
命令行格式 --clone-ssl-key=file_name
引入版本 8.0.14 系统变量 clone_ssl_key
作用范围 全局 动态 是 SET_VAR
提示适用否 类型 文件名 默认值 空字符串
指定私钥文件的路径。用于配置远程克隆操作的加密连接。此设置在接收端配置,并在连接到捐赠者时使用。
-
clone_valid_donor_list
命令行格式 --clone-valid-donor-list=value
引入版本 8.0.17 系统变量 clone_valid_donor_list
作用范围 全局 动态 是 SET_VAR
提示适用否 类型 字符串 默认值 NULL
定义远程克隆操作的有效捐赠主机地址。此设置应用于接收方 MySQL 服务器实例。允许以逗号分隔的值列表,格式如下:“
HOST1:PORT1,HOST2:PORT2,HOST3:PORT3
”。不允许有空格。clone_valid_donor_list
变量通过控制克隆数据源提供了一层安全性。配置clone_valid_donor_list
所需的权限与执行远程克隆操作所需的权限不同,这允许将这些责任分配给不同的角色。配置clone_valid_donor_list
需要SYSTEM_VARIABLES_ADMIN
权限,而执行远程克隆操作需要CLONE_ADMIN
权限。不支持 Internet Protocol version 6 (IPv6)地址格式。可以使用 IPv6 地址的别名。IPv4 地址可以直接使用。
原文:
dev.mysql.com/doc/refman/8.0/en/clone-plugin-limitations.html
7.6.7.14 克隆插件限制
克隆插件受到以下限制:
-
无法从不同的 MySQL 服务器系列克隆实例。例如,您不能在 MySQL 8.0 和 MySQL 8.4 之间克隆,但可以在同一系列内克隆,例如 MySQL 8.0.37 和 MySQL 8.0.42。在 8.0.37 之前,点发布号也必须匹配,因此不允许克隆类似于 8.0.36 到 8.0.42 或反之亦然。
-
在 MySQL 8.0.27 之前,在克隆操作期间不允许在捐赠者和接收者上进行 DDL,包括
TRUNCATE TABLE
。在选择数据源时应考虑此限制。一种解决方法是使用专用的捐赠者实例,在克隆数据时可以阻止 DDL 操作。允许并发 DML。从 MySQL 8.0.27 开始,默认情况下允许在捐赠者上进行并发 DDL。对于捐赠者上的并发 DDL 的支持由
clone_block_ddl
变量控制。请参见第 7.6.7.4 节,“克隆和并发 DDL”。 -
仅在 MySQL 8.0.26 及更高版本中支持从捐赠者 MySQL 服务器实例克隆到相同版本和发布的热修复 MySQL 服务器实例。
-
一次只能克隆一个 MySQL 实例。不支持在单个克隆操作中克隆多个 MySQL 实例。
-
未支持远程克隆操作(在
CLONE INSTANCE
语句中指定捐赠者 MySQL 服务器实例的端口号时)的mysqlx_port
指定的 X 协议端口。 -
克隆插件不支持克隆 MySQL 服务器配置。接收方 MySQL 服务器实例保留其配置,包括持久化的系统变量设置(参见第 7.1.9.3 节,“持久化系统变量”)。
-
克隆插件不支持克隆二进制日志。
-
克隆插件仅克隆存储在
InnoDB
中的数据。不会克隆其他存储引擎的数据。存储在任何模式中的MyISAM
和CSV
表将被克隆为空表。 -
通过 MySQL 路由器连接到捐赠者 MySQL 服务器实例不受支持。
-
本地克隆操作不支持使用绝对路径创建的通用表空间的克隆。克隆的表空间文件与源表空间文件具有相同路径会引起冲突。
7.6.8 密钥环代理桥插件
原文:
dev.mysql.com/doc/refman/8.0/en/daemon-keyring-proxy-plugin.html
MySQL Keyring 最初使用服务器插件实现了密钥库功能,但从 MySQL 8.0.24 开始开始过渡到使用组件基础架构。 过渡包括修改密钥环插件的底层实现以使用组件基础架构。 这是通过名为daemon_keyring_proxy_plugin
的插件实现的,它充当插件和组件服务 API 之间的桥梁,并使密钥环插件可以继续使用而不会改变用户可见的特性。
daemon_keyring_proxy_plugin
是内置的,无需安装或启用。
7.6.9 MySQL 插件服务
7.6.9.1 锁定服务
7.6.9.2 密钥环服务
MySQL 服务器插件可以访问服务器的“插件服务”。插件服务接口通过暴露插件可以调用的服务器功能来补充插件 API。有关编写插件服务的开发人员信息,请参阅 MySQL 插件服务。以下部分描述了在 SQL 和 C 语言级别可用的插件服务。
7.6.9.1 锁定服务
MySQL 发行版提供了一个可在两个级别访问的锁定接口:
-
在 SQL 级别上,作为一组可加载函数,每个函数映射到对服务例程的调用。
-
作为 C 语言接口,可作为服务器插件或可加载函数的插件服务调用。
有关插件服务的一般信息,请参见 Section 7.6.9, “MySQL Plugin Services”。有关可加载函数的一般信息,请参见添加可加载函数。
锁定接口具有以下特点:
-
锁具有三个属性:锁命名空间、锁名称和锁模式:
-
锁由命名空间和锁名称的组合标识。命名空间使不同应用程序可以在不发生冲突的情况下使用相同的锁名称,方法是在不同的命名空间中创建锁。例如,如果应用程序 A 和 B 使用命名空间
ns1
和ns2
,则每个应用程序可以使用锁名称lock1
和lock2
而不会干扰另一个应用程序。 -
锁模式为读或写。读锁是共享的:如果一个会话对给定的锁标识符有读锁定,则其他会话可以对相同标识符获取读锁定。写锁是排他的:如果一个会话对给定的锁标识符有写锁定,则其他会话无法对相同标识符获取读或写锁定。
-
-
命名空间和锁名称必须为非
NULL
、非空,并且最大长度为 64 个字符。如果命名空间或锁名称指定为NULL
、空字符串或长度超过 64 个字符,则会导致ER_LOCKING_SERVICE_WRONG_NAME
错误。 -
锁定接口将命名空间和锁名称视为二进制字符串,因此比较区分大小写。
-
锁定接口提供了获取锁定和释放锁定的函数。调用这些函数不需要特殊权限。权限检查是调用应用程序的责任。
-
如果锁定不可立即获得,可以等待锁定。锁定获取调用需要一个整数超时值,指示在放弃之前等待多少秒以获取锁定。如果超时到达而未成功获取锁定,则会发生
ER_LOCKING_SERVICE_TIMEOUT
错误。如果超时为 0,则不会等待,如果无法立即获取锁定,则调用会产生错误。 -
锁定接口检测不同会话中的锁获取调用之间的死锁。在这种情况下,锁定服务选择一个调用者,并以
ER_LOCKING_SERVICE_DEADLOCK
错误终止其锁获取请求。此错误不会导致事务回滚。在死锁情况下选择会话时,锁定服务更喜欢持有读锁的会话而不是持有写锁的会话。 -
一个会话可以通过单个锁获取调用获取多个锁。对于给定的调用,锁获取是原子的:如果所有锁都被获取,则调用成功。如果任何锁的获取失败,则调用不会获取任何锁并失败,通常会出现
ER_LOCKING_SERVICE_TIMEOUT
或ER_LOCKING_SERVICE_DEADLOCK
错误。 -
一个会话可以为相同的锁标识符(命名空间和锁名称组合)获取多个锁。这些锁实例可以是读锁、写锁或两者的混合。
-
在会话中获取的锁通过显式调用释放锁函数释放,或者在会话正常或异常终止时隐式释放。当事务提交或回滚时不会释放锁。
-
在一个会话中,释放给定命名空间的所有锁时会一起释放。
锁定服务提供的接口与GET_LOCK()
及相关 SQL 函数提供的接口不同(请参见第 14.14 节,“锁定函数”)。例如,GET_LOCK()
不实现命名空间,并且仅提供排他锁,而不是不同的读锁和写锁。
7.6.9.1.1 锁定服务 C 接口
本节描述如何使用锁定服务的 C 语言接口。要使用函数接口,请参见第 7.6.9.1.2 节,“锁定服务函数接口”有关锁定服务接口的一般特性,请参见第 7.6.9.1 节,“锁定服务”有关插件服务的一般信息,请参见第 7.6.9 节,“MySQL 插件服务”。
使用锁定服务的源文件应包含此头文件:
#include <mysql/service_locking.h>
要获取一个或多个锁,请调用此函数:
int mysql_acquire_locking_service_locks(MYSQL_THD opaque_thd,
const char* lock_namespace,
const char**lock_names,
size_t lock_num,
enum enum_locking_service_lock_type lock_type,
unsigned long lock_timeout);
参数的含义如下:
-
opaque_thd
: 一个线程句柄。如果指定为NULL
,则使用当前线程的句柄。 -
lock_namespace
: 一个以空字符结尾的字符串,表示锁命名空间。 -
lock_names
: 一个以空字符结尾的字符串数组,提供要获取的锁的名称。 -
lock_num
:lock_names
数组中名称的数量。 -
lock_type
: 锁定模式,可以是LOCKING_SERVICE_READ
或LOCKING_SERVICE_WRITE
,分别用于获取读锁或写锁。 -
lock_timeout
: 等待获取锁的秒数,超时放弃。
要释放为给定命名空间获取的锁,请调用此函数:
int mysql_release_locking_service_locks(MYSQL_THD opaque_thd,
const char* lock_namespace);
参数的含义如下:
-
opaque_thd
: 一个线程句柄。如果指定为NULL
,则使用当前线程的句柄。 -
lock_namespace
: 一个以空字符结尾的字符串,表示锁定命名空间。
通过性能模式可以在 SQL 级别监视由锁定服务获取或等待的锁。详情请参见锁定服务监视。
7.6.9.1.2 锁定服务函数接口
本节描述了如何使用其可加载函数提供的锁定服务接口。要使用 C 语言接口,请参见 Section 7.6.9.1.1, “锁定服务 C 接口”。有关锁定服务接口的一般特性,请参见 Section 7.6.9.1, “锁定服务”。有关可加载函数的一般信息,请参见添加可加载函数。
-
安装或卸载锁定服务函数接口
-
使用锁定服务函数接口
-
锁定服务监视
-
锁定服务接口函数参考
安装或卸载锁定服务函数接口
描述在 Section 7.6.9.1.1, “锁定服务 C 接口”中的锁定服务例程不需要安装,因为它们已经内置在服务器中。但映射到服务例程调用的可加载函数不是这样的:这些函数必须在使用之前安装。本节描述了如何进行安装。有关可加载函数安装的一般信息,请参见 Section 7.7.1, “安装和卸载可加载函数”。
锁定服务函数实现在一个插件库文件中,该文件位于由plugin_dir
系统变量命名的目录中。文件基本名称为locking_service
。文件名后缀因平台而异(例如,Unix 和类 Unix 系统为.so
,Windows 为.dll
)。
要安装锁定服务函数,请使用CREATE FUNCTION
语句,并根据需要调整.so
后缀以适配您的平台:
CREATE FUNCTION service_get_read_locks RETURNS INT
SONAME 'locking_service.so';
CREATE FUNCTION service_get_write_locks RETURNS INT
SONAME 'locking_service.so';
CREATE FUNCTION service_release_locks RETURNS INT
SONAME 'locking_service.so';
如果在复制源服务器上使用这些函数,请在所有副本服务器上安装它们,以避免复制问题。
一旦安装,函数将一直保持安装状态,直到卸载。要删除它们,请使用DROP FUNCTION
语句:
DROP FUNCTION service_get_read_locks;
DROP FUNCTION service_get_write_locks;
DROP FUNCTION service_release_locks;
使用锁定服务函数接口
在使用锁定服务函数之前,请根据提供的说明安装它们,详情请参阅安装或卸载锁定服务函数接口。
要获取一个或多个读锁,请调用此函数:
mysql> SELECT service_get_read_locks('mynamespace', 'rlock1', 'rlock2', 10);
+---------------------------------------------------------------+
| service_get_read_locks('mynamespace', 'rlock1', 'rlock2', 10) |
+---------------------------------------------------------------+
| 1 |
+---------------------------------------------------------------+
第一个参数是锁定命名空间。最后一个参数是一个整数超时,指示在放弃之前等待多少秒才能获取锁。中间的参数是锁定名称。
对于刚刚显示的示例,该函数获取具有锁定标识符(mynamespace, rlock1)
和(mynamespace, rlock2)
的锁。
要获取写锁而不是读锁,请调用此函数:
mysql> SELECT service_get_write_locks('mynamespace', 'wlock1', 'wlock2', 10);
+----------------------------------------------------------------+
| service_get_write_locks('mynamespace', 'wlock1', 'wlock2', 10) |
+----------------------------------------------------------------+
| 1 |
+----------------------------------------------------------------+
在这种情况下,锁定标识符为(mynamespace, wlock1)
和(mynamespace, wlock2)
。
要释放命名空间的所有锁,请使用此函数:
mysql> SELECT service_release_locks('mynamespace');
+--------------------------------------+
| service_release_locks('mynamespace') |
+--------------------------------------+
| 1 |
+--------------------------------------+
每个锁定函数成功返回非零值。如果函数失败,将会发生错误。例如,由于锁定名称不能为空,会发生以下错误:
mysql> SELECT service_get_read_locks('mynamespace', '', 10);
ERROR 3131 (42000): Incorrect locking service lock name ''.
一个会话可以为相同的锁标识符获取多个锁。只要不同会话没有对标识符的写锁,该会话可以获取任意数量的读锁或写锁。对于标识符的每个锁请求都会获取一个新锁。以下语句获取具有相同标识符的三个写锁,然后获取相同标识符的三个读锁:
SELECT service_get_write_locks('ns', 'lock1', 'lock1', 'lock1', 0);
SELECT service_get_read_locks('ns', 'lock1', 'lock1', 'lock1', 0);
如果此时检查性能模式metadata_locks
表,您应该发现会话持有六个具有相同(ns, lock1)
标识符的不同锁。(详情请参阅锁定服务监控。)
因为会话至少持有一个对(ns, lock1)
的写锁,其他会话无法为其获取读锁或写锁。如果会话仅持有标识符的读锁,其他会话可以获取其读锁,但无法获取写锁。
单个锁获取调用的锁是原子获取的,但原子性不适用于跨调用。因此,对于以下语句,其中每行结果集调用一次 service_get_write_locks()
,每个单独调用的原子性保持,但对于整个语句不保持:
SELECT service_get_write_locks('ns', 'lock1', 'lock2', 0) FROM t1 WHERE ... ;
注意
因为锁定服务为给定锁标识符的每个成功请求返回一个单独的锁,所以一个语句可能获取大量锁。例如:
INSERT INTO ... SELECT service_get_write_locks('ns', t1.col_name, 0) FROM t1;
这些类型的语句可能会产生某些不良影响。例如,如果语句在中途失败并回滚,则在失败点之前获取的锁仍然存在。如果意图是要求插入的行与获取的锁对应,那么这个意图就无法实现。此外,如果重要的是按照特定顺序授予锁,请注意结果集顺序可能会因优化器选择的执行计划而有所不同。因此,最好限制每个语句对单个锁获取调用。
锁定服务监控
锁定服务是使用 MySQL Server 元数据锁框架实现的,因此您可以通过检查性能模式 metadata_locks
表来监视锁定服务获取或等待的锁。
首先,启用元数据锁仪器:
mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES'
-> WHERE NAME = 'wait/lock/metadata/sql/mdl';
然后获取一些锁并检查 metadata_locks
表的内容:
mysql> SELECT service_get_write_locks('mynamespace', 'lock1', 0);
+----------------------------------------------------+
| service_get_write_locks('mynamespace', 'lock1', 0) |
+----------------------------------------------------+
| 1 |
+----------------------------------------------------+
mysql> SELECT service_get_read_locks('mynamespace', 'lock2', 0);
+---------------------------------------------------+
| service_get_read_locks('mynamespace', 'lock2', 0) |
+---------------------------------------------------+
| 1 |
+---------------------------------------------------+
mysql> SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS
-> FROM performance_schema.metadata_locks
-> WHERE OBJECT_TYPE = 'LOCKING SERVICE'\G
*************************** 1\. row ***************************
OBJECT_TYPE: LOCKING SERVICE
OBJECT_SCHEMA: mynamespace
OBJECT_NAME: lock1
LOCK_TYPE: EXCLUSIVE
LOCK_STATUS: GRANTED
*************************** 2\. row ***************************
OBJECT_TYPE: LOCKING SERVICE
OBJECT_SCHEMA: mynamespace
OBJECT_NAME: lock2
LOCK_TYPE: SHARED
LOCK_STATUS: GRANTED
锁定服务锁的 OBJECT_TYPE
值为 LOCKING SERVICE
。这与例如使用 GET_LOCK()
函数获取的锁不同,后者的 OBJECT_TYPE
为 USER LEVEL LOCK
。
锁定命名空间、名称和模式出现在 OBJECT_SCHEMA
、OBJECT_NAME
和 LOCK_TYPE
列中。读取和写入锁的 LOCK_TYPE
值分别为 SHARED
和 EXCLUSIVE
。
LOCK_STATUS
值为 GRANTED
表示已获取锁,PENDING
表示正在等待锁。如果一个会话持有写锁,另一个会话正在尝试获取具有相同标识符的锁,则可以期望看到 PENDING
。
锁定服务接口函数参考
锁定服务的 SQL 接口实现了本节中描述的可加载函数。有关使用示例,请参见 使用锁定服务函数接口。
这些函数具有以下特征:
-
返回值为非零表示成功。否则,将发生错误。
-
命名空间和锁名称必须为非
NULL
、非空,并且最大长度为 64 个字符。 -
超时值必须是整数,表示在放弃并产生错误之前等待获取锁的秒数。如果超时为 0,则不会等待,如果无法立即获取锁,则函数会产生错误。
这些锁定服务函数可用:
-
service_get_read_locks(*
namespace*, *
lock_name*[, *
lock_name*] ..., *
timeout*)
使用给定的锁名称在给定的命名空间中获取一个或多个读(共享)锁,在给定的超时值内未获取到锁时会超时报错。
-
service_get_write_locks(*
namespace*, *
lock_name*[, *
lock_name*] ..., *
timeout*)
使用给定的锁名称在给定的命名空间中获取一个或多个写(独占)锁,在给定的超时值内未获取到锁时会超时报错。
-
service_release_locks(*
namespace*)
对于给定的命名空间,释放当前会话中使用
service_get_read_locks()
和service_get_write_locks()
获取的所有锁。在命名空间中没有锁也不是错误。
7.6.9.2 密钥环服务
MySQL Server 支持一个密钥环服务,使内部组件和插件能够安全地存储敏感信息以供以后检索。MySQL 发行版提供了一个可在两个级别访问的密钥环接口:
-
在 SQL 级别,作为一组可加载函数,每个函数映射到对服务例程的调用。
-
作为 C 语言接口,可作为服务器插件或可加载函数的插件服务调用。
本节描述如何使用密钥环服务函数在 MySQL 密钥环中存储、检索和删除密钥。有关使用函数的 SQL 接口的信息,请参阅 Section 8.4.4.15, “通用密钥环密钥管理函数”。有关一般密钥环信息,请参阅 Section 8.4.4, “MySQL 密钥环”。
密钥环服务使用启用的底层密钥环插件,如果有的话。如果没有启用密钥环插件,密钥环服务调用将失败。
密钥库中的“记录”由数据(密钥本身)和通过该标识符访问密钥的唯一标识符组成。标识符有两部分:
-
key_id
:密钥 ID 或名称。以mysql_
开头的key_id
值由 MySQL Server 保留。 -
user_id
:会话有效用户 ID。如果没有用户上下文,此值可以为NULL
。该值实际上不必是“用户”;其含义取决于应用程序。实现密钥环函数接口的函数将
CURRENT_USER()
的值作为user_id
值传递给密钥环服务函数。
密钥环服务功能具有以下共同特点:
-
每个函数成功返回 0,失败返回 1。
-
key_id
和user_id
参数形成一个唯一组合,指示密钥环中要使用的密钥。 -
key_type
参数提供有关密钥的附加信息,例如其加密方法或预期用途。 -
密钥环服务函数将密钥 ID、用户名、类型和值视为二进制字符串,因此比较区分大小写。例如,
MyKey
和mykey
的 ID 指的是不同的密钥。
这些密钥环服务函数可用:
-
my_key_fetch()
从密钥环中解密并检索密钥,以及其类型。该函数为用于存储返回的密钥和密钥类型的缓冲区分配内存。当不再需要时,调用者应将内存清零或混淆,然后释放它。
语法:
bool my_key_fetch(const char *key_id, const char **key_type, const char* user_id, void **key, size_t *key_len)
参数:
-
key_id
、user_id
:作为一对的以空字符结尾的字符串,形成一个唯一标识符,指示要获取哪个密钥。 -
key_type
:缓冲区指针的地址。函数将一个指向提供有关密钥的附加信息的以空字符结尾的字符串的指针存储到其中(在添加密钥时存储)。 -
key
:缓冲区指针的地址。函数将一个指向包含获取的密钥数据的缓冲区的指针存储到其中。 -
key_len
:函数将*key
缓冲区的字节大小存储到一个变量的地址中。
返回值:
返回 0 表示成功,返回 1 表示失败。
-
-
my_key_generate()
生成给定类型和长度的新随机密钥,并将其存储在密钥环中。密钥的长度为
key_len
,与从key_id
和user_id
形成的标识符相关联。类型和长度值必须与底层密钥环插件支持的值一致。请参阅第 8.4.4.13 节,“支持的密钥环密钥类型和长度”。语法:
bool my_key_generate(const char *key_id, const char *key_type, const char *user_id, size_t key_len)
参数:
-
key_id
,user_id
:作为一对形成密钥的唯一标识符的以空字符结尾的字符串。 -
key_type
:一个以空字符结尾的字符串,提供有关密钥的附加信息。 -
key_len
:要生成的密钥的字节大小。
返回值:
返回 0 表示成功,返回 1 表示失败。
-
-
my_key_remove()
从密钥环中移除一个密钥。
语法:
bool my_key_remove(const char *key_id, const char* user_id)
参数:
key_id
,user_id
:作为一对形成要移除的密钥的唯一标识符的以空字符结尾的字符串。
返回值:
返回 0 表示成功,返回 1 表示失败。
-
my_key_store()
对密钥进行混淆并存储在密钥环中。
语法:
bool my_key_store(const char *key_id, const char *key_type, const char* user_id, void *key, size_t key_len)
参数:
-
key_id
,user_id
:作为一对形成要存储的密钥的唯一标识符的以空字符结尾的字符串。 -
key_type
:一个以空字符结尾的字符串,提供有关密钥的附加信息。 -
key
:包含要存储的密钥数据的缓冲区。 -
key_len
:key
缓冲区的字节大小。
返回值:
返回 0 表示成功,返回 1 表示失败。
-
7.7 MySQL 服务器可加载函数
原文:
dev.mysql.com/doc/refman/8.0/en/server-loadable-functions.html
7.7.1 安装和卸载可加载函数
7.7.2 获取有关可加载函数的信息
MySQL 支持可加载函数,即不是内置的函数,但可以在运行时(在启动期间或稍后)加载以扩展服务器功能,或卸载以移除功能。有关可加载函数的可用性描述,请参见第 14.2 节,“可加载函数参考”。可加载函数与内置(本地)函数形成对比,后者作为服务器的一部分实现并始终可用;有关表格,请参见第 14.1 节,“内置函数和运算符参考”。
注意
可加载函数以前被称为用户定义函数(UDFs)。那个术语有点不准确,因为“用户定义”也可以应用于其他类型的函数,例如存储函数(使用 SQL 编写的一种存储对象类型)和通过修改服务器源代码添加的本地函数。
MySQL 发行版包括实现这些服务器功能的可加载函数的部分或全部功能:
-
组复制使您能够在一组 MySQL 服务器实例之间创建高可用的分布式 MySQL 服务,具有数据一致性、冲突检测和解决以及内置的组成员服务。请参见第二十章,“组复制”。
-
MySQL 企业版包括基于 OpenSSL 库执行加密操作的函数。请参见第 8.6 节,“MySQL 企业加密”。
-
MySQL 企业版包括提供 SQL 级 API 用于掩码和去标识化操作的函数。请参见 MySQL 企业数据掩码和去标识化元素。
-
MySQL 企业版包括用于监视和记录连接和查询活动的审计日志功能。请参见第 8.4.5 节,“MySQL 企业审计”,以及第 8.4.6 节,“审计消息组件”。
-
MySQL 企业版包括实现应用级防火墙的防火墙功能,以使数据库管理员能够根据匹配接受语句的模式允许或拒绝 SQL 语句执行。请参见第 8.4.7 节,“MySQL 企业防火墙”。
-
查询重写器检查 MySQL Server 收到的语句,并在服务器执行之前可能对其进行重写。请参阅第 7.6.4 节,“重写器查询重写插件”
-
版本令牌使得可以创建并围绕服务器令牌进行同步,应用程序可以使用这些令牌来防止访问不正确或过时的数据。请参阅第 7.6.6 节,“版本令牌”。
-
MySQL Keyring 为敏感信息提供了安全存储。请参阅第 8.4.4 节,“MySQL Keyring”。
-
锁定服务为应用程序提供了一个锁定接口。请参阅第 7.6.9.1 节,“锁定服务”。
-
函数提供了访问查询属性的接口。请参阅第 11.6 节,“查询属性”。
以下各节描述了如何安装和卸载可加载函数,以及如何在运行时确定已安装哪些可加载函数并获取有关它们的信息。
在某些情况下,可加载函数是通过安装实现该函数的组件来加载的,而不是直接加载函数。有关特定可加载函数的详细信息,请参阅包含该函数的服务器功能的安装说明。
有关编写可加载函数的信息,请参阅向 MySQL 添加函数。
7.7.1 安装和卸载可加载函数
如其名称所示,可加载函数必须在服务器中加载后才能使用。MySQL 支持在服务器启动期间自动加载函数以及随后的手动加载。
在加载可加载函数时,有关其的信息可如第 7.7.2 节“获取有关可加载函数的信息”所述获得。
-
安装可加载函数
-
卸载可加载函数
-
重新安装或升级可加载函数
安装可加载函数
要手动加载可加载函数,请使用CREATE FUNCTION
语句。例如:
CREATE FUNCTION metaphon
RETURNS STRING
SONAME 'udf_example.so';
文件基本名称取决于您的平台。Unix 和类 Unix 系统的常见后缀是.so
,Windows 的是.dll
。
CREATE FUNCTION
具有以下效果:
-
它将函数加载到服务器中,使其立即可用。
-
它在
mysql.func
系统表中注册函数,使其在服务器重新启动时保持持久性。因此,CREATE FUNCTION
需要对mysql
系统数据库的INSERT
权限。 -
它将函数添加到性能模式
user_defined_functions
表中,该表提供有关已安装可加载函数的运行时信息。参见第 7.7.2 节“获取有关可加载函数的信息”。
可加载函数的自动加载发生在正常服务器启动序列期间:
-
在
mysql.func
表中注册的函数已安装。 -
在启动时安装的组件或插件可能会自动安装相关函数。
-
自动函数安装将函数添加到性能模式
user_defined_functions
表中,该表提供有关已安装函数的运行时信息。
如果服务器使用--skip-grant-tables
选项启动,则mysql.func
表中注册的函数不会被加载,也无法使用。这不适用于组件或插件自动安装的函数。
卸载可加载函数
要删除可加载函数,请使用DROP FUNCTION
语句。例如:
DROP FUNCTION metaphon;
DROP FUNCTION
有以下影响:
-
它会卸载函数使其不可用。
-
它会从
mysql.func
系统表中删除函数。因此,DROP FUNCTION
需要对mysql
系统数据库的DELETE
权限。由于函数不再在mysql.func
表中注册,服务器在后续重新启动时不会加载该函数。 -
它会从性能模式
user_defined_functions
表中删除提供有关已安装可加载函数的运行时信息的函数。
DROP FUNCTION
不能用于删除由组件或插件自动安装而不是使用CREATE FUNCTION
安装的可加载函数。这样的函数在卸载安装它的组件或插件时也会自动删除。
重新安装或升级可加载函数
要重新安装或升级与可加载函数关联的共享库,请发出DROP FUNCTION
语句,升级共享库,然后发出CREATE FUNCTION
语句。如果先升级共享库,然后使用DROP FUNCTION
,服务器可能会意外关闭。
7.7.2 获取有关可加载函数的信息
原文:
dev.mysql.com/doc/refman/8.0/en/obtaining-loadable-function-information.html
Performance Schema user_defined_functions
表包含当前安装的可加载函数的信息:
SELECT * FROM performance_schema.user_defined_functions;
mysql.func
系统表还列出了已安装的可加载函数,但仅列出使用 CREATE FUNCTION
安装的函数。user_defined_functions
表列出了使用 CREATE FUNCTION
安装的可加载函数,以及由组件或插件自动安装的可加载函数。这种差异使得 user_defined_functions
比 mysql.func
更适合检查已安装的可加载函数。参见 Section 29.12.21.10, “用户定义函数表”。
7.8 在一台机器上运行多个 MySQL 实例
7.8.1 设置多个数据目录
7.8.2 在 Windows 上运行多个 MySQL 实例
7.8.3 在 Unix 上运行多个 MySQL 实例
7.8.4 在多服务器环境中使用客户端程序
在某些情况下,您可能希望在单台机器上运行多个 MySQL 实例。您可能希望在保持现有生产设置不受干扰的同时测试新的 MySQL 发布版。或者您可能希望为不同的用户提供对他们自己管理的不同 mysqld 服务器的访问权限。(例如,您可能是一个希望为不同客户提供独立的 MySQL 安装的互联网服务提供商。)
每个实例可以使用不同的 MySQL 服务器二进制文件,或者对多个实例使用相同的二进制文件,或者两种方法的任意组合。例如,您可以从 MySQL 5.7 运行一个服务器,从 MySQL 8.0 运行另一个服务器,以查看不同版本如何处理给定的工作负载。或者您可以运行当前生产版本的多个实例,每个实例管理不同的数据库集。
无论您是否使用不同的服务器二进制文件,您运行的每个实例都必须配置具有几个操作参数的唯一值。这消除了实例之间冲突的可能性。参数可以在命令行上设置,选项文件中设置,或通过设置环境变量设置。请参见 Section 6.2.2, “指定程序选项”。要查看给定实例使用的值,请连接到该实例并执行 SHOW VARIABLES
语句。
MySQL 实例管理的主要资源是数据目录。每个实例应该使用不同的数据目录,其位置使用 --datadir=*
dir_name*
选项指定。有关配置每个实例使用自己的数据目录的方法,以及未能这样做的危险的警告,请参见 Section 7.8.1, “设置多个数据目录”。
除了使用不同的数据目录外,几个其他选项必须为每个服务器实例具有不同的值:
-
--port=*
port_num*
--port
控制 TCP/IP 连接的端口号。或者,如果主机有多个网络地址,您可以设置bind_address
系统变量,使每个服务器监听不同的地址。 -
--socket={*
file_name*|*
pipe_name*}
--socket
控制 Unix 上的 Unix 套接字文件路径或 Windows 上的命名管道名称。在 Windows 上,仅需要为允许命名管道连接的服务器指定不同的管道名称。 -
--shared-memory-base-name=*
name*
此选项仅在 Windows 上使用。它指定 Windows 服务器用于允许客户端使用共享内存连接的共享内存名称。仅需要为允许共享内存连接的服务器指定不同的共享内存名称。
-
--pid-file=*
file_name*
此选项指示服务器写入其进程 ID 的文件的路径名。
如果您使用以下日志文件选项,则它们的值必须对每个服务器不同:
-
--general_log_file=*
file_name*
-
--slow_query_log_file=*
file_name*
有关日志文件选项的进一步讨论,请参见 第 7.4 节,“MySQL 服务器日志”。
为了获得更好的性能,您可以为每个服务器单独指定以下选项,以在几个物理磁盘之间分散负载:
--tmpdir=*
dir_name*
拥有不同的临时目录还可以更容易地确定哪个 MySQL 服务器创建了任何给定的临时文件。
如果您在不同位置安装了多个 MySQL 实例,可以使用 --basedir=*dir_name*
选项为每个安装指定基目录。这会导致每个实例自动使用不同的数据目录、日志文件和 PID 文件,因为这些参数的默认值都是相对于基目录的。在这种情况下,您需要指定的其他选项只有 --socket
和 --port
选项。假设您使用 tar
文件二进制发行版安装了不同版本的 MySQL。这些安装在不同的位置,因此您可以在相应的基目录下使用命令 bin/mysqld_safe 启动每个安装的服务器。mysqld_safe 确定要传递给 mysqld 的正确 --basedir
选项,您只需指定给 mysqld_safe 的 --socket
和 --port
选项。
如下节所讨论的,可以通过指定适当的命令选项或设置环境变量来启动额外的服务器。但是,如果您需要更长期地运行多个服务器,最方便的方法是使用选项文件为每个服务器指定那些必须对其唯一的选项值。--defaults-file
选项对此很有用。
7.8.1 设置多个数据目录
原文:
dev.mysql.com/doc/refman/8.0/en/multiple-data-directories.html
机器上的每个 MySQL 实例应该有自己的数据目录。位置是使用--datadir=*
dir_name*
选项指定的。
有不同的方法来为新实例设置数据目录:
-
创建一个新的数据目录。
-
复制现有数据目录。
以下讨论提供了有关每种方法的更多详细信息。
警告
通常,您不应该有两个更新同一数据库中数据的服务器。如果您的操作系统不支持无故障系统锁定,这可能会导致令人不快的惊喜。如果(尽管有此警告)您运行使用相同数据目录的多个服务器,并且它们启用了日志记录,则必须使用适当的选项来指定每个服务器唯一的日志文件名。否则,服务器会尝试记录到相同的文件。
即使遵守了前面的预防措施,这种设置仅适用于MyISAM
和MERGE
表,而不适用于任何其他存储引擎。此外,在 NFS 环境中,始终不要在多个服务器之间共享数据目录。允许多个 MySQL 服务器通过 NFS 访问共享数据目录是非常糟糕的想法。主要问题是 NFS 是速度瓶颈。它不适用于这种用途。NFS 的另一个风险是你必须想出一种方法来确保两个或更多服务器不会相互干扰。通常,NFS 文件锁定由lockd
守护程序处理,但目前没有平台可以在任何情况下 100%可靠地执行锁定。
创建一个新的数据目录
使用此方法,数据目录的状态与您首次安装 MySQL 时相同,并且具有默认的 MySQL 帐户集和没有用户数据。
在 Unix 上,初始化数据目录。参见第 2.9 节,“安装后设置和测试”。
在 Windows 上,数据目录包含在 MySQL 发行版中:
-
Windows 的 MySQL Zip 存档发行版包含一个未修改的数据目录。您可以将这样的发行版解压缩到临时位置,然后将
data
目录复制到您设置新实例的位置。 -
Windows MSI 软件包安装程序创建并设置安装的服务器使用的数据目录,但还创建一个干净的“模板”数据目录,名称为
data
,位于安装目录下。使用 MSI 软件包执行安装后,可以复制模板数据目录以设置其他 MySQL 实例。
复制现有数据目录。
使用此方法,数据目录中存在的任何 MySQL 帐户或用户数据都将转移到新的数据目录。
-
停止使用数据目录的现有 MySQL 实例。这必须是一个干净的关闭,以便实例将任何未决更改刷新到磁盘。
-
将数据目录复制到新数据目录应该所在的位置。
-
复制现有实例使用的
my.cnf
或my.ini
选项文件。这将作为新实例的基础。 -
修改新的选项文件,使得任何指向原数据目录的路径都指向新数据目录。同时,修改任何其他必须在每个实例中唯一的选项,比如 TCP/IP 端口号和日志文件。有关必须在每个实例中唯一的参数列表,请参见第 7.8 节,“在一台机器上运行多个 MySQL 实例”。
-
启动新实例,并告诉它使用新的选项文件。
7.8.2 在 Windows 上运行多个 MySQL 实例
原文:
dev.mysql.com/doc/refman/8.0/en/multiple-windows-servers.html
7.8.2.1 在 Windows 命令行上启动多个 MySQL 实例
7.8.2.2 启动多个 MySQL 实例作为 Windows 服务
您可以通过从命令行手动启动每个具有适当操作参数的服务器,或者通过将多个服务器安装为 Windows 服务并以此方式运行它们,在 Windows 上运行多个服务器。有关从命令行或作为服务运行 MySQL 的一般说明,请参见第 2.3 节,“在 Microsoft Windows 上安装 MySQL”。以下各节描述了如何为每个服务器启动具有不同值的那些必须对每个服务器唯一的选项,例如数据目录。这些选项在第 7.8 节,“在一台机器上运行多个 MySQL 实例”中列出。
原文:
dev.mysql.com/doc/refman/8.0/en/multiple-windows-command-line-servers.html
7.8.2.1 在 Windows 命令行上启动多个 MySQL 实例
从命令行手动启动单个 MySQL 服务器的过程在 Section 2.3.4.6, “Starting MySQL from the Windows Command Line”中有描述。要以这种方式启动多个服务器,可以在命令行或选项文件中指定适当的选项。将选项放在选项文件中更方便,但必须确保每个服务器都有自己的选项集。为此,请为每个服务器创建一个选项文件,并在运行时使用--defaults-file
选项告诉服务器文件名。
假设您想要在端口 3307 上运行一个mysqld实例,数据目录为C:\mydata1
,另一个实例在端口 3308 上,数据目录为C:\mydata2
。使用以下步骤:
-
确保每个数据目录都存在,包括包含授权表的
mysql
数据库的副本。 -
创建两个选项文件。例如,创建一个名为
C:\my-opts1.cnf
的文件,内容如下:[mysqld] datadir = C:/mydata1 port = 3307
创建第二个名为
C:\my-opts2.cnf
的文件,内容如下:[mysqld] datadir = C:/mydata2 port = 3308
-
使用
--defaults-file
选项启动每个服务器时都使用自己的选项文件:C:\> C:\mysql\bin\mysqld --defaults-file=C:\my-opts1.cnf C:\> C:\mysql\bin\mysqld --defaults-file=C:\my-opts2.cnf
每个服务器都在前台运行(直到服务器稍后退出时不会出现新提示),因此您需要在单独的控制台窗口中发出这两个命令。
要关闭服务器,请使用适当的端口号连接到每个服务器:
C:\> C:\mysql\bin\mysqladmin --port=3307 --host=127.0.0.1 --user=root --password shutdown
C:\> C:\mysql\bin\mysqladmin --port=3308 --host=127.0.0.1 --user=root --password shutdown
如上所述配置的服务器允许客户端通过 TCP/IP 连接。如果您的 Windows 版本支持命名管道,并且您还希望允许命名管道连接,请指定启用命名管道的选项并指定其名称。支持命名管道连接的每个服务器必须使用唯一的管道名称。例如,C:\my-opts1.cnf
文件可能如下所示:
[mysqld]
datadir = C:/mydata1
port = 3307
enable-named-pipe
socket = mypipe1
类似地修改C:\my-opts2.cnf
以供第二个服务器使用。然后按照先前描述的方式启动服务器。
对于希望允许共享内存连接的服务器,也适用类似的过程。通过启动具有启用shared_memory
系统变量的服务器,并通过设置shared_memory_base_name
系统变量来为每个服务器指定唯一的共享内存名称,启用此类连接。
原文:
dev.mysql.com/doc/refman/8.0/en/multiple-windows-services.html
7.8.2.2 启动多个 MySQL 实例作为 Windows 服务
在 Windows 上,MySQL 服务器可以作为 Windows 服务运行。有关安装、控制和删除单个 MySQL 服务的过程,请参阅 Section 2.3.4.8, “Starting MySQL as a Windows Service”。
要设置多个 MySQL 服务,您必须确保每个实例除了其他必须针对每个实例唯一的参数外,还使用不同的服务名称。
对于以下说明,假设您想从分别安装在C:\mysql-5.7.9
和C:\mysql-8.0.36
的两个不同版本的 MySQL 运行mysqld服务器。(如果您正在运行 5.7.9 作为生产服务器,但也想使用 8.0.36 进行测试,可能会出现这种情况。)
要将 MySQL 安装为 Windows 服务,请使用--install
或--install-manual
选项。有关这些选项的信息,请参阅 Section 2.3.4.8, “Starting MySQL as a Windows Service”。
根据前面的信息,您有几种设置多个服务的方法。以下说明描述了一些示例。在尝试任何方法之前,请关闭并删除任何现有的 MySQL 服务。
-
方法 1: 在一个标准选项文件中为所有服务指定选项。为此,为每个服务器使用不同的服务名称。假设您想要使用服务名称
mysqld1
运行 5.7.9 mysqld,并使用服务名称mysqld2
运行 8.0.36 mysqld。在这种情况下,您可以为 5.7.9 使用[mysqld1]
组,为 8.0.36 使用[mysqld2]
组。例如,您可以像这样设置C:\my.cnf
:# options for mysqld1 service [mysqld1] basedir = C:/mysql-5.7.9 port = 3307 enable-named-pipe socket = mypipe1 # options for mysqld2 service [mysqld2] basedir = C:/mysql-8.0.36 port = 3308 enable-named-pipe socket = mypipe2
安装服务如下,使用完整的服务器路径名确保 Windows 为每个服务注册正确的可执行程序:
C:\> C:\mysql-5.7.9\bin\mysqld --install mysqld1 C:\> C:\mysql-8.0.36\bin\mysqld --install mysqld2
要启动服务,请使用服务管理器,或使用适当的服务名称使用NET START或SC START:
C:\> SC START mysqld1 C:\> SC START mysqld2
要停止服务,请使用服务管理器,或使用适当的服务名称使用NET STOP或SC STOP:
C:\> SC STOP mysqld1 C:\> SC STOP mysqld2
-
方法 2: 在单独的文件中为每个服务器指定选项,并在安装服务时使用
--defaults-file
告诉每个服务器要使用哪个文件。在这种情况下,每个文件应该使用[mysqld]
组列出选项。使用这种方法,要为 5.7.9 mysqld指定选项,请创建一个看起来像这样的文件
C:\my-opts1.cnf
:[mysqld] basedir = C:/mysql-5.7.9 port = 3307 enable-named-pipe socket = mypipe1
对于 8.0.36 版本的mysqld,创建一个名为
C:\my-opts2.cnf
的文件,内容如下:[mysqld] basedir = C:/mysql-8.0.36 port = 3308 enable-named-pipe socket = mypipe2
安装服务的步骤如下(每个命令单独一行输入):
C:\> C:\mysql-5.7.9\bin\mysqld --install mysqld1 --defaults-file=C:\my-opts1.cnf C:\> C:\mysql-8.0.36\bin\mysqld --install mysqld2 --defaults-file=C:\my-opts2.cnf
当将 MySQL 服务器安装为服务并使用
--defaults-file
选项时,服务名称必须在选项之前。安装服务后,启动和停止服务的方法与前面的示例相同。
要删除多个服务,请对每个服务使用SC DELETE mysqld_service_name
。或者,对每个服务使用mysqld --remove,在--remove
选项后指定服务名称。如果服务名称是默认值(MySQL
),在使用mysqld --remove时可以省略它。
7.8.3 在 Unix 上运行多个 MySQL 实例
原文:
dev.mysql.com/doc/refman/8.0/en/multiple-unix-servers.html
注意
这里的讨论使用 mysqld_safe 来启动多个 MySQL 实例。对于使用 RPM 发行版的 MySQL 安装,在几个 Linux 平台上,服务器的启动和关闭由 systemd 管理。在这些平台上,mysqld_safe 没有安装,因为它是不必要的。有关使用 systemd 处理多个 MySQL 实例的信息,请参见 Section 2.5.9, “Managing MySQL Server with systemd”。
在 Unix 上运行多个 MySQL 实例的一种方法是使用不同的默认 TCP/IP 端口和 Unix 套接字文件编译不同的服务器,以便每个服务器在不同的网络接口上监听。为每个安装编译不同的基目录还会自动为每个服务器生成单独的编译数据目录、日志文件和 PID 文件位置。
假设现有的 5.7 服务器配置为默认的 TCP/IP 端口号(3306)和 Unix 套接字文件(/tmp/mysql.sock
)。要配置一个新的 8.0.36 服务器具有不同的操作参数,可以使用类似以下的 CMake 命令:
$> cmake . -DMYSQL_TCP_PORT=*port_number* \
-DMYSQL_UNIX_ADDR=*file_name* \
-DCMAKE_INSTALL_PREFIX=/usr/local/mysql-8.0.36
这里,port_number
和 file_name
必须与默认的 TCP/IP 端口号和 Unix 套接字文件路径名不同,并且 CMAKE_INSTALL_PREFIX
的值应指定一个安装目录,该目录与现有 MySQL 安装所在的目录不同。
如果您有一个 MySQL 服务器在特定端口号上监听,您可以使用以下命令查找它用于几个重要可配置变量的操作参数,包括基目录和 Unix 套接字文件名:
$> mysqladmin --host=*host_name* --port=*port_number* variables
通过该命令显示的信息,您可以知道在配置额外服务器时不应使用哪些选项值。
如果将 localhost
指定为主机名,mysqladmin 默认使用 Unix 套接字文件而不是 TCP/IP。要明确指定传输协议,请使用 --protocol={TCP|SOCKET|PIPE|MEMORY}
选项。
您无需编译一个新的 MySQL 服务器只是为了使用不同的 Unix 套接字文件和 TCP/IP 端口号开始。也可以使用相同的服务器二进制文件,并在运行时使用不同的参数值启动每个调用。一种方法是使用命令行选项:
$> mysqld_safe --socket=*file_name* --port=*port_number*
要启动第二个服务器,请提供不同的--socket
和 --port
选项值,并通过--datadir=*
dir_name*
选项传递给 mysqld_safe,以便服务器使用不同的数据目录。
或者,将每个服务器的选项放在不同的选项文件中,然后使用--defaults-file
选项启动每个服务器,该选项指定适当选项文件的路径。例如,如果两个服务器实例的选项文件分别命名为 /usr/local/mysql/my.cnf
和 /usr/local/mysql/my.cnf2
,则可以这样启动服务器:命令:
$> mysqld_safe --defaults-file=/usr/local/mysql/my.cnf
$> mysqld_safe --defaults-file=/usr/local/mysql/my.cnf2
实现类似效果的另一种方法是使用环境变量设置 Unix 套接字文件名和 TCP/IP 端口号:
$> MYSQL_UNIX_PORT=/tmp/mysqld-new.sock
$> MYSQL_TCP_PORT=3307
$> export MYSQL_UNIX_PORT MYSQL_TCP_PORT
$> bin/mysqld --initialize --user=mysql
$> mysqld_safe --datadir=/path/to/datadir &
这是一个快速启动第二个用于测试的服务器的方法。这种方法的好处是环境变量设置适用于从同一 shell 中调用的任何客户端程序。因此,这些客户端的连接会自动定向到第二个服务器。
Section 6.9, “环境变量” 包括了您可以使用来影响 MySQL 程序的其他环境变量列表。
在 Unix 系统上,mysqld_multi 脚本提供了另一种启动多个服务器的方式。请参阅 Section 6.3.4, “mysqld_multi — 管理多个 MySQL 服务器”。
7.8.4 在多服务器环境中使用客户端程序
原文:
dev.mysql.com/doc/refman/8.0/en/multiple-server-clients.html
要连接到一个 MySQL 服务器的客户端程序,该服务器监听的网络接口与编译到您的客户端的不同,您可以使用以下方法之一:
-
使用
--host=*
host_name*
--port=*
port_number*
启动客户端,使用 TCP/IP 连接到远程服务器,使用--host=127.0.0.1
--port=*
port_number*
使用 TCP/IP 连接到本地服务器,或使用--host=localhost
--socket=*
file_name*
连接到本地服务器使用 Unix 套接字文件或 Windows 命名管道。 -
启动客户端时使用
--protocol=TCP
以使用 TCP/IP 进行连接,使用--protocol=SOCKET
以使用 Unix 套接字文件进行连接,使用--protocol=PIPE
以使用命名管道进行连接,或使用--protocol=MEMORY
以使用共享内存进行连接。对于 TCP/IP 连接,您可能还需要指定--host
和--port
选项。对于其他类型的连接,您可能需要指定--socket
选项以指定 Unix 套接字文件或 Windows 命名管道名称,或者指定--shared-memory-base-name
选项以指定共享内存名称。共享内存连接仅在 Windows 上受支持。 -
在 Unix 上,在启动客户端之前,将
MYSQL_UNIX_PORT
和MYSQL_TCP_PORT
环境变量设置为指向 Unix 套接字文件和 TCP/IP 端口号。如果您通常使用特定的套接字文件或端口号,您可以将设置这些环境变量的命令放在您的.login
文件中,以便每次登录时应用。参见第 6.9 节,“环境变量”。 -
在选项文件的
[client]
组中指定默认的 Unix 套接字文件和 TCP/IP 端口号。例如,您可以在 Windows 上使用C:\my.cnf
,或在 Unix 上使用您的主目录中的.my.cnf
文件。参见第 6.2.2.2 节,“使用选项文件”。 -
在 C 程序中,你可以在
mysql_real_connect()
调用中指定套接字文件或端口号参数。你也可以通过调用mysql_options()
让程序读取选项文件。参见 C API 基本函数描述。 -
如果你正在使用 Perl 的
DBD::mysql
模块,你可以从 MySQL 选项文件中读取选项。例如:$dsn = "DBI:mysql:test;mysql_read_default_group=client;" . "mysql_read_default_file=/usr/local/mysql/data/my.cnf"; $dbh = DBI->connect($dsn, $user, $password);
查看第 31.9 节,“MySQL Perl API”。
其他编程接口可能提供类似的功能来读取选项文件。
7.9 调试 MySQL
7.9.1 调试 MySQL 服务器
7.9.2 调试 MySQL 客户端
7.9.3 锁定顺序工具
7.9.4 DBUG 包
本节描述了帮助跟踪 MySQL 中问题的调试技术。
7.9.1 调试 MySQL 服务器
7.9.1.1 为调试编译 MySQL
7.9.1.2 创建跟踪文件
7.9.1.3 使用 WER 与 PDB 创建 Windows 崩溃转储
7.9.1.4 在 gdb 下调试 mysqld
7.9.1.5 使用堆栈跟踪
7.9.1.6 使用服务器日志查找 mysqld 中错误的原因
7.9.1.7 如果遇到表损坏,制作可重现的测试用例
如果您正在使用 MySQL 中非常新的功能,可以尝试使用 --skip-new
选项运行 mysqld(该选项禁用所有新的、潜在不安全的功能)。参见 附录 B.3.3.3,“如果 MySQL 一直崩溃怎么办”。
如果 mysqld 不想启动,请验证您没有干扰设置的 my.cnf
文件!您可以使用 mysqld --print-defaults 检查您的 my.cnf
参数,并通过使用 mysqld --no-defaults ... 来避免使用它们。
如果 mysqld 开始占用 CPU 或内存,或者“挂起”,您可以使用 mysqladmin processlist status 查看是否有人执行需要很长时间的查询。如果遇到性能问题或新客户端无法连接的问题,建议在某个窗口中运行 mysqladmin -i10 processlist status。
命令 mysqladmin debug 会将一些关于正在使用的锁、已使用的内存和查询使用情况的信息转储到 MySQL 日志文件中。这可能有助于解决一些问题。即使您没有为调试编译 MySQL,此命令也会提供一些有用的信息!
如果问题是某些表变得越来越慢,您应该尝试使用 OPTIMIZE TABLE
或 myisamchk 优化表。参见 第七章,MySQL 服务器管理。您还应该使用 EXPLAIN
检查慢查询。
你还应该阅读本手册中针对可能在你的环境中独特的问题的特定于操作系统的部分。参见第 2.1 节,“一般安装指导”。
原文:
dev.mysql.com/doc/refman/8.0/en/compiling-for-debugging.html
7.9.1.1 为调试编译 MySQL
如果你遇到了一些非常具体的问题,你可以尝试调试 MySQL。为此,你必须使用-DWITH_DEBUG=1
选项配置 MySQL。你可以通过执行mysqld --help来检查 MySQL 是否已启用调试。如果选项中列出了--debug
,则表示已启用调试。mysqladmin ver还会将mysqld版本列为mysql ... --debug。
如果在使用-DWITH_DEBUG=1
CMake 选项配置时,mysqld停止崩溃,那么你可能已经发现了 MySQL 中的编译器错误或定时错误。在这种情况下,你可以尝试使用CMAKE_C_FLAGS
和CMAKE_CXX_FLAGS
CMake 选项添加-g
,而不使用-DWITH_DEBUG=1
。如果mysqld崩溃,你至少可以使用gdb附加到它,或者使用gdb在核心文件上找出发生了什么。
当你为调试配置 MySQL 时,会自动启用许多额外的安全检查功能,用于监视mysqld的健康状况。如果发现了“意外”情况,会将条目写入stderr
,而这会被mysqld_safe重定向到错误日志!这也意味着,如果你在使用源代码分发时遇到了一些意外问题,你应该首先为 MySQL 配置调试。如果你认为自己发现了一个错误,请按照第 1.5 节“如何报告错误或问题”中的说明操作。
在 Windows MySQL 发行版中,默认情况下,mysqld.exe
是使用跟踪文件支持编译的。
7.9.1.2 创建跟踪文件
如果mysqld服务器无法启动或容易崩溃,您可以尝试创建一个跟踪文件来查找问题。
要执行此操作,您必须使用已编译调试支持的mysqld。您可以通过执行 mysqld -V
来检查这一点。如果版本号以 -debug
结尾,则编译时支持跟踪文件。(在 Windows 上,调试服务器的名称为mysqld-debug而不是mysqld。)
在 Unix 上以 /tmp/mysqld.trace
或在 Windows 上以 \mysqld.trace
的方式启动mysqld服务器:
$> mysqld --debug
在 Windows 上,您还应该使用--standalone
标志,以便不将mysqld作为服务启动。在控制台窗口中,使用以下命令:
C:\> mysqld-debug --debug --standalone
之后,您可以在第二个控制台窗口中使用 mysql.exe
命令行工具重现问题。您可以使用mysqladmin shutdown停止mysqld服务器。
跟踪文件可能会变得非常大!要生成较小的跟踪文件,您可以使用类似以下的调试选项:
mysqld --debug=d,info,error,query,general,where:O,/tmp/mysqld.trace
这仅将最有趣的标签信息打印到跟踪文件中。
如果您提交错误报告,请仅将跟踪文件中指示出错位置的那些行添加到错误报告中。如果找不到错误位置,请打开错误报告并将整个跟踪文件上传到报告中,以便 MySQL 开发人员查看。有关说明,请参见第 1.5 节,“如何报告错误或问题”。
跟踪文件是由 Fred Fish 的 DBUG
软件包制作的。请参见第 7.9.4 节,“DBUG 软件包”。
原文:
dev.mysql.com/doc/refman/8.0/en/making-windows-dumps.html
7.9.1.3 使用 WER 与 PDB 创建 Windows 崩溃转储
程序数据库文件(带有后缀pdb
)包含在 MySQL 的ZIP Archive Debug Binaries & Test Suite分发中。这些文件提供了在出现问题时调试 MySQL 安装的信息。这是与标准 MSI 或 Zip 文件分开下载的。
注意
PDB 文件包含在一个名为"ZIP Archive Debug Binaries & Test Suite"的单独文件中。
PDB 文件包含有关mysqld
和其他工具的更详细信息,可以创建更详细的跟踪和转储文件。您可以使用这些文件与WinDbg或 Visual Studio 一起调试mysqld。
有关 PDB 文件的更多信息,请参阅Microsoft Knowledge Base Article 121366。有关可用的调试选项的更多信息,请参阅Windows 调试工具。
要使用 WinDbg,要么安装完整的 Windows 驱动程序套件(WDK),要么安装独立版本。
重要
.exe
和.pdb
文件必须完全匹配(版本号和 MySQL 服务器版本);否则,WinDBG 在尝试加载符号时会报错。
-
要生成一个 minidump
mysqld.dmp
,请在my.ini
中的[mysqld]部分下启用core-file
选项。在进行这些更改后重新启动 MySQL 服务器。 -
创建一个目录来存储生成的文件,例如
c:\symbols
-
使用查找 GUI 或命令行确定您的windbg.exe可执行文件的路径,例如:
dir /s /b windbg.exe
-- 一个常见的默认路径是C:\Program Files\Debugging Tools for Windows (x64)\windbg.exe -
启动
windbg.exe
,并将mysqld.exe
、mysqld.pdb
、mysqld.dmp
和源代码的路径传递给它。或者,从 WinDbg GUI 中传递每个路径。例如:windbg.exe -i "C:\mysql-8.0.36-winx64\bin\"^ -z "C:\mysql-8.0.36-winx64\data\mysqld.dmp"^ -srcpath "E:\ade\mysql_archives\8.0\8.0.36\mysql-8.0.36"^ -y "C:\mysql-8.0.36-winx64\bin;SRV*c:\symbols*http://msdl.microsoft.com/download/symbols"^ -v -n -c "!analyze -vvvvv"
注意
Windows 命令行处理器会删除
^
字符和换行符,因此请确保空格保持完整。
7.9.1.4 在 gdb 下调试 mysqld
在大多数系统上,你也可以从 gdb 中启动 mysqld 以获取更多信息,如果 mysqld 崩溃。
在 Linux 上,如果你想要调试 mysqld 线程,一些较旧的 gdb 版本必须使用 run --one-thread
。在这种情况下,你一次只能有一个线程处于活动状态。
在 gdb 下运行 MySQL 时,NPTL 线程(Linux 上的新线程库)可能会导致问题。一些症状包括:
-
mysqld 在启动过程中挂起(在写入
ready for connections
之前)。 -
mysqld 在调用
pthread_mutex_lock()
或pthread_mutex_unlock()
时崩溃。
在这种情况下,在启动 gdb 之前,你应该在 shell 中设置以下环境变量:
LD_ASSUME_KERNEL=2.4.1
export LD_ASSUME_KERNEL
在 gdb 下运行 mysqld 时,你应该使用 --skip-stack-trace
禁用堆栈跟踪,以便在 gdb 中捕获段错误。
使用 --gdb
选项为 mysqld 安装一个 SIGINT
中断处理程序(用于使用 ^C
停止 mysqld 设置断点),并禁用堆栈跟踪和核心文件处理。
如果你一直在 gdb 下进行大量新连接,那么在 gdb 中不释放旧线程的内存,会导致在 gdb 下调试 MySQL 非常困难。你可以通过将 mysqld 的 thread_cache_size
设置为 max_connections
+ 1 的值来避免这个问题。在大多数情况下,只需使用 --thread_cache_size=5'
就会有很大帮助!
如果在 Linux 上 mysqld 因 SIGSEGV 信号而崩溃,你可以使用 --core-file
选项启动 mysqld 以获取核心转储文件。这个核心文件可以用来生成回溯,帮助你找出 mysqld 为何崩溃:
$> gdb mysqld core
gdb> backtrace full
gdb> quit
参见 Section B.3.3.3, “What to Do If MySQL Keeps Crashing”。
如果你在 Linux 上使用 gdb,你应该在当前目录中安装一个 .gdb
文件,包含以下信息:
set print sevenbit off
handle SIGUSR1 nostop noprint
handle SIGUSR2 nostop noprint
handle SIGWAITING nostop noprint
handle SIGLWP nostop noprint
handle SIGPIPE nostop
handle SIGALRM nostop
handle SIGHUP nostop
handle SIGTERM nostop noprint
这里是一个调试 mysqld 的示例:
$> gdb /usr/local/libexec/mysqld
gdb> run
...
backtrace full # Do this when mysqld crashes
将上述输出包含在一个 bug 报告中,你可以按照 Section 1.5, “How to Report Bugs or Problems” 中的说明进行报告。
如果 mysqld 卡住了,你可以尝试使用一些系统工具如 strace
或 /usr/proc/bin/pstack
来查看 mysqld 卡在哪里。
strace /tmp/log libexec/mysqld
如果你正在使用 Perl DBI
接口,可以通过使用 trace
方法或设置 DBI_TRACE
环境变量来打开调试信息。
7.9.1.5 使用堆栈跟踪
在一些操作系统上,如果mysqld意外死机,错误日志中会包含堆栈跟踪。您可以使用这个来找出mysqld死机的地方(也许是为什么)。参见 Section 7.4.2,“错误日志”。要获得堆栈跟踪,您不能使用 gcc 的-fomit-frame-pointer
选项来编译mysqld。参见 Section 7.9.1.1,“为调试编译 MySQL”。
错误日志中的堆栈跟踪看起来像这样:
mysqld got signal 11;
Attempting backtrace. You can use the following information
to find out where mysqld died. If you see no messages after
this, something went terribly wrong...
stack_bottom = 0x41fd0110 thread_stack 0x40000
mysqld(my_print_stacktrace+0x32)[0x9da402]
mysqld(handle_segfault+0x28a)[0x6648e9]
/lib/libpthread.so.0[0x7f1a5af000f0]
/lib/libc.so.6(strcmp+0x2)[0x7f1a5a10f0f2]
mysqld(_Z21check_change_passwordP3THDPKcS2_Pcj+0x7c)[0x7412cb]
mysqld(_ZN16set_var_password5checkEP3THD+0xd0)[0x688354]
mysqld(_Z17sql_set_variablesP3THDP4ListI12set_var_baseE+0x68)[0x688494]
mysqld(_Z21mysql_execute_commandP3THD+0x41a0)[0x67a170]
mysqld(_Z11mysql_parseP3THDPKcjPS2_+0x282)[0x67f0ad]
mysqld(_Z16dispatch_command19enum_server_commandP3THDPcj+0xbb7[0x67fdf8]
mysqld(_Z10do_commandP3THD+0x24d)[0x6811b6]
mysqld(handle_one_connection+0x11c)[0x66e05e]
如果跟踪的函数名解析失败,跟踪将包含较少信息:
mysqld got signal 11;
Attempting backtrace. You can use the following information
to find out where mysqld died. If you see no messages after
this, something went terribly wrong...
stack_bottom = 0x41fd0110 thread_stack 0x40000
[0x9da402]
[0x6648e9]
[0x7f1a5af000f0]
[0x7f1a5a10f0f2]
[0x7412cb]
[0x688354]
[0x688494]
[0x67a170]
[0x67f0ad]
[0x67fdf8]
[0x6811b6]
[0x66e05e]
较新版本的glibc
堆栈跟踪函数也会打印相对于对象的地址。在基于glibc
的系统(Linux)上,插件意外退出的跟踪看起来像这样:
plugin/auth/auth_test_plugin.so(+0x9a6)[0x7ff4d11c29a6]
要将相对地址(+0x9a6
)翻译成文件名和行号,请使用以下命令:
$> addr2line -fie auth_test_plugin.so 0x9a6
auth_test_plugin
mysql-trunk/plugin/auth/test_plugin.c:65
addr2line实用程序是 Linux 上binutils
软件包的一部分。
在 Solaris 上,该过程类似。Solaris 的printstack()
已经打印了相对地址:
plugin/auth/auth_test_plugin.so:0x1510
要进行翻译,请使用以下命令:
$> gaddr2line -fie auth_test_plugin.so 0x1510
mysql-trunk/plugin/auth/test_plugin.c:88
Windows 已经打印了地址、函数名和行号:
000007FEF07E10A4 auth_test_plugin.dll!auth_test_plugin()[test_plugin.c:72]
7.9.1.6 使用服务器日志查找mysqld错误原因
在启用了一般查询日志的情况下启动mysqld之前,你应该使用myisamchk检查所有表。参见第七章,MySQL 服务器管理。
如果mysqld死机或挂起,你应该启动启用了一般查询日志的mysqld。参见第 7.4.3 节,“一般查询日志”。当mysqld再次死机时,你可以检查日志文件末尾的查询,看看是哪个查询导致了mysqld死机。
如果使用默认的一般查询日志文件,日志将存储在数据库目录中,文件名为*
host_name*.log
。在大多数情况下,导致mysqld死机的是日志文件中的最后一个查询,但如果可能的话,你应该通过重新启动mysqld并从mysql命令行工具执行找到的查询来验证这一点。如果这样可以,你还应该测试所有未完成的复杂查询。
你也可以尝试对所有执行时间较长的SELECT
语句使用EXPLAIN
命令,以确保mysqld正确使用索引。参见第 15.8.2 节,“EXPLAIN 语句”。
通过启用慢查询日志,你可以找到执行时间较长的查询。参见第 7.4.5 节,“慢查询日志”。
如果在错误日志中找到文本mysqld restarted
(通常是一个名为*
host_name*.err
的文件),那么你可能已经找到了导致mysqld失败的查询。如果发生这种情况,你应该使用myisamchk检查所有表(参见第七章,MySQL 服务器管理),并测试 MySQL 日志文件中的查询是否有失败的情况。如果找到这样的查询,首先尝试升级到最新的 MySQL 版本。如果这没有帮助,请报告一个 bug,参见第 1.5 节,“如何报告错误或问题”。
如果你使用myisam_recover_options
系统变量启动了mysqld,MySQL 会自动检查并尝试修复MyISAM
表,如果它们被标记为'未正确关闭'或'崩溃'。如果发生这种情况,MySQL 会在hostname.err
文件中写入一个条目'Warning: Checking table ...'
,接着是Warning: Repairing table
,如果需要修复表。如果你在mysqld在意外死机之前没有出现大量这些错误,那么可能出现了问题,需要进一步调查。参见 Section 7.1.7, “Server Command Options”。
当服务器检测到MyISAM
表损坏时,它会将额外信息写入错误日志,例如源文件的名称和行号,以及访问该表的线程列表。例如:Got an error from thread_id=1, mi_dynrec.c:368
。这是在错误报告中包含的有用信息。
如果mysqld意外死机,这并不是一个好兆头,但在这种情况下,你不应该调查Checking table...
的消息,而是应该尝试找出mysqld为何死机。
原文:
dev.mysql.com/doc/refman/8.0/en/reproducible-test-case.html
7.9.1.7 创建测试用例,如果您遇到表格损坏
以下过程适用于MyISAM
表格。有关遇到InnoDB
表格损坏时应采取的步骤的信息,请参阅第 1.5 节“如何报告错误或问题”。
如果遇到损坏的MyISAM
表格,或者mysqld在一些更新语句后总是失败,您可以通过以下方式测试问题是否可重现:
-
使用mysqladmin shutdown停止 MySQL 守护程序。
-
备份表格以防修复出现问题的极小可能性。
-
使用myisamchk -s database/*.MYI检查所有表格。使用myisamchk -r database/
table
.MYI修复任何损坏的表格。 -
备份表格的第二份备份。
-
如果需要更多空间,请删除(或移动)MySQL 数据目录中的任何旧日志文件。
-
启动mysqld并启用二进制日志。如果要找到导致mysqld崩溃的语句,还应该启用常规查询日志。请参阅第 7.4.3 节“常规查询日志”和第 7.4.4 节“二进制日志”。
-
当您遇到崩溃的表格时,停止mysqld服务器。
-
恢复备份。
-
重新启动mysqld服务器,不启用二进制日志。
-
使用mysqlbinlog binary-log-file | mysql重新执行语句。二进制日志保存在 MySQL 数据库目录中,名称为
hostname-bin.*
NNNNNN*
。 -
如果表格再次损坏或者您可以通过上述命令使mysqld停止运行,那么您已经找到了一个可重现的错误。按照第 1.5 节“如何报告错误或问题”中给出的说明,将表格和二进制日志通过 FTP 上传到我们的错误数据库。如果您是支持客户,您可以使用 MySQL 客户支持中心(
www.mysql.com/support/
)通知 MySQL 团队有关问题,并尽快修复。
7.9.2 调试 MySQL 客户端
要能够使用集成的调试包调试 MySQL 客户端,您应该使用-DWITH_DEBUG=1
配置 MySQL。参见 Section 2.8.7, “MySQL Source-Configuration Options”。
在运行客户端之前,您应该设置MYSQL_DEBUG
环境变量:
$> MYSQL_DEBUG=d:t:O,/tmp/client.trace
$> export MYSQL_DEBUG
这会导致客户端在/tmp/client.trace
中生成一个跟踪文件。
如果您在自己的客户端代码中遇到问题,您应该尝试连接到服务器,并使用已知可工作的客户端运行您的查询。通过在调试模式下运行mysql(假设您已经使用调试编译 MySQL)来执行此操作:
$> mysql --debug=d:t:O,/tmp/client.trace
这将为您提供有用的信息,以防您发送错误报告。请参见 Section 1.5, “How to Report Bugs or Problems”。
如果您的客户端在某些看起来“合法”的代码处崩溃,您应该检查您的mysql.h
包含文件是否与您的 MySQL 库文件匹配。一个非常常见的错误是在新的 MySQL 库中使用来自旧的 MySQL 安装的旧mysql.h
文件。
7.9.3 LOCK_ORDER 工具
MySQL 服务器是一个使用许多内部锁定和与锁相关的原语的多线程应用程序,例如互斥锁,rwlocks(包括 prlocks 和 sxlocks),条件和文件。在服务器内部,锁相关对象的集合随着实现新功能和代码重构以提高性能而改变。与使用锁定原语的任何多线程应用程序一样,当同时持有多个锁时,在执行期间遇到死锁的风险始终存在。对于 MySQL,死锁的影响是灾难性的,会导致完全丧失服务。
从 MySQL 8.0.17 开始,为了启用锁获取死锁检测和确保运行时执行不包含死锁的强制执行,MySQL 支持LOCK_ORDER
工具。这使得可以定义锁顺序依赖图作为服务器设计的一部分,并且服务器运行时检查以确保锁获取是无环的,并且执行路径符合图形。
本节提供有关使用LOCK_ORDER
工具的信息,但仅在基本水平上。有关完整详情,请参阅 MySQL Server Doxygen 文档中的 Lock Order 部分,网址为dev.mysql.com/doc/index-other.html
。
LOCK_ORDER
工具旨在用于调试服务器,而非生产使用。
要使用LOCK_ORDER
工具,请按照以下步骤进行:
-
从源代码构建 MySQL,并使用
-DWITH_LOCK_ORDER=ON
CMake选项进行配置,以便构建包含LOCK_ORDER
工具的内容。注意
启用
WITH_LOCK_ORDER
选项后,MySQL 构建需要flex程序。 -
要在启用
LOCK_ORDER
工具的情况下运行服务器,请在服务器启动时启用lock_order
系统变量。还有其他用于LOCK_ORDER
配置的系统变量可用。 -
对于 MySQL 测试套件操作,mysql-test-run.pl具有一个
--lock-order
选项,用于控制在测试用例执行期间是否启用LOCK_ORDER
工具。
以下描述的系统变量配置LOCK_ORDER
工具的操作,假设 MySQL 已构建包含LOCK_ORDER
工具。主要变量是lock_order
,指示是否在运行时启用LOCK_ORDER
工具:
-
如果
lock_order
被禁用(默认情况下),则其他LOCK_ORDER
系统变量不会产生任何影响。 -
如果启用了
lock_order
,则其他系统变量配置哪些LOCK_ORDER
功能要启用。
注意
通常情况下,应通过使用 --lock-order
选项执行 mysql-test-run.pl 来配置 LOCK_ORDER
工具,并让 mysql-test-run.pl 将 LOCK_ORDER
系统变量设置为适当的值。
所有 LOCK_ORDER
系统变量必须在服务器启动时设置。在运行时,它们的值是可见的,但不能更改。
一些系统变量存在成对出现,例如 lock_order_debug_loop
和 lock_order_trace_loop
。对于这样的成对变量,在发生与其关联的条件时,这些变量的区别如下:
-
如果启用了
_debug_
变量,则会引发调试断言。 -
如果启用了
_trace_
变量,则会将错误打印到日志中。
表 7.8 LOCK_ORDER 系统变量摘要
变量名称 | 变量类型 | 变量范围 |
---|---|---|
lock_order | 布尔值 | 全局 |
lock_order_debug_loop | 布尔值 | 全局 |
lock_order_debug_missing_arc | 布尔值 | 全局 |
lock_order_debug_missing_key | 布尔值 | 全局 |
lock_order_debug_missing_unlock | 布尔值 | 全局 |
lock_order_dependencies | 文件名 | 全局 |
lock_order_extra_dependencies | 文件名 | 全局 |
lock_order_output_directory | 目录名 | 全局 |
lock_order_print_txt | 布尔值 | 全局 |
lock_order_trace_loop | 布尔值 | 全局 |
lock_order_trace_missing_arc | 布尔值 | 全局 |
lock_order_trace_missing_key | 布尔值 | 全局 |
lock_order_trace_missing_unlock | 布尔值 | 全局 |
变量名称 | 变量类型 | 变量范围 |
-
lock_order
命令行格式 --lock-order[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order
范围 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 OFF
是否在运行时启用
LOCK_ORDER
工具。如果禁用了lock_order
,则不会影响任何其他LOCK_ORDER
系统变量。如果启用了lock_order
,则其他系统变量配置哪些LOCK_ORDER
功能启用。如果启用了
lock_order
,则如果服务器遇到未在锁定顺序图中声明的锁定获取序列,将引发错误。 -
lock_order_debug_loop
命令行格式 --lock-order-debug-loop[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order_debug_loop
范围 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 OFF
当
LOCK_ORDER
工具遇到在锁定顺序图中标记为循环的依赖关系时,是否导致调试断言失败。 -
lock_order_debug_missing_arc
命令行格式 --lock-order-debug-missing-arc[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order_debug_missing_arc
范围 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 OFF
当
LOCK_ORDER
工具遇到在锁定顺序图中未声明的依赖关系时,是否导致调试断言失败。 -
lock_order_debug_missing_key
命令行格式 --lock-order-debug-missing-key[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order_debug_missing_key
范围 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 OFF
当
LOCK_ORDER
工具遇到未正确使用性能模式仪器的对象时,是否导致调试断言失败。 -
lock_order_debug_missing_unlock
命令行格式 --lock-order-debug-missing-unlock[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order_debug_missing_unlock
范围 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 OFF
LOCK_ORDER
工具在遇到仍在持有的锁被销毁时是否导致调试断言失败。 -
lock_order_dependencies
命令行格式 --lock-order-dependencies=file_name
引入版本 8.0.17 系统变量 lock_order_dependencies
作用域 全局 动态 否 SET_VAR
提示适用否 类型 文件名 默认值 空字符串
lock_order_dependencies.txt
文件的路径,定义了服务器锁定顺序依赖图。可以指定不使用任何依赖项。在这种情况下,将使用空依赖图。
-
lock_order_extra_dependencies
命令行格式 --lock-order-extra-dependencies=file_name
引入版本 8.0.17 系统变量 lock_order_extra_dependencies
作用域 全局 动态 否 SET_VAR
提示适用否 类型 文件名 默认值 空字符串
包含额外依赖项的文件路径,用于修改锁定顺序依赖图的主要服务器依赖图。这对于使用额外依赖项描述第三方代码行为很有用,而不是修改
lock_order_dependencies.txt
文件本身(这是不鼓励的替代方法)。如果未设置此变量,则不使用辅助文件。
-
lock_order_output_directory
命令行格式 --lock-order-output-directory=dir_name
���入版本 8.0.17 系统变量 lock_order_output_directory
作用域 全局 动态 否 SET_VAR
提示适用否 类型 目录名称 默认值 空字符串
LOCK_ORDER
工具写入其日志的目录。如果未设置此变量,则默认为当前目录。 -
lock_order_print_txt
命令行格式 --lock-order-print-txt[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order_print_txt
作用域 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 OFF
LOCK_ORDER
工具是否执行锁定顺序图分析并打印文本报告。报告包括检测到的任何锁获取循环。 -
lock_order_trace_loop
命令行格式 --lock-order-trace-loop[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order_trace_loop
作用范围 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 OFF
LOCK_ORDER
工具在遇到在锁序图中标记为循环的依赖关系时是否在日志文件中打印跟踪信息。 -
lock_order_trace_missing_arc
命令行格式 --lock-order-trace-missing-arc[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order_trace_missing_arc
作用范围 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 ON
LOCK_ORDER
工具在遇到在锁序图中未声明的依赖关系时是否在日志文件中打印跟踪信息。 -
lock_order_trace_missing_key
命令行格式 --lock-order-trace-missing-key[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order_trace_missing_key
作用范围 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 OFF
LOCK_ORDER
工具在遇到未正确使用性能模式进行仪表化的对象时是否在日志文件中打印跟踪信息。 -
lock_order_trace_missing_unlock
命令行格式 --lock-order-trace-missing-unlock[={OFF|ON}]
引入版本 8.0.17 系统变量 lock_order_trace_missing_unlock
作用范围 全局 动态 否 SET_VAR
提示适用否 类型 布尔值 默认值 ON
LOCK_ORDER
工具在遇到一个被销毁但仍被持有的锁时是否在日志文件中打印跟踪信息。
7.9.4 DBUG
包
MySQL 服务器和大多数 MySQL 客户端都是使用由 Fred Fish 最初创建的 DBUG
包编译的。当您为调试配置 MySQL 时,此包使得可以获得程序正在执行的跟踪文件。参见 Section 7.9.1.2, “Creating Trace Files”。
本节总结了在使用带有调试支持的 MySQL 程序时可以在命令行上指定的调试选项的参数值。
DBUG
包可以通过使用 --debug[=*
debug_options*]
或 -# [*
debug_options*]
选项来调用程序。如果您在不指定 debug_options
值的情况下指定 --debug
或 -#
选项,大多数 MySQL 程序会使用默认值。在 Unix 上,服务器默认值为 d:t:i:o,/tmp/mysqld.trace
,在 Windows 上为 d:t:i:O,\mysqld.trace
。这个默认值的效果是:
-
d
: 启用所有调试宏的输出 -
t
: 跟踪函数调用和退出 -
i
: 在输出行中添加 PID -
o,/tmp/mysqld.trace
,O,\mysqld.trace
: 设置调试输出文件。
大多数客户端程序使用默认的 debug_options
值为 d:t:o,/tmp/*
program_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
对于 mysqld,还可以通过设置 debug
系统变量在运行时更改 DBUG
设置。此变量具有全局和会话值:
mysql> SET GLOBAL debug = '*debug_options*';
mysql> SET SESSION debug = '*debug_options*';
更改全局 debug
值需要足够设置全局系统变量的权限。更改会话 debug
值需要足够设置受限会话系统变量的权限。参见 Section 7.1.9.1, “System Variable Privileges”。
debug_options
值是一个以冒号分隔的字段序列:
field_1:field_2:...:field_*N*
值内的每个字段由一个必需的标志字符组成,可选地前面跟着一个 +
或 -
字符,并可选地后跟着一个逗号分隔的修饰符列表:
[+|-]flag[,modifier,modifier,...,modifier]
以下表描述了允许的标志字符。未识别的标志字符会被静默忽略。
标志 | 描述 |
---|---|
d |
启用当前状态下 DBUG_* XXX* 宏的输出。可以跟随一个关键字列表,仅为具有该关键字的 DBUG 宏启用输出。空关键字列表为所有宏启用输出。在 MySQL 中,常见的调试宏关键字包括 enter 、exit 、error 、warning 、info 和 loop 。 |
D |
每个调试器输出行后延迟。参数是延迟时间,以秒为单位,取决于机器能力。例如,D,20 指定了两秒的延迟。 |
f |
将调试、跟踪和性能分析限制为命名函数列表。空列表启用所有函数。仍然必须给出适当的d 或t 标志;此标志仅限制它们的操作(如果它们已启用)。 |
F |
为每行调试或跟踪输出标识源文件名。 |
i |
为每行调试或跟踪输出标识具有 PID 或线程 ID 的进程。 |
L |
为每行调试或跟踪输出标识源文件行号。 |
n |
为每行调试或跟踪输出打印当前函数嵌套深度。 |
N |
为每行调试输出编号。 |
o |
重定向调试器输出流到指定文件。默认输出为stderr 。 |
O |
类似于o ,但文件在每次写入之间真正刷新。在需要时,文件在每次写入之间关闭并重新打开。 |
a |
类似于o ,但用于追加打开。 |
A |
类似于O ,但用于追加打开。 |
p |
将调试器操作限制为指定的进程。必须使用DBUG_PROCESS 宏标识进程,并且匹配列表中的一个进程才会发生调试器操作。 |
P |
为每行调试或跟踪输出打印当前进程名称。 |
r |
推送新状态时,不继承先前状态的函数嵌套级别。当输出从左边距开始时很有用。 |
t |
启用函数调用/退出跟踪行。可以跟随一个列表(仅包含一个修饰符),给出一个数字最大跟踪级别,超过该级别,不会发生任何输出,无论是调试还是跟踪宏。默认是编译时选项。 |
T |
为每行输出打印当前时间戳。 |
标志 | 描述 |
前导的+
或-
字符和修饰符列表用于像d
或f
这样的标志字符,可以为所有适用的修饰符启用调试操作或仅为其中一些启用:
-
没有前导的
+
或-
,标志值将准确设置为给定的修饰符列表。 -
以前导的
+
或-
,列表中的修饰符将被添加到或从当前修饰符列表中减去。
以下示例展示了d
标志的工作原理。空的d
列表启用了所有调试宏的输出。非空列表仅启用列表中的宏关键字的输出。
这些语句将d
值设置为给定的修饰符列表:
mysql> SET debug = 'd';
mysql> SELECT @@debug;
+---------+
| @@debug |
+---------+
| d |
+---------+
mysql> SET debug = 'd,error,warning';
mysql> SELECT @@debug;
+-----------------+
| @@debug |
+-----------------+
| d,error,warning |
+-----------------+
前导的+
或-
会增加或减去当前d
值:
mysql> SET debug = '+d,loop';
mysql> SELECT @@debug;
+----------------------+
| @@debug |
+----------------------+
| d,error,warning,loop |
+----------------------+
mysql> SET debug = '-d,error,loop';
mysql> SELECT @@debug;
+-----------+
| @@debug |
+-----------+
| d,warning |
+-----------+
添加到“启用所有宏”会导致没有变化:
mysql> SET debug = 'd';
mysql> SELECT @@debug;
+---------+
| @@debug |
+---------+
| d |
+---------+
mysql> SET debug = '+d,loop';
mysql> SELECT @@debug;
+---------+
| @@debug |
+---------+
| d |
+---------+
禁用所有已启用的宏会完全禁用d
标志:
mysql> SET debug = 'd,error,loop';
mysql> SELECT @@debug;
+--------------+
| @@debug |
+--------------+
| d,error,loop |
+--------------+
mysql> SET debug = '-d,error,loop';
mysql> SELECT @@debug;
+---------+
| @@debug |
+---------+
| |
+---------+
第八章 安全
目录
8.1 一般安全问题
8.1.1 安全指南
8.1.2 保持密码安全
8.1.3 使 MySQL 免受攻击者攻击
8.1.4 与安全相关的 mysqld 选项和变量
8.1.5 如何以普通用户身份运行 MySQL
8.1.6 LOAD DATA LOCAL 的安全注意事项
8.1.7 客户端编程安全指南
8.2 访问控制和帐户管理
8.2.1 帐户用户名和密码
8.2.2 MySQL 提供的权限
8.2.3 授权表
8.2.4 指定帐户名称
8.2.5 指定角色名称
8.2.6 访问控制,第一阶段:连接验证
8.2.7 访问控制,第二阶段:请求验证
8.2.8 添加帐户,分配权限和删除帐户
8.2.9 保留帐户
8.2.10 使用角色
8.2.11 帐户类别
8.2.12 使用部分撤销进行权限限制
8.2.13 权限更改何时生效
8.2.14 分配帐户密码
8.2.15 密码管理
8.2.16 服务器处理过期密码
8.2.17 可插拔认证
8.2.18 多因素认证
8.2.19 代理用户
8.2.20 帐户锁定
8.2.21 设置帐户资源限制
8.2.22 解决连接到 MySQL 的问题
8.2.23 基于 SQL 的帐户活动审计
8.3 使用加密连接
8.3.1 配置 MySQL 使用加密连接
8.3.2 加密连接 TLS 协议和密码
8.3.3 创建 SSL 和 RSA 证书和密钥
8.3.4 通过 SSH 从 Windows 远程连接到 MySQL
8.3.5 重用 SSL 会话
8.4 安全组件和插件
8.4.1 认证插件
8.4.2 连接控制插件
8.4.3 密码验证组件
8.4.4 MySQL 密钥环
8.4.5 MySQL 企业审计
8.4.6 审计消息组件
8.4.7 MySQL 企业防火墙
8.5 MySQL 企业数据脱敏和去标识化
8.5.1 数据脱敏组件与数据脱敏插件
8.5.2 MySQL 企业数据脱敏和去标识化组件
8.5.3 MySQL 企业数据脱敏和去标识化插件
8.6 MySQL 企业加密
8.6.1 MySQL 企业加密安装和升级
8.6.2 配置 MySQL 企业加密
8.6.3 MySQL 企业加密用法和示例
8.6.4 MySQL 企业加密函数参考
8.6.5 MySQL 企业加密组件功能描述
8.6.6 MySQL 企业加密传统功能描述
8.7 SELinux
8.7.1 检查 SELinux 是否已启用
8.7.2 更改 SELinux 模式
8.7.3 MySQL 服务器 SELinux 策略
8.7.4 SELinux 文件上下文
8.7.5 SELinux TCP 端口上下文
8.7.6 SELinux 故障排除
8.8 FIPS 支持
在考虑 MySQL 安装中的安全性时,您应该考虑一系列可能的主题以及它们如何影响您的 MySQL 服务器和相关应用程序的安全性:
-
影响安全性的一般因素。这些包括选择强密码,不向用户授予不必要的特权,通过防止 SQL 注入和数据损坏来确保应用程序安全,等等。请参阅第 8.1 节,“一般安全问题”。
-
安装本身的安全性。您的安装的数据文件、日志文件和所有应用程序文件应受保护,以确保未经授权的方可读取或写入。更多信息,请参阅第 2.9 节,“安装后设置和测试”。
-
数据库系统本身的访问控制和安全性,包括授予对数据库、视图和存储过程的访问权限的用户和数据库,在数据库中使用的视图和存储过程。更多信息,请参阅第 8.2 节,“访问控制和账户管理”。
-
安全相关插件提供的功能。请参阅第 8.4 节,“安全组件和插件”。
-
MySQL 和您系统的网络安全。安全性与为个别用户授予权限有关,但您可能还希望限制 MySQL 仅在 MySQL 服务器主机上本地可用,或者仅在有限的其他主机上可用。
-
确保您对数据库文件、配置文件和日志文件进行了充分和适当的备份。同时确保您有一个恢复解决方案,并测试您能够成功从备份中恢复信息。参见第九章,备份和恢复。
注意
本章中的几个主题也在安全部署指南中有所涉及,该指南提供了部署 MySQL 企业版服务器通用二进制分发版的程序,具有管理 MySQL 安装安全性功能的特点。
8.1 一般安全问题
原文:
dev.mysql.com/doc/refman/8.0/en/general-security-issues.html
8.1.1 安全指南
8.1.2 保护密码安全
8.1.3 使 MySQL 免受攻击的安全措施
8.1.4 与安全相关的 mysqld 选项和变量
8.1.5 如何以普通用户身份运行 MySQL
8.1.6 LOAD DATA LOCAL 的安全注意事项
8.1.7 客户端编程安全指南
本节描述了需要注意的一般安全问题,以及您可以采取哪些措施使您的 MySQL 安装更加安全,以防止遭受攻击或滥用。有关 MySQL 用于设置用户帐户和检查数据库访问权限的访问控制系统的具体信息,请参见第 2.9 节,“安装后设置和测试”。
有关 MySQL 服务器安全问题经常被问到的一些问题的答案,请参见第 A.9 节,“MySQL 8.0 FAQ:安全”。
8.1.1 安全指南
任何在连接到互联网的计算机上使用 MySQL 的人都应该阅读本节,以避免最常见的安全错误。
在讨论安全性时,有必要全面保护整个服务器主机(不仅仅是 MySQL 服务器)免受所有类型的适用攻击:窃听、篡改、回放和拒绝服务。我们在这里不涵盖可用性和容错性的所有方面。
MySQL 基于访问控制列表(ACL)的安全性用于所有用户可能尝试执行的连接、查询和其他操作。还支持 MySQL 客户端和服务器之间的 SSL 加密连接。这里讨论的许多概念并不特定于 MySQL;相同的一般思想适用于几乎所有应用程序。
运行 MySQL 时,请遵循以下准则:
-
绝对不要让任何人(除了 MySQL
root
帐户)访问mysql
系统数据库中的user
表! 这是至关重要的。 -
了解 MySQL 访问权限系统的工作原理(参见 Section 8.2, “Access Control and Account Management”)。使用
GRANT
和REVOKE
语句控制对 MySQL 的访问。不要授予比必要更多的权限。永远不要向所有主机授予权限。检查清单:
-
尝试
mysql -u root
。如果您能够成功连接到服务器而不需要输入密码,任何人都可以以 MySQLroot
用户的身份连接到您的 MySQL 服务器并具有完全权限!查看 MySQL 安装说明,特别注意有关设置root
密码的信息。参见 Section 2.9.4, “Securing the Initial MySQL Account”。 -
使用
SHOW GRANTS
语句检查哪些帐户具有访问权限。然后使用REVOKE
语句删除那些不必要的权限。
-
-
不要在数据库中存储明文密码。如果您的计算机受到入侵,入侵者可以获取完整的密码列表并使用它们。相反,使用
SHA2()
或其他单向哈希函数并存储哈希值。为了防止使用彩虹表进行密码恢复,不要在明文密码上使用这些函数;相反,选择一些字符串作为盐,并使用哈希(hash(password)+salt)值。
-
假设所有密码都将受到使用已知密码列表进行自动破解尝试以及使用关于您的公开信息进行有针对性猜测的影响。不要选择由容易破解或猜测的项目组成的密码,如字典词、专有名词、体育队名、首字母缩写或众所周知的短语,特别是如果它们与您相关。如果这些以可预测方式使用,则大写字母、数字替换和添加以及特殊字符也无济于事。也不要选择您在任何地方看到过的密码或其变体,即使它被作为强密码的示例呈现。
相反,选择尽可能长且不可预测的密码。这并不意味着组合需要是难以记住和重现的随机字符串,尽管如果您有例如可以生成和填充这样的密码并安全存储它们的密码管理软件,这是一个很好的方法。包含多个单词的短语易于创建、记住和重现,并且比典型用户选择的由单个修改过的单词或可预测字符序列组成的密码安全得多。要创建一个安全的短语密码,请确保其中的单词和其他项目不是已知短语或引语,不按可预测顺序出现,并且最好彼此之间没有任何先前关系。
-
投资于防火墙。这可以保护您免受任何软件中至少 50%的各种利用。将 MySQL 放在防火墙后面或者在一个非军事区(DMZ)中。
检查清单:
-
尝试使用诸如
nmap
之类的工具从互联网扫描您的端口。MySQL 默认使用端口 3306。这个端口不应该从不受信任的主机访问。作为检查您的 MySQL 端口是否开放的简单方法,尝试在某个远程机器上运行以下命令,其中server_host
是您的 MySQL 服务器运行的主机的主机名或 IP 地址:$> telnet *server_host* 3306
如果telnet挂起或连接被拒绝,则端口被阻止,这正是您希望的。如果您获得一个连接和一些垃圾字符,则端口是开放的,并且应该在您的防火墙或路由器上关闭,除非您真的有充分理由保持其开放。
-
-
访问 MySQL 的应用程序不应信任用户输入的任何数据,并且应使用适当的防御性编程技术编写。参见第 8.1.7 节,“客户端编程安全指南”。
-
不要在互联网上传输明文(未加密)数据。这些信息对于所有有时间和能力拦截并将其用于自己目的的人都是可访问的。相反,使用诸如 SSL 或 SSH 之类的加密协议。MySQL 支持内部 SSL 连接。另一种技术是使用 SSH 端口转发来创建一个加密(和压缩)通信隧道。
-
学会使用tcpdump和strings工具。在大多数情况下,您可以通过发出类似以下命令的命令来检查 MySQL 数据流是否未加密:
$> tcpdump -l -i eth0 -w - src or dst port 3306 | strings
这在 Linux 下运行,并且应该在其他系统下进行小的修改后也能运行。
警告
如果您看不到明文数据,这并不总是意味着信息实际上是加密的。如果您需要高安全性,请咨询安全专家。
8.1.2 保持密码安全
8.1.2.1 最终用户密码安全指南
8.1.2.2 管理员密码安全指南
8.1.2.3 密码和日志记录
MySQL 中存在多种上下文中的密码。以下部分提供指南,使最终用户和管理员能够保持这些密码安全,并避免暴露它们。此外,validate_password
插件可用于强制执行有关可接受密码的策略。请参阅 Section 8.4.3, “密码验证组件”。
原文:
dev.mysql.com/doc/refman/8.0/en/password-security-user.html
8.1.2.1 密码安全的最终用户准则
MySQL 用户应该遵循以下准则来保护密码安全。
当您运行客户端程序连接到 MySQL 服务器时,不建议以暴露给其他用户发现的方式指定密码。您运行客户端程序时可以使用的指定密码的方法在此列出,以及每种方法的风险评估。简而言之,最安全的方法是让客户端程序提示输入密码或在正确保护的选项文件中指定密码。
-
使用mysql_config_editor实用程序,它允许您将身份验证凭据存储在名为
.mylogin.cnf
的加密登录路径文件中。稍后,MySQL 客户端程序可以读取该文件以获取连接到 MySQL 服务器的身份验证凭据。请参阅 Section 6.6.7, “mysql_config_editor — MySQL Configuration Utility”。 -
在命令行上使用
--password=*
password*
或-p*
password*
选项。例如:$> mysql -u francis -pfrank *db_name*
警告
这很方便但不安全。在某些系统上,您的密码会对系统状态程序(如ps)可见,其他用户可以调用这些程序来显示命令行。MySQL 客户端通常在初始化序列期间用零覆盖命令行密码参数。然而,在这个值可见的瞬间仍然存在。此外,在某些系统上,这种覆盖策略是无效的,密码仍然对ps可见。(SystemV Unix 系统和其他系统可能存在这个问题。)
如果您的操作环境设置为在终端窗口的标题栏中显示当前命令,则只要命令正在运行,密码就会保持可见,即使命令已经在窗口内容区域滚动出视野。
-
在命令行上使用
--password
或-p
选项,不指定密码值。在这种情况下,客户端程序会交互式地请求密码:$> mysql -u francis -p *db_name* Enter password: ********
*
字符表示您输入密码的位置。在输入时不会显示密码。以这种方式输入密码比在命令行上指定密码更安全,因为其他用户看不到密码。然而,这种输入密码的方法只适用于您交互式运行的程序。如果您想从非交互式脚本中调用客户端,那么就没有机会从键盘输入密码。在某些系统上,您甚至可能发现脚本的第一行被读取并解释(错误地)为您的密码。
-
将密码存储在一个选项文件中。例如,在 Unix 上,您可以在主目录的
.my.cnf
文件的[client]
部分列出您的密码:[client] password=*password*
为了保护密码安全,该文件不应该对任何人开放访问,只能由您自己访问。为了确保这一点,将文件访问模式设置为
400
或600
。例如:$> chmod 600 .my.cnf
从命令行命名包含密码的特定选项文件,使用
--defaults-file=*
file_name*
选项,其中file_name
是文件的完整路径名。例如:$> mysql --defaults-file=/home/francis/mysql-opts
Section 6.2.2.2, “Using Option Files”详细讨论了选项文件。
在 Unix 系统上,mysql客户端会将执行的语句记录到一个历史文件中(参见 Section 6.5.1.3, “mysql Client Logging”)。默认情况下,该文件名为.mysql_history
,并创建在您的主目录中。密码可以以明文形式写入 SQL 语句,比如CREATE USER
和ALTER USER
,因此如果您使用这些语句,它们会被记录在历史文件中。为了保护这个文件,使用限制性的访问模式,与之前描述的.my.cnf
文件相同。
如果您的命令解释器维护一个历史记录,保存命令的任何文件都包含在命令行中输入的 MySQL 密码。例如,bash使用~/.bash_history
。任何这样的文件都应该具有限制性的访问模式。
原文:
dev.mysql.com/doc/refman/8.0/en/password-security-admin.html
8.1.2.2 密码安全的管理员准则
数据库管理员应遵循以下准则以保护密码安全。
MySQL 将用户账户的密码存储在mysql.user
系统表中。绝对不应该将对该表的访问权限授予任何非管理员账户。
可以设置账户密码过期,以便用户必须重置密码。参见第 8.2.15 节,“密码管理”,以及第 8.2.16 节,“服务器处理过期密码”。
可以使用validate_password
插件来强制执行可接受密码的策略。参见第 8.4.3 节,“密码验证组件”。
一个用户如果有权限修改插件目录(plugin_dir
系统变量的值)或指定插件目录位置的my.cnf
文件,就可以替换插件并修改插件提供的功能,包括认证插件。
应该保护可能写入密码的文件,比如日志文件。参见第 8.1.2.3 节,“密码和日志记录”。
8.1.2.3 密码和日志记录
密码可以以明文形式写入 SQL 语句,例如CREATE USER
、GRANT
和SET PASSWORD
。如果 MySQL 服务器按原样记录这些语句,其中的密码将对任何具有日志访问权限的人可见。
语句记录避免以明文形式写入以下语句的密码:
CREATE USER ... IDENTIFIED BY ...
ALTER USER ... IDENTIFIED BY ...
SET PASSWORD ...
START SLAVE ... PASSWORD = ...
START REPLICA ... PASSWORD = ...
CREATE SERVER ... OPTIONS(... PASSWORD ...)
ALTER SERVER ... OPTIONS(... PASSWORD ...)
这些语句中的密码被重写,以便在写入一般查询日志、慢查询日志和二进制日志的语句文本中不会直接显示。重写不适用于其他语句。特别是,对于引用明文密码的mysql.user
系统表的INSERT
或UPDATE
语句,将按原样记录,因此应避免使用这些语句。(直接修改授权表是不鼓励的。)
对于一般的查询日志,可以通过使用--log-raw
选项启动服务器来抑制密码重写。出于安全原因,不建议在生产环境中使用此选项。出于诊断目的,查看服务器接收到的语句的确切文本可能是有用的。
默认情况下,由审计日志插件生成的审计日志文件的内容未加密,可能包含敏感信息,例如 SQL 语句的文本。出于安全原因,审计日志文件应写入仅对 MySQL 服务器和有合法查看日志原因的用户可访问的目录。参见第 8.4.5.3 节,“MySQL 企业审计安全注意事项”。
如果安装了查询重写插件(参见查询重写插件),则服务器接收到的语句可能会被重写。在这种情况下,--log-raw
选项会影响语句记录如下:
-
没有
--log-raw
,服务器记录由查询重写插件返回的语句。这可能与接收到的语句不同。 -
使用
--log-raw
,服务器记录接收到的原始语句。
密码重写的一个影响是,无法解析的语句(例如由于语法错误)不会写入一般查询日志,因为无法确定是否不包含密码。需要记录所有语句(包括出现错误的语句)的用例应使用--log-raw
选项,但要注意这也会绕过密码重写。
当期望明文密码时,密码重写仅会发生。对于期望密码哈希值的语法语句,不会进行重写。如果错误地为此类语法提供了明文密码,则密码将被记录为给定的形式,而不进行重写。
为了保护日志文件免受未经授权的暴露,将它们放在一个限制服务器和数据库管理员访问的目录中。如果服务器将日志记录到mysql
数据库中的表中,请仅授予数据库管理员对这些表的访问权限。
复制品将复制源服务器的密码存储在它们的连接元数据存储库中,默认情况下是mysql
数据库中名为slave_master_info
的表。现在已不推荐使用数据目录中的文件作为连接元数据存储库,但仍然可能(参见第 19.2.4 节,“中继日志和复制元数据存储库”)。确保连接元数据存储库只能由数据库管理员访问。将密码存储在连接元数据存储库中的替代方法是使用START REPLICA
(或在 MySQL 8.0.22 之前,START SLAVE
)或START GROUP_REPLICATION
语句指定连接到源的凭据。
使用受限访问模式保护包含日志表或包含密码的日志文件的数据库备份。
8.1.3 使 MySQL 免受攻击者攻击
译文:
dev.mysql.com/doc/refman/8.0/en/security-against-attack.html
当连接到 MySQL 服务器时,应该使用密码。密码不会以明文形式通过连接传输。
所有其他信息都以文本形式传输,可以被能够监视连接的任何人读取。如果客户端和服务器之间的连接经过不受信任的网络,并且您对此感到担忧,您可以使用压缩协议使流量更加难以解密。您还可以使用 MySQL 的内部 SSL 支持使连接更加安全。请参见 Section 8.3, “Using Encrypted Connections”。或者,使用 SSH 在 MySQL 服务器和 MySQL 客户端之间建立加密的 TCP/IP 连接。您可以在www.openssh.org/
找到一个开源 SSH 客户端,并在en.wikipedia.org/wiki/Comparison_of_SSH_clients
找到开源和商业 SSH 客户端的比较。
要使 MySQL 系统安全,您应该强烈考虑以下建议:
-
要求所有 MySQL 账户都有密码。客户端程序不一定知道运行它的人的身份。对于客户端/服务器应用程序来说,用户可以向客户端程序指定任何用户名是很常见的。例如,任何人都可以使用mysql程序连接为任何其他人,只需通过
mysql -u *
other_user* *
db_name*
来调用,如果other_user
没有密码。如果所有账户都有密码,使用另一个用户的账户连接就变得更加困难。有关设置密码方法的讨论,请参见 Section 8.2.14, “Assigning Account Passwords”。
-
确保在数据库目录中具有读取或写入权限的唯一 Unix 用户帐户是用于运行mysqld的帐户。
-
永远不要将 MySQL 服务器作为 Unix
root
用户运行。这是极其危险的,因为任何具有FILE
权限的用户都可以使服务器以root
身份创建文件(例如,~root/.bashrc
)。为了防止这种情况,mysqld拒绝以root
身份运行,除非明确使用--user=root
选项指定。mysqld可以(而且应该)作为一个普通的、非特权用户运行。您可以创建一个名为
mysql
的单独的 Unix 帐户,以使一切更加安全。仅使用此帐户来管理 MySQL。要将mysqld作为不同的 Unix 用户启动,请在指定服务器选项的my.cnf
选项文件的[mysqld]
组中添加一个指定用户名称的user
选项。例如:[mysqld] user=mysql
这将导致服务器作为指定用户启动,无论您是手动启动还是使用mysqld_safe或mysql.server启动。有关更多详细信息,请参见 Section 8.1.5,“如何将 MySQL 作为普通用户运行”。
将mysqld作为
root
以外的 Unix 用户运行并不意味着您需要更改user
表中的root
用户名。MySQL 帐户的用户名与 Unix 帐户的用户名无关。 -
不要将
FILE
权限授予非管理员用户。拥有此权限的任何用户都可以以mysqld守护程序的权限在文件系统中的任何位置写入文件。这包括包含实现权限表的文件的服务器数据目录。为了使具有FILE
权限的操作更安全一些,使用SELECT ... INTO OUTFILE
生成的文件不会覆盖现有文件,并且可以被所有人写入。FILE
权限也可以用于读取任何对所有用户可读或对服务器运行的 Unix 用户可访问的文件。有了这个权限,您可以将任何文件读入数据库表中。例如,可以通过使用LOAD DATA
将/etc/passwd
加载到表中,然后可以使用SELECT
显示。为了限制可以读取和写入文件的位置,将
secure_file_priv
系统设置为特定目录。请参见 Section 7.1.8,“服务器系统变量”。 -
对二进制日志文件和中继日志文件进行加密。加密有助于保护这些文件和其中可能包含的敏感数据,防止外部攻击者滥用,也防止存储它们的操作系统用户未经授权查看。您可以通过将
binlog_encryption
系统变量设置为ON
来在 MySQL 服务器上启用加密。有关更多信息,请参见第 19.3.2 节,“加密二进制日志文件和中继日志文件”。 -
不要向非管理员用户授予
PROCESS
或SUPER
权限。mysqladmin processlist和SHOW PROCESSLIST
的输出显示当前正在执行的任何语句的文本,因此任何被允许查看服务器进程列表的用户可能能够看到其他用户发出的语句。mysqld为具有
CONNECTION_ADMIN
或SUPER
权限的用户保留了额外的连接,以便 MySQLroot
用户可以登录并检查服务器活动,即使所有正常连接都在使用中。SUPER
权限可用于终止客户端连接,通过更改系统变量的值改变服务器操作,并控制复制服务器。 -
不要允许使用符号链接到表格。 (可以使用
--skip-symbolic-links
选项禁用此功能。)如果您以root
身份运行mysqld,那么任何具有对服务器数据目录的写访问权限的人都可以删除系统中的任何文件!请参见第 10.12.2.2 节,“在 Unix 上为 MyISAM 表使用符号链接”。 -
存储过程和视图应该按照第 27.6 节,“存储对象访问控制”中讨论的安全准则编写。
-
如果您不信任您的 DNS,应该在授权表中使用 IP 地址而不是主机名。无论如何,您都应该非常小心地创建包含通配符的主机名值的授权表条目。
-
如果您想限制单个帐户允许的连接数,可以通过在mysqld中设置
max_user_connections
变量来实现。CREATE USER
和ALTER USER
语句还支持资源控制选项,用于限制帐户允许使用服务器的范围。请参阅 Section 15.7.1.3, “CREATE USER Statement”和 Section 15.7.1.1, “ALTER USER Statement”。 -
如果插件目录对服务器是可写的,用户可能会使用
SELECT ... INTO DUMPFILE
将可执行代码写入目录中的文件。可以通过将plugin_dir
设置为服务器只读或将secure_file_priv
设置为SELECT
可以安全写入的目录来防止这种情况发生。
8.1.4 与安全相关的 mysqld 选项和变量
以下表格显示了影响安全性的mysqld选项和系统变量。有关每个选项的描述,请参见第 7.1.7 节,“服务器命令选项”和第 7.1.8 节,“服务器系统变量”。
表 8.1 安全选项和变量摘要
名称 | 命令行 | 选项文件 | 系统变量 | 状态变量 | 变量范围 | 动态 |
---|---|---|---|---|---|---|
allow-suspicious-udfs | 是 | 是 | ||||
automatic_sp_privileges | 是 | 是 | 是 | 全局 | 是 | |
chroot | 是 | 是 | ||||
local_infile | 是 | 是 | 是 | 全局 | 是 | |
safe-user-create | 是 | 是 | ||||
secure_file_priv | 是 | 是 | 是 | 全局 | 否 | |
skip-grant-tables | 是 | 是 | ||||
skip_name_resolve | 是 | 是 | 是 | 全局 | 否 | |
skip_networking | 是 | 是 | 是 | 全局 | 否 | |
skip_show_database | 是 | 是 | 是 | 全局 | 否 | |
名称 | 命令行 | 选项文件 | 系统变量 | 状态变量 | 变量范围 | 动态 |
8.1.5 如何以普通用户身份运行 MySQL
在 Windows 上,您可以使用普通用户帐户将服务器作为 Windows 服务运行。
在 Linux 上,如果使用 MySQL 存储库或 RPM 软件包进行安装,则应该由本地mysql
操作系统用户启动 MySQL 服务器mysqld。使用其他操作系统用户启动不受 MySQL 存储库包含的 init 脚本支持。
在 Unix(或 Linux 使用tar.gz
软件包进行安装时),MySQL 服务器mysqld可以由任何用户启动和运行。但是,出于安全原因,您应避免以 Unix root
用户身份运行服务器。要将mysqld更改为以普通非特权 Unix 用户user_name
运行,您必须执行以下操作:
-
如果服务器正在运行,请停止它(使用mysqladmin shutdown)。
-
更改数据库目录和文件,以便
user_name
有权限在其中读取和写入文件(您可能需要以 Unixroot
用户的身份执行此操作):$> chown -R *user_name* */path/to/mysql/datadir*
如果不这样做,服务器在以
user_name
运行时无法访问数据库或表。如果 MySQL 数据目录中的目录或文件是符号链接,则
chown -R
可能不会为您跟随符号链接。如果没有,请您也必须跟随这些链接并更改它们指向的目录和文件。 -
以用户
user_name
启动服务器。另一种选择是以 Unixroot
用户启动mysqld,并使用--user=*
user_name*
选项。mysqld启动后,然后在接受任何连接之前切换为以 Unix 用户user_name
运行。 -
要在系统启动时自动以给定用户启动服务器,请通过在
/etc/my.cnf
选项文件的[mysqld]
组或服务器数据目录中的my.cnf
选项文件中添加user
选项来指定用户名。例如:[mysqld] user=*user_name*
如果您的 Unix 机器本身没有受到保护,您应该在授权表中为 MySQL root
帐户分配密码。否则,任何在该机器上具有登录帐户的用户都可以使用--user=root
选项运行mysql客户端并执行任何操作。(在任何情况下为 MySQL 帐户分配密码都是个好主意,但特别是在服务器主机上存在其他登录帐户时更是如此。)请参见 Section 2.9.4, “Securing the Initial MySQL Account”。
8.1.6 LOAD DATA LOCAL
的安全考虑
原文:
dev.mysql.com/doc/refman/8.0/en/load-data-local-security.html
LOAD DATA
语句将数据文件加载到表中。该语句可以加载位于服务器主机上的文件,或者如果指定了LOCAL
关键字,则可以加载位于客户端主机上的文件。
LOAD DATA
的LOCAL
版本存在两个潜在的安全问题:
-
因为
LOAD DATA LOCAL
是一个 SQL 语句,解析发生在服务器端,并且文件从客户端主机传输到服务器主机是由 MySQL 服务器发起的,MySQL 服务器告诉客户端语句中命名的文件。理论上,一个经过修补的服务器可以告诉客户端程序传输服务器选择的文件,而不是语句中命名的文件。这样的服务器可以访问客户端用户具有读取访问权限的任何文件。(事实上,一个经过修补的服务器实际上可以对任何语句回复文件传输请求,而不仅仅是LOAD DATA LOCAL
,因此更根本的问题是客户端不应连接到不受信任的服务器。) -
在 Web 环境中,客户端是从 Web 服务器连接的,用户可以使用
LOAD DATA LOCAL
读取 Web 服务器进程具有读取访问权限的任何文件(假设用户可以对 SQL 服务器运行任何语句)。在这种环境中,客户端实际上是 Web 服务器,而不是由连接到 Web 服务器的用户运行的远程程序。
为了避免连接到不受信任的服务器,客户端可以通过使用--ssl-mode=VERIFY_IDENTITY
选项和适当的 CA 证书建立安全连接并验证服务器身份。要实现这种级别的验证,您必须首先确保服务器的 CA 证书可靠地可供副本使用,否则将导致可用性问题。有关更多信息,请参见加密连接的命令选项。
为了避免LOAD DATA
问题,客户端应避免使用LOCAL
,除非已采取适当的客户端端预防措施。
为了控制本地数据加载,MySQL 允许启用或禁用该功能。此外,从 MySQL 8.0.21 开始,MySQL 使客户端能够将本地数据加载操作限制为位于指定目录中的文件。
-
启用或禁用本地数据加载功能
-
限制本地数据加载的文件
-
MySQL Shell 和本地数据加载
启用或禁用本地数据加载功能
管理员和应用程序可以配置是否允许本地数据加载如下:
-
在服务器端:
-
local_infile
系统变量控制服务器端的LOCAL
功能。根据local_infile
设置,服务器拒绝或允许请求本地数据加载的客户端的本地数据加载。 -
默认情况下,
local_infile
被禁用。(这是与 MySQL 先前版本的更改。)为了使服务器明确拒绝或允许LOAD DATA LOCAL
语句(无论客户端程序和库在构建时或运行时如何配置),请使用禁用或启用local_infile
启动mysqld。local_infile
也可以在运行时设置。
-
-
在客户端:
-
ENABLED_LOCAL_INFILE
CMake选项控制 MySQL 客户端库的编译默认LOCAL
功能(参见 Section 2.8.7,“MySQL 源配置选项”)。因此,未做明确安排的客户端根据 MySQL 构建时指定的ENABLED_LOCAL_INFILE
设置,LOCAL
功能被禁用或启用。 -
默认情况下,MySQL 二进制发行版中的客户端库编译时禁用了
ENABLED_LOCAL_INFILE
。如果从源代码编译 MySQL,请根据未做明确安排的客户端是否应该禁用或启用LOCAL
功能,配置ENABLED_LOCAL_INFILE
为禁用或启用。 -
对于使用 C API 的客户端程序,本地数据加载功能取决于编译到 MySQL 客户端库中的默认值。要显式启用或禁用它,请调用
mysql_options()
C API 函数来禁用或启用MYSQL_OPT_LOCAL_INFILE
选项。参见 mysql_options()。 -
对于mysql客户端,本地数据加载功能由默认编译到 MySQL 客户端库中。要显式禁用或启用它,请使用
--local-infile=0
或--local-infile[=1]
选项。 -
对于mysqlimport客户端,默认情况下不使用本地数据加载。要显式禁用或启用它,请使用
--local=0
或--local[=1]
选项。 -
如果您在读取选项文件中的
[client]
组的 Perl 脚本或其他程序中使用LOAD DATA LOCAL
,您可以向该组添加一个local-infile
选项设置。为了防止不理解此选项的程序出现问题,请使用loose-
前缀指定它:[client] loose-local-infile=0
或:
[client] loose-local-infile=1
-
在所有情况下,客户端成功使用
LOCAL
加载操作还需要服务器允许本地加载。
-
如果LOCAL
功能被禁用,无论是在服务器端还是客户端端,尝试发出LOAD DATA LOCAL
语句的客户端将收到以下错误消息:
ERROR 3950 (42000): Loading local data is disabled; this must be
enabled on both the client and server side
限制允许用于本地数据加载的文件
截至 MySQL 8.0.21,MySQL 客户端库使客户端应用程序能够将本地数据加载操作限制为位于指定目录中的文件。某些 MySQL 客户端程序利用了这一功能。
使用 C API 的客户端程序可以通过mysql_options()
C API 函数的MYSQL_OPT_LOCAL_INFILE
和MYSQL_OPT_LOAD_DATA_LOCAL_DIR
选项来控制允许加载数据的文件(参见 mysql_options())。
MYSQL_OPT_LOAD_DATA_LOCAL_DIR
的效果取决于LOCAL
数据加载是启用还是禁用的:
-
如果通过默认在 MySQL 客户端库中或显式启用
MYSQL_OPT_LOCAL_INFILE
来启用LOCAL
数据加载,则MYSQL_OPT_LOAD_DATA_LOCAL_DIR
选项不起作用。 -
如果通过默认在 MySQL 客户端库中或显式禁用
MYSQL_OPT_LOCAL_INFILE
来禁用LOCAL
数据加载,则可以使用MYSQL_OPT_LOAD_DATA_LOCAL_DIR
选项指定允许本地加载文件的目录。在这种情况下,LOCAL
数据加载被允许但仅限于位于指定目录中的文件。MYSQL_OPT_LOAD_DATA_LOCAL_DIR
值的解释如下:-
如果该值为空指针(默认值),则不指定任何目录,结果是不允许任何文件进行
LOCAL
数据加载。 -
如果值是目录路径名,则允许
LOCAL
数据加载,但仅限于位于指定目录中的文件。无论基础文件系统的大小写敏感性如何,目录路径名和要加载的文件的路径名的比较都是区分大小写的。
-
MySQL 客户端程序如下使用前述的mysql_options()
选项:
-
mysql客户端有一个
--load-data-local-dir
选项,接受一个目录路径或空字符串。mysql使用该选项值来设置MYSQL_OPT_LOAD_DATA_LOCAL_DIR
选项(将空字符串设置为 null 指针)。--load-data-local-dir
的效果取决于是否启用了LOCAL
数据加载:-
如果启用了
LOCAL
数据加载,无论是在 MySQL 客户端库中默认设置,还是通过指定--local-infile[=1]
,都会忽略--load-data-local-dir
选项。 -
如果
LOCAL
数据加载被禁用,无论是在 MySQL 客户端库中默认设置,还是通过指定--local-infile=0
,都会应用--load-data-local-dir
选项。
当应用
--load-data-local-dir
时,选项值指定了本地数据文件必须位于的目录。无论基础文件系统的大小写敏感性如何,目录路径名和要加载的文件的路径名的比较都是区分大小写的。如果选项值为空字符串,则不指定任何目录,结果是不允许进行本地数据加载。 -
-
mysqlimport为其处理的每个文件设置
MYSQL_OPT_LOAD_DATA_LOCAL_DIR
,以便包含文件的目录是允许的本地加载目录。 -
对应于
LOAD DATA
语句的数据加载操作,mysqlbinlog从二进制日志事件中提取文件,将它们写入本地文件系统作为临时文件,并写入LOAD DATA LOCAL
语句以使文件被加载。默认情况下,mysqlbinlog将这些临时文件写入操作系统特定的目录。可以使用--local-load
选项来明确指定mysqlbinlog应准备本地临时文件的目录。因为其他进程可以将文件写入默认的系统特定目录,建议指定
--local-load
选项给mysqlbinlog,以指定不同的目录用于数据文件,然后通过在处理来自mysqlbinlog的输出时指定--load-data-local-dir
选项给mysql来指定相同的目录。
MySQL Shell 和本地数据加载
MySQL Shell 提供了许多实用程序来转储表、模式或服务器实例,并将它们加载到其他实例中。当您使用这些实用程序处理数据时,MySQL Shell 提供额外的功能,如输入预处理、多线程并行加载、文件压缩和解压缩,以及处理访问 Oracle Cloud Infrastructure 对象存储桶的功能。为了获得最佳功能,请始终使用 MySQL Shell 的最新版本的转储和加载实用程序。
MySQL Shell 的数据上传实用程序使用LOAD DATA LOCAL INFILE
语句来上传数据,因此目标服务器实例上必须将local_infile
系统变量设置为ON
。您可以在上传数据之前执行此操作,然后再次将其删除。这些实用程序安全地处理文件传输请求,以处理本主题中讨论的安全考虑。
MySQL Shell 包括以下转储和加载实用程序:
表导出工具 util.exportTable()
将 MySQL 关系表导出为数据文件,可以使用 MySQL Shell 的并行表导入实用程序上传到 MySQL 服务器实例,导入到不同的应用程序,或用作逻辑备份。该实用程序具有预设选项和自定义选项,可生成不同的输出格式。
并行表导入实用程序 util.importTable()
将数据文件导入到 MySQL 关系表中。数据文件可以是 MySQL Shell 的表导出实用程序的输出,也可以是实用程序的预设和自定义选项支持的其他格式。该实用程序可以在将数据添加到表之前进行输入预处理。它可以接受多个数据文件合并到单个关系表中,并自动解压缩压缩文件。
实例转储实用程序 util.dumpInstance()
,模式转储实用程序 util.dumpSchemas()
和表转储实用程序 util.dumpTables()
将实例、模式或表导出为一组转储文件,然后可以使用 MySQL Shell 的转储加载实用程序将其上传到 MySQL 实例。这些实用程序提供 Oracle Cloud 基础设施对象存储流、MySQL HeatWave 服务兼容性检查和修改,并能够进行干跑以在继续进行转储之前识别问题。
转储加载实用程序 util.loadDump()
使用 MySQL Shell 的实例、模式或表转储实用程序创建的转储文件导入到 MySQL HeatWave 服务 DB 系统或 MySQL 服务器实例中。该实用程序管理上传过程,并提供从远程存储的数据流、表或表块的并行加载、进度状态跟踪、恢复和重置功能,以及在转储仍在进行时进行并发加载的选项。MySQL Shell 的并行表导入实用程序可以与转储加载实用程序结合使用,在将数据上传到目标 MySQL 实例之前修改数据。
有关实用程序的详细信息,请参见 MySQL Shell 实用程序。
8.1.7 客户端编程安全准则
原文:
dev.mysql.com/doc/refman/8.0/en/secure-client-programming.html
访问 MySQL 的客户端应用程序应遵循以下准则,以避免错误解释外部数据或暴露敏感信息。
-
正确处理外部数据
-
正确处理 MySQL 错误消息
正确处理外部数据
访问 MySQL 的应用程序不应信任用户输入的任何数据,用户可以尝试通过在网络表单、URL 或您构建的任何应用程序中输入特殊或转义字符序列来欺骗您的代码。确保如果用户尝试通过在表单中输入类似;删除数据库 mysql;
的内容来执行 SQL 注入,您的应用程序仍然保持安全。这是一个极端的例子,但如果您不为此做好准备,可能会发生大规模的安全漏洞和数据丢失。
一个常见的错误是仅保护字符串数据值。请记得检查数值数据。如果一个应用程序生成类似SELECT * FROM table WHERE ID=234
的查询,当用户输入值234
时,用户可以输入值234 OR 1=1
来导致应用程序生成查询SELECT * FROM table WHERE ID=234 OR 1=1
。结果,服务器检索表中的每一行。这暴露了每一行并导致服务器负载过大。保护免受此类攻击的最简单方法是在数值常量周围使用单引号:SELECT * FROM table WHERE ID='234'
。如果用户输入额外信息,它都将成为字符串的一部分。在数值上下文中,MySQL 会自动将此字符串转换为数字并剥离其中的任何尾随非数字字符。
有时人们认为如果数据库只包含公开可用的数据,则无需保护。这是不正确的。即使可以显示数据库中的任何行,您仍应防范拒绝服务攻击(例如,基于前一段中导致服务器浪费资源的技术)。否则,您的服务器将对合法用户不响应。
检查清单:
-
启用严格的 SQL 模式,告诉服务器更严格地接受哪些数据值。请参阅第 7.1.11 节,“服务器 SQL 模式”。
-
尝试在所有的网络表单中输入单引号和双引号(
'
和"
)。如果出现任何 MySQL 错误,请立即调查问题。 -
尝试通过向动态 URL 添加
%22
("
)、%23
(#
)和%27
('
)来修改它们。 -
尝试将动态 URL 中的数据类型从数字类型修改为字符类型,使用前面示例中显示的字符。您的应用程序应对这些攻击和类似攻击保持安全。
-
尝试在数字字段中输入字符、空格和特殊符号而不是数字。您的应用程序应在传递给 MySQL 之前将它们删除,否则会生成错误。将未经检查的值传递给 MySQL 非常危险!
-
在将数据传递给 MySQL 之前检查数据的大小。
-
让应用程序使用与您用于管理目的不同的用户名连接到数据库。不要给予应用程序任何不需要的访问权限。
许多应用程序编程接口提供了一种方法来转义数据值中的特殊字符。正确使用可以防止应用程序用户输入导致应用程序生成具有不同效果的语句:
-
MySQL SQL 语句:使用 SQL 预处理语句,并仅通过占位符接受数据值;参见 第 15.5 节,“预处理语句”。
-
MySQL C API:使用
mysql_real_escape_string_quote()
API 调用。或者,使用 C API 预处理语句接口,并仅通过占位符接受数据值;参见 C API 预处理语句接口。 -
MySQL++:对查询流使用
escape
和quote
修饰符。 -
PHP:使用
mysqli
或pdo_mysql
扩展,而不是旧的ext/mysql
扩展。首选的 API 支持改进的 MySQL 认证协议和密码,以及带有占位符的预处理语句。另请参阅 MySQL 和 PHP。如果必须使用旧的
ext/mysql
扩展,则用mysql_real_escape_string_quote()
函数进行转义,而不是mysql_escape_string()
或addslashes()
,因为只有mysql_real_escape_string_quote()
是字符集感知的;在使用(无效的)多字节字符集时,其他函数可能会被“绕过”。 -
Perl DBI:使用占位符或
quote()
方法。 -
Java JDBC:使用
PreparedStatement
对象和占位符。
其他编程接口可能具有类似的功能。
适当处理 MySQL 错误消息
应用程序有责任拦截由执行与 MySQL 数据库服务器的 SQL 语句导致的错误,并适当处理这些错误。
MySQL 错误返回的信息并非毫无意义,因为这些信息对于使用应用程序调试 MySQL 至关重要。例如,要调试一个常见的 10 路连接SELECT
语句,如果不提供涉及问题的数据库、表和其他对象的信息,几乎是不可能的。因此,MySQL 错误有时必须包含对这些对象名称的引用。
当应用程序从 MySQL 接收到这样的错误时,一个简单但不安全的方法是拦截并将其原样显示给客户端。然而,揭示错误信息是一种已知的应用程序漏洞类型(CWE-209),应用程序开发人员必须确保应用程序没有这种漏洞。
例如,一个显示如下消息的应用程序向客户端暴露了数据库名称和表名称,这是客户端可能会尝试利用的信息:
ERROR 1146 (42S02): Table 'mydb.mytable' doesn't exist
相反,当应用程序从 MySQL 接收到这样的错误时,正确的行为是记录适当的信息,包括错误信息,仅供信任人员访问的安全审计位置。应用程序可以向用户返回更通用的信息,比如“内部错误”。
8.2 访问控制和账户管理
8.2.1 账户用户名和密码
8.2.2 MySQL 提供的权限
8.2.3 授权表
8.2.4 指定账户名称
8.2.5 指定角色名称
8.2.6 访问控制,第一阶段:连接验证
8.2.7 访问控制,第二阶段:请求验证
8.2.8 添加账户、分配权限和删除账户
8.2.9 保留账户
8.2.10 使用角色
8.2.11 账户类别
8.2.12 使用部分撤销限制权限
8.2.13 权限更改何时生效
8.2.14 分配账户密码
8.2.15 密码管理
8.2.16 服务器处理过期密码
8.2.17 可插拔认证
8.2.18 多因素认证
8.2.19 代理用户
8.2.20 账户锁定
8.2.21 设置账户资源限制
8.2.22 解决连接到 MySQL 的问题
8.2.23 基于 SQL 的账户活动审计
MySQL 允许创建账户,允许客户端用户连接到服务器并访问服务器管理的数据。 MySQL 权限系统的主要功能是对来自特定主机连接的用户进行身份验证,并将该用户与数据库上的权限(如SELECT
、INSERT
、UPDATE
和DELETE
)关联起来。其他功能包括授予管理操作的权限。
为了控制哪些用户可以连接,每个账户可以分配身份验证凭据,如密码。 MySQL 账户的用户界面包括 SQL 语句,如CREATE USER
、GRANT
和REVOKE
。参见第 15.7.1 节,“账户管理语句”。
MySQL 权限系统确保所有用户只能执行其被允许的操作。作为用户,当你连接到 MySQL 服务器时,你的身份由你连接的主机和你指定的用户名确定。连接后发出请求时,系统根据你的身份和你想要做的事情授予权限。
MySQL 在识别你时考虑你的主机名和用户名,因为没有理由假设给定的用户名属于所有主机上的同一个人。例如,从office.example.com
连接的用户joe
不一定是从home.example.com
连接的用户joe
。MySQL 通过使你能够区分在不同主机上具有相同名称的用户来处理这个问题:你可以为从office.example.com
连接的joe
授予一组连接权限,为从home.example.com
连接的joe
授予另一组连接权限。要查看特定账户有哪些权限,使用SHOW GRANTS
语句。例如:
SHOW GRANTS FOR 'joe'@'office.example.com';
SHOW GRANTS FOR 'joe'@'home.example.com';
在内部,服务器将权限信息存储在mysql
系统数据库的授权表中。MySQL 服务器在启动时将这些表的内容读入内存,并基于内存中的授权表副本做出访问控制决策。
当你运行连接到服务器的客户端程序时,MySQL 访问控制涉及两个阶段:
第一阶段: 服务器根据你的身份接受或拒绝连接,并根据你是否能通过提供正确密码验证你的身份。
第二阶段: 假设你可以连接,服务器会检查你发出的每个语句,以确定你是否有足够的权限执行它。例如,如果你尝试从数据库中的表中选择行或删除数据库中的表,服务器会验证你是否具有表的SELECT
权限或数据库的DROP
权限。
有关每个阶段发生的详细描述,请参见第 8.2.6 节,“访问控制,阶段 1:连接验证”和第 8.2.7 节,“访问控制,阶段 2:请求验证”。有关诊断与权限相关问题的帮助,请参见第 8.2.22 节,“连接到 MySQL 时出现问题的故障排除”。
如果你的权限在连接时被更改(无论是由你自己还是其他人),这些更改不一定会立即生效于你发出的下一条语句。有关服务器何时重新加载授权表的详细信息,请参见第 8.2.13 节,“权限更改何时生效”。
有一些事情你不能通过 MySQL 权限系统做到:
-
你不能明确指定某个用户应被拒绝访问。也就是说,你不能明确匹配一个用户然后拒绝连接。
-
你不能指定用户有权限在数据库中创建或删除表,但不能创建或删除数据库本身。
-
密码对一个账户全局有效。你不能将密码与特定对象(如数据库、表或例程)关联起来。
8.2.1 账户用户名和密码
MySQL 将账户存储在mysql
系统数据库的user
表中。账户根据用户名称和用户可以连接到服务器的客户端主机或主机来定义。有关user
表中账户表示的信息,请参见 第 8.2.3 节,“授权表”。
一个账户可能还有身份验证凭据,比如密码。这些凭据由账户身份验证插件处理。MySQL 支持多种身份验证插件。其中一些使用内置身份验证方法,而其他一些则启用使用外部身份验证方法进行身份验证。参见 第 8.2.17 节,“可插拔身份验证”。
MySQL 和您的操作系统在使用用户名称和密码方面有几个区别:
-
用户名,用于 MySQL 身份验证目的,与 Windows 或 Unix 中用于登录的用户名无关。在 Unix 上,默认情况下,大多数 MySQL 客户端尝试使用当前 Unix 用户名作为 MySQL 用户名登录,但这仅仅是为了方便。默认设置可以轻松覆盖,因为客户端程序允许使用
-u
或--user
选项指定任何用户名。这意味着任何人都可以尝试使用任何用户名连接到服务器,因此除非所有 MySQL 账户都有密码,否则无法以任何方式使数据库安全。任何指定了没有密码的账户的用户名的人都可以成功连接到服务器。 -
MySQL 用户名称最长可达 32 个字符。操作系统用户名称可能具有不同的最大长度。
警告
MySQL 用户名称长度限制是硬编码在 MySQL 服务器和客户端中的,试图通过修改
mysql
数据库中表的定义来规避它不起作用。除了通过第三章,“升级 MySQL”中描述的过程之外,您绝对不应以任何方式更改
mysql
数据库中表的结构。试图以任何其他方式重新定义 MySQL 系统表会导致未定义和不受支持的行为。服务器可以忽略由于这些修改而变得畸形的行。 -
为使用内置身份验证方法的账户验证客户端连接,服务器使用存储在
user
表中的密码。这些密码与用于登录操作系统的密码不同。您用于登录 Windows 或 Unix 机器的“外部”密码与您用于访问该机器上的 MySQL 服务器的密码之间没有必要的联系。如果服务器使用其他插件对客户端进行身份验证,插件实现的身份验证方法可能会或可能不会使用存储在
user
表中的密码。在这种情况下,可能还会使用外部密码进行 MySQL 服务器的身份验证。 -
存储在
user
表中的密码使用特定于插件的算法进行加密。 -
如果用户名和密码只包含 ASCII 字符,则无论字符集设置如何,都可以连接到服务器。要在用户名或密码包含非 ASCII 字符时启用连接,客户端应用程序应使用
MYSQL_SET_CHARSET_NAME
选项和适当的字符集名称作为参数调用mysql_options()
C API 函数。这会导致使用指定的字符集进行身份验证。否则,除非服务器默认字符集与身份验证默认值中的编码相同,否则身份验证将失败。标准的 MySQL 客户端程序支持一个
--default-character-set
选项,会导致调用mysql_options()
如上所述。此外,支持字符集自动检测,如第 12.4 节,“连接字符集和校对规则”所述。对于不基于 C API 的连接器,可能会提供类似于mysql_options()
的等效选项供使用。请查阅连接器文档。前述注意事项不适用于
ucs2
、utf16
和utf32
,这些字符集不被允许作为客户端字符集。
MySQL 安装过程会使用初始root
账户填充授权表,如第 2.9.4 节,“保护初始 MySQL 账户”所述,该节还讨论了如何为其分配密码。之后,通常使用CREATE USER
、DROP USER
、GRANT
和REVOKE
等语句设置、修改和删除 MySQL 账户。参见第 8.2.8 节,“添加账户、分配权限和删除账户”,以及第 15.7.1 节,“账户管理语句”。
要使用命令行客户端连接到 MySQL 服务器,根据要使用的账户,指定用户名和密码选项如下所示:
$> mysql --user=finley --password *db_name*
如果您喜欢简短选项,命令如下:
$> mysql -u finley -p *db_name*
如果在命令行上省略了--password
或-p
选项后面的密码值(如上所示),客户端会提示输入密码。或者,密码可以在命令行上指定:
$> mysql --user=finley --password=*password* *db_name*
$> mysql -u finley -p*password* *db_name*
如果使用-p
选项,则-p
和后面的密码值之间不能有空格。
在命令行上指定密码应被视为不安全。请参见第 8.1.2.1 节,“密码安全的最终用户指南”。为了避免在命令行上输入密码,可以使用选项文件或登录路径文件。请参见第 6.2.2.2 节,“使用选项文件”,以及第 6.6.7 节,“mysql_config_editor — MySQL 配置实用程序”。
有关指定用户名、密码和其他连接参数的附加信息,请参见第 6.2.4 节,“使用命令选项连接到 MySQL 服务器”。
8.2.2 MySQL 提供的权限
授予 MySQL 账户的权限确定账户可以执行哪些操作。MySQL 权限在适用的上下文和操作级别上有所不同:
-
管理权限使用户能够管理 MySQL 服务器的操作。这些权限是全局的,因为它们不针对特定数据库。
-
数据库权限适用于数据库及其中的所有对象。这些权限可以针对特定数据库授予,也可以全局授予,以便适用于所有数据库。
-
对于诸如表、索引、视图和存储过程等数据库对象的权限可以针对数据库中的特定对象授予,也可以针对数据库中给定类型的所有对象(例如,数据库中的所有表)或全局授予所有数据库中给定类型的所有对象。
权限还有静态(内置到服务器中)或动态(在运行时定义)的区别。权限是静态还是动态会影响其是否可以授予用户账户和角色。有关静态和动态权限之间的区别,请参阅静态与动态权限。
关于账户权限的信息存储在mysql
系统数据库的授权表中。有关这些表的结构和内容的描述,请参阅第 8.2.3 节,“授权表”。MySQL 服务器在启动时将授权表的内容读入内存,并在第 8.2.13 节,“权限更改生效时”所示的情况下重新加载它们。服务器基于内存中的授权表副本做出访问控制决策。
重要
一些 MySQL 版本对授权表进行更改以添加新的权限或功能。为确保您能够利用任何新功能,每次升级 MySQL 时都要将授权表更新到当前结构。请参阅第三章,升级 MySQL。
以下各节总结了可用权限,提供了每个权限的更详细描述,并提供了使用指南。
-
可用权限摘要
-
静态权限描述
-
动态权限描述
-
授权指南
-
静态权限与动态权限
-
从 SUPER 迁移账户到动态权限
可用权限摘要
下表显示了在 GRANT
和 REVOKE
语句中使用的静态权限名称,以及授予表格中与每个权限相关联的列名以及权限适用的上下文。
表 8.2 GRANT 和 REVOKE 的允许静态权限
权限 | 授予表格列 | 上下文 |
---|---|---|
ALL [PRIVILEGES] |
“所有权限”的同义词 | 服务器管理 |
ALTER |
Alter_priv |
表格 |
ALTER ROUTINE |
Alter_routine_priv |
存储过程 |
CREATE |
Create_priv |
数据库、表格或索引 |
CREATE ROLE |
Create_role_priv |
服务器管理 |
CREATE ROUTINE |
Create_routine_priv |
存储过程 |
CREATE TABLESPACE |
Create_tablespace_priv |
服务器管理 |
CREATE TEMPORARY TABLES |
Create_tmp_table_priv |
表格 |
CREATE USER |
Create_user_priv |
服务器管理 |
CREATE VIEW |
Create_view_priv |
视图 |
DELETE |
Delete_priv |
表格 |
DROP |
Drop_priv |
数据库、表格或视图 |
DROP ROLE |
Drop_role_priv |
服务器管理 |
EVENT |
Event_priv |
数据库 |
EXECUTE |
Execute_priv |
存储过程 |
FILE |
File_priv |
服务器主机文件访问 |
GRANT OPTION |
Grant_priv |
数据库、表格或存储过程 |
INDEX |
Index_priv |
表格 |
INSERT |
Insert_priv |
表格或列 |
LOCK TABLES |
Lock_tables_priv |
数据库 |
PROCESS |
Process_priv |
服务器管理 |
PROXY |
查看 proxies_priv 表格 |
服务器管理 |
REFERENCES |
References_priv |
数据库或表 |
RELOAD |
Reload_priv |
服务器管理 |
REPLICATION CLIENT |
Repl_client_priv |
服务器管理 |
REPLICATION SLAVE |
Repl_slave_priv |
服务器管理 |
SELECT |
Select_priv |
表或列 |
SHOW DATABASES |
Show_db_priv |
服务器管理 |
SHOW VIEW |
Show_view_priv |
视图 |
SHUTDOWN |
Shutdown_priv |
服务器管理 |
SUPER |
Super_priv |
服务器管理 |
TRIGGER |
Trigger_priv |
表 |
UPDATE |
Update_priv |
表或列 |
USAGE |
“无权限”的同义词 | 服务器管理 |
Privilege | Grant Table Column | Context |
下表显示了在GRANT
和REVOKE
语句中使用的动态权限名称,以及权限适用的上下文。
表 8.3 GRANT 和 REVOKE 的可允许动态权限
Privilege | Context |
---|---|
APPLICATION_PASSWORD_ADMIN |
双密码管理 |
AUDIT_ABORT_EXEMPT |
允许被审计日志过滤器阻止的查询 |
AUDIT_ADMIN |
审计日志管理 |
AUTHENTICATION_POLICY_ADMIN |
认证管理 |
BACKUP_ADMIN |
备份管理 |
BINLOG_ADMIN |
备份和复制管理 |
BINLOG_ENCRYPTION_ADMIN |
备份和复制管理 |
CLONE_ADMIN |
克隆管理 |
CONNECTION_ADMIN |
服务器管理 |
ENCRYPTION_KEY_ADMIN |
服务器管理 |
FIREWALL_ADMIN |
防火墙管理 |
FIREWALL_EXEMPT |
防火墙管理 |
FIREWALL_USER |
防火墙管理 |
FLUSH_OPTIMIZER_COSTS |
服务器管理 |
FLUSH_STATUS |
服务器管理 |
FLUSH_TABLES |
服务器管理 |
FLUSH_USER_RESOURCES |
服务器管理 |
GROUP_REPLICATION_ADMIN |
复制管理 |
GROUP_REPLICATION_STREAM |
复制管理 |
INNODB_REDO_LOG_ARCHIVE |
重做日志归档管理 |
INNODB_REDO_LOG_ENABLE |
重做日志管理 |
MASKING_DICTIONARIES_ADMIN |
服务器管理 |
NDB_STORED_USER |
NDB 集群 |
PASSWORDLESS_USER_ADMIN |
认证管理 |
PERSIST_RO_VARIABLES_ADMIN |
服务器管理 |
REPLICATION_APPLIER |
用于复制通道的PRIVILEGE_CHECKS_USER |
REPLICATION_SLAVE_ADMIN |
复制管理 |
RESOURCE_GROUP_ADMIN |
资源组管理 |
RESOURCE_GROUP_USER |
资源组管理 |
ROLE_ADMIN |
服务器管理 |
SENSITIVE_VARIABLES_OBSERVER |
服务器管理 |
SESSION_VARIABLES_ADMIN |
服务器管理 |
SET_USER_ID |
服务器管理 |
SHOW_ROUTINE |
服务器管理 |
SKIP_QUERY_REWRITE |
服务器管理 |
SYSTEM_USER |
服务器管理 |
SYSTEM_VARIABLES_ADMIN |
服务器管理 |
TABLE_ENCRYPTION_ADMIN |
服务器管理 |
TELEMETRY_LOG_ADMIN |
用于 AWS 上 MySQL HeatWave 的遥测日志管理 |
TP_CONNECTION_ADMIN |
线程池管理 |
VERSION_TOKEN_ADMIN |
服务器管理 |
XA_RECOVER_ADMIN |
服务器管理 |
权限 | 上下文 |
静态权限描述
静态权限是内置到服务器中的,与在运行时定义的动态权限相对。以下列表描述了 MySQL 中每个静态权限的可用性。
特定的 SQL 语句可能具有比此处指示的更具体的权限要求。如果是这样,那么相关语句的描述将提供详细信息。
-
ALL
,ALL PRIVILEGES
这些权限标识符是“在给定权限级别下可用的所有权限”的简写(除了
GRANT OPTION
)。例如,在全局或表级别授予ALL
将授予所有全局权限或所有表级权限。 -
ALTER
允许使用
ALTER TABLE
语句更改表的结构。ALTER TABLE
还需要CREATE
和INSERT
权限。重命名表需要在旧表上具有ALTER
和DROP
权限,在新表上具有CREATE
和INSERT
权限。 -
ALTER ROUTINE
允许使用更改或删除存储例程(存储过程和函数)的语句。对于授予权限的范围内的例程,以及用户不是例程
DEFINER
的用户的例程,还允许访问除例程定义之外的例程属性。 -
CREATE
允许使用创建新数据库和表的语句。
-
CREATE ROLE
允许使用
CREATE ROLE
语句。(CREATE USER
权限也允许使用CREATE ROLE
语句。)请参阅 Section 8.2.10, “Using Roles”。CREATE ROLE
和DROP ROLE
权限不像CREATE USER
那样强大,因为它们只能用于创建和删除帐户。它们不能像CREATE USER
那样用于修改帐户属性或重命名帐户。请参阅用户和角色的互换性。 -
CREATE ROUTINE
允许使用创建存储例程(存储过程和函数)的语句。对于授予权限的范围内的例程,以及用户不是被命名为例程
DEFINER
的用户的例程,还可以访问除例程定义之外的例程属性。 -
CREATE TABLESPACE
允许使用创建、修改或删除表空间和日志文件组的语句。
-
CREATE TEMPORARY TABLES
允许使用
CREATE TEMPORARY TABLE
语句创建临时表。会话创建临时表后,服务器不会对表进行进一步的权限检查。创建会话可以对表执行任何操作,例如
DROP TABLE
,INSERT
,UPDATE
或SELECT
。更多信息,请参阅 Section 15.1.20.2, “CREATE TEMPORARY TABLE Statement”。 -
CREATE USER
允许使用
ALTER USER
,CREATE ROLE
,CREATE USER
,DROP ROLE
,DROP USER
,RENAME USER
和REVOKE ALL PRIVILEGES
语句。 -
CREATE VIEW
允许使用
CREATE VIEW
语句。 -
DELETE
允许从数据库中删除表中的行。
-
DROP
允许使用删除(移除)现有数据库、表和视图的语句。在分区表上使用
ALTER TABLE ... DROP PARTITION
语句需要DROP
权限。对于TRUNCATE TABLE
也需要DROP
权限。 -
DROP ROLE
允许使用
DROP ROLE
语句。(CREATE USER
权限也允许使用DROP ROLE
语句。)参见第 8.2.10 节,“使用角色”。CREATE ROLE
和DROP ROLE
权限不如CREATE USER
强大,因为它们只能用于创建和删除账户。它们不能像CREATE USER
那样修改账户属性或重命名账户。参见用户和角色的互换性。 -
EVENT
允许使用创建、修改、删除或显示事件调度器事件的语句。
-
EXECUTE
允许使用执行存储例程(存储过程和函数)的语句。对于权限被授予的范围内的例程以及用户不是例程
DEFINER
的用户,还允许访问例程定义之外的例程属性。 -
FILE
影响以下操作和服务器行为:
-
使用
LOAD DATA
和SELECT ... INTO OUTFILE
语句以及LOAD_FILE()
函数在服务器主机上读写文件。拥有FILE
权限的用户可以读取服务器主机上任何世界可读或 MySQL 服务器可读的文件。(这意味着用户可以读取任何数据库目录中的文件,因为服务器可以访问这些文件。) -
允许在 MySQL 服务器具有写入权限的任何目录中创建新文件。这包括包含实现权限表的文件的服务器数据目录。
-
允许使用
DATA DIRECTORY
或INDEX DIRECTORY
表选项用于CREATE TABLE
语句。
作为安全措施,服务器不会覆盖现有文件。
要限制可以读取和写入文件的位置,请将
secure_file_priv
系统变量设置为特定目录。请参阅第 7.1.8 节,“服务器系统变量”。 -
-
GRANT OPTION
允许您向其他用户授予或撤销您自己拥有的权限。
-
INDEX
允许使用创建或删除(移除)索引的语句。
INDEX
适用于现有表。如果您对表具有CREATE
权限,可以在CREATE TABLE
语句中包含索引定义。 -
INSERT
允许向数据库中的表插入行。
INSERT
也适用于ANALYZE TABLE
、OPTIMIZE TABLE
和REPAIR TABLE
表维护语句。 -
LOCK TABLES
允许使用显式的
LOCK TABLES
语句来锁定具有SELECT
权限的表。这包括使用写锁,阻止其他会话读取被锁定的表。 -
PROCESS
PROCESS
权限控制对服务器内执行的线程信息的访问(即会话执行的语句的信息)。可以通过SHOW PROCESSLIST
语句、mysqladmin processlist命令、信息模式PROCESSLIST
表和性能模式processlist
表访问线程信息,具体如下:-
拥有
PROCESS
权限的用户可以访问所有线程的信息,甚至包括其他用户的线程。 -
没有
PROCESS
权限,非匿名用户可以访问自己的线程信息,但不能访问其他用户的线程信息,匿名用户则无法访问线程信息。
注意
性能模式
threads
表还提供线程信息,但表访问使用不同的权限模型。请参阅第 29.12.21.8 节,“The threads Table”。PROCESS
权限还允许使用SHOW ENGINE
语句,访问INFORMATION_SCHEMA
InnoDB
表(名称以INNODB_
开头的表),以及(从 MySQL 8.0.21 开始)访问INFORMATION_SCHEMA
FILES
表。 -
-
PROXY
允许一个用户冒充或成为另一个用户。请参阅第 8.2.19 节,“Proxy Users”。
-
REFERENCES
创建外键约束需要父表的
REFERENCES
权限。 -
RELOAD
RELOAD
使以下操作生效:-
使用
FLUSH
语句。 -
使用mysqladmin命令等同于
FLUSH
操作:flush-hosts
,flush-logs
,flush-privileges
,flush-status
,flush-tables
,flush-threads
,refresh
和reload
。reload
命令告诉服务器重新加载授权表到内存中。flush-privileges
是reload
的同义词。refresh
命令关闭并重新打开日志文件并刷新所有表。其他flush-*
xxx*
命令执行类似于refresh
的功能,但更具体,可能在某些情况下更可取。例如,如果要刷新日志文件,flush-logs
比refresh
更好。 -
使用mysqldump选项执行各种
FLUSH
操作:--flush-logs
和--master-data
。 -
使用
RESET MASTER
和RESET REPLICA
(或在 MySQL 8.0.22 之前,RESET SLAVE
)语句。
-
-
REPLICATION CLIENT
允许使用
SHOW MASTER STATUS
、SHOW REPLICA STATUS
和SHOW BINARY LOGS
语句。 -
复制从服务器
允许帐户请求在复制源服务器上对数据库进行的更新,使用
SHOW REPLICAS
(或在 MySQL 8.0.22 之前,SHOW SLAVE HOSTS
)、SHOW RELAYLOG EVENTS
和SHOW BINLOG EVENTS
语句。还需要此权限才能使用mysqlbinlog选项--read-from-remote-server
(-R
)、--read-from-remote-source
和--read-from-remote-master
。将此权限授予由副本用于连接到当前服务器作为其复制源服务器的帐户。 -
SELECT
允许从数据库中选择表中的行。只有当
SELECT
语句实际访问表时,才需要SELECT
权限。一些SELECT
语句不访问表,可以在没有任何数据库权限的情况下执行。例如,您可以使用SELECT
作为一个简单的计算器来评估不涉及表的表达式:SELECT 1+1; SELECT PI()*2;
还需要
SELECT
权限用于读取列值的其他语句。例如,在UPDATE
语句中,需要为在col_name
=expr
赋值右侧引用的列或在DELETE
或UPDATE
语句的WHERE
子句中命名的列。用于
EXPLAIN
中使用的表或视图需要SELECT
权限,包括视图定义中的任何基础表。 -
显示数据库
通过发出
SHOW DATABASE
语句,使账户能够查看数据库名称。没有此权限的账户只能看到他们拥有某些权限的数据库,并且如果服务器是使用--skip-show-database
选项启动的,则根本无法使用该语句。注意
因为任何静态全局权限被视为所有数据库的权限,任何静态全局权限使用户可以使用
SHOW DATABASES
或通过检查INFORMATION_SCHEMA
的SCHEMATA
表来查看所有数据库名称,除了通过部分撤销在数据库级别限制的数据库。 -
SHOW VIEW
允许使用
SHOW CREATE VIEW
语句。此权限也适用于与EXPLAIN
一起使用的视图。 -
SHUTDOWN
允许使用
SHUTDOWN
和RESTART
语句,mysqladmin shutdown命令,以及mysql_shutdown()
C API 函数。 -
SUPER
SUPER
是一个强大且影响深远的权限,不应轻易授予。如果一个账户只需要执行SUPER
操作的子集,可能可以通过代替授予一个或多个动态权限来实现所需的权限集,每个动态权限都提供更有限的功能。请参阅动态权限描述。注意
SUPER
已被弃用,您应该期望它在将来的 MySQL 版本中被移除。请参阅从 SUPER 迁移到动态权限的账户迁移。SUPER
影响以下操作和服务器行为:-
允许在运行时更改系统变量:
-
允许对全局系统变量进行服务器配置更改,使用
SET GLOBAL
和SET PERSIST
。相应的动态权限是
SYSTEM_VARIABLES_ADMIN
。 -
允许设置需要特殊权限的受限会话系统变量。
对应的动态特权是
SESSION_VARIABLES_ADMIN
。
参见 Section 7.1.9.1,“系统变量特权”。
-
-
启用对全局事务特性的更改(参见 Section 15.3.7,“SET TRANSACTION 语句”)。
对应的动态特权是
SYSTEM_VARIABLES_ADMIN
。 -
启用帐户启动和停止复制,包括组复制。
对于常规复制,对应的动态特权是
REPLICATION_SLAVE_ADMIN
,对于组复制是GROUP_REPLICATION_ADMIN
。 -
启用使用
CHANGE REPLICATION SOURCE TO
语句(从 MySQL 8.0.23 开始)、CHANGE MASTER TO
语句(在 MySQL 8.0.23 之前)和CHANGE REPLICATION FILTER
语句。对应的动态特权是
REPLICATION_SLAVE_ADMIN
。 -
通过
PURGE BINARY LOGS
和BINLOG
语句实现二进制日志控制。对应的动态特权是
BINLOG_ADMIN
。 -
启用在执行视图或存储程序时设置有效授权 ID。拥有此特权的用户可以在视图或存储程序的
DEFINER
属性中指定任何帐户。对应的动态特权是
SET_USER_ID
。 -
启用使用
CREATE SERVER
、ALTER SERVER
和DROP SERVER
语句。 -
启用mysqladmin debug命令的使用。
-
启用
InnoDB
加密密钥轮换。对应的动态特权是
ENCRYPTION_KEY_ADMIN
。 -
启用执行版本令牌函数。
对应的动态特权是
VERSION_TOKEN_ADMIN
。 -
启用授予和撤销角色,使用
GRANT
语句的WITH ADMIN OPTION
子句,以及ROLES_GRAPHML()
函数结果中的非空<graphml>
元素内容。相应的动态特权是
ROLE_ADMIN
。 -
允许控制不允许非
SUPER
账户的客户端连接:-
允许使用
KILL
语句或mysqladmin kill命令终止属于其他账户的线程。(一个账户始终可以终止自己的线程。) -
当
SUPER
客户端连接时,服务器不执行init_connect
系统变量内容。 -
即使达到由
max_connections
系统变量配置的连接限制,服务器也会接受来自SUPER
客户端的一个连接。 -
处于离线模式(启用
offline_mode
)的服务器不会在下一个客户端请求时终止SUPER
客户端连接,并接受来自SUPER
客户端的新连接。 -
当
read_only
系统变量启用时,可以执行更新操作。这适用于显式表更新,以及使用诸如GRANT
和REVOKE
等更新表的账户管理语句。
前述连接控制操作对应的动态特权是
CONNECTION_ADMIN
。 -
如果启用了二进制日志记录,您可能还需要
SUPER
特权来创建或更改存储函数,如 Section 27.7, “Stored Program Binary Logging”中所述。 -
-
TRIGGER
启用触发器操作。您必须对表具有此特权才能为该表创建、删除、执行或显示触发器。
当触发器被激活(由具有执行
INSERT
、UPDATE
或DELETE
语句权限的用户激活,与触发器相关联的表),触发器执行要求定义触发器的用户仍然对表具有TRIGGER
特权。 -
UPDATE
允许更新数据库中表中的行。
-
USAGE
此特权说明符代表“无特权”。它在全局级别与
GRANT
一起使用,用于指定诸如WITH GRANT OPTION
之类的子句,而不在特权列表中命名特定帐户特权。SHOW GRANTS
显示USAGE
以指示帐户在特权级别上没有特权。
动态特权描述
动态特权在运行时定义,与内置于服务器中的静态特权相对。以下列表描述了 MySQL 中每个可用的动态特权。
大多数动态特权在服务器启动时定义。其他特权由特定组件或插件定义,如特权描述中所示。在这种情况下,除非启用定义它的组件或插件,否则该特权不可用。
特定的 SQL 语句可能具有比此处指示的更具体的特权要求。如果是这样,相关语句的描述提供详细信息。
-
APPLICATION_PASSWORD_ADMIN
(在 MySQL 8.0.14 中添加)对于双密码功能,此特权允许使用
RETAIN CURRENT PASSWORD
和DISCARD OLD PASSWORD
子句,适用于您自己的帐户的ALTER USER
和SET PASSWORD
语句。大多数用户只需要一个密码,因此需要此特权来操作自己的次要密码。如果要允许帐户操作所有帐户的次要密码,则应授予
CREATE USER
特权,而不是APPLICATION_PASSWORD_ADMIN
。有关双密码使用的更多信息,请参见第 8.2.15 节,“密码管理”。
-
AUDIT_ABORT_EXEMPT
(在 MySQL 8.0.28 中添加)允许在审计日志过滤器中由“中止”项目阻止的查询。此特权由
audit_log
插件定义;请参见第 8.4.5 节,“MySQL 企业审计”。在 MySQL 8.0.28 或更高版本中创建的带有
SYSTEM_USER
权限的帐户在创建时会自动分配AUDIT_ABORT_EXEMPT
权限。在进行 MySQL 8.0.28 或更高版本的升级过程中,如果没有现有帐户被分配该权限,则具有SYSTEM_USER
权限的现有帐户也会被分配AUDIT_ABORT_EXEMPT
权限。因此,具有SYSTEM_USER
权限的帐户可用于在审核配置错误后恢复对系统的访问。 -
AUDIT_ADMIN
启用审核日志配置。此权限由
audit_log
插件定义;请参阅第 8.4.5 节,“MySQL 企业审计”。 -
BACKUP_ADMIN
启用执行
LOCK INSTANCE FOR BACKUP
语句和访问性能模式log_status
表。注意
除了
BACKUP_ADMIN
权限外,还需要对log_status
表的SELECT
权限才能访问。在从早期版本升级到 MySQL 8.0 时,具有
RELOAD
权限的用户在执行就地升级时会自动被授予BACKUP_ADMIN
权限。 -
AUTHENTICATION_POLICY_ADMIN
(MySQL 8.0.27 中新增)authentication_policy
系统变量对CREATE USER
和ALTER USER
语句中的身份验证相关子句的使用施加了一定的约束。具有AUTHENTICATION_POLICY_ADMIN
权限的用户不受这些约束的限制。(对于否则不允许的语句会发出警告。)有关
authentication_policy
强加的约束的详细信息,请参阅该变量的描述。 -
BINLOG_ADMIN
通过
PURGE BINARY LOGS
和BINLOG
语句启用二进制日志控制。 -
BINLOG_ENCRYPTION_ADMIN
启用设置系统变量
binlog_encryption
,该变量激活或停用二进制日志文件和中继日志文件的加密。这种能力不是由BINLOG_ADMIN
、SYSTEM_VARIABLES_ADMIN
或SESSION_VARIABLES_ADMIN
权限提供的。相关的系统变量binlog_rotate_encryption_master_key_at_startup
,在服务器重新启动时自动旋转二进制日志主密钥,不需要此权限。 -
CLONE_ADMIN
启用执行
CLONE
语句。包括BACKUP_ADMIN
和SHUTDOWN
权限。 -
CONNECTION_ADMIN
启用使用
KILL
语句或mysqladmin kill 命令来终止属于其他账户的线程。 (一个账户始终可以终止自己的线程。)启用设置与客户端连接相关的系统变量,或绕过与客户端连接相关的限制。从 MySQL 8.0.31 开始,需要
CONNECTION_ADMIN
权限来激活 MySQL 服务器的离线模式,这是通过将offline_mode
系统变量的值更改为ON
来完成的。CONNECTION_ADMIN
权限使具有该权限的管理员可以绕过这些系统变量的影响:-
init_connect
: 当CONNECTION_ADMIN
客户端连接时,服务器不会执行init_connect
系统变量内容。 -
max_connections
: 即使达到由max_connections
系统变量配置的连接限制,服务器也会接受来自CONNECTION_ADMIN
客户端的一个连接。 -
offline_mode
:处于离线模式的服务器(启用了offline_mode
)不会在下一个客户端请求时终止CONNECTION_ADMIN
客户端连接,并接受来自CONNECTION_ADMIN
客户端的新连接。 -
read_only
:即使启用了read_only
系统变量,也可以执行来自CONNECTION_ADMIN
客户端的更新。这适用于显式表更新,以及更新隐式更新表的账户管理语句,如GRANT
和REVOKE
。
Group Replication 组成员需要
CONNECTION_ADMIN
特权,以便在涉及的服务器中的一个处于离线模式时,Group Replication 连接不会被终止。如果使用 MySQL 通信堆栈(group_replication_communication_stack = MYSQL
),没有此特权,处于离线模式的成员将被从组中驱逐。 -
-
ENCRYPTION_KEY_ADMIN
启用
InnoDB
加密密钥轮换。 -
FIREWALL_ADMIN
启用用户管理任何用户的防火墙规则。此特权由
MYSQL_FIREWALL
插件定义;参见第 8.4.7 节,“MySQL 企业防火墙”。 -
FIREWALL_EXEMPT
(MySQL 8.0.27 中添加)拥有此特权的用户不受防火墙限制。此特权由
MYSQL_FIREWALL
插件定义;参见第 8.4.7 节,“MySQL 企业防火墙”。 -
FIREWALL_USER
启用用户更新其自己的防火墙规则。此特权由
MYSQL_FIREWALL
插件定义;参见第 8.4.7 节,“MySQL 企业防火墙”。 -
FLUSH_OPTIMIZER_COSTS
(MySQL 8.0.23 中添加)启用
FLUSH OPTIMIZER_COSTS
语句的使用。 -
FLUSH_STATUS
(MySQL 8.0.23 中添加)启用
FLUSH STATUS
语句的使用。 -
FLUSH_TABLES
(MySQL 8.0.23 中添加)启用
FLUSH TABLES
语句的使用。 -
FLUSH_USER_RESOURCES
(MySQL 8.0.23 中添加)允许使用
FLUSH USER_RESOURCES
语句。 -
GROUP_REPLICATION_ADMIN
允许账户启动和停止组复制,使用
START GROUP REPLICATION
和STOP GROUP REPLICATION
语句,更改group_replication_consistency
系统变量的全局设置,并使用group_replication_set_write_concurrency()
和group_replication_set_communication_protocol()
函数。授予此权限给用于管理属于复制组的服务器的账户。 -
GROUP_REPLICATION_STREAM
允许用户账户用于建立组复制的组通信连接。当 MySQL 通信堆栈用于组复制时(
group_replication_communication_stack=MYSQL
),必须授予恢复用户此权限。 -
INNODB_REDO_LOG_ARCHIVE
允许账户激活和停用重做日志归档。
-
INNODB_REDO_LOG_ENABLE
允许使用
ALTER INSTANCE {ENABLE|DISABLE} INNODB REDO_LOG
语句启用或禁用重做日志。MySQL 8.0.21 中引入。参见禁用重做日志。
-
MASKING_DICTIONARIES_ADMIN
允许账户使用
masking_dictionary_term_add()
和masking_dictionary_term_remove()
组件函数添加和移除字典术语。账户还需要此动态权限使用masking_dictionary_remove()
函数移除完整字典,该函数会移除mysql.masking_dictionaries
表中与命名字典相关的所有术语。请参阅 第 8.5 节,“MySQL 企业数据脱敏和去标识化”。
-
NDB_STORED_USER
使用户或角色及其权限能够在所有加入给定 NDB 集群的
NDB
启用的 MySQL 服务器之间共享和同步。此特权仅在启用NDB
存储引擎时可用。对给定用户或角色的权限更改或撤销会立即与所有连接的 MySQL 服务器(SQL 节点)同步。您应该注意,不能保证来自不同 SQL 节点的影响权限的多个语句以相同顺序在所有 SQL 节点上执行。因此,强烈建议所有用户管理都从一个指定的 SQL 节点进行。
NDB_STORED_USER
是一个全局特权,必须使用ON *.*
进行授予或撤销。尝试为此特权设置任何其他范围将导致错误。这个特权可以授予大多数应用程序和管理用户,但不能授予系统保留帐户,如mysql.session@localhost
或mysql.infoschema@localhost
。被授予
NDB_STORED_USER
特权的用户存储在NDB
中(因此被所有 SQL 节点共享),具有此特权的角色也是如此。仅仅被授予具有NDB_STORED_USER
的角色的用户 不 存储在NDB
中;每个NDB
存储的用户必须显式授予该特权。有关在
NDB
中如何工作的更详细信息,请参阅 第 25.6.13 节,“特权同步和 NDB_STORED_USER”。NDB_STORED_USER
特权从 NDB 8.0.18 开始提供。 -
PASSWORDLESS_USER_ADMIN
(在 MySQL 8.0.27 中添加)此特权适用于无密码用户帐户:
-
对于帐户创建,执行
CREATE USER
创建无密码帐户的用户必须具有PASSWORDLESS_USER_ADMIN
特权。 -
在复制环境中,
PASSWORDLESS_USER_ADMIN
特权适用于复制用户,并允许为配置为无密码身份验证的用户帐户复制ALTER USER ... MODIFY
语句。
有关无密码身份验证的信息,请参阅 FIDO 无密码身份验证。
-
-
PERSIST_RO_VARIABLES_ADMIN
对于还具有
SYSTEM_VARIABLES_ADMIN
的用户,PERSIST_RO_VARIABLES_ADMIN
允许使用SET PERSIST_ONLY
将全局系统变量持久化到数据目录中的mysqld-auto.cnf
选项文件中。此语句类似于SET PERSIST
,但不修改运行时全局系统变量值。这使得SET PERSIST_ONLY
适用于配置只能在服务器启动时设置的只读系统变量。参见 第 7.1.9.1 节,“系统变量权限”。
-
REPLICATION_APPLIER
启用账户作为复制通道的
PRIVILEGE_CHECKS_USER
,并在mysqlbinlog输出中执行BINLOG
语句。授予此权限给通过CHANGE REPLICATION SOURCE TO
(从 MySQL 8.0.23 开始)或CHANGE MASTER TO
(在 MySQL 8.0.23 之前)分配的账户,以为复制通道提供安全上下文,并处理这些通道上的复制错误。除了REPLICATION_APPLIER
权限外,您还必须为账户授予执行复制通道接收的事务或包含在mysqlbinlog输出中的所需权限,例如更新受影响的表。有关更多信息,请参见 第 19.3.3 节,“复制权限检查”。 -
REPLICATION_SLAVE_ADMIN
允许帐户连接到复制源服务器,使用
START REPLICA
和STOP REPLICA
语句启动和停止复制,并使用CHANGE REPLICATION SOURCE TO
语句(从 MySQL 8.0.23 开始)或CHANGE MASTER TO
语句(在 MySQL 8.0.23 之前)以及CHANGE REPLICATION FILTER
语句。授予此特权给由副本使用的帐户,以连接到当前服务器作为其复制源服务器。此特权不适用于组复制;对于组复制,请使用GROUP_REPLICATION_ADMIN
。 -
RESOURCE_GROUP_ADMIN
允许资源组管理,包括创建、修改和删除资源组,以及将线程和语句分配给资源组。拥有此特权的用户可以执行与资源组相关的任何操作。
-
RESOURCE_GROUP_USER
允许将线程和语句分配给资源组。拥有此特权的用户可以使用
SET RESOURCE GROUP
语句和RESOURCE_GROUP
优化提示。 -
ROLE_ADMIN
允许授予和撤销角色,使用
GRANT
语句的WITH ADMIN OPTION
子句,以及ROLES_GRAPHML()
函数结果中的非空<graphml>
元素内容。需要设置mandatory_roles
系统变量的值。 -
SENSITIVE_VARIABLES_OBSERVER
(在 MySQL 8.0.29 中添加)允许持有者查看性能模式表中敏感系统变量的值
global_variables
,session_variables
,variables_by_thread
,和persisted_variables
,发出SELECT
语句以返回它们的值,并在会话跟踪器中跟踪连接的更改。没有此权限的用户无法查看或跟踪这些系统变量的值。请参见持久化敏感系统变量。 -
SERVICE_CONNECTION_ADMIN
允许连接到仅允许管理连接的网络接口(参见第 7.1.12.1 节,“连接接口”)。
-
SESSION_VARIABLES_ADMIN
(在 MySQL 8.0.14 中添加)对于大多数系统变量,设置会话值不需要特殊权限,任何用户都可以执行以影响当前会话。对于一些系统变量,设置会话值可能会影响当前会话之外的效果,因此是一项受限操作。对于这些情况,
SESSION_VARIABLES_ADMIN
权限使用户能够设置会话值。如果系统变量受限并需要特殊权限来设置会话值,则变量描述会指示该限制。例如包括
binlog_format
,sql_log_bin
,和sql_log_off
。在 MySQL 8.0.14 之前,当添加了
SESSION_VARIABLES_ADMIN
时,受限会话系统变量只能由具有SYSTEM_VARIABLES_ADMIN
或SUPER
权限的用户设置。SESSION_VARIABLES_ADMIN
特权是SYSTEM_VARIABLES_ADMIN
和SUPER
特权的子集。拥有这些特权之一的用户也被允许设置受限制的会话变量,并且通过暗示具有SESSION_VARIABLES_ADMIN
,无需显式授予SESSION_VARIABLES_ADMIN
。另请参阅第 7.1.9.1 节,“系统变量特权”。
-
SET_USER_ID
允许在执行视图或存储程序时设置有效的授权 ID。拥有此特权的用户可以将任何帐户指定为视图或存储程序的
DEFINER
属性。存储程序将以指定帐户的权限执行,因此请确保遵循第 27.6 节,“存储对象访问控制”中列出的风险最小化准则。截至 MySQL 8.0.22,
SET_USER_ID
还允许覆盖旨在防止(可能是无意的)导致存储对象变成孤立或导致当前孤立的存储对象被采用的安全检查。有关详细信息,请参阅孤立存储对象。 -
SHOW_ROUTINE
(MySQL 8.0.20 中添加)允许用户访问所有存储例程(存储过程和函数)的定义和属性,即使用户未被命名为例程的
DEFINER
。此访问权限包括:-
信息模式
ROUTINES
表的内容。 -
SHOW CREATE FUNCTION
和SHOW CREATE PROCEDURE
语句。 -
SHOW FUNCTION CODE
和SHOW PROCEDURE CODE
语句。 -
SHOW FUNCTION STATUS
和SHOW PROCEDURE STATUS
语句。
在 MySQL 8.0.20 之前,用户要访问未定义的例程的定义,用户必须拥有全局
SELECT
权限,这是非常广泛的。从 8.0.20 开始,可以授予SHOW_ROUTINE
权限,这是一个范围更受限制的权限,允许访问例程定义。(也就是说,管理员可以从不需要的用户那里撤销全局SELECT
并授予SHOW_ROUTINE
)。这使得一个账户可以备份存储例程而不需要广泛的权限。 -
-
SKIP_QUERY_REWRITE
(MySQL 8.0.31 中添加)拥有此权限的用户发出的查询不会被
Rewriter
插件重写(参见第 7.6.4 节,“Rewriter 查询重写插件”)。此权限应授予发出不应被重写的管理或控制语句的用户,以及用于应用来自复制源的语句的
PRIVILEGE_CHECKS_USER
账户(参见第 19.3.3 节,“复制权限检查”)。 -
SYSTEM_USER
(MySQL 8.0.16 中添加)SYSTEM_USER
权限区分系统用户和普通用户:-
拥有
SYSTEM_USER
权限的用户是系统用户。 -
没有
SYSTEM_USER
权限的用户是普通用户。
SYSTEM_USER
权限影响用户可以应用其它权限的账户,以及用户是否受到其他账户的保护:-
系统用户可以修改系统账户和普通账户。也就是说,拥有在普通账户上执行特定操作的适当权限的用户,通过拥有
SYSTEM_USER
权限也可以在系统账户上执行该操作。只有拥有适当权限的系统用户才能修改系统账户,普通用户无法修改。 -
拥有适当权限的普通用户可以修改普通账户,但不能修改系统账户。系统账户可以被拥有适当权限的系统用户和普通用户修改。
这也意味着,由拥有
SYSTEM_USER
权限的用户创建的数据库对象不能被没有该权限的用户修改或删除。对于定义者拥有此权限的例程也适用。更多信息,请参见第 8.2.11 节,“账户类别”。
由于
SYSTEM_USER
权限为系统帐户提供的修改保护不适用于具有对mysql
系统模式特权的常规帐户,因此可以直接修改该模式中的授权表。为了完全保护,请不要向常规帐户授予mysql
模式特权。请参见防止常规帐户操纵系统帐户。如果使用了
audit_log
插件(参见第 8.4.5 节,“MySQL 企业审计”),从 MySQL 8.0.28 开始,具有SYSTEM_USER
权限的帐户将自动被分配AUDIT_ABORT_EXEMPT
权限,允许他们的查询即使在过滤器中配置了“中止”项目也能执行。具有SYSTEM_USER
权限的帐户因此可用于在审计配置错误后恢复对系统的访问。 -
-
SYSTEM_VARIABLES_ADMIN
影响以下操作和服务器行为:
-
允许在运行时更改系统变量:
-
允许使用
SET GLOBAL
和SET PERSIST
对全局系统变量进行服务器配置更改。 -
允许使用
SET PERSIST_ONLY
对全局系统变量进行服务器配置更改,如果用户还具有PERSIST_RO_VARIABLES_ADMIN
权限。 -
允许设置需要特殊权限的受限会话系统变量。实际上,
SYSTEM_VARIABLES_ADMIN
意味着未明确授予SESSION_VARIABLES_ADMIN
。
另请参阅第 7.1.9.1 节,“系统变量权限”。
-
-
允许更改全局事务特性(参见第 15.3.7 节,“SET TRANSACTION 语句”)。
-
-
TABLE_ENCRYPTION_ADMIN
(在 MySQL 8.0.16 中添加)当启用
table_encryption_privilege_check
时,允许用户覆盖默认加密设置;参见为模式和通用表空间定义加密默认值。 -
TELEMETRY_LOG_ADMIN
启用遥测日志配置。此特权由部署在 AWS 上的 MySQL HeatWave 的
telemetry_log
插件定义。 -
TP_CONNECTION_ADMIN
允许使用特权连接连接到服务器。当达到由
thread_pool_max_transactions_limit
定义的限制时,不允许新连接。特权连接忽略事务限制,并允许连接到服务器以增加事务限制、移除限制或终止运行中的事务。此特权不会默认授予任何用户。要建立特权连接,发起连接的用户必须具有TP_CONNECTION_ADMIN
特权。当达到由
thread_pool_max_transactions_limit
定义的限制时,特权连接可以执行语句并启动事务。特权连接被放置在Admin
线程组中。参见特权连接。 -
VERSION_TOKEN_ADMIN
允许执行版本令牌函数。此特权由
version_tokens
插件定义;参见第 7.6.6 节,“版本令牌”。 -
XA_RECOVER_ADMIN
允许执行
XA RECOVER
语句;参见第 15.3.8.1 节,“XA 事务 SQL 语句”。在 MySQL 8.0 之前,任何用户都可以执行
XA RECOVER
语句来发现未完成的准备好的 XA 事务的 XID 值,可能导致由非启动者执行提交或回滚 XA 事务。在 MySQL 8.0 中,XA RECOVER
仅允许拥有XA_RECOVER_ADMIN
权限的用户执行,这个权限预期只授予有需要的管理用户。例如,如果 XA 应用程序崩溃并且需要找到应用程序启动的未完成事务以便回滚,那么可能会出现这种情况。这种权限要求阻止用户发现除自己之外的未完成准备好的 XA 事务的 XID 值。它不会影响 XA 事务的正常提交或回滚,因为启动它的用户知道它的 XID。
权限授予指南
为账户授予它所需的权限是一个好主意。在授予 FILE
和管理权限时应特别小心:
-
FILE
可以被滥用来读取 MySQL 服务器在服务器主机上可以读取的任何文件到数据库表中。这包括所有可读取的文件和服务器数据目录中的文件。然后可以使用SELECT
访问该表,将其内容传输到客户端主机。 -
GRANT OPTION
允许用户将他们的权限授予其他用户。拥有不同权限并且具有GRANT OPTION
权限的两个用户可以组合权限。 -
ALTER
可能被用于通过重命名表来破坏权限系统。 -
SHUTDOWN
可以被滥用来通过终止服务器来完全拒绝其他用户的服务。 -
PROCESS
可以用于查看当前执行语句的纯文本,包括设置或更改密码的语句。 -
SUPER
可以用于终止其他会话或更改服务器操作方式。 -
为
mysql
系统数据库本身授予的权限可以用于更改密码和其他访问权限信息:-
密码被加密存储,因此恶意用户不能简单地读取它们以了解明文密码。但是,具有对
mysql.user
系统表authentication_string
列的写入访问权限的用户可以更改账户的密码,然后使用该账户连接到 MySQL 服务器。 -
为
mysql
系统数据库授予的INSERT
或UPDATE
权限使用户能够添加权限或修改现有权限。 -
为
mysql
系统数据库的DROP
允许用户删除权限表,甚至是数据库本身。
-
静态权限与动态权限
MySQL 支持静态和动态权限:
-
静态权限内置于服务器中。它们始终可供授予给用户帐户,并且无法注销。
-
动态权限可以在运行时注册和注销。这会影响它们的可用性:未注册的动态权限无法授予。
例如,SELECT
和 INSERT
权限是静态的,并且始终可用,而实现它的组件已启用时才会提供动态权限。
本节的其余部分描述了 MySQL 中动态权限的工作原理。讨论中使用术语“组件”,但同样适用于插件。
注意
服务器管理员应该了解哪些服务器组件定义了动态权限。对于 MySQL 发行版,定义动态权限的组件的文档描述了这些权限。
第三方组件也可能定义动态权限;管理员应该了解这些权限,并且不安装可能会与服务器操作发生冲突或危害的组件。例如,如果两个组件都定义了相同名称的权限,则一个组件会与另一个组件发生冲突。组件开发人员可以通过选择以组件名称为前缀的权限名称来减少此类情况发生的可能性。
服务器在内存中维护已注册的动态权限集。在服务器关闭时进行注销。
通常,定义动态权限的组件在安装时会注册它们,在初始化序列期间。当卸载时,组件不会注销其已注册的动态权限。(这是当前的做法,而不是要求。也就是说,组件可以,但不会在任何时候注销其注册的权限。)
尝试注册已经注册的动态权限不会出现警告或错误。考虑以下语句序列:
INSTALL COMPONENT 'my_component';
UNINSTALL COMPONENT 'my_component';
INSTALL COMPONENT 'my_component';
第一个 INSTALL COMPONENT
语句注册了组件 my_component
定义的任何权限,但 UNINSTALL COMPONENT
不会注销它们。对于第二个 INSTALL COMPONENT
语句,它注册的组件权限已经被发现已经注册,但不会出现警告或错误。
动态权限仅适用于全局级别。服务器在mysql.global_grants
系统表中存储有关动态权限对用户帐户的当前分配的信息:
-
服务器在启动时自动注册
global_grants
中命名的权限(除非提供了--skip-grant-tables
选项)。 -
GRANT
和REVOKE
语句修改global_grants
的内容。 -
在
global_grants
中列出的动态权限分配是持久的。它们在服务器关闭时不会被移除。
示例:以下语句授予用户u1
在副本上控制复制(包括组复制)和修改系统变量所需的权限:
GRANT REPLICATION_SLAVE_ADMIN, GROUP_REPLICATION_ADMIN, BINLOG_ADMIN
ON *.* TO 'u1'@'localhost';
授予的动态权限将出现在SHOW GRANTS
语句和INFORMATION_SCHEMA
USER_PRIVILEGES
表的输出中。
对于GRANT
和REVOKE
在全局级别,任何未被识别为静态的命名权限将与当前注册的动态权限集合进行检查,如果找到则授予。否则,将发生错误以指示未知的权限标识符。
对于GRANT
和REVOKE
在全局级别,ALL [PRIVILEGES]
的含义包括所有静态全局权限,以及当前注册的所有动态权限:
-
GRANT ALL
在全局级别授予所有静态全局权限和当前注册的所有动态权限。在执行GRANT
语句后注册的动态权限不会追溯地授予任何帐户。 -
REVOKE ALL
在全局级别撤销所有授予的静态全局权限和所有授予的动态权限。
FLUSH PRIVILEGES
语句读取global_grants
表中的动态权限分配,并注册在那里找到的任何未注册的权限。
有关 MySQL Server 和 MySQL 发行版中包含的组件提供的动态权限的描述,请参见 Section 8.2.2, “MySQL 提供的权限”。
从 SUPER 迁移帐户到动态权限
在 MySQL 8.0 中,许多以前需要SUPER
权限的操作也与更有限范围的动态权限相关联。(有关这些权限的描述,请参见第 8.2.2 节,“MySQL 提供的权限”。)每个这样的操作可以通过授予相关的动态权限而不是SUPER
权限来允许给账户。这种变化通过使 DBA 避免授予SUPER
权限并更紧密地调整用户权限以符合允许的操作来提高安全性。SUPER
现已被弃用;预计它将在 MySQL 的未来版本中被移除。
当移除SUPER
权限时,以前需要SUPER
权限的操作将失败,除非被授予SUPER
权限的账户迁移到适当的动态权限。使用以下说明来实现这一目标,以便在SUPER
权限被移除之前账户已准备就绪:
-
执行此查询以识别被授予
SUPER
权限的账户:SELECT GRANTEE FROM INFORMATION_SCHEMA.USER_PRIVILEGES WHERE PRIVILEGE_TYPE = 'SUPER';
-
对于由上述查询确定的每个账户,确定其需要
SUPER
权限的操作。然后授予相应操作的动态权限,并撤销SUPER
。例如,如果
'u1'@'localhost'
需要SUPER
权限用于二进制日志清除和系统变量修改,以下语句将对账户进行所需更改:GRANT BINLOG_ADMIN, SYSTEM_VARIABLES_ADMIN ON *.* TO 'u1'@'localhost'; REVOKE SUPER ON *.* FROM 'u1'@'localhost';
在修改了所有适用账户之后,第一步中的
INFORMATION_SCHEMA
查询应产生一个空结果集。
8.2.3 授权表
mysql
系统数据库包含几个授权表,其中包含有关用户账户和其持有的权限的信息。本节描述了这些表。有关系统数据库中其他表的信息,请参阅第 7.3 节,“mysql 系统模式”。
此处讨论了授权表的基本结构以及服务器在与客户端交互时如何使用其内容。然而,通常情况下不直接修改授权表。当您使用诸如CREATE USER
, GRANT
, 和 REVOKE
等账户管理语句设置账户和控制每个账户可用权限时,修改是间接发生的。请参阅第 15.7.1 节,“账户管理语句”。当您使用这些语句执行账户操作时,服务器会代表您修改授权表。
注意
不鼓励直接使用诸如INSERT
, UPDATE
, 或 DELETE
等语句直接修改授权表,这样做风险自负。服务器可以忽略由于这些修改导致的格式错误的行。
对于任何修改授权表的操作,服务器会检查表是否具有预期的结构,如果不是,则会产生错误。要将表更新为预期的结构,请执行 MySQL 升级过程。请参阅第三章,“升级 MySQL”。
-
授权表概述
-
用户和数据库授权表
-
tables_priv 和 columns_priv 授权表
-
procs_priv 授权表
-
proxies_priv 授权表
-
全局授权表
-
默认角色授权表
-
角色边缘授权表
-
密码历史授权表
-
授予表范围列属性
-
授予表权限列属性
-
授予表并发性
授予表概述
这些mysql
数据库表包含授予信息:
-
user
: 用户账户,静态全局权限和其他非权限列。 -
global_grants
: 动态全局权限。 -
db
: 数据库级权限。 -
tables_priv
: 表级权限。 -
columns_priv
: 列级权限。 -
procs_priv
: 存储过程和函数权限。 -
proxies_priv
: 代理用户权限。 -
default_roles
: 默认用户角色。 -
role_edges
: 角色子图的边缘。 -
password_history
: 密码更改历史。
有关静态和动态全局权限之间的区别,请参阅静态与动态权限。
在 MySQL 8.0 中,授予表使用InnoDB
存储引擎并且是事务性的。在 MySQL 8.0 之前,授予表使用MyISAM
存储引擎并且是非事务性的。授予表存储引擎的更改使得伴随的账户管理语句的行为也发生了变化,例如CREATE USER
或GRANT
。以前,命名多个用户的账户管理语句可能对某些用户成功,对其他用户失败。现在,每个语句都是事务性的,要么对所有命名用户成功,要么回滚并且如果出现任何错误则不起作用。
每个授予表包含范围列和权限列:
-
范围列确定表中每行的范围;也就是说,行适用的上下文。例如,具有
Host
和User
值为'h1.example.net'
和'bob'
的user
表行适用于由指定用户名称为bob
的客户端从主机h1.example.net
进行的身份验证连接。类似地,具有Host
,User
和Db
列值为'h1.example.net'
,'bob'
和'reports'
的db
表行适用于bob
从主机h1.example.net
连接以访问reports
数据库。tables_priv
和columns_priv
表包含指示每行适用于哪些表或表/列组合的范围列。procs_priv
范围列指示每行适用于的存储过程。 -
特权列指示表行授予哪些特权;也就是说,它允许执行哪些操作。服务器将各种授权表中的信息组合起来,形成用户特权的完整描述。第 8.2.7 节,“访问控制,第 2 阶段:请求验证”描述了这方面的规则。
此外,授权表可能包含用于除范围或特权评估之外的其他目的的列。
服务器使用授权表的方式如下:
-
user
表的范围列确定是否拒绝或允许传入连接。对于允许的连接,在user
表中授予的任何特权表示用户的静态全局特权。在此表中授予的任何特权都适用于所有服务器上的数据库。注意
因为任何静态全局特权都被视为所有数据库的特权,任何静态全局特权都使用户能够通过
SHOW DATABASES
或通过检查INFORMATION_SCHEMA
的SCHEMATA
表来查看所有数据库名称,除了在数据库级别通过部分撤销限制的数据库。 -
global_grants
表列出了对用户帐户分配的动态全局特权。对于每行,范围列确定具有特权列中命名的特权的用户。 -
db
表的范围列确定哪些用户可以从哪些主机访问哪些数据库。特权列确定允许的操作。在数据库级别授予的特权适用于数据库及其所有对象,如表和存储程序。 -
tables_priv
和columns_priv
表类似于db
表,但更加精细:它们适用于表和列级别,而不是数据库级别。在表级别授予的特权适用于表及其所有列。在列级别授予的特权仅适用于特定列。 -
procs_priv
表适用于存储例程(存储过程和函数)。在例程级别授予的权限仅适用于单个过程或函数。 -
proxies_priv
表指示哪些用户可以代表其他用户行事,以及用户是否可以向其他用户授予PROXY
权限。 -
default_roles
和role_edges
表包含有关角色关系的信息。 -
password_history
表保留先前选择的密码,以启用对密码重用的限制。请参阅 第 8.2.15 节,“密码管理”。
服务器在启动时将授予表的内容读入内存。您可以通过发出 FLUSH PRIVILEGES
语句或执行 mysqladmin flush-privileges 或 mysqladmin reload 命令来告诉它重新加载表。对授权表的更改将按照 第 8.2.13 节,“权限更改生效时间” 中指示的方式生效。
当您修改帐户时,最好验证您的更改是否产生预期效果。要检查给定帐户的权限,请使用 SHOW GRANTS
语句。例如,要确定授予具有用户名和主机名值为 bob
和 pc84.example.com
的帐户的权限,请使用此语句:
SHOW GRANTS FOR 'bob'@'pc84.example.com';
要显示帐户的非权限属性,请使用 SHOW CREATE USER
:
SHOW CREATE USER 'bob'@'pc84.example.com';
用户和数据库授权表
服务器在访问控制的第一和第二阶段都使用 mysql
数据库中的 user
和 db
表(参见 第 8.2 节,“访问控制和账户管理”)。这里显示了 user
和 db
表中的列。
表 8.4 user 和 db 表列
表名 | user |
db |
---|---|---|
范围列 | Host |
Host |
User |
Db |
|
User |
||
权限列 | Select_priv |
Select_priv |
Insert_priv |
Insert_priv |
|
Update_priv |
Update_priv |
|
Delete_priv |
Delete_priv |
|
Index_priv |
Index_priv |
|
Alter_priv |
Alter_priv |
|
Create_priv |
Create_priv |
|
Drop_priv |
Drop_priv |
|
Grant_priv |
Grant_priv |
|
Create_view_priv |
Create_view_priv |
|
Show_view_priv |
Show_view_priv |
|
Create_routine_priv |
Create_routine_priv |
|
Alter_routine_priv |
Alter_routine_priv |
|
Execute_priv |
Execute_priv |
|
Trigger_priv |
Trigger_priv |
|
Event_priv |
Event_priv |
|
Create_tmp_table_priv |
Create_tmp_table_priv |
|
Lock_tables_priv |
Lock_tables_priv |
|
References_priv |
References_priv |
|
Reload_priv |
||
Shutdown_priv |
||
Process_priv |
||
File_priv |
||
Show_db_priv |
||
Super_priv |
||
Repl_slave_priv |
||
Repl_client_priv |
||
Create_user_priv |
||
Create_tablespace_priv |
||
Create_role_priv |
||
Drop_role_priv |
||
安全列 | ssl_type |
|
ssl_cipher |
||
x509_issuer |
||
x509_subject |
||
plugin |
||
authentication_string |
||
password_expired |
||
password_last_changed |
||
password_lifetime |
||
account_locked |
||
Password_reuse_history |
||
Password_reuse_time |
||
Password_require_current |
||
User_attributes |
||
资源控制列 | max_questions |
|
max_updates |
||
max_connections |
||
max_user_connections |
||
表名 | user |
db |
user
表的plugin
和authentication_string
列存储身份验证插件和凭据信息。
服务器使用帐户行的plugin
列中命名的插件来验证帐户的连接尝试。
plugin
列必须非空。在启动时,以及在执行FLUSH PRIVILEGES
时,服务器会检查user
表行。对于任何plugin
列为空的行,服务器会向错误日志写入以下警告:
[Warning] User entry '*user_name*'@'*host_name*' has an empty plugin
value. The user will be ignored and no one can login with this user
anymore.
要为缺少插件的帐户分配插件,请使用ALTER USER
语句。
password_expired
列允许 DBA 过期帐户密码并要求用户重置密码。默认的password_expired
值为'N'
,但可以使用ALTER USER
语句设置为'Y'
。帐户密码过期后,在后续连接到服务器时,帐户执行的所有操作都会导致错误,直到用户发出ALTER USER
语句以建立新的帐户密码。
注意
尽管可以通过将过期密码设置为当前值来“重置”过期密码,但最好根据良好的政策选择不同的密码。DBA 可以通过建立适当的密码重用策略来强制不重用。请参阅密码重用策略。
password_last_changed
是一个TIMESTAMP
列,指示密码上次更改的时间。该值仅对使用 MySQL 内置身份验证插件(mysql_native_password
、sha256_password
或caching_sha2_password
)的帐户为非NULL
。对于其他帐户,如使用外部身份验证系统进行身份验证的帐户,该值为NULL
。
password_last_changed
由 CREATE USER
、ALTER USER
和 SET PASSWORD
语句以及创建帐户或更改帐户密码的 GRANT
语句更新。
password_lifetime
指示帐户密码的生命周期,以天为单位。如果密码已超过其生命周期(使用 password_last_changed
列进行评估),当客户端使用该帐户连接时,服务器将视密码为已过期。大于零的 N
值表示密码必须每 N
天更改一次。值为 0 禁用自动密码过期。如果值为 NULL
(默认值),则全局过期策略适用,由 default_password_lifetime
系统变量定义。
account_locked
指示帐户是否被锁定(参见第 8.2.20 节,“帐户锁定”)。
Password_reuse_history
是帐户的 PASSWORD HISTORY
选项的值,或者对于默认历史记录为 NULL
。
Password_reuse_time
是帐户的 PASSWORD REUSE INTERVAL
选项的值,或者对于默认间隔为 NULL
。
Password_require_current
(MySQL 8.0.13 中新增)对应于帐户的 PASSWORD REQUIRE
选项的值,如下表所示。
表 8.5 允许的 Password_require_current 值
Password_require_current 值 | 对应的 PASSWORD REQUIRE 选项 |
---|---|
'Y' |
PASSWORD REQUIRE CURRENT |
'N' |
PASSWORD REQUIRE CURRENT OPTIONAL |
NULL |
PASSWORD REQUIRE CURRENT DEFAULT |
User_attributes
(MySQL 8.0.14 中新增)是一个以 JSON 格式存储帐户属性的列,这些属性未存储在其他列中。截至 MySQL 8.0.21,INFORMATION_SCHEMA
通过 USER_ATTRIBUTES
表公开这些属性。
User_attributes
列可能包含这些属性:
-
additional_password
:次要密码,如果有的话。请参阅双密码支持。 -
Restrictions
:限制列表,如果有的话。限制是通过部分撤销操作添加的。属性值是一个元素数组,每个元素都有Database
和Restrictions
键,指示受限数据库的名称和适用于其上的限制(参见第 8.2.12 节,“使用部分撤销进行权限限制”)。 -
Password_locking
: 如果有的话,是关于失败登录跟踪和临时账户锁定的条件(参见失败登录跟踪和临时账户锁定)。Password_locking
属性根据CREATE USER
和ALTER USER
语句的FAILED_LOGIN_ATTEMPTS
和PASSWORD_LOCK_TIME
选项进行更新。该属性值是一个哈希,其中包含failed_login_attempts
和password_lock_time_days
键,指示为该账户指定的选项值。如果某个键缺失,则其值隐式为 0。如果键值隐式或显式为 0,则相应的功能被禁用。此属性在 MySQL 8.0.19 中添加。 -
multi_factor_authentication
:mysql.user
系统表中的行具有一个plugin
列,指示认证插件。对于单因素认证,该插件是唯一的认证因素。对于多因素认证的两因素或三因素形式,该插件对应于第一个认证因素,但必须为第二和第三因素存储额外信息。multi_factor_authentication
属性保存了这些信息。此属性在 MySQL 8.0.27 中添加。multi_factor_authentication
值是一个数组,其中每个数组元素都是一个哈希,描述了使用以下属性描述的认证因素:-
plugin
: 认证插件的名称。 -
authentication_string
: 认证字符串的值。 -
passwordless
: 一个标志,表示用户是否可以在没有密码的情况下使用(仅使用安全令牌作为唯一的认证方法)。 -
requires_registration
: 一个定义用户账户是否已注册安全令牌的标志。
第一个和第二个数组元素描述了多因素认证因素 2 和 3。
-
如果没有属性适用,则User_attributes
为NULL
。
例子:一个具有次要密码和部分撤销数据库权限的账户在列值中具有additional_password
和Restrictions
属性:
mysql> SELECT User_attributes FROM mysql.User WHERE User = 'u'\G
*************************** 1\. row ***************************
User_attributes: {"Restrictions":
[{"Database": "mysql", "Privileges": ["SELECT"]}],
"additional_password": "*hashed_credentials*"}
要确定存在哪些属性,请使用JSON_KEYS()
函数:
SELECT User, Host, JSON_KEYS(User_attributes)
FROM mysql.user WHERE User_attributes IS NOT NULL;
要提取特定属性,例如Restrictions
,请执行以下操作:
SELECT User, Host, User_attributes->>'$.Restrictions'
FROM mysql.user WHERE User_attributes->>'$.Restrictions' <> '';
这是存储在multi_factor_authentication
中的信息示例:
{
"multi_factor_authentication": [
{
"plugin": "authentication_ldap_simple",
"passwordless": 0,
"authentication_string": "ldap auth string",
"requires_registration": 0
},
{
"plugin": "authentication_fido",
"passwordless": 0,
"authentication_string": "",
"requires_registration": 1
}
]
}
tables_priv 和 columns_priv 授权表
在访问控制的第二阶段,服务器执行请求验证,以确保每个客户端对其发出的每个请求都具有足够的权限。除了user
和db
授权表外,服务器还可能在涉及表的请求中查阅tables_priv
和columns_priv
表。后者在表和列级别提供更精细的权限控制。它们具有以下表中显示的列。
表 8.6 tables_priv 和 columns_priv 表列
表名 | tables_priv |
columns_priv |
---|---|---|
范围列 | 主机 |
主机 |
数据库 |
数据库 |
|
用户 |
用户 |
|
表名 |
表名 |
|
列名 |
||
权限列 | 表权限 |
列权限 |
列权限 |
||
其他列 | 时间戳 |
时间戳 |
授权者 |
时间戳
和 授权者
列分别设置为当前时间戳和CURRENT_USER
值,但其他情况下未使用。
procs_priv 授权表
为了验证涉及存储例程的请求,服务器可能会查阅procs_priv
表,该表具有以下表中显示的列。
表 8.7 procs_priv 表列
表名 | procs_priv |
---|---|
范围列 | 主机 |
数据库 |
|
用户 |
|
例程名 |
|
例程类型 |
|
权限列 | Proc 权限 |
其他列 | 时间戳 |
授权者 |
例程类型
列是一个具有值 'FUNCTION'
或 'PROCEDURE'
的ENUM
列,用于指示行所指的例程类型。该列使得可以分别为具有相同名称的函数和过程授予权限。
时间戳
和 授权者
列未使用。
proxies_priv 授权表
proxies_priv
表记录有关代理账户的信息。它具有以下列:
-
主机
,用户
:代理账户;即具有被代理账户的PROXY
权限的账户。 -
被代理主机
,被代理用户
:被代理账户。 -
授权者
,时间戳
:未使用。 -
With_grant
:代理账户是否可以将PROXY
权限授予其他账户。
要使账户能够向其他账户授予PROXY
特权,必须在proxies_priv
表中具有一行,其中With_grant
设置为 1,Proxied_host
和Proxied_user
设置为指示可以授予特权的账户或账户。例如,在 MySQL 安装期间创建的'root'@'localhost'
账户在proxies_priv
表中有一行,该行允许为''@''
,即所有用户和所有主机,授予PROXY
特权。这使得root
能够设置代理用户,以及委派给其他账户权限设置代理用户的权限。参见 Section 8.2.19, “Proxy Users”。
全局授权授权表
global_grants
表列出了动态全局特权当前分配给用户账户的情况。该表包含以下列:
-
用户
,主机
:被授予权限的账户的用户名和主机名。 -
权限
:权限名称。 -
WITH_GRANT_OPTION
:账户是��可以向其他账户授予特权。
默认角色授权表
default_roles
表列出了默认用户角色。它包含以下列:
-
主机
,用户
:应用默认角色的账户或角色。 -
DEFAULT_ROLE_HOST
,DEFAULT_ROLE_USER
:默认角色。
角色边缘授权表
role_edges
表列出了角色子图的边缘。它包含以下列:
-
FROM_HOST
,FROM_USER
:被授予角色的账户。 -
TO_HOST
,TO_USER
:授予给账户的角色。 -
WITH_ADMIN_OPTION
:账户是否可以通过使用WITH ADMIN OPTION
向其他账户授予角色和撤销角色。
密码历史授权表
password_history
表包含有关密码更改的信息。它包含以下列:
-
主机
,用户
:密码更改发生的账户。 -
密码时间戳
:密码更改发生的时间。 -
密码
:新密码哈希值。
password_history
表累积每个账户的足够数量的非空密码,以使 MySQL 能够执行针对账户密码历史长度和重用间隔的检查。当密码更改尝试发生时,会自动修剪超出这两个限制的条目。
注意
空密码不计入密码历史记录,并可随时重新使用。
如果账户被重命名,其条目也将被重命名以匹配。如果账户被删除或其认证插件被更改,其条目将被移除。
授权表范围列属性
授权表中的范围列包含字符串。每个的默认值为空字符串。以下表显示了每个列中允许的字符数。
表 8.8 授权表范围列长度
列名 | 最大允许字符数 |
---|---|
主机 ,代理主机 |
255(在 MySQL 8.0.17 之前为 60) |
用户 ,代理用户 |
32 |
数据库 |
64 |
表名 |
64 |
Column_name |
64 |
Routine_name |
64 |
Host
和 Proxied_host
的值在存储在授权表中之前会被转换为小写。
为了访问检查目的,User
、Proxied_user
、authentication_string
、Db
和 Table_name
的值的比较是区分大小写的。Host
、Proxied_host
、Column_name
和 Routine_name
的值的比较是不区分大小写的。
授权表权限列属性
user
和 db
表中列出了每个权限在一个单独的列中声明为 ENUM('N','Y') DEFAULT 'N'
。换句话说,每个权限可以被禁用或启用,默认情况下为禁用。
tables_priv
、columns_priv
和 procs_priv
表将权限列声明为 SET
列。这些列中的值可以包含由表控制的任何权限的任意组合。只有列值中列出的权限才会被启用。
表 8.9 Set-Type Privilege Column Values
表名 | 列名 | 可能的集合元素 |
---|---|---|
tables_priv |
Table_priv |
'Select', 'Insert', 'Update', 'Delete', 'Create', 'Drop', 'Grant', 'References', 'Index', 'Alter', 'Create View', 'Show view', 'Trigger' |
tables_priv |
Column_priv |
'Select', 'Insert', 'Update', 'References' |
columns_priv |
Column_priv |
'Select', 'Insert', 'Update', 'References' |
procs_priv |
Proc_priv |
'Execute', 'Alter Routine', 'Grant' |
只有 user
和 global_grants
表指定了管理权限,例如 RELOAD
、SHUTDOWN
和 SYSTEM_VARIABLES_ADMIN
。管理操作是对服务器本身的操作,不是特定于数据库的,因此没有理由在其他授权表中列出这些权限。因此,服务器只需要查看 user
和 global_grants
表来确定用户是否可以执行管理操作。
FILE
权限也仅在 user
表中指定。它不是一种管理权限,而是用户在服务器主机上读取或写入文件的能力与所访问的数据库无关。
授权表并发性
从 MySQL 8.0.22 开始,为了允许在 MySQL 授权表上进行并发的 DML 和 DDL 操作,以前在 MySQL 授权表上获取行锁的读操作将作为非锁定读取执行。在 MySQL 授权表上执行为非锁定读取的操作包括:
-
通过联接列表和子查询从授权表中读取数据的
SELECT
语句和其他只读语句,包括使用任何事务隔离级别的SELECT ... FOR SHARE
语句。 -
从授权表中读取数据的 DML 操作(通过连接列表或子查询)但不修改它们,在任何事务隔离级别下使用。
从授权表中读取数据时不再获取行锁的语句,在使用基于语句的复制时执行会报告警告。
当使用 -binlog_format=mixed
时,从授权表中读取数据的 DML 操作会被写入二进制日志作为行事件,以使操作对混合模式复制安全。
SELECT ... FOR SHARE
语句从授权表中读取数据时会报告警告。使用FOR SHARE
子句时,不支持在授权表上进行读取锁定。
从授权表中读取数据并使用SERIALIZABLE
隔离级别执行的 DML 操作会报告警告。在使用SERIALIZABLE
隔离级别时通常会获取的读取锁在授权表上不受支持。
8.2.4 指定账户名
MySQL 账户名由用户名和主机名组成,这使得可以为从不同主机连接的具有相同用户名的用户创建不同的账户。本节描述了账户名的语法,包括特殊值和通配符规则。
在大多数方面,账户名与 MySQL 角色名类似,但有一些差异,详见第 8.2.5 节“指定角色名”。
账户名出现在 SQL 语句中,如CREATE USER
、GRANT
和SET PASSWORD
,并遵循以下规则:
-
账户名语法为
'*
user_name*'@'*
host_name*'
。 -
@'*
host_name*'
部分是可选的。仅由用户名组成的账户名等同于'*
user_name*'@'%'
。例如,'me'
等同于'me'@'%'
。 -
如果用户名和主机名作为未引用的标识符是合法的,则不需要加引号。如果
user_name
字符串包含特殊字符(如空格或-
),或者host_name
字符串包含特殊字符或通配符字符(如.
或%
),则必须使用引号。例如,在账户名'test-user'@'%.com'
中,用户名称和主机名部分都需要引号。 -
将用户名称和主机名引用为标识符或字符串,可以使用反引号进行引用。
-
如果用户名和主机名部分被引用,则必须分别引用。也就是说,写成
'me'@'localhost'
,而不是'me@localhost'
。(后者实际上等同于'me@localhost'@'%'
,尽管这种行为现在已被弃用。) -
对
CURRENT_USER
或CURRENT_USER()
函数的引用等同于直接指定当前客户端的用户名和主机名。
MySQL 使用独立列在mysql
系统数据库的授权表中存储账户名的用户名称和主机名部分:
-
user
表为每个账户包含一行。User
和Host
列存储用户名和主机名。该表还指示了账户具有哪些全局权限。 -
其他授权表显示了账户对数据库和数据库内对象的权限。这些表有
User
和Host
列用于存储账户名。这些表中的每一行都与user
表中具有相同User
和Host
值的账户相关联。 -
对于访问检查目的,User 值的比较区分大小写。Host 值的比较不区分大小写。
有关存储在授权表中的用户名称和主机名属性的详细信息,例如最大长度,请参阅 Grant Table Scope Column Properties。
用户名和主机名具有特定的特殊值或通配符约定,如下所述。
帐户名的用户名部分要么是一个非空值,字面上匹配传入连接尝试的用户名,要么是一个空值(空字符串),匹配任何用户名。具有空用户名的帐户是匿名用户。要在 SQL 语句中指定匿名用户,请使用带引号的空用户名部分,例如''@'localhost'
。
帐户名的主机名部分可以采用多种形式,并且允许使用通配符:
-
主机值可以是主机名或 IP 地址(IPv4 或 IPv6)。名称
'localhost'
表示本地主机。IP 地址'127.0.0.1'
表示 IPv4 环回接口。IP 地址'::1'
表示 IPv6 环回接口。 -
在主机名或 IP 地址值中允许使用
%
和_
通配符字符,但自 MySQL 8.0.35 起已不推荐使用,并且可能在将来的 MySQL 版本中删除。这些字符的含义与使用LIKE
运算符执行的模式匹配操作相同。例如,主机值'%'
匹配任何主机名,而值'%.mysql.com'
匹配mysql.com
域中的任何主机。'198.51.100.%'
匹配 198.51.100 类 C 网络中的任何主机。因为主机值中允许使用 IP 通配符值(例如,
'198.51.100.%'
匹配子网上的每个主机),因此某人可能尝试利用此功能,将主机命名为198.51.100.somewhere.com
。为了阻止这种尝试,MySQL 不会对以数字和点开头的主机名进行匹配。例如,如果主机名为1.2.example.com
,其名称永远不会与帐户名的主机部分匹配。IP 通配符值只能匹配 IP 地址,而不能匹配主机名。如果
partial_revokes
为ON
,MySQL 将%
和_
视为文字字符,而不是通配符。从 MySQL 8.0.35 开始,不推荐使用这些通配符(无论此变量的值如何),您应该期望这种功能在将来的 MySQL 版本中被移除。 -
对于指定为 IPv4 地址的主机值,可以提供一个子网掩码来指示用于网络号的地址位数。子网掩码表示法不能用于 IPv6 地址。
语法是
*
host_ip*/*
netmask*
。例如:CREATE USER 'david'@'198.51.100.0/255.255.255.0';
这使得
david
可以从具有 IP 地址client_ip
的任何客户端主机连接,条件如下:*client_ip* & *netmask* = *host_ip*
对于刚刚显示的
CREATE USER
语句:*client_ip* & 255.255.255.0 = 198.51.100.0
满足此条件的 IP 地址范围从
198.51.100.0
到198.51.100.255
。掩码通常以设置为 1 的位开始,然后是设置为 0 的位。示例:
-
198.0.0.0/255.0.0.0
: 198 类 A 网络上的任何主机 -
198.51.0.0/255.255.0.0
: 198.51 类 B 网络上的任何主机 -
198.51.100.0/255.255.255.0
: 198.51.100 类 C 网络上的任何主机 -
198.51.100.1
: 仅具有此特定 IP 地址的主机
-
-
自 MySQL 8.0.23 起,指定为 IPv4 地址的主机值可以使用 CIDR 表示法,例如
198.51.100.44/24
。
服务器通过系统 DNS 解析器返回的客户端主机名或 IP 地址的值来匹配帐户名称中的主机值。除非使用网络掩码表示帐户主机值,服务器将执行此比较作为字符串匹配,即使帐户主机值以 IP 地址形式给出。这意味着您应该以 DNS 使用的相同格式指定帐户主机值。以下是需要注意的问题示例:
-
假设本地网络上的主机具有完全限定名称
host1.example.com
。如果 DNS 返回此主机的名称查找为host1.example.com
,请在帐户主机值中使用该名称。如果 DNS 只返回host1
,请改用host1
。 -
如果 DNS 返回给定主机的 IP 地址为
198.51.100.2
,则匹配帐户主机值为198.51.100.2
,但不匹配198.051.100.2
。同样,它匹配帐户主机模式如198.51.100.%
,但不匹配198.051.100.%
。
为避免此类问题,建议检查 DNS 返回主机名和地址的格式。在 MySQL 帐户名称中使用相同格式的值。
8.2.5 指定角色名称
MySQL 角色名称指的是权限的命名集合。有关角色使用示例,请参阅第 8.2.10 节“使用角色”。
角色名称的语法和语义与帐户名称类似;请参阅第 8.2.4 节“指定帐户名称”。在授予表中存储时,它们具有与帐户名称相同的属性,这些属性在授予表作用域列属性中描述。
角色名称在以下几个方面与帐户名称不同:
-
角色名称的用户部分不能留空。因此,没有类似于“匿名用户”概念的“匿名角色”。
-
对于帐户名称,省略角色名称的主机部分将导致主机部分为
'%'
。但是,与帐户名称中的'%'
不同,在角色名称中的'%'
主机部分没有通配符属性。例如,对于作为角色名称使用的名称'me'@'%'
,主机部分('%'
)只是一个文字值;它没有“任何主机”匹配属性。 -
在角色名称的主机部分中使用的网络掩码表示法没有意义。
-
在几种情况下,帐户名称允许为
CURRENT_USER()
。角色名称不允许。
mysql.user
系统表中的一行可以同时充当帐户和角色。在这种情况下,在将名称用作角色名称的上下文中,任何特殊的用户或主机名匹配属性都不适用。例如,您不能执行以下语句,并期望它设置当前会话角色,使用所有具有用户部分为myrole
和任何主机名的角色:
SET ROLE 'myrole'@'%';
相反,该语句将会话的活动角色设置为名称为'myrole'@'%'
的角色。
因此,通常只使用用户名称部分指定角色名称,并让主机名部分隐式地为'%'
。如果您打算创建一个既可以作为角色又可以从给定主机连接的用户帐户的名称,则指定具有非'%'
主机部分的角色可能很有用。
8.2.6 访问控制,阶段 1:连接验证
当你尝试连接到 MySQL 服务器时,服务器根据以下条件接受或拒绝连接:
-
你的身份以及是否可以通过提供适当的凭证来验证。
-
你的帐户是锁定还是解锁的。
服务器首先检查凭证,然后检查帐户锁定状态。任一步骤失败都会导致服务器完全拒绝您的访问。否则,服务器接受连接,然后进入阶段 2 并等待请求。
服务器使用user
表中的列执行身份和凭证检查,仅当满足以下条件时才接受连接:
-
客户端主机名和用户名与某个
user
表行中的Host
和User
列匹配。有关允许的Host
和User
值的规则,请参见 Section 8.2.4, “Specifying Account Names”。 -
客户端提供行中指定的凭证(例如,密码),如
authentication_string
列所示。凭证使用plugin
列中命名的身份验证插件进行解释。 -
该行指示帐户未锁定。锁定状态记录在
account_locked
列中,其值必须为'N'
。可以使用CREATE USER
或ALTER USER
语句设置或更改帐户锁定。
你的身份基于两个信息:
-
你的 MySQL 用户名。
-
你连接的客户端主机。
如果User
列的值非空,则传入连接中的用户名必须完全匹配。如果User
值为空,则匹配任何用户名。如果与传入连接匹配的user
表行具有空白用户名,则该用户被视为没有名称的匿名用户,而不是客户端实际指定的用户名。这意味着在连接期间(即在阶段 2 期间)使用空白用户名进行所有进一步的访问检查。
authentication_string
列可以为空。这不是通配符,也不意味着任何密码都匹配。这意味着用户必须在不指定密码的情况下连接。验证客户端的插件实现的身份验证方法可能会或可能不会使用authentication_string
列中的密码。在这种情况下,可能还会使用外部密码来对 MySQL 服务器进行身份验证。
存储在user
表的authentication_string
列中的非空密码值已加密。MySQL 不会将密码以明文形式存储供任何人查看。相反,尝试连接的用户提供的密码会被加密(使用帐户认证插件实现的密码哈希方法)。然后,在检查密码是否正确的连接过程中使用加密密码。这样做时,加密密码永远不会通过连接传输。参见 Section 8.2.1, “Account User Names and Passwords”。
从 MySQL 服务器的角度来看,加密密码是真实密码,因此您不应该向任何人提供访问权限。特别是不要向非管理员用户提供对mysql
系统数据库中表的读取权限。
以下表格显示了user
表中各种User
和Host
值的组合如何应用于传入连接。
User 值 |
Host 值 |
允许的连接 |
---|---|---|
'fred' |
'h1.example.net' |
fred ,从h1.example.net 连接 |
'' |
'h1.example.net' |
任何用户,从h1.example.net 连接 |
'fred' |
'%' |
fred ,从任何主机连接 |
'' |
'%' |
任何用户,从任何主机连接 |
'fred' |
'%.example.net' |
fred ,从example.net 域中的任何主机连接 |
'fred' |
'x.example.%' |
fred ,从x.example.net ,x.example.com ,x.example.edu 等连接;这可能没有用处 |
'fred' |
'198.51.100.177' |
fred ,从具有 IP 地址198.51.100.177 的主机连接 |
'fred' |
'198.51.100.%' |
fred ,从198.51.100 类 C 子网中的任何主机连接 |
'fred' |
'198.51.100.0/255.255.255.0' |
与前一个示例相同 |
客户端主机名和用户名可能与user
表中的多行匹配。前面的示例集演示了这一点:所示的几个条目与fred
通过h1.example.net
的连接匹配。
当存在多个匹配项时,服务器必须确定使用哪个。它解决此问题如下:
-
每当服务器将
user
表读入内存时,它会对行进行排序。 -
当客户端尝试连接时,服务器按顺序查看行。
-
服务器使用与客户端主机名和用户名匹配的第一行。
服务器使用排序规则,首先按最具体的Host
值对行进行排序:
-
字面 IP 地址和主机名是最具体的。
-
在 MySQL 8.0.23 之前,字面 IP 地址的特异性不受其是否具有网络掩码的影响,因此
198.51.100.13
和198.51.100.0/255.255.255.0
被视为同等特异性。从 MySQL 8.0.23 开始,主机部分带有 IP 地址的帐户具有以下特异性顺序:-
具有 IP 地址作为主机部分的帐户:
CREATE USER '*user_name*'@'127.0.0.1'; CREATE USER '*user_name*'@'198.51.100.44';
-
具有使用 CIDR 表示法给定 IP 地址作为主机部分的帐户:
CREATE USER '*user_name*'@'192.0.2.21/8'; CREATE USER '*user_name*'@'198.51.100.44/16';
-
具有给定子网掩码的 IP 地址作为主机部分的帐户:
CREATE USER '*user_name*'@'192.0.2.0/255.255.255.0'; CREATE USER '*user_name*'@'198.51.0.0/255.255.0.0';
-
-
模式
'%'
表示“任何主机”,是最不具体的。 -
空字符串
''
也表示“任何主机”,但在'%'
之后排序。
非 TCP(套接字文件、命名管道和共享内存)连接被视为本地连接,并且如果存在这样的帐户,则与localhost
的主机部分匹配,否则与匹配localhost
的通配符主机部分匹配(例如,local%
,l%
,%
)。
将'%'
视为等同于localhost
的处理在 MySQL 8.0.35 中已弃用,您应该期望这种行为在将来的 MySQL 版本中被移除。
具有相同Host
值的行按照最具体的User
值首先排序。空白的User
值表示“任何用户”,是最不具体的,因此对于具有相同Host
值的行,非匿名用户排在匿名用户之前。
对于具有同等特定Host
和User
值的行,顺序是不确定的。
要了解这是如何工作的,假设user
表如下所示:
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| % | root | ...
| % | jeffrey | ...
| localhost | root | ...
| localhost | | ...
+-----------+----------+-
当服务器将表读入内存时,使用刚才描述的规则对行进行排序。排序后的结果如下所示:
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| localhost | root | ...
| localhost | | ...
| % | jeffrey | ...
| % | root | ...
+-----------+----------+-
当客户端尝试连接时,服务器会浏览排序后的行,并使用找到的第一个匹配项。对于由jeffrey
从localhost
连接的连接,表中的两行匹配:具有Host
和User
值为'localhost'
和''
的行,以及具有值为'%'
和'jeffrey'
的行。'localhost'
行在排序顺序中首先出现,因此服务器使用该行。
这里是另一个例子。假设user
表如下所示:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| % | jeffrey | ...
| h1.example.net | | ...
+----------------+----------+-
排序后的表如下所示:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| h1.example.net | | ...
| % | jeffrey | ...
+----------------+----------+-
第一行匹配来自h1.example.net
的任何用户的连接,而第二行匹配来自任何主机的jeffrey
的连接。
注意
一个常见的误解是认为,对于给定的用户名,当服务器尝试找到连接匹配时,所有明确命名该用户的行都会首先使用。这是不正确的。前面的例子说明了这一点,其中来自h1.example.net
的jeffrey
的连接首先匹配的不是包含'jeffrey'
作为User
列值的行,而是没有用户名的行。结果,jeffrey
被认证为匿名用户,即使他在连接时指定了用户名。
如果您能够连接到服务器,但您的权限不符合您的期望,那么您可能正在以其他帐户进行身份验证。要找出服务器用于对您进行身份验证的帐户,请使用CURRENT_USER()
函数。 (参见第 14.15 节,“信息函数”.) 它以*
user_name*@*
host_name*
格式返回一个值,指示匹配的user
表行中的User
和Host
值。假设jeffrey
连接并发出以下查询:
mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| @localhost |
+----------------+
这里显示的结果表明,匹配的user
表行具有空白的User
列值。换句话说,服务器将jeffrey
视为匿名用户。
诊断身份验证问题的另一种方法是打印出user
表,并手动按照顺序排列,以查看第一个匹配是在哪里进行的。
8.2.7 访问控制,第 2 阶段:请求验证
服务器接受连接后,进入访问控制的第 2 阶段。对于通过连接发出的每个请求,服务器确定您要执行的操作,然后检查您的权限是否足够。这是授予权限表中的权限列发挥作用的地方。这些权限可以来自user
、global_grants
、db
、tables_priv
、columns_priv
或procs_priv
表。(您可能会发现参考第 8.2.3 节,“授予权限表”有所帮助,该节列出了每个授予权限表中存在的列。)
user
和global_grants
表授予全局权限。这些表中的行针对给定帐户指示适用于全局基础的帐户权限,无论默认数据库是什么。例如,如果user
表授予您DELETE
权限,您可以在服务器主机上的任何数据库中删除行。明智的做法是仅向需要这些权限的人授予user
表中的权限,例如数据库管理员。对于其他用户,将user
表中的所有权限设置为'N'
,并仅在更具体的级别(特定数据库、表、列或例程)上授予权限。还可以全局授予数据库权限,但使用部分撤销来限制它们在特定数据库上的执行(参见第 8.2.12 节,“使用部分撤销限制权限”)。
db
表授予特定数据库的权限。此表的范围列中的值可以采用以下形式:
-
空白的
User
值匹配匿名用户。非空值字面匹配;用户名称中没有通配符。 -
通配符字符
%
和_
可以在Host
和Db
列中使用。这些字符的含义与使用LIKE
运算符执行的模式匹配操作相同。如果要在授予权限时使用这两个字符,必须使用反斜杠进行转义。例如,要将下划线字符(_
)包含在数据库名称中,请在GRANT
语句中指定为\_
。 -
'%'
或空白的Host
值表示“任何主机”。 -
空白的
Db
值或%
表示“任何数据库”。
服务器将db
表读入内存并同时对其进行排序,同时读取user
表。服务器根据Host
、Db
和User
范围列对db
表进行排序。与user
表一样,排序将最具体的值放在最前面,最不具体的值放在最后,当服务器查找匹配行时,它使用找到的第一个匹配项。
tables_priv
、columns_priv
和procs_priv
表授予特定于表、特定于列和特定于例程的权限。这些表的作用域列中的值可以采用以下形式:
-
通配符字符
%
和_
可以在Host
列中使用。这些与使用LIKE
运算符执行的模式匹配操作具有相同的含义。 -
一个
'%'
或空白的Host
值表示“任何主机”。 -
Db
、Table_name
、Column_name
和Routine_name
列不能包含通配符或为空。
服务器根据Host
、Db
和User
列对tables_priv
、columns_priv
和procs_priv
表进行排序。这类似于db
表的排序,但更简单,因为只有Host
列可以包含通配符。
服务器使用排序后的表来验证收到的每个请求。对于需要管理员权限的请求,如SHUTDOWN
或RELOAD
,服务器仅检查user
和global_privilege
表,因为这些是唯一指定管理员权限的表。如果这些表中的帐户行允许请求的操作,则授予访问权限;否则拒绝访问。例如,如果您想执行mysqladmin shutdown,但您的user
表行没有授予您SHUTDOWN
权限,服务器会拒绝访问,甚至不会检查db
表。(后者表中不包含Shutdown_priv
列,因此无需检查它。)
对于与数据库相关的请求(INSERT
、UPDATE
等),服务器首先检查user
表行中的用户全局权限(减去部分撤销所施加的权限限制)。如果行允许请求的操作,则授予访问权限。如果user
表中的全局权限不足,则服务器从db
表中确定用户的数据库特定权限:
-
服务器在
db
表中查找与Host
、Db
和User
列匹配的内容。 -
Host
和User
列与连接用户的主机名和 MySQL 用户名匹配。 -
Db
列与用户想要访问的数据库匹配。 -
如果
Host
和User
没有对应的行,则拒绝访问。
在确定由db
表行授予的特定于数据库的权限后,服务器将其添加到由user
表授予的全局权限中。如果结果允许请求的操作,则授予访问权限。否则,服务器将逐个检查tables_priv
和columns_priv
表中的用户表和列权限,将它们添加到用户的权限中,并根据结果允许或拒绝访问。对于存储过程操作,服务器使用procs_priv
表而不是tables_priv
和columns_priv
。
用布尔术语表达,关于如何计算用户权限的前述描述可以总结如下:
global privileges
OR database privileges
OR table privileges
OR column privileges
OR routine privileges
如果发现全局权限最初不足以执行请求的操作,为什么服务器会在后来将这些权限添加到数据库、表和列权限中可能并不明显。原因在于一个请求可能需要多种类型的权限。例如,如果执行INSERT INTO ... SELECT
语句,您需要INSERT
和SELECT
权限。您的权限可能是user
表行全局授予一个权限,而db
表行专门为相关数据库授予另一个权限。在这种情况下,您具有执行请求所需的权限,但服务器无法仅从您的全局或数据库权限中判断出来。它必须根据综合权限做出访问控制决定。
8.2.8 添加账户、分配权限和删除账户
要管理 MySQL 账户,请使用为此目的而设计的 SQL 语句:
-
CREATE USER
和DROP USER
创建和移除账户。 -
GRANT
和REVOKE
分配权限给账户并从账户中撤销权限。 -
SHOW GRANTS
显示账户权限分配。
账户管理语句会导致服务器对底层授权表进行适当的修改,这些表在第 8.2.3 节,“授权表”中讨论。
注意
不鼓励直接使用诸如INSERT
、UPDATE
或DELETE
等语句直接修改授权表,并且自担风险。服务器可以忽略由于这些修改而变得畸形的行。
对于任何修改授权表的操作,服务器会检查表是否具有预期的结构,如果不是,则会产生错误。要将表更新为预期的结构,请执行 MySQL 升级过程。参见第三章,“升级 MySQL”。
创建账户的另一种选择是使用 GUI 工具 MySQL Workbench。此外,一些第三方程序提供了用于 MySQL 账户管理的功能。phpMyAdmin
就是这样一个程序。
本节讨论以下主题:
-
创建账户和授予权限
-
检查账户权限和属性
-
撤销账户权限
-
删除账户
有关本文讨论的语句的更多信息,请参阅第 15.7.1 节,“账户管理语句”。
创建账户和授予权限
以下示例展示如何使用mysql客户端程序设置新账户。这些示例假定 MySQL root
账户具有CREATE USER
权限以及授予其他账户的所有权限。
在命令行中,以 MySQL root
用户连接到服务器,并在密码提示符处输入适当的密码:
$> mysql -u root -p
Enter password: *(enter root password here)*
连接到服务器后,您可以添加新账户。以下示例使用 CREATE USER
和 GRANT
语句设置四个账户(在看到 '*
password*'
时,请替换为适当的密码):
CREATE USER 'finley'@'localhost'
IDENTIFIED BY '*password*';
GRANT ALL
ON *.*
TO 'finley'@'localhost'
WITH GRANT OPTION;
CREATE USER 'finley'@'%.example.com'
IDENTIFIED BY '*password*';
GRANT ALL
ON *.*
TO 'finley'@'%.example.com'
WITH GRANT OPTION;
CREATE USER 'admin'@'localhost'
IDENTIFIED BY '*password*';
GRANT RELOAD,PROCESS
ON *.*
TO 'admin'@'localhost';
CREATE USER 'dummy'@'localhost';
由这些语句创建的账户具有以下属性:
-
有两个用户名为
finley
的账户。两者都是具有完全全局权限可以执行任何操作的超级用户账户。'finley'@'localhost'
账户仅可在从本地主机连接时使用。'finley'@'%.example.com'
账户在主机部分使用'%'
通配符,因此可用于从example.com
域中的任何主机连接。如果存在
localhost
的匿名用户账户,则'finley'@'localhost'
账户是必需的。如果没有'finley'@'localhost'
账户,则当finley
从本地主机连接时,匿名用户账户优先,finley
将被视为匿名用户。原因是匿名用户账户的Host
列值比'finley'@'%'
账户更具体,因此在user
表排序顺序中排在前面。(有关user
表排序的信息,请参见 Section 8.2.6, “Access Control, Stage 1: Connection Verification”。) -
'admin'@'localhost'
账户仅可由admin
用于从本地主机连接。它被授予全局RELOAD
和PROCESS
管理权限。这些权限使admin
用户能够执行 mysqladmin reload、mysqladmin refresh 和 mysqladmin flush-xxx
命令,以及 mysqladmin processlist。未授予访问任何数据库的权限。您可以使用GRANT
语句添加此类权限。 -
'dummy'@'localhost'
账户没有密码(这是不安全且不推荐的)。此账户仅可用于从本地主机连接。未授予任何权限。假定您使用GRANT
语句为该账户授予特定权限。
前面的示例授予了全局级别的权限。下一个示例创建了三个账户,并在较低级别授予了访问权限;即,对特定数据库或数据库中的对象。每个账户的用户名都是custom
,但主机名部分不同:
CREATE USER 'custom'@'localhost'
IDENTIFIED BY '*password*';
GRANT ALL
ON bankaccount.*
TO 'custom'@'localhost';
CREATE USER 'custom'@'host47.example.com'
IDENTIFIED BY '*password*';
GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
ON expenses.*
TO 'custom'@'host47.example.com';
CREATE USER 'custom'@'%.example.com'
IDENTIFIED BY '*password*';
GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
ON customer.addresses
TO 'custom'@'%.example.com';
这三个账户可以如下使用:
-
'custom'@'localhost'
账户具有访问bankaccount
数据库的所有数据库级别权限。该账户只能从本地主机连接到服务器。 -
'custom'@'host47.example.com'
账户具有特定数据库级别权限,可以访问expenses
数据库。该账户只能从主机host47.example.com
连接到服务器。 -
'custom'@'%.example.com'
账户具有特定表级别权限,可以访问customer
数据库中的addresses
表,来自example.com
域中的任何主机。由于在账户名的主机部分使用了%
通配符字符,该账户可以从该域中的所有机器连接到服务器。
检查账户权限和属性
要查看账户的权限,请使用SHOW GRANTS
:
mysql> SHOW GRANTS FOR 'admin'@'localhost';
+-----------------------------------------------------+
| Grants for admin@localhost |
+-----------------------------------------------------+
| GRANT RELOAD, PROCESS ON *.* TO `admin`@`localhost` |
+-----------------------------------------------------+
要查看账户的非权限属性,请使用SHOW CREATE USER
:
mysql> SET print_identified_with_as_hex = ON;
mysql> SHOW CREATE USER 'admin'@'localhost'\G
*************************** 1\. row ***************************
CREATE USER for admin@localhost: CREATE USER `admin`@`localhost`
IDENTIFIED WITH 'caching_sha2_password'
AS 0x24412430303524301D0E17054E2241362B1419313C3E44326F294133734B30792F436E77764270373039612E32445250786D43594F45354532324B6169794F47457852796E32
REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK
PASSWORD HISTORY DEFAULT
PASSWORD REUSE INTERVAL DEFAULT
PASSWORD REQUIRE CURRENT DEFAULT
启用print_identified_with_as_hex
系统变量(自 MySQL 8.0.17 起可用)会导致SHOW CREATE USER
显示包含不可打印字符的哈希值为十六进制字符串,而不是常规字符串文字。
撤销账户权限
要撤销账户权限,请使用REVOKE
语句。权限可以在不同级别撤销,就像它们可以在不同级别授予一样。
撤销全局权限:
REVOKE ALL
ON *.*
FROM 'finley'@'%.example.com';
REVOKE RELOAD
ON *.*
FROM 'admin'@'localhost';
撤销数据库级别权限:
REVOKE CREATE,DROP
ON expenses.*
FROM 'custom'@'host47.example.com';
撤销表级别权限:
REVOKE INSERT,UPDATE,DELETE
ON customer.addresses
FROM 'custom'@'%.example.com';
要检查权限撤销的效果,请使用SHOW GRANTS
:
mysql> SHOW GRANTS FOR 'admin'@'localhost';
+---------------------------------------------+
| Grants for admin@localhost |
+---------------------------------------------+
| GRANT PROCESS ON *.* TO `admin`@`localhost` |
+---------------------------------------------+
删除账户
要移除一个账户,请使用DROP USER
语句。例如,要删除之前创建的一些账户:
DROP USER 'finley'@'localhost';
DROP USER 'finley'@'%.example.com';
DROP USER 'admin'@'localhost';
DROP USER 'dummy'@'localhost';