0
点赞
收藏
分享

微信扫一扫

MySQL(InnoDB剖析):26---B+树索引之Cardinality值


  • 关于Cardinality,还可参阅前面介绍过的SHOW INDEX命令

 

一、什么是Cardinality

什么时候使用索引比较合适

  • 并不是在所有的查询条件中出现的列都需要添加索引。对于什么时候添加B+树索引,一般的经验是:在访问表中很少一部分时使用B+树索引才有意义
  • 例如,对于性别、读取、类型这样的字段,它们可取值的范围很小,称为“低选择性”。例如

MySQL(InnoDB剖析):26---B+树索引之Cardinality值_数据

  • 按性别进行查询时,可取值的范围一般只有'M','F'。因此上述SQL语句得到结果可能是该表50%的数据(假设男女比例1:1),这是添加B+树索引是完全没有必要的
  • 相反,如果某个字段的取值范围很广,属于“高选择性”。例如对于名字字段,基本上在一个应用中不允许重名的出现,此时使用B+树索引是有必要的

Cardinality的作用

  • 怎么样查看索引是否是高选择性呢?可以通过SHOW INDEX结果中的Cardinality来观察
  • Cardinality值非常关键,表示索引中不重复记录数量的估计值
  • 注意:Cardinality是一个估计值,而不是准确值,基本上用户也不可能得到一个准确的值
  • 在实际应用中,Cardinality/n_rows_in_table应尽可能地接近1。如果非常小,那么用户需要考虑是否还有必要创建这个索引
  • 故在访问高选择性属性的字段并从表中取出很少一部分数据时,对这个字段添加B+树索引是非常有必要的

演示案例

  • 一个表中大约有500万行数据。usernick字段上有一个唯一的索引

MySQL(InnoDB剖析):26---B+树索引之Cardinality值_存储引擎_02

  • 这是查找用户名未David的用户,将会得到如下的执行计划:

MySQL(InnoDB剖析):26---B+树索引之Cardinality值_存储引擎_03

  • 可以看到使用了usernick这个索引,这也符合之前提高的高选择性,即SQL语句选取表中较少行的原则

二、InnoDB存储引擎的Cardinality的技术实现

  • MySQL数据库有各种不同的存储引擎,每种存储引擎对于B+树索引的实现又各不相同,所以对Cardinality的统计是放在存储引擎层进行的
  • 此外需要考虑到的是:
  • 在生产环境中,索引的更新操作可能是非常频繁的,如果在每次索引发生操作时就对其进行Cardinality的统计,那么会给数据库带来很大的负担
  • 因此数据库对于Cardinality的统计都是通过采样的方法来完成的
  • 在InnoDB存储引擎中,Cardinality统计信息的更新发生在两个操作中:INSERT和UPDATE

更新Cardinality的策略

  • ①表中1/16的数据已发生变化:自上次统计Cardinality信息后,表中1/16的数据已经发生过变化,这时需要更新Cardinality信息
  • ②stat_modified_counter>2000000000:如果表中某一行数据频繁地进行更新(update)操作,这时表中的数据实际并没有增加,实际发生变化的还是这一行,此时策略①就无效了。因此在InnoDB内部有一个计数器stat_modified_counter,用来表示发生变化的次数,当stat_modified_counter大于2000000000时,则同样需要更新Cardinality信息

更新Cardinality的数据采样

  • 上面已经说过了,Cardinality的更新是根据采样的方法来实现的
  • 默认InnoDB对8个叶子节点进行采样
  • 采样的过程如下:
  • 取得B+树索引中叶子节点的数量,记为A
  • 随机取得B+树索引中的8个叶子节点。统计每个页不同记录的个数,即为P1,P2,P3....,P8
  • 根据采样信息给出Cardinality的预估值:Cardinality=(P1+P2+P3+...+P8)*A/8
  • 通过上面可知:
  • Cardinality值是通过对8个叶子阶段预估得到的,不是一个实际精确的值
  • 再者,每次对Cardinality值的统计,都是通过随机取8个叶子节点得到的,因此每次得到的Cardinality值可能是不同的

此种情况会导致Cardinality每回查看都一样

  • 如果表足够小,表的叶子节点数小于或者等于8个。此时即使是随机采样,那么会没采取到的都是这些页,因此最终Cardinality值都是一样的

三、演示案例

  • 例如,对orderDetails表(这个表的数据足够多,叶子节点数目大于8)进行一次索引Cardinality值查看,第一次的结果如下图所示

MySQL(InnoDB剖析):26---B+树索引之Cardinality值_存储引擎_04

MySQL(InnoDB剖析):26---B+树索引之Cardinality值_存储引擎_05

  • 第二次再运行语句的时候,Cardinality的值改变了

MySQL(InnoDB剖析):26---B+树索引之Cardinality值_子节点_06

  • 因为每次Cardinality都是随机取8个叶子节点进行分析的,所以即使表没有发生变化,但是每次Cardinality还是变化了,这就是Cardinality随机采样的结果

四、与Cardinality有关的一些参数

innodb_stats_sample_pages

  • 在InnoDB1.2版本之前,可以通过这个参数来设置统计Cardinality时每次采样页的数量
  • 默认值为8

MySQL(InnoDB剖析):26---B+树索引之Cardinality值_存储引擎_07

innodb_stats_method

  • 该参数用来判断如何对待索引中出现的NULL值记录
  • 可取值有:
  • nulls_equal(默认值):表示将NULL值记录视为相等的记录
  • nulls_unequal:将NULL值记录视为不同的记录
  • nulls_ignored:忽略NULL值记录
  • 例如:如果某页索引记录为NULL、NULL、1、2、2、3、3、3
  • 参数设为nulls_equal时,该页中不同的记录数为4
  • 参数设为nulls_unequal时,该页中不同的记录数为5
  • 参数设为nulls_ignored时,该页中不同的记录数为3

MySQL(InnoDB剖析):26---B+树索引之Cardinality值_数据_08

其他参数

  • 当执行SQL语句ANALYZE TABLE、SHOW TABLE STATUS、SHOW INDEX以及访问information_schema数据库下的tbales和statistics表时会导致InnoDB存储引擎重新计算索引的Cardinality值。若表中的数据量非常大,并且表中存在多个辅助索引时,执行上述这些操作可能会非常慢。虽然用户可能并不希望去更新Cardinality值
  • InnoDB 1.2版本提供了下面的参数对Cardinality统计进行设置,参数如下:

MySQL(InnoDB剖析):26---B+树索引之Cardinality值_Cardinality值_09

举报

相关推荐

MySQL索引(B树、B+树)

Mysql B+树索引

0 条评论