处理和防止死锁的正确方法

时间:2016-03-31 20:48:32

标签: php mysql pdo

我已经知道当sql查询试图锁定已经锁定的行并且我当前遇到死锁时会发生死锁。这是我的SQL查询:

INSERT INTO transactions (product_id, category, C, amount, date) SELECT 'SomeProduct', 'SomeCategory', v.username, 10, '2016-3-31' FROM balance v WHERE v.username = 'SomeUsername' AND v.balance + 10 >= 0

balance是一个虚拟表,它对事务进行求和以获得用户的余额。

当有合理数量的用户使其难以测试时,通常会注意到此错误,有关如何避免死锁或任何可能的解决方案的任何提示,因为我以非常多的方式将行插入事务表中并查找解决它!

我也尝试过捕获异常,但我无法创建一个循环来重做查询,直到它完成。

2 个答案:

答案 0 :(得分:0)

一般答案

只有当您拥有两个或更多资源,两个或更多进程,并且进程以不同顺序锁定资源时,才会发生死锁。

说,进程1想要锁定资源A,然后是B,然后是C.进程2想要锁定B,然后是A,然后是C。

如果1获得A,则可能导致死锁,然后2获得B,然后1等待B,2等待A - 无限期。

谢天谢地,解决方案非常简单:如果进程需要锁定两个或多个资源,它必须在" sorted"时尚。在这个例子中, 如果进程2也得到A,那么B,然后是C,死锁永远不会发生。

具体答案

我的情况是,您似乎在一个事务中以或多或少的随机顺序锁定不同的表行。尝试找出如何使用mysql释放锁定,并确保您只持有实际需要的数量。如果您需要一次保留多个,请尝试以某种方式订购您的请求。

在不了解更多关于您的代码的情况下很难说出来......第一次谷歌攻击" mysql死锁"虽然显示了一些有前途的东西:https://www.percona.com/blog/2014/10/28/how-to-deal-with-mysql-deadlocks

答案 1 :(得分:0)

我创建了一个包含2个字段的示例表。 id有主键

MariaDB [who]> DESC mynum;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| num   | float            | YES  |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

我已经填写了100000条记录

MariaDB [who]> SELECT * FROM mynum LIMIT 10;
+----+----------+
| id | num      |
+----+----------+
|  1 |  0.47083 |
|  2 | 0.670773 |
|  3 | 0.941373 |
|  4 |  0.69455 |
|  5 | 0.648627 |
|  6 | 0.159488 |
|  7 | 0.851557 |
|  8 | 0.779321 |
|  9 | 0.341933 |
| 10 | 0.371704 |
+----+----------+
10 rows in set (0.00 sec)

MariaDB [who]> SELECT count(*) FROM mynum;
+----------+
| count(*) |
+----------+
|   100000 |
+----------+
1 row in set (0.02 sec)

现在我选择行并计算ID为+10。你看他必须阅读所有行

MariaDB [who]> EXPLAIN SELECT * FROM mynum WHERE id +10 > 20;
+------+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+------+-------------+-------+------+---------------+------+---------+------+--------+-------------+
|    1 | SIMPLE      | mynum | ALL  | NULL          | NULL | NULL    | NULL | 100464 | Using where |
+------+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)

现在我将id与常量进行比较。您可以看到只读取他们使用的行并使用索引

MariaDB [who]> EXPLAIN SELECT * FROM mynum WHERE id < 10;
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id   | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
|    1 | SIMPLE      | mynum | range | PRIMARY       | PRIMARY | 4       | NULL |    9 | Using where |
+------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)