PostgreSQL更新與刪除+插入有什麼“ 特別”
我的理解是更新鎖定一個元組,將其標記為已刪除,然後添加一個新元組。
換句話說,更新 = 刪除 + 插入。
至少我是這麼相信的。但它似乎與 MVCC 中的 delete+insert 的更新有根本的不同。
設置:
CREATE TABLE example (a int PRIMARY KEY, b int); INSERT INTO example VALUES (1, 1);
方法一:更新
-- session A session B BEGIN; UPDATE example SET b = 2 WHERE a = 1; DELETE FROM example WHERE a = 1; COMMIT; -- now there are 0 rows in table example (1 row was deleted by session B)
方法二:刪除插入
-- session A session B BEGIN; DELETE FROM example WHERE a = 1; INSERT INTO example VALUES (1, 2); DELETE FROM example WHERE a = 1; COMMIT; -- now there is 1 row in table example (0 rows deleted by session B)
因此
UPDATE example SET b = 2 WHERE a = 1;
不同於
DELETE FROM example WHERE a = 1; INSERT INTO example VALUES (1, 2);
**我如何理解更新的 MVCC 本質?**元組是否具有在更新期間保留的某種 MVCC“身份”?它是什麼?
UPDATE
是的,和DELETE
+之間有區別INSERT
。讓我們使用
pageinspect
擴展來查看元組和元組標頭。如果你想重複我的實驗,你必須在兩者之間刪除並重新創建表。此外,如果您在檢查行之前選擇了行,則可能會有其他標誌(提示位)。
infomask2
和的含義infomask
可以在 中找到src/include/access/htup_details.h
,請參閱答案末尾的引文。之後
UPDATE
:SELECT lp, t_xmin, t_xmax, t_ctid, t_infomask2, t_infomask, t_attrs FROM heap_page_item_attrs(get_raw_page('example', 0), 'example'); lp | t_xmin | t_xmax | t_ctid | t_infomask2 | t_infomask | t_attrs ----+--------+--------+--------+-------------+------------+------------------------------- 1 | 380943 | 380944 | (0,2) | 16386 | 256 | {"\\x01000000","\\x02000000"} 2 | 380944 | 0 | (0,2) | 32770 | 10240 | {"\\x01000000","\\x02000000"} (2 rows)
- 第一個元組是死元組。它
t_ctid
已更改為指向更新的版本。這是關鍵點之一,所以讓我擴展一下:
ctid
元組的 是塊號和“行指針”(lp
在查詢結果中的組合。t_ctid
通常是多餘的,但在這種情況下它用於指向新的行版本。這是原始元組和更新版本之間的連結。
t_infomask2
是 2(列數)加上HEAP_HOT_UPDATED
,所以這一行收到了HOT 更新(塊中有足夠的空間,並且沒有索引)。t_infomask
是HEAP_XMIN_COMMITTED
(提示位)。
- 第二個元組是新版本。
t_infomask2
是 2 plusHEAP_ONLY_TUPLE
,所以這是“僅堆元組”,只能通過ctid
舊版本的更新訪問。t_infomask
是HEAP_XMAX_INVALID
(真的,它是 0)加上HEAP_UPDATED
(這是更新版本)。
DELETE
+之後INSERT
:SELECT lp, t_xmin, t_xmax, t_ctid, t_infomask2, t_infomask, t_attrs FROM heap_page_item_attrs(get_raw_page('example', 0), 'example'); lp | t_xmin | t_xmax | t_ctid | t_infomask2 | t_infomask | t_attrs ----+--------+--------+--------+-------------+------------+------------------------------- 1 | 380958 | 380961 | (0,1) | 8194 | 256 | {"\\x01000000","\\x02000000"} 2 | 380961 | 0 | (0,2) | 2 | 2048 | {"\\x01000000","\\x02000000"} (2 rows)
- 同樣,第一個元組是死元組。
t_infomask2
is 2 plusHEAP_KEYS_UPDATED
(這是一個刪除或更新的元組),並且t_infomask
isHEAP_XMIN_COMMITTED
(元組在被刪除之前是有效的)。
- 第二個元組是插入的:
t_infomask2
is 2 plus,t_infomask
isHEAP_XMAX_INVALID
(it is 0), 所以這是一個新的元組。觀察到的差異的解釋:
在
READ COMMITTED
隔離級別,事務總是看到行的最新送出版本。會話 B 中的該DELETE
行必須鎖定該行並被會話 A 中的UPDATE
or阻塞。DELETE
該文件解釋了釋放鎖時會發生什麼:
UPDATE
,DELETE
,SELECT FOR UPDATE
, 和SELECT FOR SHARE
命令的行為與SELECT
在搜尋目標行方面:他們只會找到在命令開始時間送出的目標行。但是,這樣的目標行在找到時可能已經被另一個並發事務更新(或刪除或鎖定)。在這種情況下,可能的更新程序將等待第一個更新事務送出或回滾(如果它仍在進行中)。如果第一個更新程序回滾,則其效果被否定,第二個更新程序可以繼續更新最初找到的行。如果第一個更新程序送出,如果第一個更新程序刪除了該行,則第二個更新程序將忽略該行,否則它將嘗試將其操作應用於該行的更新版本。命令的搜尋條件(WHERE
子句)被重新評估以查看該行的更新版本是否仍然匹配搜尋條件。如果是這樣,則第二個更新程序使用該行的更新版本繼續其操作。
UPDATE
在新舊行版本之間有聯繫的情況下,PostgreSQL 鎖定並刪除新行版本,而在DELETE
+的情況下,INSERT
鎖消失後行沒有有效版本,什麼都沒有被刪除。因此,雖然在許多方面
UPDATE
和DELETE
+INSERT
在 PostgreSQL 中非常相似,但它們並不相同:在第二種情況下,刪除的行和插入的行之間沒有聯繫。附錄:和的
infomask
含義infomask2
t_infomask
:/* * information stored in t_infomask: */ #define HEAP_HASNULL 0x0001 /* has null attribute(s) */ #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ #define HEAP_HASOID 0x0008 /* has an object-id field */ #define HEAP_XMAX_KEYSHR_LOCK 0x0010 /* xmax is a key-shared locker */ #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ #define HEAP_XMAX_LOCK_ONLY 0x0080 /* xmax, if valid, is only a locker */ /* xmax is a shared locker */ #define HEAP_XMAX_SHR_LOCK (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK) #define HEAP_LOCK_MASK (HEAP_XMAX_SHR_LOCK | HEAP_XMAX_EXCL_LOCK | \ HEAP_XMAX_KEYSHR_LOCK) #define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */ #define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */ #define HEAP_XMIN_FROZEN (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID) #define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */ #define HEAP_XMAX_INVALID 0x0800 /* t_xmax invalid/aborted */ #define HEAP_XMAX_IS_MULTI 0x1000 /* t_xmax is a MultiXactId */ #define HEAP_UPDATED 0x2000 /* this is UPDATEd version of row */ #define HEAP_MOVED_OFF 0x4000 /* moved to another place by pre-9.0 * VACUUM FULL; kept for binary * upgrade support */ #define HEAP_MOVED_IN 0x8000 /* moved from another place by pre-9.0 * VACUUM FULL; kept for binary * upgrade support */ #define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN) #define HEAP_XACT_MASK 0xFFF0 /* visibility-related bits */
t_infomask2
:/* * information stored in t_infomask2: */ #define HEAP_NATTS_MASK 0x07FF /* 11 bits for number of attributes */ /* bits 0x1800 are available */ #define HEAP_KEYS_UPDATED 0x2000 /* tuple was updated and key cols * modified, or tuple deleted */ #define HEAP_HOT_UPDATED 0x4000 /* tuple was HOT-updated */ #define HEAP_ONLY_TUPLE 0x8000 /* this is heap-only tuple */ #define HEAP2_XACT_MASK 0xE000 /* visibility-related bits */