偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

面試官:PostgreSQL的MVCC是怎樣實(shí)現(xiàn)的?

數(shù)據(jù)庫 PostgreSQL
PostgreSQL 基于數(shù)據(jù)庫表存儲(chǔ)歷史版本的方式實(shí)現(xiàn)了 MVCC,這種實(shí)現(xiàn)方式的優(yōu)點(diǎn)是讀取歷史版本更加方便,不用像 MySQL 那樣依賴額外的 Undo Log 存儲(chǔ)。缺點(diǎn)是在高并發(fā)的場(chǎng)景下,數(shù)據(jù)庫表會(huì)保存大量的過期 tuple(dead tuples),可能會(huì)導(dǎo)致表膨脹。

為了解決讀寫沖突,數(shù)據(jù)庫提供了多種手段,比如表級(jí)和行級(jí)鎖。而為了進(jìn)一步提升并發(fā)性能,引入了 MVCC(多版本并發(fā)控制)。今天來聊一聊 PostgreSQL 的 MVCC 是怎樣實(shí)現(xiàn)的。

1.隔離級(jí)別

1.1 四類問題

使用數(shù)據(jù)庫時(shí),事務(wù)的并發(fā)常常會(huì)遇到四類問題:

  • 臟讀:事務(wù) A 讀取了事務(wù) B 未提交的修改數(shù)據(jù)。如果事務(wù) B 回滾,事務(wù) A 讀取的數(shù)據(jù)就是無效的臟數(shù)據(jù)。
  • 不可重復(fù)讀:同一事務(wù)內(nèi)多次讀取同一行數(shù)據(jù),這條數(shù)據(jù)因?yàn)楸黄渌聞?wù)修改過并且已經(jīng)提交事務(wù),導(dǎo)致多次讀取到的結(jié)果不一致。
  • 幻讀:同一事務(wù)內(nèi)多次查詢同一范圍內(nèi)的數(shù)據(jù),因其他事務(wù)插入或刪除符合條件的數(shù)據(jù),導(dǎo)致事務(wù)在后面讀取到的結(jié)果集不一樣,像產(chǎn)生了幻覺。
  • 序列化異常:成功提交一個(gè)事務(wù),結(jié)果可能跟按照不同順序提交這組事務(wù)的結(jié)果不一致。

1.2 隔離級(jí)別

針對(duì)臟讀、幻讀、不可重復(fù)讀的問題,數(shù)據(jù)庫引入了 4 種標(biāo)準(zhǔn)隔離級(jí)別:

  • 串行化(Serializable):事務(wù)對(duì)數(shù)據(jù)讀寫都是串行化的。
  • 可重復(fù)讀(Repeatable Read):事務(wù)執(zhí)行過程中,多次讀取同一行數(shù)據(jù),讀取結(jié)果一致。
  • 讀已提交數(shù)據(jù)(Read Committed):事務(wù)執(zhí)行過程中,如果有其他事務(wù)修改了數(shù)據(jù)并且提交事務(wù),當(dāng)前事務(wù)可以讀取到最新提交的數(shù)據(jù)。
  • 讀未提交數(shù)據(jù)(Read Uncommitted):事務(wù)執(zhí)行過程中,可以讀取到其他事務(wù)未提交的數(shù)據(jù)。

在多數(shù)數(shù)據(jù)庫中,我們可以使用四個(gè)標(biāo)準(zhǔn)事務(wù)隔離級(jí)別中的任何一個(gè)。 但其實(shí) PostgreSQL 內(nèi)部只實(shí)現(xiàn)了三個(gè)不同的隔離級(jí)別,PostgreSQL 的 Read Uncommitted 級(jí)別跟 Read Committed 類似。這是將標(biāo)準(zhǔn)隔離級(jí)別映射到 PostgreSQL 的 MVCC 架構(gòu)的唯一合理方法。如下表格:

Isolation Level

Dirty Read

Nonrepeatable Read

Phantom Read

Serialization Anomaly

Read uncommitted

?(pg不允許)

?

?

?

Read committed

?

?

?

?

Repeatable read

?

?

?(pg不允許)

?

Serializable

?

?

?

?

這個(gè)表格也可以看出,在 Repeatable read 這個(gè)隔離級(jí)別下,PostgreSQL 是不允許出現(xiàn)幻讀的。這個(gè)也可以看出 PostgreSQL 的隔離級(jí)別比 SQL 標(biāo)準(zhǔn)有更高要求的保障。


PostgreSQL 的一些數(shù)據(jù)類型和函數(shù)對(duì)事務(wù)有特殊的規(guī)則,比如 sequence 的修改對(duì)其他事務(wù)立即可見,即使修改 sequence 的事務(wù)回滾,sequence 的修改也不能回滾。

2.MVCC 原理

我們知道,在 MySQL 中,InnoDB 采用聚簇索引保存數(shù)據(jù),MVCC 則通過數(shù)據(jù)的版本鏈來實(shí)現(xiàn),版本鏈中舊版本的數(shù)據(jù)保存在 Undo Log,數(shù)據(jù)表中只保留最新版本的數(shù)據(jù)。

PostgreSQL 采用堆表存儲(chǔ)數(shù)據(jù),MVCC 實(shí)現(xiàn)機(jī)制也有所不同。當(dāng)數(shù)據(jù)被修改時(shí),舊版本的數(shù)據(jù)仍然保存在數(shù)據(jù)表中,新版本的數(shù)據(jù)作為新的行插入。通過在每行數(shù)據(jù)中額外維護(hù)版本相關(guān)字段,實(shí)現(xiàn) MVCC。

2.1 存儲(chǔ)結(jié)構(gòu)

在 PostgreSQL 的堆表結(jié)構(gòu)中,存儲(chǔ)數(shù)據(jù)的單元是 tuple(元組)。我們可以把每個(gè) tuple 比作一行數(shù)據(jù)的一個(gè)版本,結(jié)構(gòu)如下:

圖片圖片

在用戶數(shù)據(jù)之前,會(huì)增加 8 個(gè)字段,其中 null bitmap 和 object ID 是可選,就是說會(huì)固定有 23 個(gè)字節(jié)。

  • t_xmin:最小事務(wù) id,也就是插入這一行數(shù)據(jù)的事務(wù) id。
  • t_xmax:最大事務(wù) id,也就是刪除這一行數(shù)據(jù)的事務(wù) id。
  • t_cid:CommandId,用于記錄插入或刪除操作是事務(wù)中的第幾條命令。
  • t_xvac:事務(wù) id,用來記錄 VACUUM 移動(dòng)行的版本。
  • t_ctid:數(shù)據(jù)指針,指向事務(wù)對(duì)此行數(shù)據(jù)修改的當(dāng)前版本或者更新的版本。結(jié)構(gòu)為(頁號(hào), 行偏移),例如 (0,1) 表示第 0 頁的第 1 行。
  • t_infomask2:表中字段數(shù)量和各種標(biāo)志位。
  • t_infomask:各種標(biāo)志位。
  • t_hoff:記錄用戶數(shù)據(jù)的偏移量。
  • null bitmap:記錄表中的空值,只有 HEAP_HASNULL 標(biāo)識(shí)被設(shè)置在 t_infomask,null bitmap 才會(huì)有值。如果有值,則為表中每個(gè)字段分配一個(gè) bit(1 代表非空,0 代表空),也就是說這里定義的 bit 的數(shù)量等于 t_infomask2 定義的字段數(shù)量。如果 null bitmap 沒有值,則表中所有字段都是非空。
  • object ID:兼容老版本,記錄 OID(每個(gè)表、索引、視圖、函數(shù)、類型等都會(huì)被分配一個(gè)唯一的 OID)。如果 HEAP_HASOID_OLD 標(biāo)識(shí)被設(shè)置在 t_infomask,則 object ID 就會(huì)有值。

2.2 更新操作

下面我們看一下 PostgreSQL 存儲(chǔ)結(jié)構(gòu)的使用。首先我們執(zhí)行一條插入 SQL:

insert into tb_test(id, name) value(1, 'jam');

這條 SQL 執(zhí)行的事務(wù) xid=10,執(zhí)行這個(gè)事務(wù)后,插入一條 tuple,記為 tuple1, tuple1 的值是 [t_xmin:10, t_xmax:0, t_cid:0, t_ctid: (0,1)]

  • t_xmin:10,插入這個(gè) tuple 的事務(wù) xid=10。
  • t_xmax:0,這行數(shù)據(jù)未被刪除。
  • t_cid:0,事務(wù) xid=10 這個(gè)事務(wù)的第一個(gè) SQL 命令。
  • t_ctid:設(shè)置為 (0,1),指向自身,因?yàn)檫@是該 tuple 的最新版本。

接著,我們執(zhí)行第二個(gè)事務(wù) xid=11,里面包含一條更新語句。

update tb_test set name = 'tom' where id = 1;

