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
4

4 回答 4

3

您可能会因死锁而遇到严重的性能问题

SELECT 将获得一个页面上的共享锁,然后 DELETE 将尝试将这些锁升级为独占锁。

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

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

于 2010-04-05T21:17:08.693 回答
1

整个事情对我来说都很奇怪。选择的目的是什么?它一事无成。编写删除以选择所需的记录。并发用户的问题是他们将尝试插入相同的记录,因为您对值进行了硬编码,因此可能会遇到您可能对 tid 或 tid、pid 组合具有的唯一约束。

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

于 2010-04-05T21:15:10.063 回答
0

如果您使用的是 oracle 或 postgres,您真的应该提及。此外,您应该明确指定您的锁定,而不是依赖默认行为。它们可能会随着其他数据库或数据库版本而改变。

于 2010-04-05T21:13:13.413 回答
0

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

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

于 2010-04-06T06:45:30.093 回答