使用PostgreSQL MVCC在多个表中进行事务隔离

时间:2017-02-18 19:16:05

标签: postgresql mvcc

问题摘要

这是一个关于SQL事务中查询可序列化的问题。

具体来说,我正在使用PostgreSQL。可以假设我使用的是最新版本的PostgreSQL。根据我的阅读,我相信用于支持我所做的工作的技术被称为“MultiVersion Concurrency Control”或“MVCC”。

总结一下:如果我有一个主表,并且多于一个外键链接表连接到该主表,我如何保证,对于表中的给定键和任何数字在一个事务中使用该密钥的SELECT语句,每个都是从任何链接表中选择,我将获得在开始事务时存在的数据吗?

其他问题

这个问题类似,但更广泛,问题和答案与PostgreSQL无关: Transaction isolation and reading from multiple tables on SQL Server Express and SQL Server 2005

实施例

假设我有3张桌子:

bricks
    brickworks (primary key)
    completion_time (primary key)
    has_been_sold

brick_colors
    brickworks (primary key, foreign key pointing to "bricks")
    completion_time (primary key, foreign key pointing to "bricks")
    quadrant (primary key)
    color

brick_weight
    brickworks (primary key, foreign key pointing to "bricks")
    completion_time (primary key, foreign key pointing to "bricks")
    weight

砖厂一次生产一块砖。它使砖块在其四个象限中的每一个都有不同的颜色。

有人后来分析砖块以确定它们的颜色组合,并将结果写入brick_colors表。

其他人分析砖块以确定其重量,并将结果写入brick_weight表。

在任何给定时间,现有的砖块可能有也可能没有记录的颜色,可能有也可能没有记录的重量。

应用程序存在,并且此应用程序收到有人想要购买特定块的单词(此时已通过其brickworks / completion_time复合键已知道应用程序)。

该应用程序希望在它开始查询的确切时间内选择砖的所有已知属性。

如果添加颜色或重量信息MID-TRANSACTION,应用程序不想知道它。

应用程序想要执行SEPARATE QUERIES(不是具有多个JOIN的SELECT到外键链接表,由于brick_colors表,它可能返回多行)。

这个例子非常简单;如果我的例子中包含10个外键链接表,并且其中许多或全部可以为同一个主键返回多行,那么在没有一个带有多个JOIN的SELECT的情况下执行此操作的愿望会更加清晰(就像brick_colors一样)例如我上面的内容。)

尝试解决方案

这是我到目前为止所提出的:

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY ;

-- All this statement accomplishes is telling the database what rows should be returned from the present point-in-time in future queries within the transaction
SELECT DISTINCT true
FROM bricks b
LEFT JOIN brick_colors bc ON bc.brickworks = b.brickworks AND bc.completion_time = b.completion_time
LEFT JOIN brick_weight bw ON bw.brickworks = b.brickworks AND bw.completion_time = b.completion_time
WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ;

SELECT * FROM brick_colors WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ;
SELECT * FROM brick_weight WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ;

COMMIT ;

将第一个SELECT与JOIN一起用于确保可串行化的目的似乎很浪费。

还有其他办法吗?

参考

PostgreSQL Concurrency Control

PostgreSQL Transcation Isolation

PostgreSQL SET TRANSACTION statement

1 个答案:

答案 0 :(得分:1)

这是你问题的本质:

  

我如何保证,......任意数量的SELECT语句   .....在一次交易中.......我会得到数据,因为它存在于   我开始交易的时间

这正是Repeatable Read Isolation Level所保证的:

  

仅可重复读取隔离级别查看之前提交的数据   交易开始了;它永远不会看到未提交的数据或   在并发事务执行期间提交的更改   事务。(但是,查询确实看到了之前的效果   在自己的事务中执行的更新,即使它们不是   但承诺。)这是一个更强有力的保证   这个隔离级别的SQL标准,并阻止所有的   表13-1中描述的现象。如上所述,这是   标准特别允许的,仅描述最小值   保护每个隔离级别必须提供。

     

这个级别与Read Committed的不同之处在于   可重复读取事务查看快照开始时的快照   事务,而不是当前查询的开始   交易。因此,连续的SELECT命令在单个内   事务看到相同的数据,即他们看不到所做的更改   在自己的交易开始后提交的其他交易。

一个实际的例子 - 假设我们有两个简单的表:

CREATE TABLE t1( x int );
INSERT INTO t1 VALUES (1),(2),(3);
CREATE TABLE t2( y int );
INSERT INTO t2 VALUES (1),(2),(3);

许多表格,它们的结构,主键,外键等在这里都不重要。

让我们打开第一个会话,启动可重复的读隔离级别,并运行两个简单且独立的SELECT语句:

test=# START TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION
test=# SELECT * FROM t1;
 x
---
 1
 2
 3
(3 wiersze)


test=# SELECT * FROM t2;
 y
---
 1
 2
 3
(3 wiersze)

请注意,START TRANSACTION命令会自动禁用会话中的自动提交模式。

现在在另一个会话中(启用默认自动提交模式)将一些记录插入t1

test2=# INSERT INTO t1 VALUES(10),(11);

新值被接入并自动提交(因为启用了自动提交)。

现在回到第一个会话并再次运行SELECT: test =#select * from t1;

 x
---
 1
 2
 3
(3 wiersze)

如您所见,session1(具有主动可重复读取事务)在转换开始后未看到任何更改。

让我们做相同的实验白色表t2 - 转到第二个会话并发出:

test2=# DELETE FROM t2 WHERE y = 2;
DELETE 1

现在回到第一个会话并再次运行SELECT:

test=# SELECT * FROM t2;
 y
---
 1
 2
 3
(3 wiersze)

如您所见,session1(具有主动可重复读取事务)在转换开始后没有看到任何更改。

现在,在session1中,完成发出COMMIT的事务,然后选择SELECT:

test=# SELECT * FROM t1;
 x
---
 1
 2
 3
(3 wiersze)

test=# SELECT * FROM t2;
 y
---
 1
 2
 3
(3 wiersze)

test=# COMMIT;
COMMIT

test=# select * from t1;
 x
----
  1
  2
  3
 10
 11
(5 wierszy)


test=# select * from t2;
 y
---
 1
 3
(2 wiersze)

如您所见,当可重复读取事务启动并处于活动状态时,您可以多次运行多个单独的select语句,并且所有这些select语句都会看到与事务开始时相同的稳定数据快照,其他会话中任何提交的数据。