【MySQL】执行计划分析、单表数据量

15 阅读3分钟

执行计划分析

参数有:

  • possible_keys :可能用到的索引;

  • key:实际用的索引,如果这一项为 NULL,说明没有使用索引;

  • key_len:索引的长度;

  • rows:扫描的数据行数。

  • type:数据扫描类型,执行效率从低到高的顺序为:

    • All:全表扫描
    • index:全索引扫描,不再需要对数据进行排序,但是开销依然很大。
    • range:索引范围扫描,使用 < 、>、in、between 等关键词,只检索给定范围的行。
    • ref:非唯一索引扫描,或者是唯一索引的非唯一性前缀,返回数据返回可能是多条。
    • eq_ref:唯一索引扫描,使用主键或唯一索引时产生的访问方式,通常使用在多表联查中。
    • const:结果只有一条的主键或唯一索引扫描
  • extra

    • Using filesort :查询包含 group by 操作,而且无法利用索引完成排序操作,尽量避免。

    • Using temporary:用临时表保存中间结果,常见于order by 和 group by。效率低,要避免。

    • Using index:使用了覆盖索引,避免了回表操作,效率不错。

2000W魔咒

What?

单表数据量达到2000w左右,查询效率断崖下跌。

Why?

B+树深度越深,读盘次数越多,效率越低。下面计算2、3层B+树的数据量:

  • X:非页节点索引数

    • File Header (38 byte)、Page Header (56 Byte)、Infimum + Supermum(26 byte)、File Trailer(8byte), 再加上页目录,大概 1k
    • 页的大小是 16K, 剩下 15k 用于存数据
    • 主键假设是 Bigint (8 byte), 页号也是固定的(4Byte), 那么索引页中的一条数据也就是 12byte。
    • X=15*1024/12≈1280 行。
  • Y:页节点数据行树

    • 假设一条行数据 1k ,那一页就能存下 15 条,Y = 15*1024/1000 ≈15。
  • Z:B+树高度,2或3

  • Total =x^(z-1) *y

    • z = 2, Total = (1280 ^1 )*15 = 19200
    • z = 3, Total = (1280 ^2) *15 = 24576000 (约 2450kw)

所以三层的极限差不多2000W,超过就变四层了。

count(*)、count(1)、count(字段)

按照性能排序:count(*)=count(1)>count(主键字段)>count(字段)

  • count():统计符合查询条件的记录中,函数指定的参数不为 NULL 的记录有多少个。

    • count(*)

      • count(*) 其实等于 count(0)
    • count(1)

      • InnoDB 循环遍历聚簇索引(主键索引),将读取到的记录返回给 server 层,但是不会读取记录中的任何字段的值,因为 count 函数的参数是 1,不是字段,所以不需要读取记录中的字段值。参数 1 很明显并不是 NULL,因此 server 层每从 InnoDB 读取到一条记录,就将 count 变量加 1。
      • 二级索引时,使用key_len 最小的二级索引进行扫描。
    • count(主键字段)

      • 只有主键索引:循环遍历聚簇索引,将读取到的记录返回给 server 层,然后读取记录中的 id 值,就会 id 值判断是否为 NULL,如果不为 NULL,就将 count 变量加 1。
      • 二级索引时,InnoDB 循环遍历的对象是二级索引。
    • count(字段)

      • 采用全表扫描的方式来计数,所以它的执行效率是比较差的。

为什么要通过遍历的方式来计数?

  • MyISAM 引擎,执行 count 函数只需要 O(1 )复杂度,MyISAM 的数据表都有一个 meta 信息有存储了row_count值,由表级锁保证一致性,所以直接读取 row_count 值就是 count 函数的执行结果。
  • InnoDB 存储引擎是支持事务的,同一个时刻的多个查询,由于多版本并发控制(MVCC)的原因,InnoDB 表“应该返回多少行”也是不确定的,所以无法像 MyISAM一样,只维护一个 row_count 变量。

如何优化 count(*)?

  • 近似值:使用 show table status 或者 explain 命令来表进行估算。
  • 额外表保存计数值:插入一条记录的同时,将计数表中的计数字段 + 1