MySQL 中 Cardinality 值的介绍

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

转自 熊出陌

  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 是最流行的关系型数据库管理系统之一。

    693 引用 • 537 回帖
  • Cardinality
    1 引用

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    21 引用 • 245 回帖 • 227 关注
  • V2Ray
    1 引用 • 15 回帖
  • 创造

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

    184 引用 • 1015 回帖 • 1 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 735 关注
  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    15 引用 • 136 回帖
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    99 引用 • 367 回帖
  • Oracle

    Oracle(甲骨文)公司,全称甲骨文股份有限公司(甲骨文软件系统有限公司),是全球最大的企业级软件公司,总部位于美国加利福尼亚州的红木滩。1989 年正式进入中国市场。2013 年,甲骨文已超越 IBM,成为继 Microsoft 后全球第二大软件公司。

    107 引用 • 127 回帖 • 341 关注
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖 • 1 关注
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 486 关注
  • Bootstrap

    Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。

    18 引用 • 33 回帖 • 644 关注
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖 • 4 关注
  • flomo

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

    6 引用 • 141 回帖 • 2 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 532 关注
  • RemNote
    2 引用 • 16 回帖 • 7 关注
  • SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。

    5 引用 • 7 回帖
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    315 引用 • 547 回帖
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 110 关注
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    95 引用 • 901 回帖
  • Sandbox

    如果帖子标签含有 Sandbox ,则该帖子会被视为“测试帖”,主要用于测试社区功能,排查 bug 等,该标签下内容不定期进行清理。

    431 引用 • 1250 回帖 • 596 关注
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 6 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    12 引用 • 5 回帖 • 637 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 246 关注
  • Office

    Office 现已更名为 Microsoft 365. Microsoft 365 将高级 Office 应用(如 Word、Excel 和 PowerPoint)与 1 TB 的 OneDrive 云存储空间、高级安全性等结合在一起,可帮助你在任何设备上完成操作。

    5 引用 • 34 回帖
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 67 回帖 • 451 关注
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    6 引用 • 26 回帖 • 547 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 4 关注