AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 278476
Accepted
Paul Draper
Paul Draper
Asked: 2020-10-22 17:41:23 +0800 CST2020-10-22 17:41:23 +0800 CST 2020-10-22 17:41:23 +0800 CST

PostgreSQL更新与删除+插入有什么“特别”

  • 772

我的理解是更新锁定一个元组,将其标记为已删除,然后添加一个新元组。

换句话说,更新 = 删除 + 插入。

至少我是这么相信的。但它似乎与 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“身份”?它是什么?

postgresql concurrency
  • 1 1 个回答
  • 2958 Views

1 个回答

  • Voted
  1. Best Answer
    Laurenz Albe
    2020-10-22T23:34:24+08:002020-10-22T23:34:24+08:00

    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 plus HEAP_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_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锁消失后该行没有有效版本,什么都没有被删除。

    因此,虽然在许多方面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 */
    
    • 15

相关问题

  • 我可以在使用数据库后激活 PITR 吗?

  • 运行时间偏移延迟复制的最佳实践

  • 存储过程可以防止 SQL 注入吗?

  • PostgreSQL 中 UniProt 的生物序列

  • PostgreSQL 9.0 Replication 和 Slony-I 有什么区别?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve