找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4279

积分

0

好友

563

主题
发表于 3 小时前 | 查看: 4| 回复: 0

动态黄圆背景中上下浮动的深灰色箭头

页面加载缓慢、查询超时、系统卡顿——当这些问题集中爆发时,数据库慢查询往往是罪魁祸首之一。很多人的第一反应是:“让开发加个索引”。这招有时候管用,但更多时候,盲目加索引不仅白费力气,还可能把事情搞得更糟。

真正的慢查询优化,走的是一套系统性的排查与决策流程。你得先搞清楚“慢”是不是真的存在,然后揪出执行计划背后的瓶颈,最后才能开出对症的药方。只盯着索引,等于放弃了 SQL 重写、配置调优、表结构改造等其他选项。本文就以 MySQL 为例,梳理从发现到根治慢查询的完整实战路径,这些方法对 PostgreSQL 和 Oracle 同样有极高的参考价值。

在深入排查之前,你或许需要一个能集中讨论这些复杂技术问题、分享脚本和心得的去处。在云栈社区,有不少同道中人会探讨这类运维与架构上的硬骨头,说不定能给日常排查带来新思路。

1 慢查询的发现与确认

发现问题,是解决问题的第一步。如果连哪些 SQL 在拖后腿都不知道,优化自然无从谈起。

1.1 开启慢查询日志

MySQL 慢查询日志是排障的基石,但它默认处于关闭状态。可以通过以下命令查验和开启。

查看当前慢查询配置:

-- 查看慢查询相关变量
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_output';

-- 查看是否启用了慢查询日志
SHOW VARIABLES LIKE 'slow_query_log';

-- 查看慢查询日志文件路径
SHOW VARIABLES LIKE 'slow_query_log_file';

动态配置慢查询日志:

-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';

-- 设置慢查询阈值为 1 秒(可以是浮点数)
SET GLOBAL long_query_time = 1;

-- 设置日志输出格式(TABLE 或 FILE)
SET GLOBAL log_output = 'FILE';

-- 设置慢查询日志文件路径
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

以上配置在 MySQL 重启后会丢失。欲永久生效,需写入配置文件 /etc/mysql/my.cnf/etc/my.cnf

[mysqld]
# 开启慢查询日志
slow_query_log = 1

# 慢查询日志文件路径
slow_query_log_file = /var/log/mysql/slow.log

# 慢查询阈值(秒)
long_query_time = 1

# 记录没有使用索引的查询
log_queries_not_using_indexes = 1

# 将慢查询记录到文件
log_output = FILE

保存后重启服务:

systemctl restart mysql
# 或
systemctl restart mysqld

1.2 慢查询日志格式解读

一旦启用,日志便会忠实地记录每一次慢查询的细节:

# Time: 2024-01-15T10:30:45.123456Z
# User@Host: app_user[app_user] @ localhost []
# Query_time: 5.234567  Lock_time: 0.001234 Rows_sent: 100  Rows_examined: 50000
SET timestamp=1705315845;
SELECT * FROM orders WHERE user_id = 12345 AND status = 'paid' ORDER BY created_at DESC LIMIT 20;

几个关键字段揭示了查询的本质:

  • Query_time:查询实际执行时长(秒)。这是最核心的指标。
  • Lock_time:等待锁的时间。
  • Rows_sent / Rows_examinedRows_examined 是扫描行数,Rows_sent 是实际返回给客户端的行数。二者比值越大,说明查询在大量数据中“大海捞针”,效率往往很低,是需要优化的典型特征。高效的查询应当让 Rows_examined 尽可能接近 Rows_sent

1.3 使用 mysqldumpslow 分析日志

直接翻阅原始日志文件无异于大海捞针。mysqldumpslow 可以帮助你快速汇总出最需要关注的头部慢查询:

# 显示最慢的 10 条查询
mysqldumpslow -t 10 /var/log/mysql/slow.log

# 常用参数说明:
# -t N     显示前 N 条最慢的查询
# -s 排序标准 (c:计数, t:时间, l:锁时间, r:返回行数)
# -a       不聚合相同的查询
# -g PAT   只显示匹配 pattern 的查询

