vCPU飙升
在一个漆黑的深夜,MySQL丛库的vCPU在做一个三点任务的时候突然飙升,从MySQL面板中可以查到是以下查询导致的。
表数据及相关索引说明:
- hotel_info_tbl: 数据量:100w+,id 为 primary key
- dynamic_cache_task_hotel_tbl: 数据量:45w+,hotel_id 为unique key,status建立了单字段索引(就0和1为啥单独建索引?)
首先使用Explain查看一下执行计划
- 首先对于dynamic_cache_task_hotel_tbl表中status这种区分度不高的字段单独建了一个索引有待商榷。
- 对于执行计划中,使用dynamic_cache_task_hotel_tbl作为驱动表符合小表作为驱动表的原则,但是排序的时候使用了临时表和文件排序,所以这就是vcpu飙升的原因了。 extra中并未打印
Using join buffer (Block Nested Loop)
说明使用的是Nested-Loop Join(NLJ) 算法。 - 由于排序使用的是
hotel_info_tbl.id
而不是dynamic_cache_task_hotel_tbl.hotel_id
,而驱动表又是dynamic_cache_task_hotel_tbl,那么优化思路一下子就清晰了。
以下是使用dynamic_cache_task_hotel_tbl.hotel_id
排序的执行计划。
从上图中可以看出使用了索引进行排序,而且使用的索引从index(status)
变成了uk_hotel(hotel_id)
,且返回结果并未有任何变化。那么接下来看看查询时间的提升有多大呢。下面两张图告诉你:4.593s -> 0.01s
思考:为什么使用index(status)
而不是uk_hotel(hotel_id)
作为索引?
我取了优化器做出的各种决策的日志发现,index(status)
索引中rowid是有序的,而uk_hotel(hotel_id)
并非是有序的,所以优化器做决策的时候使用了index(status)
。
思考:为什么使用了临时表还会使用文件排序?
我取了优化器做出的各种决策的日志发现,sort_model的值为<fixed_sort_key, additional_fields>
,表明使用文件排序时采用了单路排序。
查询MySQLmax_length_for_sort_data
变量发现这个值为4096,而我们的查询语句是50条数据,id是bigint类型,占用为8位,所以 50*8=400,远小于4096,故而使用单路排序。
单路排序:是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序;如果结果值大于sort_buffer_size变量的设置值还会使用硬盘排序。
嵌套循环连接 Nested-Loop Join(NLJ) 算法
一次一行循环地从第一张表(称为驱动表)中读取行,在这行数据中取到关联字段,根据关联字段在另一张表(被驱动表)里取出满足条件的行,然后取出两张表的结果合集。
如上使用dynamic_cache_task_hotel_tbl作为驱动表,先执行的就是dynamic_cache_task_hotel_tbl表,那么上述sql的执行流程就是:
- 从表 dynamic_cache_task_hotel_tbl 中读取一行数据(如果t2表有查询过滤条件的,用先用条件过滤完,再从过滤结果里取出一行数据);
- 从第 1 步的数据中,取出关联字段 hotel_id,到表 hotel_info_tbl 中查找;
- 取出表 hotel_info_tbl 中满足条件的行,跟 dynamic_cache_task_hotel_tbl 中获取到的结果合并,作为结果返回给客户端;
- 重复上面 3 步。
count(1)、count(*)、count(id)、count(col)
count(col) 不会统计字段为null值的数据行
- 字段有索引:count(*)≈count(1)>count(字段)>count(主键 id)
- 字段有索引,count(字段)统计走二级索引,二级索引存储数据比主键索引少,所以count(字段)>count(主键 id)
- 字段无索引:count(*)≈count(1)>count(主键 id)>count(字段)
- 字段没有索引count(字段)统计走不了索引,count(主键 id)还可以走主键索引,所以count(主键 id)>count(字段)
表中其它字段有索引时,去count(主键),MySQL优化器会帮我们优化成使用其它索引计数。