网站首页 > 技术文章 正文
概述
前面已经介绍了PG锁机制的一些概念性内容,这里主要做一些相关实验,帮助大家理解下PG的锁机制。
以下基于PG10.9进行测试。
1、创建测试表
CREATE TABLE parent ( id serial NOT NULL PRIMARY KEY, name text NOT NULL); CREATE TABLE child ( id serial NOT NULL PRIMARY KEY, parent_id int4 NOT NULL, name text NOT NULL, CONSTRAINT child_parent_fk FOREIGN KEY (parent_id) REFERENCES parent(id) );
2、简单事务测试
尝试一些简单的事务,看看锁是什么样的:
BEGIN; SELECT * FROM active_locks; -- There are no active locks yet INSERT INTO parent (name) VALUES ('Parent 1'); SELECT * FROM active_locks; COMMIT; SELECT * FROM active_locks;
可以看到在 parent 表里插入一行后,获得了 parent 表上的行独占锁。parent_id_seq 是 parent 表的主键序列。由于这种关系被选中(如表),获得了访问共享锁。
3、插入数据
咱们试着往 child 表里插点东西;
BEGIN; INSERT INTO child (parent_id, name) VALUES (1, 'Child 1 Parent 1'); COMMIT;
可以看到 parent 表上增加的行共享锁。我们看不到的是,这个插入同样获得了 parent 表上引用行的行级共享锁。并行执行两个事务我们就可以看到它了:
-- Transaction 1 BEGIN; INSERT INTO child (parent_id, name) VALUES (1, 'Child 2 Parent 1'); -- Transaction 2 BEGIN; DELETE FROM parent WHERE id = 1;
现在开始第三个会话,看看锁怎么样了:
SELECT * FROM active_locks;
DELETE 查询被阻塞了,等待事务 1 完成。我们可以看到它在元组 1 上获得了一个锁。但是如果我们看到所有的锁都是准许的(granted=t),为什么 DELETE 查询被阻塞了?这两个事务在任何关系上的锁都没有同步过。事实上,如果一个事务在某些行上持有一个锁,第二个事务请求这个锁,第二个事务会尝试获取持有此锁的事务上的共享锁。当第一个事务完成时,第二个事务将继续。这是可能的,因为每个事务都持有它自身的排他锁。我们可以看看 pg_locks 视图,这是输出(只有部分是重要的):
可以看到事务 563(pid 5690)和事务564(pid 5790)拥有它们事务标识上的排他锁,事务564 获得了事务563 上的共享锁。
4、更新子表
可以玩玩更新子表但不实际改变任何父表与相关的东西(在这个案例中是parent_id列)。
BEGIN; UPDATE child SET name = 'My new name' WHERE id = 1; SELECT * FROM active_locks; UPDATE child SET name = 'My new name' WHERE id = 1; SELECT * FROM active_locks; COMMIT;
可以看到正在执行的 UPDATE 查询不会触及任何与父表相关的东西。第一次执行后,我们可以看到,只有 child 表包含表级锁。行级锁也是如此。只有 child 表的行有更新锁。这是 Postgres 中的优化。
如果锁管理器可以从第一个查询中发现外键没有改变(没有被更新查询提及或被设置为相同的值),它不会锁定父表。但在第二个查询它会像文档描述的那样处理(它将锁定 parent 表为行共享锁定模式和涉及的行为分享模式)。这是非常危险的,因为它会导致最危险的和最难找到的死锁。不过可以在事务开头使用显式锁定以避免它。
防止死锁的最好方式,是当我们意识到它们可能在两个事务之间发生时,去按一定顺序获取行级锁(例如主键排序)和首先获取最严格的锁。一旦在 Postgres 发生死锁,可以通过中止一个参与死锁的事务来消除。
总结
对 Postgres 中锁的工作机制铭记于心非常重要。在高并发环境中死锁可能无法避免,但重要的是要知道如何发现、监控并解决它们。即使所有事情都照书而行也不一定能解决所有潜在的锁问题,但会减少它们并使其易于解决。表级锁可以通过 pg_locks 系统视图查看,但行级锁不行,所以这让调试锁更为困难,不过Postgres 的未来版本中还是有可能实现的。后面会分享更多devops和DBA方面的内容,感兴趣的朋友可以关注一下~
猜你喜欢
- 2024-10-08 干货总结:彻底搞懂MySQL数据库锁机制(上篇)
- 2024-10-08 慢雾安全团队:代币如果加了锁机制可能会威胁用户资金权限
- 2024-10-08 一文理解MySQL的锁机制与死锁排查
- 2024-10-08 深究用户模式锁的使用场景(异变结构、互锁、旋转锁)
- 2024-10-08 mysql 锁机制介绍 mysql锁原理详细
- 2024-10-08 苹果新增“休眠软件锁”机制,严防更换原装电池
- 2024-10-08 Mysql锁机制 mysql三种锁定机制
- 2024-10-08 Oracle数据库锁机制 oracle数据库 解锁
- 2024-10-08 一文由浅入深带你完全掌握MySQL的锁机制原理与应用
- 2024-10-08 让人闻风丧胆的 Mysql 锁机制 mysql锁机制应用场景
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)