如何防止并发事务中的数据库死锁?

时间:2016-02-21 00:21:39

标签: sql transactions database-deadlocks

情景:

交易A开始......

START TRANSACTION;
UPDATE table_name SET column_name=column_name+1 WHERE id = 1 LIMIT 1;

同时,交易B开始......

START TRANSACTION;
UPDATE table_name SET column_name=column_name+1 WHERE id = 2 LIMIT 1;
UPDATE table_name SET column_name=column_name-1 WHERE id = 1 LIMIT 1;
COMMIT;

现在,事务B正在等待第1行,它在事务A中被锁定。

交易A继续......

UPDATE table_name SET column_name=column_name-1 WHERE id = 2 LIMIT 1;
COMMIT;

现在我们有一个死锁,因此两个交易都在等待彼此解锁他们想要更新的行:'(

正如我在标题中提到的,我们如何防止RDBMS事务中的死锁?

我认为解决这种情况的唯一方法是回滚事务B并重新执行它。但是,我们怎样才能发现我们处于僵局,并立即摆脱它,我们怎样才能保证我们不会进入无限循环(例如,在非常繁重的Web应用程序中)。

如果有必要,我使用MySQL。但是欢迎任何其他RDBMS的解​​决方案 - 帮助其他人从谷歌来到这里:)

1 个答案:

答案 0 :(得分:3)

大多数数据库(如果不是全部)将自动检测到死锁,选择一个会话作为受害者,并自动回滚该会话的事务以打破僵局。例如,这是MySQL deadlock detection and rollback文档。

死锁是编程错误。避免死锁的一个简单解决方案是确保始终按特定顺序锁定行。例如,如果您有一个想要更新两个不同行的事务,请始终先更新public static void insertionSort(int[] array) { for (int top = 1; top < array.length; top++) { int temp = array[top]; // copy item that into temp variable int pos = top - 1; while (pos > 0 && array[pos] > temp) { //<----- need to change '>' to '>=' // move items that are bigger than temp up one position array[pos+1] = array[pos]; pos--; } array[pos ] = temp; // place temp into last vacated position // <------------------------need to change 'array[pos ]' to 'array[pos + 1]' } } 行,然后更大 public static boolean contains(int[] array, int val) { for (int i = 0; i < array.length; i++) { if (array[i] == val) return true; else //<---------- need to remove this return false; //<---------- need to remove this } return false; } 秒。如果您的代码始终如此,那么您至少不会遇到行级死锁。除此之外,为代码中的关键部分实现适当的序列化。具体而言,这取决于您的应用程序。