1. 项目概述:MySQL 8.0的“系统心脏”
当你安装好MySQL 8.0,兴冲冲地登录进去,准备大展拳脚创建自己的第一个数据库时,有没有注意到,在SHOW DATABASES;命令的返回结果里,除了你创建的库,总有几个“不请自来”的家伙?它们就是information_schema、mysql、performance_schema和sys。对于很多刚入门的朋友来说,这几个数据库既熟悉又陌生——知道它们很重要,但又不敢轻易动它们,甚至有点“敬而远之”。
其实,这四个数据库是MySQL 8.0默认安装的系统数据库,你可以把它们理解为MySQL数据库管理系统的“操作系统”或“控制中心”。它们不存储你的业务数据,而是存储着MySQL服务器运行所需的所有元数据、权限信息、性能指标和优化工具。理解它们,是每一位从“会用MySQL”迈向“懂MySQL”的开发者或DBA的必经之路。无论是排查一个诡异的权限问题,还是深挖一条慢SQL的性能瓶颈,亦或是进行日常的运维监控,你的操作都离不开与这四个系统数据库打交道。
今天,我们就来彻底拆解MySQL 8.0中这四位“幕后英雄”。我会结合十多年踩坑填坑的经验,不仅告诉你它们是什么,更会深入讲解它们怎么用,以及在什么场景下能成为你的“救命稻草”。你会发现,用好它们,你的数据库运维和开发效率将提升一个档次。
2. 核心设计思路:为何是这四个库?
在深入每个库之前,我们先从整体上理解MySQL设计者的思路。这四个库并非随意堆砌,它们构成了一个层次清晰、职责分明的运维支撑体系。
2.1 分层监控与管理架构
你可以把这四个系统数据库想象成一个现代化城市的四大职能部门:
information_schema:市政信息查询中心。这里提供全市(整个MySQL实例)所有建筑物(数据库)、房间(表)、住户(列)的静态档案信息。你想知道某个表有多少列、是什么类型、有没有索引,来这里查档案最快。performance_schema:实时交通与资源监控中心。这里布满了摄像头和传感器,实时收集每条道路(SQL语句)的车流量(执行次数)、拥堵情况(锁等待)、资源消耗(内存、I/O)。用于性能分析和瓶颈定位。sys:市长决策支持与市民服务大厅。它基于前两个中心(特别是performance_schema)的原始数据,加工成高度可读的报告、视图和函数。把晦涩的监控数据变成“过去一小时最堵的十条路”、“哪个小区用水量异常”这样直观的结论,并提供一键优化的建议。mysql:公安与户籍管理系统。这里存储了所有市民(用户)的身份信息、护照(权限)、以及一些基础的城市运行规则(时区、插件等)。负责安全和访问控制。
2.2 从存储引擎到内存仪表的演进
在MySQL 5.6之前,系统的信息分散且不易查询。information_schema虽然存在,但部分数据来自存储引擎,查询可能触发昂贵的I/O操作。performance_schema在5.5版本引入,并在后续版本中不断增强,它最大的特点是在内存中完成绝大部分性能数据的收集,对业务性能影响极小。到了MySQL 8.0,performance_schema默认开启且功能空前强大,而sys库则作为其最佳拍档,让性能数据变得“平易近人”。
这种设计体现了MySQL从“一个数据存储软件”向“一个可观测性极强的数据平台”的演进。作为使用者,我们的运维思路也应该从“出问题了再登录服务器看日志”,转变为“通过系统数据库主动洞察潜在风险”。
注意:这四个系统数据库在物理上大多以
InnoDB表或特殊的内存表形式存在。绝对不要尝试用DROP DATABASE命令删除它们,这会导致MySQL实例崩溃或无法启动。也尽量避免直接修改其中的表数据(mysql库中的权限表除外,但需使用GRANT、REVOKE等专用命令)。
3.information_schema:数据库的“活字典”
这是最常用,也最容易被误解的系统数据库。很多人把它当作一系列只读视图的集合,用来查表结构。这没错,但只看到了它功能的冰山一角。
3.1 核心功能与常用视图解析
information_schema库中的所有对象都是视图(VIEW),而非实体表。这意味着你查询它们时,MySQL会动态地从系统元数据中生成结果。它的视图大致可分为以下几类:
模式与对象元数据:这是最常用的部分。
SCHEMATA:查看所有数据库。TABLES:查看所有表的信息,包括表引擎、行数、数据长度、创建时间等。这里TABLE_ROWS对于InnoDB表是估算值,并不精确,用于快速了解数据规模。COLUMNS:查看所有表的列信息,包括数据类型、是否为空、默认值等。STATISTICS:查看表索引的详细信息,包括索引名称、包含的列、唯一性、基数等。索引基数(CARDINALITY)是优化器选择索引的关键参考。KEY_COLUMN_USAGE&TABLE_CONSTRAINTS:查看主键、外键、唯一键等约束信息。
权限与安全信息:
SCHEMA_PRIVILEGES,TABLE_PRIVILEGES,COLUMN_PRIVILEGES:查看数据库、表、列级别的权限授予情况。比直接查mysql.user表更清晰。
服务器状态与设置:
GLOBAL_VARIABLES&SESSION_VARIABLES:查看全局和当前会话的系统变量。等同于SHOW GLOBAL VARIABLES和SHOW VARIABLES命令。GLOBAL_STATUS&SESSION_STATUS:查看全局和当前会话的状态变量。等同于SHOW GLOBAL STATUS和SHOW STATUS命令。
进程与锁信息:
PROCESSLIST:查看当前所有连接线程的信息。功能类似于SHOW PROCESSLIST,但可以通过SQL条件过滤,更灵活。INNODB_LOCKS&INNODB_LOCK_WAITS(在8.0中部分功能已迁移至performance_schema):用于诊断InnoDB锁等待问题。
3.2 实战应用场景与脚本示例
场景一:快速生成数据库文档作为开发或DBA,经常需要梳理数据库结构。你可以写一个查询,快速导出所有表的字段清单。
-- 查询某个数据库下所有表的核心字段信息 SELECT TABLE_SCHEMA as '数据库', TABLE_NAME as '表名', COLUMN_NAME as '字段名', COLUMN_TYPE as '数据类型', IS_NULLABLE as '可空', COLUMN_DEFAULT as '默认值', COLUMN_COMMENT as '字段说明' FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'your_database_name' ORDER BY TABLE_NAME, ORDINAL_POSITION;场景二:找出没有主键的表没有主键的InnoDB表对性能和复制都很不友好。可以用以下语句筛查。
SELECT TABLE_SCHEMA, TABLE_NAME FROM information_schema.TABLES t LEFT JOIN information_schema.STATISTICS s ON t.TABLE_SCHEMA = s.TABLE_SCHEMA AND t.TABLE_NAME = s.TABLE_NAME AND s.INDEX_NAME = 'PRIMARY' WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys') AND s.INDEX_NAME IS NULL AND t.TABLE_TYPE = 'BASE TABLE';场景三:监控长时间运行的SQL结合PROCESSLIST和TIME字段,可以定期检查是否有执行时间过长的查询。
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, LEFT(INFO, 100) AS SQL_Snippet -- 截取前100个字符,避免信息过长 FROM information_schema.PROCESSLIST WHERE COMMAND = 'Query' AND TIME > 60 -- 查找执行超过60秒的查询 ORDER BY TIME DESC;实操心得:查询
information_schema中的TABLES视图获取InnoDB表的行数(TABLE_ROWS)时,这个值是通过采样统计估算出来的,在数据量频繁变更的表上可能误差极大。如果需要精确行数,请务必使用SELECT COUNT(*) FROM table_name,但要注意后者会对大表产生全表扫描的成本。通常,information_schema.TABLE_ROWS用于快速评估数据量级,而不是精确计算。
4.performance_schema:深潜性能的“仪表盘”
如果说information_schema是档案室,那performance_schema(简称P_S)就是配备了高速摄像机和各类传感器的指挥中心。它默认启用,专注于收集服务器运行时的性能事件数据,其设计目标是对生产环境性能影响最小(通常开销在1%-3%)。
4.1 核心概念:消费者、生产者与仪器
理解P_S,先要理解三个核心概念:
- 仪器(Instruments):代码中的“埋点”。MySQL在关键代码路径(如函数调用、锁操作、I/O等待)上插装了成千上万个仪器。例如,
wait/io/file/innodb/innodb_data_file这个仪器就用于收集InnoDB数据文件I/O等待事件。 - 消费者(Consumers):存储事件的“表”。仪器产生的事件原始数据,会被存储到不同的消费者表中。主要分为几类:
events_waits_*:等待事件(如I/O、锁等待)。events_statements_*:语句执行事件(SQL语句)。events_stages_*:阶段事件(语句执行的子阶段,如解析、排序)。events_transactions_*:事务事件。summary_*:上述事件的聚合摘要表,最常用。
- 配置表:
setup_*开头的表,用于动态启用/禁用仪器和消费者,设置事件过滤等。这是灵活使用P_S的关键。
4.2 关键配置与启停
P_S功能强大但复杂,默认不会收集所有数据(以免开销过大)。常用配置如下:
-- 查看当前启用了哪些消费者 SELECT * FROM performance_schema.setup_consumers; -- 启用所有语句事件消费者(会略微增加开销,但非常有用) UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE '%events_statements_%'; -- 启用所有等待事件消费者(用于分析I/O、锁瓶颈) UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE '%events_waits_%'; -- 查看和启用特定仪器,例如启用所有与文件I/O相关的仪器 SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE '%file%'; UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'wait/io/file/%';注意事项:在生产环境修改P_S配置需谨慎。建议先在测试环境或业务低峰期进行。开启过多仪器和消费者会增加内存和CPU开销。通常,按需开启(如排查问题时开启,问题解决后关闭)是更稳妥的策略。
4.3 实战性能诊断案例
案例:找出全表扫描的罪魁祸首全表扫描是性能杀手。我们可以利用events_statements_summary_by_digest视图来发现它。这个视图将SQL语句按“摘要”(去掉参数值后的模式)聚合,非常强大。
-- 首先,确保语句事件消费者已开启(见上文配置) -- 执行一些业务查询后,运行以下诊断语句 SELECT SCHEMA_NAME, DIGEST_TEXT AS normalized_sql_pattern, -- 标准化后的SQL模式 COUNT_STAR AS exec_count, -- 执行次数 SUM_ROWS_EXAMINED AS total_rows_examined, -- 总共检查的行数(如果远大于返回行数,可能索引不佳) SUM_ROWS_SENT AS total_rows_sent, -- 总共返回的行数 ROUND(SUM_ROWS_EXAMINED / COUNT_STAR) AS avg_rows_examined_per_exec, -- 每次执行平均检查行数 ROUND(SUM_ROWS_SENT / COUNT_STAR) AS avg_rows_sent_per_exec, -- 每次执行平均返回行数 FIRST_SEEN, LAST_SEEN FROM performance_schema.events_statements_summary_by_digest WHERE SCHEMA_NAME IS NOT NULL AND DIGEST_TEXT LIKE 'SELECT%' -- 关注SELECT语句 AND SUM_ROWS_EXAMINED > 10000 -- 检查行数超过1万,可能有问题 ORDER BY SUM_ROWS_EXAMINED DESC LIMIT 10;这个查询能帮你快速定位那些“检查了大量数据但只返回少量结果”的SQL,这些SQL通常就是全表扫描或索引使用不当的候选者。你可以根据DIGEST_TEXT找到具体的SQL模式,然后去优化它。
4.4 深入锁等待分析
在MySQL 8.0中,InnoDB的锁信息更深度地集成到了P_S中。
-- 查看当前正在发生的锁等待 SELECT r.trx_id AS waiting_trx_id, r.trx_mysql_thread_id AS waiting_thread, r.trx_query AS waiting_query, b.trx_id AS blocking_trx_id, b.trx_mysql_thread_id AS blocking_thread, b.trx_query AS blocking_query FROM information_schema.innodb_lock_waits w INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id; -- 结合P_S,查看更详细的锁等待事件历史 SELECT EVENT_ID, THREAD_ID, EVENT_NAME, SOURCE, TIMER_WAIT/1000000000 AS wait_time_sec, -- 将皮秒转换为秒 OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_STATUS FROM performance_schema.events_waits_history_long WHERE EVENT_NAME LIKE '%innodb%lock%' ORDER BY TIMER_WAIT DESC LIMIT 10;5.sys库:化繁为简的“性能仪表盘”
直接查询performance_schema的表,数据原始且字段繁多,对新手不友好。sys库应运而生,它由一系列视图、函数和存储过程构成,全部基于performance_schema和information_schema,目的是将复杂的性能数据转化为人类可读的格式。
5.1 视图分类与核心视图解读
sys库的视图命名非常有规律,通常以x$开头的视图提供原始数据,而不带x$的同名视图提供格式化后的友好输出。主要类别包括:
- 主机与进程摘要:
host_summary,processlist - IO与内存摘要:
io_global_by_file_by_bytes,memory_global_by_current_bytes - 语句与模式摘要:
statement_analysis,schema_table_statistics - 用户与索引摘要:
user_summary,schema_index_statistics
5.2 一键式性能诊断报告
sys库最强大的功能之一是它的存储过程,能生成一份全面的健康报告。
-- 生成一份标准的诊断报告,输出到控制台 CALL sys.diagnostics(1, 10, 0); -- 参数解释: -- 1: 诊断间隔(秒),这里指收集1秒内的瞬时数据。 -- 10: 诊断次数,这里指连续收集10次。 -- 0: 是否包含PS(performance_schema)的原始数据,0为不包含(更简洁)。这个报告会包含大量的章节,如系统变量、状态变量差值、等待事件排名、全表扫描的SQL、索引使用情况等,是进行周期性健康检查或故障排查的利器。
5.3 日常运维必备查询
1. 查看当前最消耗资源的SQL(简化版)
-- 查看平均执行时间最长的SQL SELECT * FROM sys.statement_analysis ORDER BY avg_latency DESC LIMIT 5; -- 查看总执行时间最长的SQL(可能因为执行次数多) SELECT * FROM sys.statements_with_runtimes_in_95th_percentile LIMIT 5; -- 查看全表扫描或临时表使用的SQL SELECT * FROM sys.statements_with_full_table_scans LIMIT 5; SELECT * FROM sys.statements_with_temp_tables LIMIT 5;2. 查看索引使用情况
-- 找出从未使用过的索引(冗余索引候选) SELECT * FROM sys.schema_unused_indexes; -- 查看索引使用统计(读/写比例) SELECT * FROM sys.schema_index_statistics WHERE table_schema = 'your_db' ORDER BY rows_selected DESC LIMIT 10;3. 查看内存使用情况
-- 按线程/用户/事件查看内存分配 SELECT * FROM sys.memory_global_by_current_bytes LIMIT 10; -- 查看InnoDB缓冲池的使用详情 SELECT * FROM sys.innodb_buffer_stats_by_schema;5.4 自定义视图与扩展
sys库本身是开源的(位于/usr/share/mysql或MySQL安装目录的share文件夹下),其视图定义就是标准的SQL。这意味着你可以学习它的写法,甚至创建自己的自定义视图来监控你关心的特定指标。例如,你可以创建一个视图,专门监控某个业务库中特定表的I/O延迟。
实操心得:对于日常运维,我强烈建议将
sys库的statement_analysis、schema_unused_indexes、host_summary_by_statement_latency等视图的查询,集成到你的监控系统(如Zabbix、Prometheus)或定期巡检脚本中。它们提供的信息质量远高于简单的SHOW STATUS,能让你提前发现潜在的性能退化问题,而不是等到用户投诉。
6.mysql库:权限与系统的“基石”
这是最“古老”也最核心的系统数据库。它存储了用户账户、权限、存储过程/函数/触发器定义、时区、插件加载信息等。直接操作这个库的表风险很高,但理解其结构至关重要。
6.1 核心表结构解析
用户与权限表:
user:用户账户、全局权限、密码插件、密码过期策略等。一行代表一个用户@主机。db:数据库级别的权限。tables_priv:表级别的权限。columns_priv:列级别的权限。procs_priv:存储过程和函数的权限。role_edges和default_roles:MySQL 8.0引入的角色(Role)相关表。
其他系统表:
time_zone_*:时区信息表。servers:用于FEDERATED存储引擎。plugin:已安装的插件信息。general_log和slow_log:如果通用查询日志和慢查询日志设置为写入表(而非文件),日志内容就存在这里。
6.2 权限系统的工作流与安全实践
当客户端发起连接并执行操作时,MySQL的权限验证流程如下:
- 连接验证:检查
mysql.user表中的Host,User,authentication_string(密码)以及账户是否被锁定。 - 权限检查:这是一个从全局(
user)到数据库(db),再到表(tables_priv),最后到列(columns_priv)的逐级检查过程。只要在某一级找到匹配的权限并验证通过,即允许操作。
绝对不要使用INSERT,UPDATE,DELETE语句直接修改mysql库中的权限表!这会导致权限缓存不一致,必须执行FLUSH PRIVILEGES;才能刷新,而此操作在高并发下可能引发阻塞。正确做法是使用MySQL提供的权限管理语句:
-- 创建用户并授权(8.0推荐方式,创建和授权分离) CREATE USER 'app_user'@'192.168.1.%' IDENTIFIED BY 'StrongPassword123!'; GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'192.168.1.%'; -- 使用角色管理权限(8.0新特性,更清晰) CREATE ROLE 'app_read_only'; GRANT SELECT ON app_db.* TO 'app_read_only'; GRANT 'app_read_only' TO 'report_user'@'%'; SET DEFAULT ROLE 'app_read_only' TO 'report_user'@'%';6.3 密码管理与安全加固
MySQL 8.0默认使用caching_sha2_password插件,比旧的mysql_native_password更安全。在mysql.user表中可以看到相关字段。
- 密码过期策略:可以通过
ALTER USER ... PASSWORD EXPIRE;强制用户定期修改密码。 - 账户锁定:
ALTER USER ... ACCOUNT LOCK;可以临时锁定可疑账户。 - 密码历史:通过
password_reuse_interval和password_reuse_history系统变量,可以防止重复使用旧密码。
6.4 误操作与恢复
如果不慎误删了root用户或其他关键用户,在还能通过其他方式(如--skip-grant-tables启动)访问数据库的情况下,可以重建:
-- 在跳过权限表模式下启动MySQL后 USE mysql; -- 重建root@localhost用户(请替换为你自己的强密码) CREATE USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'YourNewStrongPassword'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION; FLUSH PRIVILEGES;踩坑记录:曾经有一次,同事直接在
mysql.user表里删除了一个用户,然后业务立刻报错连接不上,但FLUSH PRIVILEGES后依然不行。原因是某些连接池或长连接在连接建立时就缓存了权限信息,即使服务器端刷新了,客户端会话可能仍持有旧的权限缓存。最终是通过重启应用服务器(迫使连接池重建连接)才解决的。所以,权限变更后,要考虑对现有连接的影响,对于重要业务,可能需要在低峰期操作并安排应用重启。
7. 常见问题排查与运维技巧实录
在实际工作中,这四个系统数据库是排查问题的“瑞士军刀”。下面记录几个典型场景。
7.1 连接数爆满,是谁干的?
应用突然报“Too many connections”。首先,增大max_connections是临时办法,找到根源才是关键。
-- 1. 查看当前所有连接详情(来自information_schema) SELECT * FROM information_schema.PROCESSLIST; -- 或者使用sys库更清晰的视图 SELECT * FROM sys.processlist WHERE conn_id IS NOT NULL; -- 2. 按用户和主机分组,看哪个来源的连接最多(可能是连接池配置错误或攻击) SELECT USER, HOST, COUNT(*) as connection_count FROM information_schema.PROCESSLIST GROUP BY USER, HOST ORDER BY connection_count DESC; -- 3. 查看正在执行的SQL,找出可能卡住的慢查询 SELECT * FROM sys.session WHERE command = 'Query' ORDER BY time DESC LIMIT 10; -- 4. 如果发现大量“Sleep”状态的空闲连接,可能是应用没有正确关闭连接。 -- 可以设置 interactive_timeout 和 wait_timeout 来断开超时空闲连接。7.2 数据库突然变慢,如何快速定位瓶颈?
业务反馈系统变慢,你需要像侦探一样快速收集线索。
-- 第一步:快速健康检查(使用sys库) -- 查看当前最慢的SQL SELECT * FROM sys.statement_analysis ORDER BY avg_latency DESC LIMIT 5; -- 查看当前的锁等待 SELECT * FROM sys.innodb_lock_waits; -- 第二步:检查系统资源(结合performance_schema) -- 查看哪些文件IO延迟最高 SELECT FILE_NAME, COUNT_READ, COUNT_WRITE, SUM_NUMBER_OF_BYTES_READ, SUM_NUMBER_OF_BYTES_WRITE, (SUM_TIMER_WAIT/COUNT_STAR)/1000000000 as avg_latency_sec FROM performance_schema.file_summary_by_instance ORDER BY avg_latency_sec DESC LIMIT 5; -- 第三步:检查内存使用 -- 查看哪些SQL使用了最多的内存(可能导致磁盘临时表) SELECT * FROM sys.statements_with_temp_tables ORDER BY disk_tmp_tables DESC LIMIT 5;7.3 如何安全地清理performance_schema和sys库的数据?
P_S的数据默认存储在内存表中,重启MySQL实例会清零。但一些汇总表(summary_*)可能会积累大量历史数据。你可以安全地重置它们:
-- 重置所有performance_schema的汇总表和事件历史表 TRUNCATE TABLE performance_schema.events_waits_history; TRUNCATE TABLE performance_schema.events_waits_history_long; TRUNCATE TABLE performance_schema.events_statements_history; TRUNCATE TABLE performance_schema.events_statements_history_long; -- 重置所有摘要表(这是最常用的清理操作,对运行中服务无影响) TRUNCATE TABLE performance_schema.events_statements_summary_by_digest; TRUNCATE TABLE performance_schema.events_waits_summary_global_by_event_name; -- ... 可以按需重置其他summary表 -- 注意:不要对`setup_*`配置表执行TRUNCATE或DELETE!7.4 迁移或复制时,系统数据库如何处理?
这是一个常见误区。在大多数情况下,你不需要也不应该复制这四个系统数据库。
- 逻辑备份(如
mysqldump):默认使用--databases或--all-databases参数时,会包含mysql库(因为里面有用户权限),但通常不包含information_schema,performance_schema,sys。这是正确的,因为后三个库的数据是实例运行时动态生成或特定的。 - 物理备份(如直接复制数据文件):备份整个数据目录时,自然包含了它们。但在恢复到新服务器时,
mysql库中的用户权限信息会被覆盖,而其他三个库的数据在新实例启动后会被重置或重新生成。 - 主从复制:系统数据库的表默认不会被复制。权限的复制需要通过复制
mysql库的相关表来实现,但这需要特别配置(--replicate-ignore-db除外),且容易出错。更推荐的做法是在主从库上分别管理权限,或使用像pt-table-sync这样的工具同步mysql库。
最佳实践:将用户和权限的创建、变更做成SQL脚本,纳入版本管理(Git)。在搭建新从库或恢复备份后,先恢复业务数据,然后执行这份权限脚本来重建用户,而不是直接复制mysql库。对于performance_schema的配置,如果有自定义调整(如启用了某些仪器),也应记录下相应的UPDATE语句,作为初始化脚本的一部分。