# 示例:按总查询时间排序
mysqldumpslow -t 10 -s t /var/log/mysql/slow.log

# 显示扫描行数最多的查询
mysqldumpslow -t 10 -s r /var/log/mysql/slow.log

# 显示查询次数最多的查询
mysqldumpslow -t 10 -s c /var/log/mysql/slow.log

# 过滤特定表的查询
mysqldumpslow -t 10 -g 'orders' /var/log/mysql/slow.log

# 使用正则过滤
mysqldumpslow -t 10 -a -g 'SELECT.*FROM.*WHERE' /var/log/mysql/slow.log

1.4 使用 pt-query-digest 进行深度分析

mysqldumpslow 提供的是摘要,那么 Percona Toolkit 里的 pt-query-digest 就是一份详尽的分析报告。

# 安装 Percona Toolkit (Ubuntu/Debian)
apt-get install percona-toolkit

# 安装 (RHEL/CentOS)
yum install percona-toolkit

# 基本用法
pt-query-digest /var/log/mysql/slow.log

# 输出到文件
pt-query-digest /var/log/mysql/slow.log > /tmp/query_analysis.txt

# 只分析最近 24 小时的记录
pt-query-digest --since '24h' /var/log/mysql/slow.log

# 分析特定时间段
pt-query-digest --since '2024-01-15 10:00:00' --until '2024-01-15 12:00:00' /var/log/mysql/slow.log

它的输出会包含查询的响应时间分布、执行频次、平均扫描行数等,甚至能直接标记出潜在的问题查询。

2 使用 EXPLAIN 剖析执行计划

找出了是哪些 SQL 慢,接下来就是理解它们“为什么慢”。EXPLAIN 是你手中的显微镜。

2.1 EXPLAIN 基本用法

EXPLAIN 能展示 MySQL 优化器是如何“盘算”着执行一条查询的。

-- 基本格式
EXPLAIN SELECT * FROM orders WHERE user_id = 12345;

-- 更详细的 JSON 格式输出
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE user_id = 12345;

-- 同样适用于 UPDATE、DELETE、INSERT
EXPLAIN UPDATE orders SET status = 'shipped' WHERE order_id = 100;
EXPLAIN DELETE FROM orders WHERE status = 'cancelled';

2.2 输出字段详解

以一条简单的 JOIN 查询为例:

EXPLAIN SELECT o.*, u.name FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.user_id = 12345 AND o.status = 'paid'
ORDER BY o.created_at DESC LIMIT 20;

输出可能如下:

+----+-------------+-------+------+---------------+------+---------+-------+------+----------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra          |
+----+-------------+-------+------+---------------+------+---------+-------+------+----------+----------------+
|  1 | SIMPLE      | o     | ref  | idx_user      | idx_user | 5     | const |   50 |    10.00 | Using where; Using filesort |
|  1 | SIMPLE      | u     | const| PRIMARY       | PRIMARY | 4     | const |    1 |   100.00 | NULL           |
+----+-------------+-------+------+---------------+------+---------+-------+------+----------+----------------+

逐一理解这里的列非常重要:

  • id:查询的序列号。id 越大越先执行,id 相同则从上往下执行。
  • select_type:查询类型,如 SIMPLE(简单查询)、PRIMARY(主查询)、SUBQUERY(子查询)、DERIVED(派生表)等。
  • table:操作的表。
  • type访问类型,这是衡量查询效率的核心指标。从好到差依次为:system > const > eq_ref > ref > range > index > ALL。出现 ALL 即全表扫描,是必须重点优化的对象。
  • possible_keys:可能用到的索引。
  • key实际使用的索引。如果这里是 NULL,说明没有用上索引。
  • rows预估需要扫描的行数。这个值是基于统计信息的估算,越小越好。
  • filtered:按条件过滤后剩余行数的百分比。
  • Extra包含关键的性能提示,如 Using filesort(需额外排序)和 Using temporary(需临时表),出现这些通常是明确的优化信号。

2.3 type 字段详解

这是需要你刻在脑子里的性能优劣顺序表:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

日常优化中,你只需确保关键查询的 type 达到 range 级别以上。

