【面试突击】MySQL InnoDB存储引擎:B+树叶子节点能存多少数据?

文章目录

MySQL InnoDB存储引擎:B+树叶子节点能存多少数据?

前言

很多人知道MySQL使用B+树作为索引结构,但对于一个叶子节点到底能存多少行数据这个问题,往往只知道一个模糊的概念。本文将详细解析这个问题,并通过实际计算帮助你理解。

一、叶子节点存什么?

1.1 叶子节点 vs 非叶子节点

在InnoDB的B+树中,节点分为两类:

非叶子节点(包括根节点):

  • 只存储索引值 + 指针
  • 不存储完整数据
  • 作用:导航,快速定位到叶子节点

叶子节点:

  • 存储完整的数据行
  • 包含所有字段的值
  • 所有叶子节点通过双向链表连接

1.2 叶子节点的结构

叶子节点(16KB的页) ┌──────────────────────────────────────────────┐ │ 第1行:id=1 | name=张三 | age=25 | email=... │ │ 第2行:id=2 | name=李四 | age=30 | email=... │ │ 第3行:id=3 | name=王五 | age=28 | email=... │ │ ... │ │ 第N行:能存多少行,取决于每行大小 │ └──────────────────────────────────────────────┘ 

关键点:

  • 页大小固定:16KB(InnoDB默认)
  • 每行大小:根据表结构动态变化

二、计算公式

2.1 核心公式

每个叶子节点能存的行数 = 页大小 ÷ 每行数据大小 = 16384字节 ÷ 每行大小 

2.2 每行大小的构成

一行数据的大小包括:

  1. 字段数据:所有列的实际数据
  2. InnoDB行格式开销
    • 变长字段长度列表(1-2字节)
    • NULL标志位(1字节)
    • 记录头信息(5字节)
    • 隐藏列:
      • DB_TRX_ID(事务ID,6字节)
      • DB_ROLL_PTR(回滚指针,7字节)

总开销约:20-30字节

三、实际计算示例

示例1:普通用户表(每行200字节)

表结构:

CREATETABLE users ( id BIGINT,-- 8字节 name VARCHAR(50),-- 假设平均20字节 age INT,-- 4字节 email VARCHAR(100),-- 假设平均50字节 created_at DATETIME,-- 8字节statusTINYINT,-- 1字节PRIMARYKEY(id));

计算过程:

字段数据大小:8 + 20 + 4 + 50 + 8 + 1 = 91字节 行格式开销:约30字节 每行总大小:91 + 30 = 121字节 为了方便计算,估算为:200字节/行 每个叶子节点能存:16384 ÷ 200 = 81. 92 ≈ 80行 

图示:

叶子节点(16KB) ┌────────────────────────────────────────┐ │ 第1行(200字节) │ ├────────────────────────────────────────┤ │ 第2行(200字节) │ ├────────────────────────────────────────┤ │ ... │ ├────────────────────────────────────────┤ │ 第80行(200字节) │ └────────────────────────────────────────┘ 

示例2:文章表(每行1KB)

表结构:

CREATETABLE articles ( id BIGINT,-- 8字节 title VARCHAR(200),-- 假设平均100字节 content TEXT,-- 假设平均800字节 author VARCHAR(50),-- 假设平均20字节 created_at DATETIME,-- 8字节PRIMARYKEY(id));

计算过程:

字段数据大小:8 + 100 + 800 + 20 + 8 = 936字节 行格式开销:约88字节 每行总大小:1024字节 = 1KB 每个叶子节点能存:16384 ÷ 1024 = 16行 

结论:包含大字段(如TEXT)的表,每页只能存储很少的行数。

示例3:极简表(每行100字节)

表结构:

CREATETABLEsimple( id INT,-- 4字节valueINT,-- 4字节 flag TINYINT,-- 1字节PRIMARYKEY(id));

计算过程:

字段数据大小:4 + 4 + 1 = 9字节 行格式开销:约30字节 每行总大小:约40字节 估算为:100字节/行 每个叶子节点能存:16384 ÷ 100 = 163行 

四、不同表结构对比

