innodb and heap table

昨日和同事探讨了一下pg与mysql,发现对于mysql的innodb存储引擎了解甚少,正好周末,深入学习了解了一下,整理一下这两天学到的东西。

概念介绍

InnoDB:MySQL 的主要存储引擎
heap table:postgres数据库P表在物理上的存储(Heap File)

数据存储方式

innodb 底层存储

innodb是一个基于聚簇索引(Clustered Index)的存储引擎。

数据存储方式

  • 聚簇索引存储表数据
    • 表的主键索引就是数据本身的物理存储顺序。
    • 每个页(page)一般 16KB,页内数据按主键顺序排列。
    • 非主键列数据和行信息都在叶子节点。
  • 二级索引
    • 非主键索引存储的是主键值而不是行指针。
    • 查询非主键列时,需要先通过二级索引拿到主键,再去主键聚簇索引里查数据(回表,row lookup)。

页与数据组织

  • 数据按 B+ 树页组织。
  • 插入/更新可能导致页分裂。

postgres heap table

数据存储方式

  • 数据按插入顺序存放,没有聚簇索引。
  • 行标识符 TID(tuple id) 用于指向表的页和行。
  • 页(默认 8KB)内存储多行 tuple。
  • 没有聚簇索引,非索引扫描可能会更慢,但插入非常快。

索引

  • B+ 树、哈希、GIN、GiST 等索引都是附加结构,存储独立于表。
  • 回表操作是通过 TID 定位行。

对比

特性 InnoDB PostgreSQL 堆表
数据组织 聚簇索引(主键决定物理顺序) 堆表,顺序插入
主键访问 快(顺序扫描/范围查询) 需要 B+ 树索引或全表扫描
二级索引访问 回表(先索引后主键查行) 回表(索引 -> TID -> 行)
插入性能 如果主键顺序插入快,否则可能页分裂 快,顺序写入,几乎不分裂
更新/删除 支持 in-place 更新,但大行更新可能迁移 创建新行,旧行留存,需要 vacuum
MVCC Undo log + hidden columns xmin/xmax + heap tuple
表膨胀 自动管理页空间 容易膨胀,需要 vacuum
查询优化 聚簇索引优化范围查询 多依赖索引,或者全表扫描

因为pg的存储结构导致访问数据,多了许多随机io:从索引到数据文件。导致pg的查询性能不如innnodb

pg的一些优化

CLUSTER 命令(物理重排)

1
CLUSTER t USING idx_id;

把表物理顺序按照索引顺序重排 —— 效果类似 InnoDB 聚簇索引。
缺点:

  • 是离线操作;
  • 之后新插入的行会破坏顺序(除非周期性 recluster)

索引仅扫描(Index Only Scan)

如果查询的字段都在索引里且可见性检查通过(可见性 map 中标记为 all-visible),可以不访问堆表,直接从索引返回结果。

BRIN 索引

BRIN 全称是 Block Range INdex,是一种 非常轻量级的索引,设计理念与 B-Tree 不同:
不是存储每一行的索引它只存储堆表的一段范围(block range)内的最小值和最大值等摘要信息。每个 BRIN 索引条目覆盖 多个物理数据页(例如 128 个 8KB 页面 = 1MB 的行数据)。
通过这些范围信息,可以快速排除不可能匹配的块,再去 heap 查找具体 tuple。
简单理解:BRIN 是“粗粒度索引”,通过块范围(block range)而非单行建立索引
非常适合 大表 + 顺序或局部相关数据:大表 + 顺序或局部相关数据: