2010-04-05 51 views
3

如果由READ COMMITTED的默认隔离级别中的并发用户执行,那么以下事务可能会出错?选择,插入,删除时的事务隔离

BEGIN TRANSACTION 

SELECT * FROM t WHERE pid = 10 and r between 40 and 60 
-- ... this returns tid = 1, 3, 5 
-- ... process returned data ... 
DELETE FROM t WHERE tid in (1, 3, 5) 
INSERT INTO t (tid, pid, r) VALUES (77, 10, 35) 
INSERT INTO t (tid, pid, r) VALUES (78, 10, 37) 
INSERT INTO t (tid, pid, r) VALUES (79, 10, 39) 

COMMIT 

回答

3

你可以具有死锁

严重的性能问题的SELECT将获得一个网页上的共享锁,然后删除将尝试升级这些锁独占锁。

如果另一个用户正在执行相同的查询,它可能会在另一个用户同时获取同一页面上的共享锁。然后,当试图升级到独占锁时,它将等待所有其他共享锁被释放。另一个也将等待所有共享锁被释放。两者都会有一个共享锁,并等待另一个共享锁释放该共享锁,以便它本身可以获得排他锁。其他查询也会堆积起来,试图做同样的事情,很快就会开始检测到死锁,查询将开始取消并回滚。根据查询的频率,数据库引擎的死锁检测可能不会像新进程一样快速地查询查询,这意味着所有查询都不会成功。

您需要在select中添加一些提示,以请求从开始获得排它锁。或者您可以将事务选择移到事务外部,并在其他语句的条件中使用并发冲突检测。

+0

谢谢。这是我正在寻找的信息。 – Bradford 2010-04-06 18:35:39

0

如果您使用的是oracle或postgres,您应该提一提。 另外,您应该明确指定您的锁定,而不是依赖于默认行为。 他们可能会更改其他数据库或数据库版本。

+0

我使用的是Oracle作为数据库 – Bradford 2010-04-06 18:29:37

1

这件事对我来说很奇怪。选择的目的是什么?它没有完成任何事情。写一个删除来选择你想要的记录。什么会给我一个并发用户的问题是,他们会尝试插入相同的记录,因为您对这些值进行了硬编码,因此可能会遇到您可能在tid或tid,pid组合中具有的唯一约束。

老实说,你想在这里完成什么?这看起来像是一个专门用于一次性使用的特别查询,您尝试多次运行该查询。像这样硬编码几乎总是一个坏主意。

+0

select是存在的,因为插入语句并非真正如此硬编码,它取决于从select返回的数据。以下是发生了什么:我有一个包含tid,product_id,start_date,end_date和折扣的表。当用户插入产品的日期范围和折扣时,我选择所有重叠日期,删除这些日期,然后根据某些算法插入新记录,这些算法取决于重叠选择中返回的记录。我只是想要避免使用INSERT AFTER触发器在数据库中发生重叠。 – Bradford 2010-04-06 18:33:27

0

您不会对SELECT使用锁,因此每个人都会得到相同的结果,每个人都会看到记录tid 1,3和5.每个人都会处理这些记录,每个人都会尝试删除这些记录。这不起作用,删除操作会锁定。只有一个事务可以锁定这些记录,所有其他事务都必须等待第一个事务的提交。此事务将插入新记录并提交,所有其他人将不删除任何内容(无法找到记录,没有问题)并插入新记录。这些记录具有相同的数据,是一个问题吗?

也许你想要一个SELECT ... FROM ... FOR UPDATE;来锁定你想要处理的记录。 http://www.postgresql.org/docs/8.4/interactive/sql-select.html

+0

INSERT INTO语句是插入内容的示例。它们将是动态的,取决于tid和pid - 与删除相同。基本上,如果另一个事务在同一时间在同一个tid和pid上运行,我希望一切都失败。所以,我想我想在选择时“锁定”,但只在pid = 10的行上进行“锁定”,以便其他事务不会同时为此条件移除或插入数据。我只是不明白锁定。我有很高的读取数量低写入。如果poss。我不希望读取挂起或因插入而失败。 – Bradford 2010-04-06 18:27:54

+0

我的意思是,我不会在这些事务之外的正常选择语句失败,因为这种类型的事务正在运行。 – Bradford 2010-04-06 18:39:11