表类型每行大小每页行数典型场景
极简表50字节327行配置表、字典表
小表100字节163行简单业务表
普通表200字节80行用户表、订单表
中等表500字节32行包含多字段的业务表
大表1KB16行文章表、带TEXT字段
超大表5KB3行包含大字段的表

最常见:每行200字节,每页80行

五、对B+树总容量的影响

5.1 以"每行200字节"为例

假设:

  • 主键:BIGINT(8字节)
  • 指针:6字节
  • 非叶子节点每个索引项:8 + 6 = 14字节
  • 非叶子节点能存指针数:16384 ÷ 14 ≈ 1170个
  • 叶子节点能存数据:16384 ÷ 200 = 80行

2层B+树:

第1层:1个根节点(1170个指针) 第2层:1170个叶子节点 总数据量 = 1170 × 80 = 93,600行 

3层B+树:

第1层:1个根节点(1170个指针) 第2层:1170个中间节点(每个1170个指针) 第3层:1170 × 1170 = 1,368,900个叶子节点 总数据量 = 1,368,900 × 80 = 109,512,000行 ≈ 1亿行 

5.2 不同行大小的影响

每行大小每页行数2层容量3层容量
100字节163行19万2.2亿
200字节80行9万1亿
500字节32行3. 7万4300万
1KB16行1.8万2100万

结论:行越大,B+树能存储的总行数越少,树的高度增加越快。

六、如何查看实际行大小

方法1:查询表统计信息

SELECT table_name, table_rows,-- 总行数 data_length,-- 数据大小(字节)ROUND(data_length / table_rows,2)AS avg_row_length -- 平均每行FROM information_schema.tablesWHERE table_schema ='your_database'AND table_name ='users';

示例输出:

table_name: users table_rows: 1000000 data_length: 200000000 avg_row_length: 200. 00 

方法2:使用SHOW TABLE STATUS

SHOWTABLESTATUSLIKE'users'\G 

示例输出:

*************************** 1. row *************************** Name: users Engine: InnoDB Version: 10 Row_format: Dynamic Rows: 1000000 Avg_row_length: 200 Data_length: 200000000 

七、优化建议

7.1 减小行大小

不推荐:

CREATETABLE articles ( id BIGINT, title VARCHAR(200), content TEXT,-- ❌ 大字段放在主表PRIMARYKEY(id));

推荐:拆分表

-- 主表:常用字段CREATETABLE articles ( id BIGINT, title VARCHAR(200), author VARCHAR(50), created_at DATETIME,PRIMARYKEY(id));-- 扩展表:大字段CREATETABLE article_content ( article_id BIGINT, content TEXT,PRIMARYKEY(article_id));

好处:

  • 主表每行小,每页能存更多行
  • 只有需要content时才JOIN查询
  • B+树层数减少,查询更快

7.2 选择合适的数据类型

不推荐:浪费空间

age BIGINT-- 8字节,但年龄只需要0-200statusVARCHAR(20)-- 变长,且占用大

推荐:精确类型

age TINYINTUNSIGNED-- 1字节,范围0-255statusTINYINT-- 1字节,用枚举值(0=待审核,1=已发布... )

节省效果:

  • 1000万行,每行节省7字节(BIGINT改TINYINT)
  • 总共节省:70MB存储
  • 每页能存更多行,查询更快

7.3 避免过长的VARCHAR

不推荐:

name VARCHAR(500)-- 实际很少超过50字符

推荐:

name VARCHAR(100)-- 足够用,减少空间浪费

八、总结

核心要点

  1. 叶子节点存完整数据行,不是只存索引
  2. 每页能存行数 = 16384 ÷ 每行大小
  3. 常见估算:每行200字节,每页80行
  4. 行越大,B+树层数越高,查询越慢
  5. 优化行大小可以显著提升性能

实际应用

  • 设计表时:考虑每行大小,避免不必要的大字段
  • 性能优化:大字段拆分到单独的表
  • 容量规划:根据行大小估算表能存储的数据量
  • 分表时机:单表超过1000万-2000万行时考虑分表

参考资料

Read more

Visual C++ 6.0 中文版安装包下载及 Win11 安装教程

Visual C++ 6.0 中文版安装包下载及 Win11 安装教程

