Postgres中的锁:使用锁的7个技巧

你好! 下周二, “关系数据库管理系统”课程将重新开始学习,因此我们将继续发布有关该主题的有用材料。 走吧



上周,我写了关于Postgres中竞争性访问的信息 ,哪些团队互相阻塞,以及如何诊断阻塞的团队。 当然,在诊断后,您可能需要治疗。 使用Postgres,您可以用脚射击自己,但是Postgres还为您提供了避免误解的方法。 以下是一些有关如何操作和如何不执行操作的重要提示,当与用户合作时,从他们的单个Postgres数据库迁移到Citus实时创建新的分析应用程序时,这些提示非常有用。

1.切勿添加具有默认值的列


PostgreSQL的黄金法则:在生产环境中向表中添加列时, 切勿指定默认值

添加列需要非常积极的表锁定,该锁定会阻止读取和写入。 如果您添加具有默认值的列,PostgreSQL将覆盖整个表以为每一行填写默认值,在大表中可能要花费几个小时。 同时,所有请求都将被阻止,因此您的数据库将不可用。

不要这样做:

--     ,       (?) ALTER TABLE items ADD COLUMN last_update timestamptz DEFAULT now(); 

这样做得更好:

 -- select, update, insert  delete ,      () ALTER TABLE items ADD COLUMN last_update timestamptz; -- select  insert ,  update  delete ,    UPDATE items SET last_update = now(); 

或者,甚至更好的是,避免长时间更新和delete锁,而要进行小批量更新,例如:

 do { numRowsUpdated = executeUpdate( "UPDATE items SET last_update = ? " + "WHERE ctid IN (SELECT ctid FROM items WHERE last_update IS NULL LIMIT 5000)", now); } while (numRowsUpdate > 0); 

这样,您可以在对用户造成最小干扰的情况下添加和填充新列。

2.当心锁队列,使用超时


PostgreSQL中的每个锁都有一个优先级。 如果事务B尝试持有具有冲突级别的事务A已经持有的锁,则事务B将在锁定队列中等待。 现在发生了一些有趣的事情:如果另一个事务C到达,它将不仅要检查与A的冲突,还要检查与事务B和锁队列中任何其他事务的冲突。

这意味着,即使您的DDL命令能够非常快速地执行,它也可能在队列中很长一段时间,等待请求完成, 并且在它之后运行的请求将被阻止

如果您可能遇到针对表的冗长的SELECT查询,请不要这样做:

 ALTER TABLE items ADD COLUMN last_update timestamptz; 

最好这样做:

 SET lock_timeout TO '2s' ALTER TABLE items ADD COLUMN last_update timestamptz; 

如果设置lock_timeout则它在等待锁定时将不会执行DDL命令,因此将阻止请求2秒钟以上。 缺点是您的ALTER TABLE可能不会执行,但您可以稍后再试。 您可以在运行DDL命令之前查询pg_stat_activity以查看是否查询时间过长。

3.使用非阻塞索引创建


PostgreSQL的另一个黄金法则:始终使用非阻塞索引创建。
为大型数据集创建索引可能需要数小时甚至数天,而常规的CREATE INDEX命令会在命令期间锁定所有记录。 尽管它不会阻止SELECT的事实,但它仍然很糟糕,还有一种更好的方法: CREATE INDEX CONCURRENTLY

不要这样做:

 --    CREATE INDEX items_value_idx ON items USING GIN (value jsonb_path_ops); 

相反,请执行以下操作:

 --    DDL CREATE INDEX CONCURRENTLY items_value_idx ON items USING GIN (value jsonb_path_ops); 

非阻塞索引的创建有一个缺点。 如果出现问题,它不会回滚并留下不完整的(“无效”)索引。 如果发生这种情况,不用担心,只需运行
 DROP INDEX CONCURRENTLY items_value_idx 
并尝试再次创建它。

4.尽早使用主动锁


当您需要运行接收主动表锁的命令时,请尝试在事务中尽可能晚地执行此操作,以使查询可以尽可能长地继续。

