MariaDB性能优化 #
一、性能优化概述 #
1.1 优化方向 #
text
性能优化方向
├── 查询优化
│ ├── SQL语句优化
│ ├── 索引使用
│ └── 执行计划分析
├── 结构优化
│ ├── 表设计
│ ├── 索引设计
│ └── 分区设计
├── 配置优化
│ ├── 内存配置
│ ├── 缓存配置
│ └── 连接配置
└── 硬件优化
├── CPU
├── 内存
└── 存储
1.2 优化原则 #
| 原则 | 说明 |
|---|---|
| 监控先行 | 先监控定位问题 |
| 逐步优化 | 一次改一个变量 |
| 测试验证 | 优化后验证效果 |
| 文档记录 | 记录优化过程 |
二、查询优化 #
2.1 使用EXPLAIN #
sql
-- 查看执行计划
EXPLAIN SELECT * FROM users WHERE name = 'John';
-- 查看完整执行计划
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE name = 'John';
-- 关键字段说明
-- id: 查询标识符
-- select_type: 查询类型
-- table: 表名
-- type: 访问类型
-- system > const > eq_ref > ref > range > index > ALL
-- possible_keys: 可能使用的索引
-- key: 实际使用的索引
-- key_len: 索引长度
-- rows: 预估扫描行数
-- Extra: 额外信息
2.2 避免全表扫描 #
sql
-- 避免:无索引条件
SELECT * FROM users WHERE YEAR(created_at) = 2024;
-- 优化:使用范围条件
SELECT * FROM users
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
-- 避免:LIKE以%开头
SELECT * FROM users WHERE name LIKE '%John%';
-- 优化:使用前缀匹配
SELECT * FROM users WHERE name LIKE 'John%';
-- 避免:OR条件
SELECT * FROM users WHERE name = 'John' OR email = 'john@example.com';
-- 优化:使用UNION
SELECT * FROM users WHERE name = 'John'
UNION
SELECT * FROM users WHERE email = 'john@example.com';
2.3 索引使用优化 #
sql
-- 使用覆盖索引
-- 创建索引
CREATE INDEX idx_user_status_name ON users(status, name);
-- 使用覆盖索引(不需要回表)
SELECT name FROM users WHERE status = 1;
-- 使用索引排序
SELECT * FROM users WHERE status = 1 ORDER BY name;
-- 避免索引失效
-- 错误:使用函数
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
-- 正确:不使用函数
SELECT * FROM users WHERE name = 'John';
2.4 分页优化 #
sql
-- 传统分页(大偏移量性能差)
SELECT * FROM users ORDER BY id LIMIT 10000, 10;
-- 优化:使用WHERE条件
SELECT * FROM users WHERE id > 10000 ORDER BY id LIMIT 10;
-- 优化:使用JOIN
SELECT u.* FROM users u
JOIN (SELECT id FROM users ORDER BY id LIMIT 10000, 10) t
ON u.id = t.id;
-- 优化:使用覆盖索引
SELECT u.* FROM users u
INNER JOIN (SELECT id FROM users ORDER BY id LIMIT 10000, 10) t
ON u.id = t.id;
2.5 子查询优化 #
sql
-- 避免:相关子查询
SELECT * FROM users u
WHERE (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) > 0;
-- 优化:使用JOIN
SELECT DISTINCT u.* FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- 避免:IN子查询
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);
-- 优化:使用EXISTS
SELECT * FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
三、索引优化 #
3.1 索引设计原则 #
text
索引设计原则
├── 选择性高的列
│ └── 基数/总行数 接近1
├── WHERE条件列
│ └── 经常用于过滤
├── JOIN连接列
│ └── 外键列
├── ORDER BY列
│ └── 排序列
├── 复合索引顺序
│ └── 高选择性在前
└── 避免过度索引
└── 索引有成本
3.2 索引维护 #
sql
-- 查看索引使用情况
SELECT
object_schema,
object_name,
index_name,
count_read,
count_fetch
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = 'mydb'
ORDER BY count_read DESC;
-- 查找未使用的索引
SELECT
object_schema,
object_name,
index_name
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL
AND count_star = 0
AND object_schema = 'mydb';
-- 删除未使用的索引
ALTER TABLE users DROP INDEX idx_unused;
-- 重建索引
ALTER TABLE users ENGINE=InnoDB;
-- 更新统计信息
ANALYZE TABLE users;
四、表结构优化 #
4.1 选择合适数据类型 #
sql
-- 整数类型
-- 根据范围选择最小类型
TINYINT < SMALLINT < MEDIUMINT < INT < BIGINT
-- 字符串类型
-- 定长用CHAR,变长用VARCHAR
-- 长文本用TEXT
-- 日期时间
-- DATE: 只存日期
-- DATETIME: 日期时间
-- TIMESTAMP: 时间戳
-- 避免过度设计
-- 错误:VARCHAR(1000)存储10字符
-- 正确:VARCHAR(50)
4.2 范式与反范式 #
sql
-- 范式设计(减少冗余)
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
department_id INT
);
CREATE TABLE departments (
id INT PRIMARY KEY,
name VARCHAR(50)
);
-- 反范式设计(提高查询性能)
CREATE TABLE users_denormalized (
id INT PRIMARY KEY,
name VARCHAR(50),
department_id INT,
department_name VARCHAR(50) -- 冗余字段
);
-- 根据场景选择
-- OLTP:偏向范式
-- OLAP:偏向反范式
4.3 分区表 #
sql
-- 按日期分区
CREATE TABLE logs (
id INT,
log_date DATE,
message TEXT
) PARTITION BY RANGE (TO_DAYS(log_date)) (
PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),
PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')),
PARTITION p202403 VALUES LESS THAN (TO_DAYS('2024-04-01')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
-- 查询特定分区
SELECT * FROM logs PARTITION(p202401);
五、配置优化 #
5.1 内存配置 #
ini
[mysqld]
# InnoDB缓冲池(物理内存的50-70%)
innodb_buffer_pool_size = 4G
# 缓冲池实例数
innodb_buffer_pool_instances = 4
# 日志缓冲区
innodb_log_buffer_size = 16M
# 排序缓冲区
sort_buffer_size = 4M
# 连接缓冲区
join_buffer_size = 4M
# 读缓冲区
read_buffer_size = 2M
read_rnd_buffer_size = 4M
# 表缓存
table_open_cache = 4000
table_definition_cache = 2000
# 线程缓存
thread_cache_size = 64
5.2 InnoDB配置 #
ini
[mysqld]
# 日志文件大小
innodb_log_file_size = 512M
# 日志刷新策略
# 0: 每秒刷新
# 1: 每次提交刷新(默认,最安全)
# 2: 每次提交写入,每秒刷新
innodb_flush_log_at_trx_commit = 1
# 刷新方法
innodb_flush_method = O_DIRECT
# IO线程数
innodb_read_io_threads = 8
innodb_write_io_threads = 8
# 锁等待超时
innodb_lock_wait_timeout = 50
# 死锁检测
innodb_deadlock_detect = ON
5.3 连接配置 #
ini
[mysqld]
# 最大连接数
max_connections = 500
# 最大错误连接数
max_connect_errors = 100
# 连接超时
connect_timeout = 10
# 等待超时
wait_timeout = 28800
interactive_timeout = 28800
5.4 查询缓存(MariaDB 10.5以下) #
ini
[mysqld]
# 查询缓存(注意:10.5+已移除)
query_cache_type = 1
query_cache_size = 128M
query_cache_limit = 2M
六、慢查询分析 #
6.1 启用慢查询日志 #
sql
-- 启用慢查询日志
SET GLOBAL slow_query_log = 'ON';
-- 设置阈值(秒)
SET GLOBAL long_query_time = 2;
-- 记录未使用索引的查询
SET GLOBAL log_queries_not_using_indexes = 'ON';
-- 查看配置
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
6.2 分析慢查询 #
bash
# 使用mysqldumpslow分析
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# 选项说明
# -s: 排序方式
# t: 按查询时间
# c: 按查询次数
# l: 按锁定时间
# r: 按返回记录数
# -t: 显示前N条
6.3 使用performance_schema #
sql
-- 查看慢查询统计
SELECT
digest_text,
count_star,
avg_timer_wait/1000000000 as avg_ms,
sum_timer_wait/1000000000 as total_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY sum_timer_wait DESC
LIMIT 10;
七、监控与诊断 #
7.1 状态监控 #
sql
-- 查看全局状态
SHOW GLOBAL STATUS;
-- 查看关键指标
SHOW STATUS LIKE 'Threads%';
SHOW STATUS LIKE 'Connections';
SHOW STATUS LIKE 'Innodb%';
SHOW STATUS LIKE 'Qcache%';
-- 查看进程列表
SHOW PROCESSLIST;
SHOW FULL PROCESSLIST;
7.2 性能指标 #
sql
-- 连接数
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
-- 查询统计
SHOW STATUS LIKE 'Questions';
SHOW STATUS LIKE 'Queries';
SHOW STATUS LIKE 'Slow_queries';
-- InnoDB统计
SHOW STATUS LIKE 'Innodb_row_lock%';
SHOW STATUS LIKE 'Innodb_buffer_pool%';
-- 计算命中率
SELECT
(1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)) * 100
AS buffer_pool_hit_rate
FROM (
SELECT variable_value AS Innodb_buffer_pool_reads
FROM information_schema.global_status
WHERE variable_name = 'Innodb_buffer_pool_reads'
) r,
(
SELECT variable_value AS Innodb_buffer_pool_read_requests
FROM information_schema.global_status
WHERE variable_name = 'Innodb_buffer_pool_read_requests'
) rr;
7.3 锁监控 #
sql
-- 查看锁等待
SELECT * FROM information_schema.innodb_lock_waits;
-- 查看锁信息
SELECT * FROM information_schema.innodb_locks;
-- 查看事务
SELECT * FROM information_schema.innodb_trx;
八、最佳实践 #
8.1 优化检查清单 #
text
优化检查清单
├── 查询优化
│ ├── 使用EXPLAIN分析
│ ├── 避免全表扫描
│ └── 合理使用索引
├── 索引优化
│ ├── 选择性高的列
│ ├── 复合索引顺序
│ └── 定期维护
├── 结构优化
│ ├── 合适的数据类型
│ ├── 适当的范式
│ └── 分区设计
├── 配置优化
│ ├── 内存配置
│ ├── 连接配置
│ └── InnoDB配置
└── 监控告警
├── 慢查询监控
├── 连接数监控
└── 资源使用监控
8.2 优化建议 #
| 场景 | 建议 |
|---|---|
| 查询慢 | 分析执行计划,添加索引 |
| 连接数高 | 增加max_connections,优化连接池 |
| 内存不足 | 调整buffer_pool_size |
| 锁等待 | 优化事务,减少锁持有时间 |
| 磁盘IO | 使用SSD,优化查询 |
九、总结 #
优化要点:
| 方向 | 关键点 |
|---|---|
| 查询 | EXPLAIN分析,避免全表扫描 |
| 索引 | 合理设计,定期维护 |
| 结构 | 合适类型,适当范式 |
| 配置 | 内存、连接、InnoDB |
| 监控 | 慢查询、状态、锁 |
最佳实践:
- 监控先行,定位问题
- 逐步优化,验证效果
- 文档记录,持续改进
- 定期维护,预防问题
恭喜你完成了MariaDB完全指南的学习!
最后更新:2026-03-27