Mysql Index Cardinality

mysql cardinality 是用来进行索引选择的,数值太小,那么优化器会认为,这个索引对语句没有太大帮助,而不使用索引。

Cardinality 值非常关键,表示索引中不重复记录数量的预估值,在实际应用中,Cardinality/n_rows_in_table 应尽可能地接近 1。如果非常小,那么就需要考虑是否还有必要创建这个索引。既然这个数值十分重要,那么数据库是统计这个信息的呢?

首先要考虑的是在生产环境中,索引的更新操作可能是非常频繁的。如果每次索引在发生操作时就对其进行 Cardinality 的统计,那么将会给数据库带来很大的负担。另外需要考虑的是,如果一张表的数据非常大,如一张表有 50G 的数据,那么统计次 Cardinality 信息所需要的时间可能非常长。这在生产环境下,也是不能接受的。因此,数据库对于 Cardinality 的统计都是通过采样(Sample)的方法来完成的。

InnoDB 存储引擎中, Cardinality 统计信息的更新发生在两个操作中: INSERTUPDATE。根据前面的叙述,不可能在每次发生 INSERTUPDATE 时就去更新 Cardinality信息,这样会增加数据库系统的负荷,同时对于大表的统计,时间上也不允许数据库这样去操作。因此, InnoDB 存储引擎内部对更新 Cardinality 信息的策略为:

第一种策略为自从上次统计 Cardinality 信息后,表中 1/16 的数据已经发生过变化,这时需要更新 Cardinality 信息。第二种情况考虑的是,如果对表中某一行数据频繁地进行更新操作,这时表中的数据实际并没有增加,实际发生变化的还是这一行数据,则第一种更新策略就无法适用这这种情况。故在 InnoDB 存储引擎内部有一个计数器 stat_modified_counter,用来表示发生变化的次数,当 stat_modified_counter 大于 2000000000 时,则同样需要更新 Cardinality 信息。

InnoDB 存储引擎中, Cardinality 默认是通过对 8 个叶子节点预估而得的,可以通过参数 innodb_stats_sample_pages 用来设置统计 Cardinality 时每次采样页的数量,当执行 SQL 语句 ANALYZE TABLESHOW TABLE STATUSSHOW INDEX 以及访问 INFORMATION SCHEMA架构下的表 TABLESSTATISTICS 时会导致 InnoDB 存储引擎去重新计算索引的 Cardinality 值。