MySQL 中 Cardinality 值的介绍

本贴最后更新于 2680 天前,其中的信息可能已经事过境迁

转自 熊出陌

  1.     什么是Cardinality
    

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

SELECT * FROM student WHERE sex='M'

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

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

SELECT * FROM member WHERE usernick='David';

表 member 大约有 500W 行数据,usernick 字段上有一个唯一索引。这是如果查找用户名为 David 的用户,将得到如下执行计划

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

  1.     InnoDB存储引擎的Cardinality统计
    

建立索引的前提是高选择性。这对数据库来说才具有实际意义,那么数据库是怎样统计 Cardinality 的信息呢?因为 MySQL 数据库中有各种不同的存储引擎,而每种存储引擎对于 B+ 树索引的实现又各不相同。所以对 Cardinality 统计时放在存储引擎层进行的

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

在 InnoDB 存储引擎中,Cardinality 统计信息的更新发生在两个操作中:insert 和 update。InnoDB 存储引擎内部对更新 Cardinality 信息的策略为:

表中 1/16 的数据已发生了改变

stat_modified_counter>2000 000 000

第一种策略为自从上次统计 Cardinality 信息后,表中的 1/16 的数据已经发生过变化,这是需要更新 Cardinality 信息

第二种情况考虑的是,如果对表中某一行数据频繁地进行更新操作,这时表中的数据实际并没有增加,实际发生变化的还是这一行数据,则第一种更新策略就无法适用这种情况,故在 InnoDB 存储引擎内部有一个计数器 start_modified_counter,用来表示发生变化的次数,当 start_modified_counter>2 000 000 000 时,则同样更新 Cardinality 信息

接着考虑 InnoDB 存储引擎内部是怎样进行 Cardinality 信息统计和更新操作呢?同样是通过采样的方法。默认的 InnoDB 存储引擎对 8 个叶子节点 Leaf Page 进行采用。采用过程如下

取得 B+ 树索引中叶子节点的数量,记为 A

随机取得 B+ 树索引中的 8 个叶子节点,统计每个页不同记录的个数,即为 P1,P2....P8

通过采样信息给出 Cardinality 的预估值:Cardinality=(P1+P2+...+P8)*A/8

根据上述的说明可以发现,在 InnoDB 存储引擎中,Cardinality 值通过对 8 个叶子节点预估而得的。而不是一个实际精确的值。再者,每次对 Cardinality 值的统计,都是通过随机取 8 个叶子节点得到的,这同时有暗示了另外一个 Cardinality 现象,即每次得到的 Cardinality 值可能不同的,如

SHOW INDEX FROM OrderDetails

上述 SQL 语句会触发 MySQL 数据库对于 Cardinality 值的统计,第一次运行得到的结果如图 5-20

在上述测试过程中,并没有通过 INSERT、UPDATE 这类的操作来改变 OrderDetails 中的内容,但是当第二次运行 SHOW INDEX FROM OrderDetails 语句是,发生了变化,如图 5-21

可以看到,当第二次运行 SHOW INDEX FROM OrderDetails 语句时,表 OrderDetails 索引中的 Cardinality 值发生了变化,虽然表 OrderDetails 本身并没有发生任何变化,但是由于 Cardinality 是随机取 8 个叶子节点进行分析,所以即使表没有发生变化,用户观察到索引 Cardinality 值还是会发生变化,这本身不是 Bug,而是随机采样而导致的结果

当然,有一种情况可以使得用户每次观察到的索引 Cardinality 值是一样的。那就是表足够小,表的叶子节点树小于或者等于 8 个。这时即使随机采样,也总是会采取倒这些页,因此每次得到的 Cardinality 值是相同的

在 InnoDB1.2 版本之前,可以通过 innodb_stats_sample_pages 用来设置统计 Cardinality 时每次采样页的数量,默认为 8.同时,参数 innodb_stats_method 用来判断如何对待索引中出现 NULL 值记录。该参数默认值为 nulls_equal,表示将 NULL 值记录为相等的记录。其有效值还 nulls_unequal,nulls_ignored,分别表示将 NULL 值记录视为不同的记录和忽略 NULL 值记录。例如某夜中索引记录为 NULL、NULL、1、2、2、3、3、3,在参数 innodb_stats_method 默认设置下,该页的 Cardinality 为 4;若参数 innodb_stats_method 为 nulls_unequal,则该页的 Cardinality 为 5,若参数 innodb_stats_method 为 nulls_ignored,则 Cardinality 值为 3

当执行 ANALYZE TABLE、SHOW TABLE STATUS、SHOW INDEX 以及访问 INFORMATION_SCHEMA 架构下的表 TABLES 和 STATISTICS 时会导致 InnoDB 存储引擎会重新计算索引 Cardinality 值,若表中的数据量非常大,并且表中存在多个辅助索引时,执行上述操作可能会非常慢,虽然用户可能并不希望去更新 Cardinality 值

InnoDB1.2 版本提供了更多参数对 Cardinality 进行设置。如表

  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    692 引用 • 535 回帖
  • Cardinality
    1 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 232 回帖
  • danl
    146 关注
  • Telegram

    Telegram 是一个非盈利性、基于云端的即时消息服务。它提供了支持各大操作系统平台的开源的客户端,也提供了很多强大的 APIs 给开发者创建自己的客户端和机器人。

    5 引用 • 35 回帖
  • 安装

    你若安好,便是晴天。

    132 引用 • 1184 回帖 • 1 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    8449 引用 • 38490 回帖 • 155 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    5 引用 • 107 回帖
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    171 引用 • 512 回帖
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    21 引用 • 140 回帖 • 2 关注
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖 • 6 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 400 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖
  • Notion

    Notion - The all-in-one workspace for your notes, tasks, wikis, and databases.

    7 引用 • 40 回帖
  • GitHub

    GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。

    210 引用 • 2036 回帖
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 3 关注
  • Swagger

    Swagger 是一款非常流行的 API 开发工具,它遵循 OpenAPI Specification(这是一种通用的、和编程语言无关的 API 描述规范)。Swagger 贯穿整个 API 生命周期,如 API 的设计、编写文档、测试和部署。

    26 引用 • 35 回帖 • 5 关注
  • WiFiDog

    WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。

    1 引用 • 7 回帖 • 592 关注
  • 反馈

    Communication channel for makers and users.

    123 引用 • 913 回帖 • 250 关注
  • SQLServer

    SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。

    21 引用 • 31 回帖 • 4 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖 • 2 关注
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    407 引用 • 3578 回帖
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    71 引用 • 535 回帖 • 789 关注
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    178 引用 • 997 回帖
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 632 关注
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    170 引用 • 414 回帖 • 387 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 779 关注
  • 导航

    各种网址链接、内容导航。

    42 引用 • 175 回帖