Gna*_*nam 15 sql postgresql database-deadlocks
我们有一个基于网络的应用程序.应用程序中有时间限制的数据库操作(INSERT和UPDATE)需要更多时间才能完成,因此这个特定的流程已经变为Java线程,因此它不会等待(阻止)完成整个数据库操作.
我的问题是,如果超过1个用户遇到这个特定的流程,我面临PostgreSQL抛出的以下错误:
org.postgresql.util.PSQLException: ERROR: deadlock detected
Detail: Process 13560 waits for ShareLock on transaction 3147316424; blocked by process 13566.
Process 13566 waits for ShareLock on transaction 3147316408; blocked by process 13560.
Run Code Online (Sandbox Code Playgroud)
INSERT语句中始终抛出上述错误.
附加信息: 1)我在此表中定义了PRIMARY KEY.2)此表中有FOREIGN KEY引用.3)将单独的数据库连接传递给每个Java线程.
Technologies Web Server:Tomcat v6.0.10 Java v1.6.0 Servlet数据库:PostgreSQL v8.2.3连接管理:pgpool II
Ant*_*sma 24
处理死锁的一种方法是使用等待随机间隔的重试机制并尝试再次运行事务.随机间隔是必要的,以便碰撞事务不会持续地相互碰撞,导致所谓的实时锁定 - 甚至更糟糕的调试.实际上,大多数复杂的应用程序迟早需要处理事务序列化失败时需要这样的重试机制.
当然,如果你能够确定死锁的原因,通常更好的消除它,否则它会回来咬你.对于几乎所有情况,即使在死锁条件很少的情况下,以确定性顺序获得锁定或获得更粗粒度锁定的一点点吞吐量和编码开销也是值得的,以避免偶尔的大延迟命中和突然性能悬崖缩放并发时.
当你一直得到两个INSERT语句死锁时,它很可能是一个唯一的索引插入顺序问题.在两个psql命令窗口中尝试以下示例:
Thread A | Thread B
BEGIN; | BEGIN;
| INSERT uniq=1;
INSERT uniq=2; |
| INSERT uniq=2;
| block waiting for thread A to commit or rollback, to
| see if this is an unique key error.
INSERT uniq=1; |
blocks waiting |
for thread B, |
DEADLOCK |
V
Run Code Online (Sandbox Code Playgroud)
通常,解决此问题的最佳方法是找出保护所有此类事务的父对象.大多数应用程序都有一个或两个主要实体,例如用户或帐户,这些实体很适合.然后,您只需要为每个事务获取通过SELECT ... FOR UPDATE触及的主要实体上的锁定.或者如果接触了几个,那么每次都会锁定所有这些锁,但每次都按相同的顺序(按主键排序是个不错的选择).
Gre*_*ith 12
PostgreSQL的内容在Explicit Locking的文档中有所介绍."死锁"部分中的示例显示了您可能正在执行的操作.您可能没有预料到的部分是,当您更新某些内容时,会获取该行上的锁定,该锁定将持续到涉及的事务结束.如果你有多个客户端同时对多个事物进行更新,你将不可避免地遇到死锁,除非你不顾一切地阻止它们.
如果你有多个东西可以取出像UPDATE这样的隐式锁,你应该将整个序列包装在BEGIN/COMMIT事务块中,并确保你对它们获取锁定的顺序(甚至像UPDATE抓取的隐式锁)一致.到处.如果您需要更新表A中的内容然后表B,并且应用程序的一部分执行A然后B而另一部分执行B然后A,则有一天您将陷入僵局.针对同一个表的两个UPDATE同样注定要失败,除非你可以强制执行两个在客户端之间可重复的顺序.一旦您拥有要更新的记录集并且始终首先获取"较低"的记录,则按主键排序是一种常见策略.
你的INSERT在这里被责备的可能性较小,除非你像Ants已经描述过的那样违反主键,否则进入陷入僵局的情况要困难得多.
您不想做的是尝试在您的应用程序中重复锁定,这将变成巨大的可伸缩性和可靠性混乱(并且可能仍会导致数据库死锁).如果您无法在标准数据库锁定方法的范围内解决此问题,请考虑使用建议锁定工具或显式LOCK TABLE来强制执行您需要的操作.这将为您节省一个痛苦的编码世界,而不是试图将所有锁推送到客户端.如果您对表有多个更新并且无法强制执行它们发生的顺序,那么除了在执行表时锁定整个表,您别无选择; 这是唯一不会引发死锁的路线.
死锁解释说:
简而言之,正在发生的事情是特定的SQL语句(INSERT或其他)正在等待另一个语句在数据库的特定部分上释放锁定,然后才能继续.在释放此锁之前,第一个SQL语句,称之为"语句A"将不允许自己访问数据库的这一部分来完成其工作(=常规锁定情况).但是......语句A还锁定了数据库的另一部分,以确保没有其他数据库用户访问(用于读取或修改/删除,具体取决于锁的类型).现在......第二个SQL语句本身需要访问由语句A的锁定标记的数据部分.这是一个DEAD LOCK:两个语句将无限制地等待彼此.
补救措施......
这需要知道这些不同线程正在运行的特定SQL语句,并查看是否有办法:
a) removing some of the locks, or changing their types. For example, maybe the whole table is locked, whereby only a given row, or a page thereof would be necessary. b) preventing multiple of these queries to be submitted at a given time. This would be done by way of semaphores/locks (aka MUTEX) at the level of the multi-threading logic.
请注意,"b)"方法,如果没有正确实现,可能只是将SQL内的死锁情况移到程序/线程逻辑中.关键是只创建一个互斥体,首先由任何即将运行这些容易出现死锁的查询的线程获得.
| 归档时间: |
|
| 查看次数: |
15733 次 |
| 最近记录: |