這條 SQL 更新這條數(shù)據(jù)的 name 為 tom,這時(shí)需要插入一條新的 tuple,記為 tuple2,tuple2 的值是 [t_xmin:11, t_xmax:0, t_cid:0, t_ctid: (0,2)]

  • t_xmin:11,插入這個(gè) tuple 的事務(wù) xid=11。
  • t_xmax:0,這行數(shù)據(jù)未被刪除。
  • t_cid:0,事務(wù) id xid=11 這個(gè)事務(wù)的第 1 個(gè) SQL 命令。
  • t_ctid:設(shè)置為 (0,2),指向自身。

這時(shí) tuple1 會(huì)變成 [t_xmin:10, t_xmax:11, t_cid:0, t_ctid: (0,2)]

  • t_xmax:11,這個(gè) tuple 被 xid=11 這個(gè)事務(wù)邏輯刪除。
  • t_ctid:設(shè)置為 (0,2),指向 tuple2。

接著再執(zhí)行第三個(gè)事務(wù) xid=12,里面包含一條刪除語句:

delete from tb_test where id = 1;

這條 SQL 刪除了 id=1 的這條數(shù)據(jù),這時(shí) tuple2 變成 [t_xmin:11, t_xmax:12, t_cid:0, t_ctid: (0,2)]

  • t_xmin:11,插入個(gè) tuple 的事務(wù) xid=11。
  • t_xmax:12,這行數(shù)據(jù)被 xid=12 這個(gè)事務(wù)刪除。
  • t_cid:0,事務(wù) id 等于 xid=12 這個(gè)事務(wù)的第 1 個(gè) SQL 命令。
  • t_ctid:(0,2),指向自身。

整個(gè)過程如下圖:

圖片圖片

2.3 可見性

當(dāng)隔離級(jí)別是 Read committed 時(shí),判斷 tuple 對(duì)一個(gè)事務(wù) x 是否可見,需要滿足下面條件:

  1. 創(chuàng)建這個(gè) tuple 的事務(wù)在事務(wù) x 中查詢語句執(zhí)行之前已經(jīng)提交;
  2. 刪除這個(gè) tuple 的事務(wù)在事務(wù) x 中查詢語句執(zhí)行之前未提交或者已經(jīng)回滾;
  3. 事務(wù) x 創(chuàng)建的 tuple,未提交也能看到。

當(dāng)隔離級(jí)別是 Repeatable Read 時(shí),可見性需要滿足創(chuàng)建這個(gè) tuple 的事務(wù)在事務(wù) x 開始之前已經(jīng)提交。事務(wù) x 開始后,即使這個(gè) tuple 被其他事務(wù)修改或刪除,這些變化對(duì)事務(wù) x 也是不可見的。

3.總結(jié)

PostgreSQL 基于數(shù)據(jù)庫表存儲(chǔ)歷史版本的方式實(shí)現(xiàn)了 MVCC,這種實(shí)現(xiàn)方式的優(yōu)點(diǎn)是讀取歷史版本更加方便,不用像 MySQL 那樣依賴額外的 Undo Log 存儲(chǔ)。缺點(diǎn)是在高并發(fā)的場(chǎng)景下,數(shù)據(jù)庫表會(huì)保存大量的過期 tuple(dead tuples),可能會(huì)導(dǎo)致表膨脹。

PostgreSQL 使用 VACUUM 機(jī)制清理過期的 tuple 數(shù)據(jù),步驟包括識(shí)別過期 tuple、清理過期 tuple、標(biāo)記過期 tuple 空間可重用、更新系統(tǒng)統(tǒng)計(jì)信息。

責(zé)任編輯:武曉燕 來源: 君哥聊技術(shù)
相關(guān)推薦

2025-07-18 07:19:00

2024-11-19 15:13:02

2023-12-27 18:16:39

MVCC隔離級(jí)別幻讀

2021-12-02 08:19:06

MVCC面試數(shù)據(jù)庫

2021-04-30 20:25:20

Spring MVCJava代碼

2024-02-04 10:08:34

2024-12-25 15:44:15

2024-10-15 10:00:06

2025-10-28 02:00:00

2025-04-14 11:41:12

RocketMQ長(zhǎng)輪詢配置

2025-06-13 02:10:00

MySQL大表業(yè)務(wù)場(chǎng)景

2025-09-03 04:00:00

小紅書Feed流系統(tǒng)

2025-02-26 12:19:52

2021-09-27 07:11:18

MySQLACID特性

2024-02-22 09:19:52

2025-04-08 00:00:00

@AsyncSpring異步

2021-02-19 10:02:57

HTTPSJava安全

2024-05-11 15:11:44

系統(tǒng)軟件部署

2025-08-04 08:05:28

2015-08-13 10:29:12

面試面試官
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)