当前位置: 首页 > news >正文

MySQL的Cardinality的庖丁解牛

MySQL 的 Cardinality(基数)是数据库优化器(Optimizer)做出“走索引”还是“全表扫描”决策的核心依据

它的本质是:索引列中唯一值数量的估算值。

  • 高基数 (High Cardinality):唯一值很多(如身份证号、UUID)。索引区分度高,查询效率高。
  • 低基数 (Low Cardinality):唯一值很少(如性别、状态码)。索引区分度低,查询效率低,甚至不如全表扫描。

如果把索引比作图书馆的目录卡片

  • Cardinality就是这张卡片背后对应了多少本不同的书
  • 如果卡片是“书名”,每本书名都不同,Cardinality 很高 -> 查书名很快。
  • 如果卡片是“出版社”,只有几家出版社,Cardinality 很低 -> 查出版社可能比直接去书架找还慢。

一、定义与计算:它不是精确计数

1. 公式

Cardinality≈总行数 (Rows)平均重复次数 \text{Cardinality} \approx \frac{\text{总行数 (Rows)}}{\text{平均重复次数}}Cardinality平均重复次数总行数(Rows)

或者更直观地理解:

  • Cardinality= 索引列中不同值 (Distinct Values)的数量。
  • 例如:表有 1000 行。
    • id列:1000 个不同值 -> Cardinality ≈ 1000。
    • gender列:2 个不同值 (M/F) -> Cardinality ≈ 2。
2. 关键特性:估算值 (Estimate)
  • InnoDB 不实时计算 Cardinality。因为每次插入/删除都重新统计全表太慢了。
  • 它通过采样 (Sampling)算法估算。
  • 结果SHOW INDEX看到的 Cardinality 可能每次都不一样,也可能不准确。

💡 核心洞察Cardinality 是一个“概率统计值”,而非“精确计数值”。优化器依赖这个近似值来做代价模型(Cost Model)判断。


二、对优化器的影响:决定执行计划

优化器使用 Cardinality 来计算选择性 (Selectivity)

Selectivity=CardinalityTotal Rows \text{Selectivity} = \frac{\text{Cardinality}}{\text{Total Rows}}Selectivity=Total RowsCardinality

1. 高选择性 (High Selectivity) -> 走索引
  • 场景SELECT * FROM users WHERE id = 1001;
  • Cardinality: 接近总行数。
  • 优化器判断:通过这个索引,我能迅速定位到极少数的行(甚至 1 行)。
  • 动作:使用refeq_ref访问类型,走索引。
2. 低选择性 (Low Selectivity) -> 全表扫描
  • 场景SELECT * FROM users WHERE gender = 'M';
  • Cardinality: 2 (假设只有男女)。
  • 优化器判断:通过这个索引,我要回表查询50% 的数据。随机 I/O 开销巨大。
  • 动作:忽略索引,使用ALL访问类型,全表扫描。
3. 范围查询 (Range)
  • 场景SELECT * FROM orders WHERE create_time > '2023-01-01';
  • 优化器判断:根据直方图或索引统计,估算满足条件的行数比例。
  • 动作:如果比例小,走索引;如果比例大(如超过 20%-30%),全表扫描。

三、统计机制:InnoDB 是如何“猜”的?

InnoDB 使用两种模式来更新 Cardinality:

1. 持久化统计 (Persistent Statistics) -默认推荐
  • 存储位置mysql.innodb_index_stats系统表。
  • 更新时机
    • 表结构变更 (ALTER TABLE)。
    • 手动执行ANALYZE TABLE
    • 当表数据变化超过一定阈值(默认 10%)。
  • 优点:重启后统计信息不丢失,稳定。
  • 缺点:如果数据剧烈变化未及时更新,统计信息会过时。
2. 瞬时统计 (Transient Statistics)
  • 存储位置:内存中。
  • 更新时机:每次打开表时,或定期后台线程更新。
  • 算法随机潜入 (Random Dive)
    • InnoDB 随机选取索引树中的 N 个叶子页(默认 8-20 个)。
    • 统计这些页中的不同值数量。
    • 推算整棵树的 Cardinality。
  • 优点:无需磁盘 I/O 读取统计表。
  • 缺点:波动大,不同次查询可能得到不同结果。

💡 核心洞察当你发现执行计划不稳定(有时走索引,有时不走),往往是瞬时统计波动导致的。切换到持久化统计并定期ANALYZE可解决此问题。


四、认知陷阱与最佳实践

陷阱 1:认为 Cardinality 必须精确
  • 误区:“为什么SHOW INDEX显示的 Cardinality 和COUNT(DISTINCT col)不一样?”
  • 真相:不一样是正常的。只要数量级正确,优化器就能做出大致正确的决定。不要为了追求精确而频繁ANALYZE TABLE,这会锁表并消耗资源。
陷阱 2:忽略联合索引的最左前缀
  • 场景:联合索引(A, B, C)
  • 真相
    • Cardinality(A):A 列的唯一值数。
    • Cardinality(A, B):A 和 B 组合后的唯一值数。
    • Cardinality(A, B, C):三者组合后的唯一值数。
    • 规律:越往后,Cardinality 通常越高(或相等)。
    • 优化:将区分度最高的列放在联合索引的前面,有助于优化器更早地过滤数据。
