欢迎来到「数据库急诊监护室」!今天我们将化身"SQL性能侦探",用一家日活百万的虚拟吃瓜论坛"瓜田社"的崩库案例,教你如何用监控工具抓住"性能杀手",让数据库从"垂死挣扎"变"生龙活虎"。
# prometheus.yml 配置示例
scrape_configs:
- job_name: 'mysql'
static_configs:
- targets: ['mysql-host:9104'] # 使用mysqld_exporter
效果:像赛车仪表盘一样实时显示QPS、连接数、缓冲池命中率
# 实时抓取问题查询
pt-query-digest /var/log/mysql/slow.log
# 死锁现场还原
pt-deadlock-logger --user=root --password=xxx h=localhost
#!/bin/bash
# 实时监控基础指标
mysql -uroot -p密码 -e "
SHOW GLOBAL STATUS LIKE 'Questions';
SHOW ENGINE INNODB STATUS\G
SHOW PROCESSLIST;
" > /tmp/mysql_mon.log
指标名称 | 健康范围 | 报警阈值 | 检查方法 |
---|---|---|---|
QPS | < 5000 | > 8000 | SHOW GLOBAL STATUS LIKE 'Questions' |
连接数 | < max_connections的80% | > 90% | SHOW STATUS LIKE 'Threads_connected' |
缓冲池命中率 | > 95% | < 90% | SHOW ENGINE INNODB STATUS |
锁等待时间 | < 50ms | > 200ms | SELECT * FROM sys.innodb_lock_waits |
经典案例:
某吃瓜论坛因明星绯闻突发热搜,QPS瞬间飙到12000,连接数突破2000,触发自动熔断机制——相当于给数据库打了镇静剂!
-- 开启慢查询日志(超过1秒的查询)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_output = 'FILE';
# 生成TOP 10慢查询报告
pt-query-digest /var/log/mysql/slow.log --limit=10
# 输出样例
# 260ms avg, 95% worse
# Attribute pct total min max avg 95%
# ============ === ======= ======= ======= ======= =======
# Count 100 1000
# Query time 100 260s 10ms 5s 260ms 480ms
# Lock time 100 38s 0s 1s 38ms 120ms
# Rows sent 100 1.2M 1 1k 1.2k 980
典型慢查询犯罪现场:
# 凶手SQL:统计用户发帖量(全表扫描+文件排序)
SELECT user_id, COUNT(*) FROM posts
WHERE create_time > '2024-01-01'
GROUP BY user_id ORDER BY COUNT(*) DESC;
优化方案:
-- 添加覆盖索引
ALTER TABLE posts ADD INDEX idx_user_time (user_id, create_time);
-- 使用汇总表
CREATE TABLE user_post_stats (
user_id BIGINT PRIMARY KEY,
post_count INT,
last_updated DATETIME
);
-- 查看哪些SQL消耗最多内存
SELECT * FROM performance_schema.memory_summary_global_by_event_name
ORDER BY SUM_NUMBER_OF_BYTES_ALLOC DESC LIMIT 5;
-- 追踪锁等待事件
SELECT * FROM performance_schema.events_waits_history_long
WHERE EVENT_NAME LIKE 'wait/io/table/sql/%';
-- 查看文件IO热点
SELECT FILE_NAME, COUNT_READ, COUNT_WRITE
FROM performance_schema.file_summary_by_instance
ORDER BY SUM_NUMBER_OF_BYTES_READ DESC LIMIT 10;
诊断案例:
通过events_statements_summary_by_digest
表发现,某个LIKE '%xxx%'
查询占用了80%的CPU时间——相当于发现数据库在挖矿!
-- 糟糕的索引
ALTER TABLE comments ADD INDEX idx_content (content(255));
-- 优化后(使用前缀索引+覆盖索引)
ALTER TABLE comments
ADD INDEX idx_content_user (content(20), user_id);
-- 优化前(嵌套地狱)
SELECT * FROM users
WHERE user_id IN (
SELECT user_id FROM orders
WHERE amount > 100
);
-- 优化后(JOIN替代)
SELECT users.* FROM users
INNER JOIN orders ON users.user_id = orders.user_id
WHERE orders.amount > 100;
# my.cnf 关键参数
innodb_buffer_pool_size = 24G # 内存的70%
innodb_flush_log_at_trx_commit = 2 # 适当降低持久性
max_prepared_stmt_count = 1000000 # 防止预编译语句爆炸
# 使用Redis缓存热点数据
r = redis.Redis()
top_posts = r.get('hot_posts_top100')
if not top_posts:
top_posts = db.query("SELECT ...") # 数据库查询
r.setex('hot_posts_top100', 300, top_posts)
紧急止血方案:
-- 1. 杀死所有慢查询
mysql> SELECT CONCAT('KILL ',id,';') FROM information_schema.processlist
WHERE TIME > 60 AND Command = 'Query'\G
-- 2. 快速降级服务
SET GLOBAL max_connections = 100; # 限制连接数
SET GLOBAL innodb_buffer_pool_size = 4G; # 防止OOM
-- 3. 禁用危险功能
SET GLOBAL event_scheduler = OFF;
max_allowed_packet
设置过小,导致用户发长帖失败现在你已经成为"SQL性能急救专家"!下一章我们将进入《新兴 SQL 技术与工具——当SQL遇见"量子计算"和"元宇宙"的奇幻漂流》的玄学领域,记得给你的服务器准备心电图仪——数据库的心跳由你守护!