MySQL 中 Cardinality 值的介绍

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

转自 熊出陌

  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 引用

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Logseq

    Logseq 是一个隐私优先、开源的知识库工具。

    Logseq is a joyful, open-source outliner that works on top of local plain-text Markdown and Org-mode files. Use it to write, organize and share your thoughts, keep your to-do list, and build your own digital garden.

    7 引用 • 69 回帖 • 7 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 350 关注
  • Laravel

    Laravel 是一套简洁、优雅的 PHP Web 开发框架。它采用 MVC 设计,是一款崇尚开发效率的全栈框架。

    19 引用 • 23 回帖 • 738 关注
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖
  • BND

    BND(Baidu Netdisk Downloader)是一款图形界面的百度网盘不限速下载器,支持 Windows、Linux 和 Mac,详细介绍请看这里

    107 引用 • 1281 回帖 • 36 关注
  • 知乎

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

    10 引用 • 66 回帖
  • MyBatis

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

    173 引用 • 414 回帖 • 365 关注
  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    497 引用 • 934 回帖 • 1 关注
  • GraphQL

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

    4 引用 • 3 回帖 • 11 关注
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    26115 引用 • 108426 回帖
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 644 关注
  • V2Ray
    1 引用 • 15 回帖 • 4 关注
  • Swagger

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

    26 引用 • 35 回帖 • 4 关注
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    10 引用 • 54 回帖 • 181 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖 • 3 关注
  • 招聘

    哪里都缺人,哪里都不缺人。

    188 引用 • 1057 回帖
  • Electron

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

    15 引用 • 136 回帖
  • OpenShift

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

    14 引用 • 20 回帖 • 661 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 1 关注
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    37 引用 • 157 回帖
  • 自由行
    2 关注
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    92 引用 • 752 回帖 • 2 关注
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    230 引用 • 1432 回帖
  • 倾城之链
    23 引用 • 66 回帖 • 166 关注
  • abitmean

    有点意思就行了

    33 关注
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 728 关注