陷阱 3:低基数列建索引无用?
  • 误区:“性别只有两个值,建索引没用。”
  • 真相:单独建索引确实没用。但如果是联合索引的一部分,就有用。
    • 例如:(gender, age)。虽然gender区分度低,但它能把数据分成两半,然后在每一半里按age排序。对于WHERE gender='M' ORDER BY age这样的查询,非常有效。
陷阱 4:统计信息过期导致性能骤降
  • 现象:昨天还快的 SQL,今天突然慢了。
  • 原因:大量数据导入/删除后,Cardinality 未更新,优化器误判。
  • 解决
    ANALYZETABLEyour_table;
    或者开启自动更新:
    innodb_stats_persistent = ON innodb_stats_auto_recalc = ON

🚀 总结:Cardinality 全景图

维度高基数 (High)低基数 (Low)
示例ID, UUID, 手机号性别, 状态, 布尔值
选择性高 (> 0.1)低 (< 0.01)
索引效果极佳(快速定位)(可能全表扫描)
优化器行为倾向于走索引 (ref/range)倾向于全表扫描 (ALL)
建议适合做主键或独立索引适合做联合索引的前缀或后缀

终极心法

Cardinality 的本质,是“数据的区分度”。
它是优化器眼中的“地图精度”。精度高,路径规划就准;精度低,就容易迷路。
不要迷信精确值,要关注趋势和量级。
定期维护统计信息,让优化器始终拥有最新的“地图”。
于统计中见分布,于估算中见决策;以选择性为尺,解索引之牛,于查询优化中,求精准之真。

行动指令

  1. 检查现状:对核心大表执行SHOW INDEX FROM table_name;,观察各索引的 Cardinality。
  2. 对比验证:运行SELECT COUNT(DISTINCT col) FROM table;,与SHOW INDEX的结果对比,理解误差范围。
  3. 强制更新:如果发现执行计划异常,执行ANALYZE TABLE,观察 Cardinality 变化及执行计划是否回归正常。
  4. 思维升级:在设计索引时,不仅考虑“查什么”,还要考虑“区分度如何”。将高基数列前置,提升过滤效率。
http://www.jsqmd.com/news/609449/

相关文章:

  • Mach-O 文件结构
  • Equalizer APO:彻底改变你的Windows音频体验
  • 保姆级避坑指南:在只有一台能上网的服务器上,搞定Proxmox VE 7.0三节点集群和Ceph存储
  • 工业缺陷标注自动化:YOLO+SAM+云端部署的完整教程
  • 世界第一个开源可商用 .NET Office 转 PDF 工具/库 - MiniPdf环
  • 医疗AI多模态Transformer入门基础教程(非常详细),看视网膜无创查肾病看这篇就够了!
  • 2026 输氢管道品牌选购白皮书|氢能储运管线专业评测 - 外贸老黄
  • Unity游戏视觉优化:开源去马赛克插件技术指南
  • 从铝合金外壳到硅胶减震:VCU硬件设计如何实现IP67防护与EMC抗干扰?
  • WeChatMsg:本地微信聊天记录管理与分析的技术方案
  • Day15——Arrays类
  • 六自由度机械臂力控实战:从传感器标定到恒力打磨的完整流程(附MATLAB/Simulink代码)
  • 把RAG融入模型,开源MSA记住1亿Token实现永久记忆
  • Xilinx PCIe传输卡壳?手把手教你修改XDMA驱动,突破8MB数据量限制
  • 一键完整网页截图:解决长页面捕获难题的终极方案
  • PostgreSQL内核慢SQL优化分享-慢SQL收集
  • 游戏行业的测试:趣味性与稳定性的权衡
  • OpenClaw 定时任务-Cron 配置介绍
  • 知识付费小程序开发详细步骤 - 码云数智
  • hello-agent-第二章:从感知到行动,构建你的第一个智能体循环
  • 工业缺陷检测AI方案:YOLO定位+SAM分割+云端部署
  • 最新 AI 论文盘点(2026-04-07):6 篇新作看 latent reasoning 可解释性、小模型搜索代理、持久化 agent runtime,以及机器人系统如何开始认真补工程短板
  • 研一到研二:LLM实习准备的时间线规划
  • TMC9660:无需编程的智能伺服驱动解决方案,硬件集成FOC与降压转换器
  • Synology群晖Audio Station终极歌词插件:3分钟免费安装QQ音乐歌词方案
  • 2026 输氢管道品牌实力排名 君诚凭全链优势领跑氢能储运赛道 - 外贸老黄
  • JAVA教练培训课程培训教练排课系统源码的设计理念
  • Phimp.me性能优化实践:如何提升图片处理速度的10个技巧
  • 从单机到多机:手把手教你用Docker搭建跨服务器日志收集(LPG实战)
  • 突破Win11游戏联机壁垒:IPXWrapper实现经典游戏网络重生