diva-notes
  • README
  • Ads
    • 定价策略
    • 广告层级
    • 归因模型
    • 买量
    • Chat GPT
    • Google
  • AI
    • 参考资料
    • Chat GPT
    • stable-diffusion-webui安装
  • Algorithm
    • 倍增
    • 并查集
    • 参考
    • 环的判断
    • 凸包
    • 蓄水池抽样
    • 最短路径
    • 最小生成树
    • KMP算法
    • Rabin-Karp算法
    • Tarjan桥算法
  • Architecture
    • Serverless
  • Career
  • CICD
    • 代码质量
    • CICD实践
  • Data Structure
    • 布谷鸟过滤器
    • 布隆过滤器
    • 浮点
    • 红黑树
    • 锁
    • LSM树
  • DB
    • My SQL
      • 隔离级别
      • 架构
      • 索引
      • 锁
      • 页结构
      • 主从同步
      • ACID
      • Log
      • MVCC
      • Questions
    • Postgres
      • 持久化
      • 对比MySQL
      • 隔离级别
      • 索引
      • Greenpulm
      • MVCC
    • 倒排索引
    • 列式存储
    • H Base
    • HDFS
    • MPP数据库选型
    • Questions
  • Distributed System
    • 分布式事务
    • 服务网格
    • BASE理论
    • CAP
    • Etcd
    • Raft协议
    • ZAB协议
  • Go
    • 1.语言基础
      • 1.CPU寄存器
      • 2-1.函数调用
      • 2-2.函数调用栈
      • 2.接口
      • 3.汇编
      • 4.调试
    • 2.编译
      • 1.编译
      • 2.词法与语法分析
      • 3.类型检查
      • 4.中间代码生成
      • 5.机器码生成
    • 3.数据结构
      • 1.数组array
      • 2.切片slice
      • 3.哈希表map
      • 4.字符串
    • 4.常用关键字
      • 1.循环
      • 2.defer
      • 3.panic和recover
      • 4.make和new
    • 5.并发编程
      • 1.上下文Context的实现
      • 2-1.runtime.sema信号量
      • 2-2.sync.Mutex的实现
      • 2-3.sync.WaitGroup
      • 2-4.sync.Once的实现
      • 2-5.sync.Map的实现
      • 2-6.sync.Cond
      • 2-7.sync.Pool的实现
      • 2-8.sync.Semaphore的实现
      • 2-9.sync.ErrGroup
      • 3.定时器Timer的实现
      • 4.Channel的实现
      • 5-1.调度-线程
      • 5-2.调度-MPG
      • 5-3.调度-程序及调度启动
      • 5-4.调度-调度策略
      • 5-5.调度-抢占
      • 6.netpoll实现
      • 7.atomic
    • 6.内存管理
      • 1-1.内存分配基础-TCmalloc
      • 1-2.内存分配
      • 2.垃圾回收
      • 3.栈内存管理
    • 参考
    • 各版本特性
    • 坑
    • Go程序性能优化
    • http.Client
    • net.http路由
    • profile采样的实现
    • Questions
    • time的设计
  • Kafka
    • 高可用
    • 架构
    • 消息队列选型
    • ISR
    • Questions
  • Network
    • ARP
    • DNS
    • DPVS
    • GET和POST
    • HTTP 2
    • HTTP 3
    • HTTPS
    • LVS的转发模式
    • NAT
    • Nginx
    • OSI七层模型
    • Protobuf
    • Questions
    • REST Ful
    • RPC
    • socket缓冲区
    • socket详解
    • TCP滑动窗口
    • TCP连接建立源码
    • TCP连接四元组
    • TCP三次握手
    • TCP数据结构
    • TCP四次挥手
    • TCP拥塞控制
    • TCP重传机制
    • UDP
  • OS
    • 磁盘IO
    • 调度
    • 进程VS线程
    • 零拷贝
    • 内存-虚拟内存
    • 内存分配
    • 用户态VS内核态
    • 中断
    • COW写时复制
    • IO多路复用
    • Questions
  • Redis
    • 安装
    • 参考
    • 高可用-持久化
    • 高可用-主从同步
    • 高可用-Cluster
    • 高可用-Sentinel
    • 缓存一致性
    • 事务
    • 数据结构-SDS
    • 数据结构-Skiplist
    • 数据结构-Ziplist
    • 数据结构
    • 数据类型-Hashtable
    • 数据类型-List
    • 数据类型-Set
    • 数据类型-Zset
    • 数据淘汰机制
    • 通信协议-RESP
    • Questions
    • Redis6.0多线程
    • Redis分布式锁
    • Redis分片
  • System Design
    • 本地缓存
    • 错误处理
    • 大文件处理
    • 点赞收藏关注
    • 短链接生成系统
    • 负载均衡
    • 高并发高可用
    • 规则引擎
    • 集卡活动
    • 秒杀系统
    • 评论系统
    • 熔断
    • 限流
    • 延迟队列
    • Docker
    • ES
    • K 8 S
    • Node.js
    • Questions
  • Work
    • Bash
    • Charles
    • Code Review
    • Ffmpeg
    • Git
    • intellij插件
    • I Term 2
    • Mac
    • mysql命令
    • Nginx
    • postgresql命令
    • Protoc
    • Ssh
    • Systemd
    • Tcp相关命令
    • Vim