-- 查看 type 为 ALL 的查询(无索引,全表扫)
EXPLAIN SELECT * FROM orders WHERE created_at > '2024-01-01';

-- 创建索引后,type 优化为 range
CREATE INDEX idx_orders_created_at ON orders(created_at);
EXPLAIN SELECT * FROM orders WHERE created_at > '2024-01-01';

2.4 Extra 字段详解

Extra 就像是详细医嘱,告诉你查询具体经历了什么。

-- 示例查询,出现 Using filesort,因为排序无法利用索引
EXPLAIN SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC;

-- 优化方案:创建覆盖索引,一举消除 filesort
CREATE INDEX idx_orders_user_created ON orders(user_id, created_at DESC);

一旦在 Extra 里看到 Using filesortUsing temporary,你就要立刻警觉起来,这意味着磁盘 I/O 和额外开销。

2.5 使用 EXPLAIN ANALYZE(MySQL 8.0+)

从 MySQL 8.0 开始,EXPLAIN ANALYZE 登场。它不仅给出计划,还会真实执行查询,并给出每个步骤的实际时间消耗和返回行数。这让“预估”与“现实”的差距一览无余,统计信息过时或执行计划错误的隐藏问题再也无处遁形。

EXPLAIN ANALYZE
SELECT o.*, u.name FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.user_id = 12345 AND o.status = 'paid'
ORDER BY o.created_at DESC LIMIT 20;

3 索引的创建与优化

理解了优化器如何做决策,现在可以主动出击——用索引来引导它。但索引是把双刃剑。

3.1 索引原理简述

在 MySQL 的 InnoDB 引擎中,最常用的是 B+Tree 索引。数据或数据指针按序存储在叶子节点上,叶子节点间通过链表相连。它既能保证高效的等值查找,也能优雅地处理范围查询,这就是它能同时加速 => 的原因。

代价同样明显:索引占空间,且每次写操作(增、删、改)都需要同步维护索引,从而拖慢写入速度。

3.2 创建索引的原则

1. 高区分度优先
区分度,即 COUNT(DISTINCT column) / COUNT(*),是衡量一个列“值不值得”被索引的关键。

-- 查看列的区分度
SELECT COUNT(DISTINCT status) / COUNT(*) FROM orders;  -- 可能很低,只有几种状态
SELECT COUNT(DISTINCT user_id) / COUNT(*) FROM orders; -- 可能很高

为区分度低的列(如 sex, status)创建索引,好比在新华字典里为“的”字建一个索引,意义不大。反之,订单表里的 user_id 就是索引的首要人选。

2. 遵循最左前缀原则
对于复合索引 INDEX idx(a, b, c),它能有效支持 aa,ba,b,c 这三种组合的查询。但单独用 bc 来查,这个索引就爱莫能助了。

-- 创建复合索引
CREATE INDEX idx_orders_user_status ON orders(user_id, status, created_at);

-- 能走索引的查询
SELECT * FROM orders WHERE user_id = 123;
SELECT * FROM orders WHERE user_id = 123 AND status = 'paid';
SELECT * FROM orders WHERE user_id = 123 AND status = 'paid' AND created_at > '2024-01-01';

-- 无法高效利用索引的查询
SELECT * FROM orders WHERE status = 'paid';

3.3 索引创建实战

综合运用前面的原则,来看一个真实的优化场景:

-- 慢查询语句
-- SELECT * FROM orders WHERE user_id = 12345 AND status = 'paid' ORDER BY created_at DESC LIMIT 20;

-- 优化思路:
-- 1. 等值查询字段 user_id 和 status 放前面,高区分度的 user_id 在前。
-- 2. 排序字段 created_at 跟在其后,并指定降序(MyQL 8.0+支持)。

-- MySQL 8.0+ 的一步到位方案
CREATE INDEX idx_orders_user_status_created ON orders(user_id, status, created_at DESC);

-- MySQL 5.7 的折中方案(降序索引不被支持,排序会依然用到 filesort)
CREATE INDEX idx_orders_user_status ON orders(user_id, status);

3.4 索引失效的陷阱

