MYSQL数据库性能优化

从 配置、SQL、连接、存储引擎、程序、操作系统 六个方向排查

SQL查询过程:
先查缓冲池 ,若在则直接读取,反之则查询磁盘并将磁盘数据放到缓冲池以供下次查询使用

1. 缓冲池优化·增加缓存提高查询速度

sql 复制代码
#查询缓存大小
show variables like 'innodb_buffer_pool_size'
#设置缓存(1G=1073741824字节)
set global innodb_buffer_pool_size = 1073741824   
#注意上面设置方式重启后会失效

**#永久设置方式:修改my.ini文件中的 innodb_buffer_pool_size 值大小(记得重启服务)

**

大小设置建议:物理内存的80%

2. 慢SQL

sql 复制代码
-- 1. 查看占用内存最高的执行语句
SELECT query, exec_count, total_latency, avg_examined_rows 
FROM sys.statement_analysis 
ORDER BY total_latency DESC LIMIT 10;
# 查询正在执行的、消耗资源较多的 SQL:
SELECT * FROM sys.statement_analysis ORDER BY total_latency DESC LIMIT 10;

-- 2. 查看哪些线程当前正在占用大量内存
SELECT thread_id, processlist_id, current_used_mem_mb 
FROM sys.memory_thread_by_current_bytes 
WHERE current_used_mem_mb > 50; -- 查找占用超过50MB的线程

--- 定位正在运行的"慢"过程 (如果内存正在飙升,立即查看当前正在运行的 SQL)
-- 查看当前正在执行的线程,观察 Time 和 Info
SHOW FULL PROCESSLIST;
--关注点: Time 很长且 Command 为 Query 的语句;以及 State 显示为 Copying to tmp table 或 Sending data 的语句。
sql 复制代码
#慢SQL开启
-- 开启慢查询日志功能
SET GLOBAL slow_query_log = 'ON';

-- 设置慢查询的判定阈值(单位:秒,建议先设为 1 或 2,视业务情况而定)
SET GLOBAL long_query_time = 1;

-- 设置日志输出路径(通常在数据目录下)
-- SET GLOBAL slow_query_log_file = '/var/lib/mysql/mysql-slow.log';


#验证
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';

永久开启,修改 MySQL 的配置文件(my.cnf 或 my.ini)

bash 复制代码
[mysqld]
# 开启慢查询日志
slow_query_log = 1
# 设置慢查询时间阈值(超过此时间会被记录)
long_query_time = 1
# 日志文件路径(确保 MySQL 用户对该路径有写权限)
slow_query_log_file = /var/lib/mysql/mysql-slow.log
# 记录未使用索引的查询(可选,建议开启)
log_queries_not_using_indexes = 1

3. 排查手段

bash 复制代码
# 查看操作系统内存占用

1.使用 top 或 htop 查看 RES (常驻内存) 和 SHR (共享内存) 
2.观察 free -m,注意 buff/cache 是否过高。MySQL 依赖系统缓存,这部分高通常是正常的



# 查看 MySQL 进程内存(RSS 是物理内存)
top -p $(pidof mysqld)   # 或 htop

# 更详细的内存映射
pmap -x $(pidof mysqld) | tail -n 5

# 或用
cat /proc/$(pidof mysqld)/status | grep -E 'Vm'
sql 复制代码
#查看 MySQL 内部内存分配
-- 查看各内存分类的占用情况
SELECT * FROM sys.memory_global_by_current_bytes;
-- 查看哪些线程占用了较多内存
SELECT * FROM sys.memory_thread_by_current_bytes;
sql 复制代码
#排查配置问题
-- 查看所有相关内存参数
SHOW VARIABLES WHERE Variable_name IN (
    'innodb_buffer_pool_size', 
    'innodb_log_buffer_size', 
    'sort_buffer_size', 
    'join_buffer_size', 
    'read_buffer_size', 
    'max_connections'
);

核算逻辑:

  • 全局内存 = innodb_buffer_pool_size + innodb_log_buffer_size + ...
  • 单连接内存 = sort_buffer_size + join_buffer_size + read_buffer_size + ...
  • 总预估内存 = 全局内存 + (max_connections × 单连接内存)
    注意: 不能超过了服务器物理内存的 80%

@TOC################# 优先排查 ######################

  • 实时"抓现行"
bash 复制代码
#这条命令能列出当前 MySQL 实例中所有正在运行的线程
  SHOW FULL PROCESSLIST;
  

核心列:

  • Time:该线程执行了多久(单位:秒)。如果数值很大且状态不是 Sleep,说明该 SQL 执行时间过长
  • State:线程当前正在做什么(重点关注: Sending data(可能正在从磁盘读取大批量数据)、Copying to tmp table(正在创建临时表,极度消耗内存)、Sorting result(正在排序,消耗 CPU 和内存)
  • info:当前执行的完整 SQL 语句。如果语句很长,FULL 关键字能保证你看到完整内容,而不是被截断。

典型场景:

如果你发现某一行的 Time 很高,且 State 为 Sending data,这通常就是那个正在"吃掉"内存的 SQL。

  • 查历史帐
    你可以运行以下语句查看前 10 条最耗资源的 SQL:
sql 复制代码
SELECT 
    query, 
    exec_count,            -- 执行次数
    total_latency,         -- 总耗时
    avg_examined_rows,     -- 平均扫描行数(重点关注)
    rows_sent_avg,         -- 平均返回行数
    tmp_tables,            -- 使用临时表的次数(内存杀手!)
    disk_tmp_tables        -- 使用磁盘临时表的次数
FROM sys.statement_analysis 
ORDER BY total_latency DESC 
LIMIT 10;

@TOC4. max_connections(最大连接数)

sql 复制代码
-- 当前连接数
SHOW STATUS LIKE 'Threads_connected';

-- 当前正在执行的连接
SHOW STATUS LIKE 'Threads_running';

-- 历史最大连接数
SHOW STATUS LIKE 'Max_used_connections';

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

-- 是否发生连接数耗尽
SHOW STATUS LIKE 'Connection_errors_max_connections';

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

-- 按状态统计连接
SELECT COMMAND, COUNT(*)
FROM information_schema.PROCESSLIST
GROUP BY COMMAND;

-- 按用户统计连接
SELECT USER, COUNT(*)
FROM information_schema.PROCESSLIST
GROUP BY USER;
bash 复制代码
#1.查看当前连接数,Threads_connected:表示当前有多少个客户端连接到 MySQL
SHOW STATUS LIKE 'Threads_connected';
#2.再看真正执行 SQL 的连接:
SHOW STATUS LIKE 'Threads_running';
# Threads_running    4
#说明:
#58 个连接
#真正工作的只有 4 个
#其它 54 个都是 Sleep,这是正常现象



#3.查看历史最大连接数
SHOW STATUS LIKE 'Max_used_connections';
# Max_used_connections    100  表示自 MySQL 启动以来,最多只用过 126 个连接。

#4.再看
SHOW VARIABLES LIKE 'max_connections';

#max_connections = 1000
1000
↓
历史最高
126
配置过大。

#5.看连接是否打满
SHOW STATUS LIKE 'Connection_errors_max_connections';

#6.查看连接详情
SHOW FULL PROCESSLIST;