Powered by GitBook
On this page
  • InnoDB 记录格式
  • 页
  • 数据区
  • 行
  • 总结
  1. DB
  2. My SQL

页结构

Previous锁Next主从同步

Last updated 2 years ago

InnoDB 记录格式

页

InnoDB 从磁盘中读取数据的最小单位是 页,一页默认 16KB。就是说,即便我们只查询一条记录,InnoDB 也会把至少 16KB 的内容从磁盘读到内存中。

页可以存放索引、数据、undo log、buffer 等。

页之间通过指针组成链表。

记录插入过程

在数据页中,当记录为空时,User Records 是不存在的。随着记录的一条条插入,会不断从 Free Space 开辟空间分配给记录

数据区

同一个表空间内的一组连续的数据页为一个 extent(区),默认区的大小为 1MB,页的大小为 16KB。16*64=1024,也就是说一个区里面会有 64 个连续的数据页。连续的 256 个数据区为一组数据区。

和广告管理里分广告组、广告计划、广告创意一样,主要是方便管理。

页分裂与页合并

这个过程类似 redis 的 quicklist。

当向写满的数据页里插入数据行时,就需要创建一个新页、移动部分行到新页、修改指针将新页和前后页串联起来。

这样逻辑上水平方向的一致性仍然满足。然而从物理存储上讲页是乱序的,而且大概率会落到不同的区。(即错位)

当你删了一行记录时,实际上记录并没有被物理删除,记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用。

当页中删除的记录达到 MERGE_THRESHOLD(默认页体积的50%),InnoDB 会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。

填充因子

MySQL 可以给表设置一个填充因子,意为给每个页预留一定空间,为以后的插入留出空闲的空间,减少页分裂的产生,但可能会造成浪费空间。

行

InnoDB 的行格式有四种,分别是 Compact、Redundant、Dynamic 和 Compressed,它们在原理上大体都是相同的。

Compact 行格式的结构大概如图所示:

变长字段长度列表

MySQL 中有些类型的字段长度是不固定的,比如 VARCHAR、TEXT 等。MySQL 在存储这些变长类型的数据时,实际上分成了两部分存储:

  1. 数据长度

  2. 真实的数据

其中数据占用的字节数就保存在「变长字段长度列表」中。它是以列的「逆序」存储表中变长字段的实际长度的。

为啥是逆序?

因为每行是通过中间那个 记录头信息 串联起来的,因此通过指针遍历时,遍历到的地址是中部的头信息的地址。这样从中部向左,就是变长字段长度列表,和NULL值列表。从中部向右,就是列的值。

如果向左读到的第一个数据长度为10,那我们就可以知道,向右应当读取10个字节,读到的就是第一列的值。

NULL 值列表

以一个「二进制位」bitmap 来表示一个允许为空的列,仍是「逆序」排列,其中 1 表示 NULL,0 表示非 NULL。

通过变长字段长度列表 + NULL 标志位,就可以精准读取到列。读取列时,先读取 NULL 标志位判断是不是 NULL,不是的话再访问变长列表获取长度即偏移量,通过偏移量即可访问到列。

记录头信息

  • delete_mask:1 位,标记该行是否被删除

  • min_rec_mask:1位,标记每层非叶子节点中的最小记录

  • n_owned:4 位,当前记录拥有的记录数

  • heap_no:13 位,当前记录在记录堆的位置

  • record_type:3 位,记录的类型(索引/数据等)

  • next_record:16 位,指向下一行

数据

数据又分隐藏列和用户真实数据。

隐藏列

包含 row_id、transaction_id、roll_pointer 三列。

row_id 是建表时若未定义主键 / Unique Key,InnoDB 会用一个全局的自增ID 作为 row_id,否则表无法按顺序存储

transaction_id 和事务有关。InnoDB 里事务进行查询时,会使用一个 readView。如果某行的事务ID > readView 的事务ID,说明该行是在查询期间由其他事务生成的,这时就不应该展示这行,避免脏读/不可重复读。

roll_pointer 是指向 undo log 的指针,主要用于 MVCC 。

真实数据

用户真实数据会以数据库指定的编码格式存在文件上。

如果数据长度超过一页,(即行溢出),会使用多页存储。

总结

  1. 数据在磁盘中的存储在物理空间上面是连续的

  2. 数据是被存放在 MySQL 设计出来的数据页上面的,数据页上面存储的才是最终的一行一行的记录

  3. 行的存储格式默认是 Compact

  4. 每一行数据都会有相应的行描述部分,描述部分有【变长字段列表】【NULL标志位】【记录头信息】

  5. 每一行都会有 next_record 指针,指向记录头和变长字段列表的中间某个位置,方便寻址

  6. 变长列表中的 varchar 列、NULL 列的描述是逆序的

参考

赐我白日梦 - MySQL数据页以及页分裂机制
WriteOnRead - InnoDB的一条记录长什么样子
zyz1992 - MySql数据是如何存储在磁盘上存储的?
img
img
img