建了索引不代表万事大吉,使用不当,优化器会直接绕过它。

  • 对索引列使用函数或计算
    -- 索引失效
    SELECT * FROM orders WHERE YEAR(created_at) = 2024;
    -- 应该这样
    SELECT * FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
  • 前导模糊查询
    -- 索引失效
    SELECT * FROM orders WHERE order_no LIKE '%123%';
    -- 可以使用索引
    SELECT * FROM orders WHERE order_no LIKE 'ABC123%';
  • 隐式类型转换
    -- 若 user_id 是 INT 类型,与字符串比较可能导致索引失效
    SELECT * FROM orders WHERE user_id = '12345';
    -- 正确用法:类型严格匹配
    SELECT * FROM orders WHERE user_id = 12345;

3.5 查看索引使用情况

索引建完上线后,一定要复查它是否真的被用上了。

-- 查看表上有哪些索引
SHOW INDEX FROM orders;

-- 通过 EXPLAIN 确认 key 字段是否是你期望的索引
EXPLAIN SELECT * FROM orders WHERE user_id = 12345;

-- 查看索引的基数(Cardinality),了解区分度
SHOW TABLE STATUS LIKE 'orders';

-- MySQL 8.0+ 专有视图
SELECT * FROM mysql.index_statistics WHERE table_name = 'orders';

4 SQL 语句优化

有时候,问题的根子不在索引,而在 SQL 本身。哪怕有着完善的索引,糟糕的 SQL 也能让性能一塌糊涂。

4.1 消灭常见低效模式

  • *杜绝 `SELECT `**:只取所需列,尤其是避免回表,也让覆盖索引能真正派上用场。
    -- 低效
    SELECT * FROM orders WHERE order_id = 12345;
    -- 高效
    SELECT order_id, user_id, total_amount FROM orders WHERE order_id = 12345;
  • LIMIT 不光是功能,也是性能:不加 LIMIT 的应用层过滤是在浪费网络和内存。
    SELECT * FROM orders WHERE user_id = 12345 ORDER BY created_at DESC LIMIT 20;
  • 分解大查询:一个复杂的多表 JOIN,不如拆成多个简单查询,在应用层组装数据。这样做可以减小锁的竞争,也更容易利用缓存。
    -- 可以尝试将这一步拆分为:
    -- 1. 从 orders 表筛选出主键
    SELECT order_id FROM orders WHERE created_at > '2024-01-01' LIMIT 1000;
    -- 2. 再用这些主键去关联其他表,或用 IN 查询

4.2 驯服 ORDER BY 和 GROUP BY

排序和分组是性能消耗大户。最优解是让索引来有序地提供数据。

-- 配合索引 idx_orders_user_status_created(user_id, status, created_at DESC)
-- 以下查询可直接利用索引顺序,避免 filesort
SELECT * FROM orders
WHERE user_id = 123
ORDER BY status, created_at DESC
LIMIT 20;

4.3 优化 JOIN 操作

  • 索引驱动:确保 ON 子句上的所有连接键都建有索引。
  • 小表驱动大表:MySQL 优化器通常会自行决定,但原则上,让数据量小的表作为驱动表。
    -- 假设 users 表远小于 orders 表,以 users 作为驱动表通常更优
    SELECT o.*, u.name
    FROM users u
    JOIN orders o ON o.user_id = u.id
    WHERE u.status = 'vip';

4.4 用 EXISTS 替代 IN(在特定场景下)

当子查询结果集较大时,EXISTS 找到第一条匹配记录即返回,性能往往优于 IN

-- 子查询可能返回大量数据时,用 EXISTS
SELECT * FROM orders o
WHERE EXISTS (
    SELECT 1 FROM users u WHERE u.id = o.user_id AND u.status = 'vip'
);

5 数据库配置优化

软件层面的优化是前提,但配置决定了这些优化的天花板。尤其是与 MySQL 核心引擎相关的参数。

5.1 关键配置参数

一份值得你在生产环境细细打磨的配置片断:

[mysqld]
# InnoDB 缓冲池大小,官方建议设为物理内存的 70%-80%。
innodb_buffer_pool_size = 12G

# 日志文件大小,越大性能越好,但崩溃恢复时间也越久
innodb_log_file_size = 1G

