MySQL 把資料放在主鍵索引的葉子,PostgreSQL 放在獨立的 Heap
這兩套資料庫最本質的差別不在語法、不在功能,而在資料行實際躺在哪裡。 MySQL(InnoDB)是索引組織表(Index-Organized Table),PostgreSQL 是堆疊表(Heap Table)。 後面所有的差異——讀取路徑、寫入放大、空間膨脹——都是從這一條長出來的。
兩種放法
| MySQL (InnoDB) | PostgreSQL | |
|---|---|---|
| 資料行放在 | 主鍵 B+ Tree 的葉子節點裡(表就是索引) | 獨立的 Heap,像一堆文件丟進大桶子,沒有順序 |
| 物理排列 | 按主鍵順序排列 | 無序 |
| 索引存的是 | 主鍵索引的葉子=資料本身 | 一律是指針(CTID),指向 Heap 中的物理位置 |
| 主鍵與二級索引 | 主鍵是本體,其他是附屬 | 平權,沒有主從之分 |
主鍵查詢:差一趟
MySQL 找到主鍵索引的葉子 → 資料就在這 ← 1 趟
PG 找到索引葉子 → 拿到 CTID → 去 Heap 撈 ← 2 趟(多一次 I/O)所以用主鍵查時 MySQL 快——找到索引就等於找到資料,不需要二次跳轉。
各自的代價
- MySQL:主鍵很長或很亂(例如 UUID)會讓整個表結構碎片化,寫入效能大幅下降——因為資料是照主鍵物理排序的,亂序主鍵等於每次都插在中間
- PostgreSQL:寫入很快(直接丟進 Heap),但連主鍵查詢都要多跳一次
這條差異的下游
- 加上二級索引後,快慢會反過來 → 加上二級索引之後,主鍵查詢的快慢反過來
- 「資料在 Heap 有固定位置」正是 PG 寫入放大的根源 → PG 的寫入放大來自位置過期,不是欄位變動
- 舊版本放哪裡決定了誰會膨脹 → 舊版本放哪裡,決定了誰會膨脹
- B+ 樹的葉子為什麼放得下資料 → B-tree 靠一個節點放多個鍵把樹壓矮,換到更少的讀取次數