本文分享的是 Visual C++ 6.0(简称 VC++ 6.0)中文版的安装包下载及安装教程,包括在 Win11 系统下的安装和使用问题解答。如果您在安装过程中遇到任何问题,请随时留言寻求帮助! 一、安装包的下载 vc6.0 安装包下载链接:点击这里下载 https://pan.quark.cn/s/bc24c385ee87 二、安装 VC++ 6.0 等待安装完成: 点击“安装”开始安装: 创建桌面快捷方式,然后点击“下一步”。 完成后点击“下一步”。 选择 C 盘以外的盘符: 更改安装路径,建议不要安装在 C 盘(默认盘符),可以选择其他的盘符,

By Ne0inhk

GCC 14编译选项配置实战(高性能C++构建秘籍)

第一章:GCC 14编译器的新特性与构建环境准备 GCC 14作为GNU编译器集合的最新稳定版本,引入了多项增强功能,显著提升了C++标准支持、诊断能力以及优化性能。开发者在使用前需确保构建环境满足最低依赖要求,并正确配置工具链。 核心新特性概览 * 全面支持C++23关键特性,包括std::expected和模板参数冗余推导 * 增强静态分析能力,新增对未定义行为的深度检测机制 * 优化跨函数边界内联策略,提升生成代码的执行效率 * 引入更精准的调试信息格式(DWARF-5),改善GDB调试体验 构建环境搭建步骤 在主流Linux发行版中安装GCC 14,推荐通过官方源或自定义编译方式获取: # 添加Ubuntu Toolchain PPA并安装 sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install gcc-14 g++-14 # 设置默认编译器版本 sudo update-alternatives --install /usr/bin/

By Ne0inhk
【C++】B2108 图像模糊处理

【C++】B2108 图像模糊处理

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]本文专栏: C++ 文章目录 * 💯前言 * 💯题目描述 * 题目内容 * 输入格式 * 输出格式 * 示例 * 输入: * 输出: * 💯题目分析 * 问题拆解 * 💯我的做法 * 代码实现 * 代码分析 * 💯老师的做法 * 代码实现 * 代码分析 * 💯两种实现的对比 * 💯相关概念拓展 * 1. 四舍五入的实现 * 2. 二维数组的边界处理 * 💯优化建议 * 💯小结 💯前言 在C++程序设计学习中,处理二维数组与图像问题是一个重要的实践内容,能够帮助我们熟悉矩阵操作、边界条件处理以及浮点运算等核心技能。本篇文章将以一个图像模糊处理的题目为切入点,详细剖析题目背景、解题思路与两种代码实现(我的做法与老师的代码),并对两者进行深入比较与优化。同时,还将补充相关概念的详细解析,以期让读者对问题有全面而深入的理解。 C++ 参考手册 💯题目描述 题目来源于一个二维矩阵的图像模糊处理问题,其具体要求如下

By Ne0inhk

基于MISRA C++的车载软件开发实战案例

车载C++为何必须“自我约束”?一个电机控制项目的MISRA实战手记 你有没有想过,为什么在性能越来越强的车载芯片上,工程师反而要主动放弃C++里那些炫酷的功能? 比如异常处理、动态内存分配、宏函数、多重继承……这些在普通软件开发中习以为常的特性,在车规级代码里却成了“禁区”。这不是技术倒退,而是一场为了 安全与确定性 的必要妥协。 最近我参与了一个新能源车永磁同步电机控制器(PMSM)的软件开发项目,运行平台是英飞凌AURIX TC3xx系列多核MCU,系统等级达到ASIL-D——功能安全的最高级别。在这个项目中,我们不仅要用C++写高效控制算法,还得让每一行代码都经得起第三方审计的拷问。 最终的答案很明确: MISRA C++:2008 。 这不是一套可有可无的编码风格指南,而是整个软件生命周期中的“法律条文”。它不教你如何实现FOC算法,但它确保你的算法不会因为一个未初始化变量或一次非法指针访问而导致整车失控。 下面,我就以这个真实项目为背景,带你走进MISRA C++的实战世界——不是照本宣科地念规则,而是告诉你: 为什么非得这么做?不这么做会出什么事?我们又是怎么

By Ne0inhk