# 日志刷新策略,1 代表每次事务提交都刷盘,保证 ACID,但性能较差
innodb_flush_log_at_trx_commit = 1

# 最大连接数
max_connections = 500

# 临时表和内存表大小设置,防止因结果集过大而被迫写入磁盘
tmp_table_size = 256M
max_heap_table_size = 256M

# 慢查询配置
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1

5.2 InnoDB 缓冲池优化

你要时常了解它的“健康状况”:

-- 查看缓冲池整体使用情况
SHOW STATUS LIKE 'Innodb_buffer_pool%';

-- 查看缓冲池大小配置
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

-- 动态调整缓冲池大小(MySQL 5.7+)
SET GLOBAL innodb_buffer_pool_size = 12873741824;  -- 12GB

-- 设置缓冲池实例数,建议与 CPU 核心数一致,减少内部争用
innodb_buffer_pool_instances = 8

-- 数据库重启后自动读取热数据到缓冲池
innodb_buffer_pool_load_at_startup = 1

5.3 连接数管理

连接数过少会阻塞应用,过多则会耗尽内存。

-- 查看当前连接数
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';

-- 查看最大连接数配置
SHOW VARIABLES LIKE 'max_connections';

-- 临时调整最大连接数
SET GLOBAL max_connections = 1000;

-- 查看当前所有连接
SHOW FULL PROCESSLIST;

-- 批量杀掉空闲时间超过 3600 秒的连接
SELECT CONCAT('KILL ', id, ';')
FROM information_schema.processlist
WHERE Command = 'Sleep' AND Time > 3600;

6 慢查询优化实战案例

理论看完了,我们用几个典型的线上场景来操练一下。

6.1 案例一:分页查询性能“跳水”

后台列表常见的 LIMIT N, M 深分页(页面越往后翻越快不起来),是经典的优化案例。

-- 低效的深分页,offset 大到一定程度时,查询会慢得离谱
SELECT * FROM orders ORDER BY order_id DESC LIMIT 1000000, 20;

-- 问题剖析:MySQL 需要顺序扫描到 offset + limit 行的位置,offset 越大,扫描越多。
-- 优化方案一:基于游标的分页(记住上一页最后一条的 order_id)
SELECT * FROM orders
WHERE order_id < 1234567    -- 上次查询返回的最小 ID
ORDER BY order_id DESC
LIMIT 20;

-- 优化方案二:若必须跳页,优先走覆盖索引减少扫描量
SELECT order_id FROM orders ORDER BY order_id DESC LIMIT 1000000, 20;

6.2 案例二:统计查询的“数据洪流”

在大表上直接做 GROUP BY 的统计查询,无疑是拿服务器性能和用户耐心在裸奔。

-- 低效:每次请求都扫描整个 orders 表的当月数据进行聚合计算
SELECT DATE(created_at) AS day, COUNT(*) AS order_count
FROM orders
WHERE created_at >= '2024-01-01'
GROUP BY DATE(created_at);