例如,如果您想完全替换表的内容。 不要这样做:

 BEGIN; --     : TRUNCATE items; -  : \COPY items FROM 'newdata.csv' WITH CSV COMMIT; 

而是将数据加载到新表中,然后替换旧表:

 BEGIN; CREATE TABLE items_new (LIKE items INCLUDING ALL); --  : \COPY items_new FROM 'newdata.csv' WITH CSV --     : DROP TABLE items; ALTER TABLE items_new RENAME TO items; COMMIT; 

有一个问题:我们从一开始就没有阻止记录,并且在重新设置元素时旧的元素表可能已经更改。 为了防止这种情况,我们可以显式锁定表以进行写入,但不能进行读取:

 BEGIN; LOCK items IN EXCLUSIVE MODE; ... 

有时候,最好将障碍物掌握在自己手中。

5.添加具有最小阻塞的主键


向表中添加主键通常是一个好主意。 例如,如果要使用逻辑复制或使用Citus Warp迁移数据库。

Postgres使得使用ALTER TABLE创建主键非常容易,但是现在为主键创建一个索引,如果表很大,可能会花费很多时间,所有请求都将被阻止。

 ALTER TABLE items ADD PRIMARY KEY (id); --      

幸运的是,您可以首先使用CREATE UNIQUE INDEX CONCURRENTLY进行所有艰苦的工作,然后使用唯一索引作为主键,这是一项快速的操作。

 CREATE UNIQUE INDEX CONCURRENTLY items_pk ON items (id); --   ,     ALTER TABLE items ADD CONSTRAINT items_pk PRIMARY KEY USING INDEX items_pk; --  ,   

将主键的创建分为两个阶段实际上并不影响用户。

6.切勿使用VACUUM FULL


用户体验postgres有时可能会很棒。 尽管VACUUM FULL听起来像您要清理数据库的“灰尘”,但是更合适的命令是:

 PLEASE FREEZE MY DATABASE FOR HOURS; 

VACUUM FULL将整个表覆盖到磁盘,这可能需要数小时或数天,并且同时会阻止所有请求。 尽管VACUUM FULL有几种有效的用VACUUM FULL ,例如一个曾经很大的表,但是现在它很小,仍然占用很多空间,但这可能不是您的选择。
尽管您应该努力配置自动清除选项并使用索引来加快查询速度,但是有时您可以运行VACUUM ,但不能运行VACUUM FULL

7.通过安排命令避免死锁


如果您使用PostgreSQL已有一段时间,则很可能已经看到以下错误:

 ERROR: deadlock detected DETAIL: Process 13661 waits for ShareLock on transaction 45942; blocked by process 13483. Process 13483 waits for ShareLock on transaction 45937; blocked by process 13661. 

当并发事务以不同顺序获得相同的锁时,就会发生这种情况。 例如,一个事务执行以下命令。

 BEGIN; UPDATE items SET counter = counter + 1 WHERE key = 'hello'; --    hello UPDATE items SET counter = counter + 1 WHERE key = 'world'; --    world END; 

同时,另一个事务可以发出相同的命令,但是顺序不同。

 BEGIN UPDATE items SET counter = counter + 1 WHERE key = 'world'; --    world UPDATE items SET counter = counter + 1 WHERE key = 'hello'; --    hello END; 

如果这些事务块同时执行,则它们很可能会卡在彼此之间等待,并且永远不会结束。 Postgres大约在一秒钟内就会意识到这种情况,并将取消其中一项交易以完成另一项交易。 发生这种情况时,应查看您的应用程序,以了解是否可以确保始终以相同的顺序执行事务。 如果两个事务都先更改为hello ,然后更改world ,则第一个事务将在您可以捕获任何其他锁之前将第二个锁定为hello
分享您的提示!

我们希望这些建议对您有所帮助。 如果您还有其他建议,请随时在Slack上发推特@citusdata或我们活跃的Citus用户社区。

我们提醒您,在几个小时内会有一个开放日 ,我们将详细讨论即将开设的课程的计划。

Source: https://habr.com/ru/post/zh-CN452986/


All Articles