性能分析
数据库调优的目标:响应时间更快,吞吐量更大。
数据库服务器的优化步骤

观察服务器在状态
- 观察服务器状态
- 开启慢查询日志
- explain、show profiling
行动
- 加缓存、更改缓存失效策略 (存在周期性波动)
- 调优服务器参数(SQL 等待时间长)
- 适当的增加数据库缓冲池
- 表相关优化(SQL 执行时间长)
- 索引设计优化
- join 表过多, 需要优化
- 数据表设计优化
- 架构优化(SQL 查询达到瓶颈)
- 读写分离(主从架构)
- 分库分表(垂直分库、垂直分表、水平分表)
查询系统性能参数
SHOW [GLOBAL|SESSION] STATUS LIKE '参数';
- Connections:连接 MySQL 服务器的次数
- Uptime:mysql 服务器的上线时间
- Slow_queries:慢查询次数
- Innodb_rows_read
- Innodb_rows_inserted
- Innodb_rows_updated
- Innodb_rows_deleted
- Com_select:查询操作的次数
- Com_insert:插入操作的次数
- Com_update:更新操作的次数
- Com_delete:删除操作的次数
统计 sql 的查询成本 last_query_cost
查询成本指的是:SQL 语句所需要读取的页的数量。
last_query_cost 是优化器估计出来的,并不是实际值。
show status like 'last_query_cost';
sql 查询是一个动态的过程,从页加载的角度来看,我们可以得到以下两点结论
- 位置决定效率。如果页就在数据库缓冲池中,那么效率是最高的,否则还需要从内存或者磁盘中进行读取,当然针对单个页的读取来说,如果页存在于内存中,会比在磁盘中读取效率高很多。
- 批量决定效率。如果我们从磁盘中对单一页进行随机读,那么效率是很低的(差不多10ms),而采用顺序读取的方式,批量对页进行读取,平均一页的读取效率就会提升很多,甚至要快于单个页面在内存中的随机读取。
定位执行慢的 sql:慢查询日志
MySQL 的慢查询日志,用来记录在 MySQL 中响应时间超过阈值的语句,具体指运行时间超过 long_query_time 的值的 SQL,则会被记录到慢查询日志中。 long_query_time 的默认值为 10,意思是运行 10 秒以上(不含10秒)的语句,认为是超出了我们的最大忍耐时间值。
开启 慢查询
查询慢查询是否开启
show variables like `%slow_query_log%`;
开启
set global slow_query_log='ON';
修改慢查询的阈值
show variables like '%long_query_time%';
设置超时时间为 1 秒。
set long_query_time=1;
查看慢查询的数目
SHOW GLOBAL STATUS LIKE '%Slow_queries%';
查询慢查询日志的路径
SHOW VARIABLES LIKE 'slow_query_log_file';
慢查询日志分析工具:mysqldumpslow
按照查询时间排序,查看前 5 条 sql 语句
mysqldumpslow -s t -t 5 /var/lib/mysql/atguigu1-slow.log
得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu1-slow.log
得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu1-slow.log
得到按照时间排序的前10条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/atguigu1-slow.log
另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现爆屏情况
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more
查看 sql 执行成本:show profile
show profile 默认处于关闭状态
开启这个功能
show variables like 'profiling';
set profiling = 'ON';
show profiles;
分析查询语句:explain
id
在一个大的查询语句中每个 select 关键字都对应一个唯一的 id。
- id 如果相同,可以认为是同一组,从上往下顺序执行
- 在所有组中,id 值越大,优先级越高,越先执行
- 关注点:每个 id 号码,表示一趟独立的查询,一个 sql 的查询次数越少越好
select_type
- simple:查询中不包含子查询或者 union
- primary:查询中若包含任何复杂的子部分,最外层查询则被标记为 primary
- subquery:select 或 where 中包含的子查询
- derived:from 中包含的子查询
- union
- union result
- dependent subquery:select、where 中包含的子查询,子查询基于外层
- uncacheable subquery:无法被缓存的子查询
table
表名
partitions
匹配的分区信息
type:访问类型
- system:表只有一行记录,(const 的特例)
- const:表示通过索引一次就找到了, const 用于比较 primary key 或这 unique 索引。因为只需要匹配一行数据,所以很快。如果将主键置于 where 列表中,MySQL 就能将该查询转换为一个 const 。
- eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。
- ref:非唯一索引扫描,返回匹配某个单独值的所有行。(可能会返回多个符合条件的行)
- range:只检索给定范围的行,使用一个索引来选择行。 key 列显示使用了哪个索引。一般就是在 where 语句中出现了 between、
<、>、in 等查询。 - index:Full Index Scan , index 与 All 区别为 index 类型只遍历索引树。这通常比 ALL 快,因为索引文件通常比数据文件小。(Index 与 ALL 虽然都是读全表,但是 index 是从索引树中读取,而 ALL 是从硬盘读取)
- all:Full Table Scan,遍历全表以找到匹配的行。
system > const > eq_ref > ref > range > index > all
实际
show create table user; 查看 user表结构。
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`age` int NOT NULL,
`email` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_name` (`name`),
KEY `index_age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
const

eq_ref

ref

range

index

all

possible_keys
可能用到的索引
key
实际上使用的索引
key_len
显示 MySQL 实际决定使用的索引的长度。如果索引是 null ,则长度为 null。如果不是 null,则为使用的索引的长度。
计算规则
- 定长字段, int 占用 4 个字节,date 占用 3 个字节。char(n) 占用 n 个字符。
- 变长字段 varchar(n) 占用 n 个字符 + 2 个字节。
- 不同的字符集,一个字符占用的字节数是不同的。gbk 一个字符占 2 字节,utf-8 一个字符占 3 个字节。
- 对于所有的索引字段,如果设置为允许为 null ,则还需要加 1 个字节。(MySQL 需要额外一个字节来表示是否为 null)
ref
当使用索引列等值查询时,与索引列进行等值匹配的对象信息
rows
预估的需要读取的记录条数