-- 优化方案:建立一张轻量级的日汇总表,通过定时任务灌注数据
CREATE TABLE orders_daily_summary (
    stat_date DATE PRIMARY KEY,
    order_count INT NOT NULL DEFAULT 0,
    total_amount DECIMAL(15,2) NOT NULL DEFAULT 0,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 定时任务执行合并写入
INSERT INTO orders_daily_summary (stat_date, order_count, total_amount)
SELECT DATE(created_at), COUNT(*), SUM(total_amount)
FROM orders WHERE DATE(created_at) = CURDATE()
GROUP BY DATE(created_at)
ON DUPLICATE KEY UPDATE
    order_count = VALUES(order_count),
    total_amount = VALUES(total_amount);

6.3 案例三:模糊搜索的优化路径

LIKE '%keyword%' 是全表扫描的同义词,但业务上确实存在复杂的文本搜索需求。

-- 最差劲的做法
SELECT * FROM users WHERE name LIKE '%zhang%';

-- 优化方案一:用 MySQL 内置的全文索引(5.6+),适用于简单的中文分词搜索
ALTER TABLE users ADD FULLTEXT INDEX ft_users_name(name);
SELECT * FROM users WHERE MATCH(name) AGAINST('zhang');

-- 优化方案二:引入 Elasticsearch 这类专业的搜索引擎,是处理复杂模糊搜索的最佳实践。

-- 优化方案三:如果搜索场景总是前缀匹配(如搜索框自动补全)
CREATE INDEX idx_users_name_prefix ON users(name(10));
SELECT * FROM users WHERE name LIKE 'zhang%'; -- 可以利用到索引

7 监控与预防

优化不是一劳永逸的买卖。建立常态化的监控与治理流程,才能长治久安。

7.1 持续监控慢查询

一段 shell 脚本,每天自动生成慢查询分析报告,让你的告警系统介入。

#!/bin/bash
# 每天凌晨分析昨天的慢查询日志

DATE=$(date -d "yesterday" +%Y-%m-%d)
SLOW_LOG="/var/log/mysql/slow.log"
REPORT="/var/log/mysql/slow_query_report_${DATE}.txt"

pt-query-digest --since "$(date -d 'yesterday 00:00:00' +%s) seconds" \
                --until "$(date -d 'yesterday 23:59:59' +%s) seconds" \
                --report-format=query_report \
                $SLOW_LOG > $REPORT

# 如果新发现的慢查询超过阈值,则发送告警
if [ -s "$REPORT" ]; then
    count=$(grep -c "Query" $REPORT || true)
    if [ "$count" -gt 10 ]; then
        echo "Found $count slow queries in the report" | mail -s "Slow Query Alert" ops@example.com
    fi
fi

7.2 善用 Performance Schema

MySQL 5.6+ 的原生监控利器,性能开销极低,是查看当前实时热点查询的不二之选。

-- 启用相关的 instrument 和 consumer
UPDATE performance_schema.setup_instruments
SET ENABLED = 'YES' WHERE NAME LIKE 'statement/%';

UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES' WHERE NAME LIKE 'events_statements%';

-- Top 10 总耗时最长的 SQL 摘要
SELECT
    DIGEST,
    COUNT_STAR,
    SUM_TIMER_WAIT / 1000000000000 AS total_time_sec,
    AVG_TIMER_WAIT / 1000000000000 AS avg_time_sec,
    SUM_ROWS_EXAMINED,
    SUM_ROWS_SENT,
    SUBSTR(DIGEST_TEXT, 1, 100) AS query_sample
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;

7.3 建立慢查询治理闭环

将慢查询的发现、分析、解决、验证、归档形成一个闭环:

  1. 日常监控:基于报表和告警,主动发现新增慢查询。
  2. 分析根因:使用 EXPLAIN 进行精准分析,定位瓶颈。
  3. 制定方案:选择索引优化、SQL 重写、参数调整等手段。
  4. 上线验证:在测试环境或预发环境验证效果,确认 Rows_examined 显著降低。
  5. 归档记录:将问题和解决方案写入团队知识库,形成长效积累。

8 结论

数据库慢查询优化绝非“加个索引”这么简单,它是一个系统性的工程。牢记这套排障心法:慢查询日志定目标,EXPLAIN 分析抓根因,然后精准施策,最后靠监控守住成果

加索引是手段,不是终极答案。未经分析的索引添加,不仅可能无效,引入的额外写入负担和空间占用甚至会恶化整体性能。同样地,SQL 语句重写、数据库配置调优等手段,也都各有其适用的战场。运维工程师的价值,正在于能够系统性地运用这些工具和方法,形成自己解决复杂问题的逻辑闭环。


参考资料:

  • MySQL 8.0 Reference Manual - Optimizing Queries
  • MySQL 8.0 Reference Manual - EXPLAIN Statement
  • Percona Blog - pt-query-digest 文档
  • High Performance MySQL, 3rd Edition
  • SHOW [GLOBAL] STATUS, SHOW VARIABLES 相关文档



上一篇:Joe Liemandt 谈 Founders School:百万美元保证背后的家长教育与早期采用者
下一篇:AI正在自我迭代:1134名从业者联名倡议减速,但80%代码已由AI生成
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-8-4 05:52 , Processed in 1.026983 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表