Postgresql

PostgreSQL更新與刪除+插入有什麼“ 特別”

  • November 13, 2020

我的理解是更新鎖定一個元組,將其標記為已刪除,然後添加一個新元組。

換句話說,更新 = 刪除 + 插入。

至少我是這麼相信的。但它似乎與 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_infomaskHEAP_XMIN_COMMITTED(提示位)。

  • 第二個元組是新版本。

t_infomask2是 2 plus HEAP_ONLY_TUPLE,所以這是“僅堆元組”,只能通過ctid舊版本的更新訪問。t_infomaskHEAP_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_infomask2is 2 plus HEAP_KEYS_UPDATED(這是一個刪除或更新的元組),並且t_infomaskis HEAP_XMIN_COMMITTED(元組在被刪除之前是有效的)。

  • 第二個元組是插入的:

t_infomask2is 2 plus, t_infomaskis HEAP_XMAX_INVALID(it is 0), 所以這是一個新的元組。

觀察到的差異的解釋:

READ COMMITTED隔離級別,事務總是看到行的最新送出版本。會話 B 中的該DELETE行必須鎖定該行並被會話 A 中的UPDATEor阻塞。DELETE

該文件解釋了釋放鎖時會發生什麼:

UPDATE, DELETE, SELECT FOR UPDATE, 和SELECT FOR SHARE命令的行為與SELECT在搜尋目標行方面:他們只會找到在命令開始時間送出的目標行。但是,這樣的目標行在找到時可能已經被另一個並發事務更新(或刪除或鎖定)。在這種情況下,可能的更新程序將等待第一個更新事務送出或回滾(如果它仍在進行中)。如果第一個更新程序回滾,則其效果被否定,第二個更新程序可以繼續更新最初找到的行。如果第一個更新程序送出,如果第一個更新程序刪除了該行,則第二個更新程序將忽略該行,否則它將嘗試將其操作應用於該行的更新版本。命令的搜尋條件(WHERE子句)被重新評估以查看該行的更新版本是否仍然匹配搜尋條件。如果是這樣,則第二個更新程序使用該行的更新版本繼續其操作。

UPDATE在新舊行版本之間有聯繫的情況下,PostgreSQL 鎖定並刪除新行版本,而在DELETE+的情況下,INSERT鎖消失後行沒有有效版本,什麼都沒有被刪除。

因此,雖然在許多方面UPDATEDELETE+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 */

引用自:https://dba.stackexchange.com/questions/278476