1、什么是deadlock(死锁)?
你可能会在程序日志中,遇到过类似“ERROR: deadlock detected Detail: Process 1766 waits for ShareLock on transaction 11322; blocked by process 1788. Process 1788 waits for ShareLock on transaction 11321; blocked by process 1766” 这样的报错信息。
含义很明显,即发生了死锁。那么死锁是什么?
死锁是DBMS不能执行在特定时间点运行的所有并发事务的一种情况。
2、为什么会出现死锁?
服务器会尽力确保多个事务可以并发工作而不互相干扰,但是还是会出现互相干扰的情况。为了实现事务的特性,PG为数据库中的每个表和每一行都维护了锁,并且每次只会将锁授予第一个事务。之后出现的所有事务都不得不等待,直到第一个事务完成,并释放它所持有的锁。未没有获取锁的事务被挂起,无法继续执行。因为锁的这种机制,所以在数据库繁忙的场景,是极易发生死锁的。
3、举个死锁的例子?
发生死锁时,一个比较常见的现象就是两个事务互相等待,由于两者都在等待,没有人能取得进展。此时数据库要么无限期等待下去,要么选择终止其中一个,让另一个继续。
举一个死锁发生在行级锁(表级锁发生死锁的概率更大)的情况。
考虑两个并发事务修改一个表的情况。
第一个事务执行:
UPDATE t_data SET data = data * 10 WHERE id = 1
复制
这个语句将获取id=1的行上的行级锁,注意这个时候第一个事务还未提交,然后第二个事务执行:
UPDATE t_data SET data = data * 10 WHERE id = 2
UPDATE t_data SET data = data * 10 WHERE id = 1
复制
第一个UPDATE语句成功地获取了id=2行上的行级锁,因此它成功地更新了该行。但是,第二个UPDATE语句发现它试图更新的行(id=1的行正在被事务1锁定)已经被锁定,所以它等待获得锁的事务完成。事务2现在等待事务1完成后再继续执行。现在,事务1继续执行:
UPDATE t_data SET data = data * 10 WHERE id = 2
复制
事务1试图获取指定id=2行上的行级锁,此时它已经被事务2持有,因此它等待事务2完成。因此,事务1在事务2上阻塞,事务2在事务1上阻塞。两者互相等待,死锁发生。
4、deadlock_timeout参数?
前面我们说过在死锁发生后,数据库要么无限期等待下去(不可能允许这种情况发生),要么选择终止其中一个,让另一个继续。
那么数据库会在什么时机去终止事务呢?这个就跟deadlock_time参数相关。
此参数表示,在等待一个lock被释放的时间里,多久可以启动deadlock检查机制。
死锁检测相对昂贵,因此服务器不会在每次等待锁时都运行这个它。我们乐观地假设在生产应用中死锁是不常出现的,并且只在开始检测死锁之前等待一会儿。增加这个值就减少了浪费在无用的死锁检测上的时间,但是减慢了报告真正死锁错误的速度。 在lock动作比较多的系统里,可以适当增大deadlock_timeout。
5、怎么预防死锁或减少死锁?
死锁只有在锁存在的情况下才会发生,所以缓解死锁的一个策略就是减少锁的使用,比如说尽量减少显式锁的使用,减小锁的粒度等。
另外,想要预防死锁,一般我们要确保使用数据的所有应用程序,以一致的顺序获取多个对象上的锁。比如上面我们提到的死锁的例子,如果两个事务都以相同的顺序更新行,就不会出现互相等待的情况。
同时程序里需要实现重试机制,通过重试来恢复因死锁而中止的事务操作。
6、查询当前系统中所有活动的锁信息?
postgres=> SELECT virtualtransaction, relation::regclass, locktype, page, tuple, mode, granted, transactionid
FROM pg_locks
ORDER BY granted, virtualtransaction;
virtualtransaction | relation | locktype | page | tuple | mode | granted | transactionid
--------------------+----------+------------+------+-------+-----------------+---------+---------------
16/2062658 | pg_locks | relation | | | AccessShareLock | t |
16/2062658 | | virtualxid | | | ExclusiveLock | t |
(2 rows)
复制
关于pg中的locktype类型,
可以参考:https://www.postgresql.org/docs/current/explicit-locking.html。
参考:http://www.moioli.net/progetti/postgres-deadlocks-debugging-guidelines/
https://www.postgresql.org/docs/current/explicit-locking.html#LOCKING-DEADLOCKS