一次线上MySQL vCPU飙升引发的思考

vCPU飙升

在一个漆黑的深夜,MySQL丛库的vCPU在做一个三点任务的时候突然飙升,从MySQL面板中可以查到是以下查询导致的。
一次线上MySQL vCPU飙升引发的思考_第1张图片
表数据及相关索引说明:

  • hotel_info_tbl: 数据量:100w+,id 为 primary key
  • dynamic_cache_task_hotel_tbl: 数据量:45w+,hotel_id 为unique key,status建立了单字段索引(就0和1为啥单独建索引?)

首先使用Explain查看一下执行计划
一次线上MySQL vCPU飙升引发的思考_第2张图片

  1. 首先对于dynamic_cache_task_hotel_tbl表中status这种区分度不高的字段单独建了一个索引有待商榷。
  2. 对于执行计划中,使用dynamic_cache_task_hotel_tbl作为驱动表符合小表作为驱动表的原则,但是排序的时候使用了临时表和文件排序,所以这就是vcpu飙升的原因了。 extra中并未打印Using join buffer (Block Nested Loop)说明使用的是Nested-Loop Join(NLJ) 算法。
  3. 由于排序使用的是hotel_info_tbl.id而不是dynamic_cache_task_hotel_tbl.hotel_id,而驱动表又是dynamic_cache_task_hotel_tbl,那么优化思路一下子就清晰了。

以下是使用dynamic_cache_task_hotel_tbl.hotel_id排序的执行计划
一次线上MySQL vCPU飙升引发的思考_第3张图片

从上图中可以看出使用了索引进行排序,而且使用的索引从index(status)变成了uk_hotel(hotel_id),且返回结果并未有任何变化。那么接下来看看查询时间的提升有多大呢。下面两张图告诉你:4.593s -> 0.01s

一次线上MySQL vCPU飙升引发的思考_第4张图片

思考:为什么使用index(status)而不是uk_hotel(hotel_id)作为索引?

我取了优化器做出的各种决策的日志发现,index(status)索引中rowid是有序的,而uk_hotel(hotel_id)并非是有序的,所以优化器做决策的时候使用了index(status)
一次线上MySQL vCPU飙升引发的思考_第5张图片

思考:为什么使用了临时表还会使用文件排序?

我取了优化器做出的各种决策的日志发现,sort_model的值为,表明使用文件排序时采用了单路排序。
一次线上MySQL vCPU飙升引发的思考_第6张图片
查询MySQLmax_length_for_sort_data变量发现这个值为4096,而我们的查询语句是50条数据,id是bigint类型,占用为8位,所以 50*8=400,远小于4096,故而使用单路排序。
单路排序:是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序;如果结果值大于sort_buffer_size变量的设置值还会使用硬盘排序。
一次线上MySQL vCPU飙升引发的思考_第7张图片

嵌套循环连接 Nested-Loop Join(NLJ) 算法

一次一行循环地从第一张表(称为驱动表)中读取行,在这行数据中取到关联字段,根据关联字段在另一张表(被驱动表)里取出满足条件的行,然后取出两张表的结果合集。

如上使用dynamic_cache_task_hotel_tbl作为驱动表,先执行的就是dynamic_cache_task_hotel_tbl表,那么上述sql的执行流程就是:

  1. 从表 dynamic_cache_task_hotel_tbl 中读取一行数据(如果t2表有查询过滤条件的,用先用条件过滤完,再从过滤结果里取出一行数据);
  2. 从第 1 步的数据中,取出关联字段 hotel_id,到表 hotel_info_tbl 中查找;
  3. 取出表 hotel_info_tbl 中满足条件的行,跟 dynamic_cache_task_hotel_tbl 中获取到的结果合并,作为结果返回给客户端;
  4. 重复上面 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优化器会帮我们优化成使用其它索引计数。
在这里插入图片描述

你可能感兴趣的:(mysql,数据库,java)