2019独角兽企业重金招聘Python工程师标准>>>

At Citus Data, we engineers take an active role in helping our customers scale out their Postgres database, be it for migrating an existing application or building a new application from scratch. This means we help you with distributing your relational data model—and also with getting the most out of Postgres.

One problem I often see users struggle with when it comes to Postgres is locks. While Postgres is amazing at running multiple operations at the same time, there are a few cases in which Postgres needs to block an operation using a lock. You therefore have to be careful about which locks your transactions take, but with the high-level abstractions that PostgreSQL provides, it can be difficult to know exactly what will happen. This post aims to demystify the locking behaviors in Postgres, and to give advice on how to avoid common problems.

Postgres is pretty amazing with its support for complex, concurrent, ACID transactions

PostgreSQL is unique among open source databases in its support for complex, concurrent, ACID transactions.

To make sure complex transactions can safely run at the same time, PostgreSQL uses several layers of locks to serialise changes to critical sections of the database. Transactions run concurrently until they try to acquire a conflicting lock, for example when they update the same row. In that case, the first transaction to acquire the lock can proceed, and the second one waits until the first transaction commits or aborts.

Table locks block reads &/or writes during DDL

Whenever you run a command or a query on a table, you take a lock on the table. The primary purpose of table-level locks is to block reads and/or writes when changes to the underlying table structure are made during DDL commands such as ALTER TABLE. However, not all DDL commands need to block reads or writes, some only block each other.

Whether a transaction can acquire a lock depends on whether its “lock level” conflicts with that of the holder(s) of the lock. The PostgreSQL documentation gives a detailed overview of how different lock levels conflict.

To make this more meaningful, we’ve compiled a table of the most common Postgres commands and whether they can run concurrently with each other on the same table:

Runs concurrently with SELECT INSERT
UPDATE
DELETE
CREATE INDEX CONC
VACUUM
ANALYZE
CREATE INDEX CREATE TRIGGER ALTER TABLE
DROP TABLE
TRUNCATE
VACUUM FULL
SELECT
INSERT
UPDATE
DELETE
CREATE INDEX CONC
VACUUM
ANALYZE
CREATE INDEX
CREATE TRIGGER
ALTER TABLE
DROP TABLE
TRUNCATE
VACUUM FULL

If there’s a red X in the table, it means commands block each other. For example, when you run ALTER TABLE items ADD COLUMN last_update timestamptz, the command will first block until all queries on the items table have finished, and other queries on the table will block until the ALTER TABLE is done.

If you have access to a Postgres server, it’s easy to see this in practice.

In one psql session, run:

CREATE TABLE items (key text primary key,value jsonb
);BEGIN;
ALTER TABLE items ADD COLUMN last_update timestamptz;

Now open another terminal and in psql, run:

SELECT * FROM items;
< nothing happens (waiting for a lock) >

If you go back to the first session, and run COMMIT, you’ll see that the second session finishes immediately afterwards. Locks are always kept until commit or rollback.

One other thing to be aware of is that Postgres uses lock queues. If you run an ALTER TABLEcommand then that command goes into the queue and blocks until all queries on that table are finished, but any SELECT that comes immediately after will be blocked until the ALTER TABLE is finished even if the ALTER TABLE is not yet running.

Row locks serialise concurrent changes to rows

Every row in a PostgreSQL table is also protected with a lock. Row locks only come in two flavours:

  • share
  • exclusive

Many transactions can hold a share lock concurrently, but only one transaction can hold an exclusive lock.

If concurrent transactions modify the same row, one of them will get blocked on a row lock. You can also take row-level locks without modifying anything using SELECT … FOR UPDATE or SELECT … FOR SHARE, which lets you temporarily prevent changes to a set of rows.

Can access row concurrently with: SELECT SELECT FOR SHARE SELECT FOR UPDATE UPDATE
DELETE
SELECT
SELECT FOR SHARE
SELECT FOR UPDATE
UPDATE 

Going back to our items table, there’s an easy way to see this in practice.

In one psql session, run:

INSERT INTO items VALUES ('key-1', '{"hello":"world"}');BEGIN;
SELECT * FROM items WHERE key = 'key-1' FOR UPDATE;

Now open another terminal and in psql, run:

UPDATE items SET value = '{"hello":"globe"}' WHERE key = 'key-1';
< nothing happens (waiting for a lock) >

Again, if you go back to the first session, and run COMMIT, you’ll see that the second session finishes immediately afterwards.

Making sense of pg_locks

Sometimes you notice a command is taking awfully long, but the process is not actually doing anything. In that case it might be waiting for a lock and you should have a look at pg_locks.

To see which query is waiting for a lock, the PostgreSQL wiki has a number of useful queries for displaying lock information, which give results such as:

 blocked_pid | blocking_pid |           blocked_statement           | current_statement_in_blocking_process
-------------+--------------+----------------------------------------+----------------------------------------15317 |        15206 | UPDATE test SET y = 10 WHERE x = '2'; | UPDATE test SET y = 5 WHERE x = '2';15242 |        15206 | ALTER TABLE test ADD COLUMN a int;    | UPDATE test SET y = 5 WHERE x = '2';15242 |        15317 | ALTER TABLE test ADD COLUMN a int;    | UPDATE test SET y = 10 WHERE x = '2';
(3 rows)

The table above tells us that an ALTER TABLE is blocked waiting for two UPDATEs to finish, while the UPDATE with SET y = 10 is waiting for the UPDATE with SET y = 5 to finish.

Getting visibility into locks on a distributed Citus database cluster

If you’re using the Citus extension to Postgres to shard database tables across multiple nodes, one more thing to take into consideration is that queries might get blocked on row-level locks on one of the shards on a distributed Citus worker node—and if that happens, then those queries would not show up in pg_locks on the Citus coordinator node. To get visibility into blocked operations across a distributed Citus database cluster, you can use the following query:

WITH citus_xacts AS (SELECT * FROM get_all_active_transactions() WHERE initiator_node_identifier = 0
),
citus_wait_pids AS (SELECT(SELECT process_id FROM citus_xacts WHERE transaction_number = waiting_transaction_num) AS waiting_pid,(SELECT process_id FROM citus_xacts WHERE transaction_number = blocking_transaction_num) AS blocking_pidFROMdump_global_wait_edges()
)
SELECTwaiting_pid AS blocked_pid,blocking_pid,waiting.query AS blocked_statement,blocking.query AS current_statement_in_blocking_process
FROMcitus_wait_pids
JOINpg_stat_activity waiting ON (waiting_pid = waiting.pid)
JOINpg_stat_activity blocking ON (blocking_pid = blocking.pid)blocked_pid │ blocking_pid │           blocked_statement           │ current_statement_in_blocking_process
─────────────┼──────────────┼───────────────────────────────────────┼───────────────────────────────────────147619 │       147613 │ UPDATE test SET y = 1000 WHERE x = 2; │ UPDATE test SET y = 0 WHERE x = 2;

These diagnostic queries give you a good starting point to figure out which sessions are blocking each other. You can then look at pg_stat_activity to get more information such as how long the query was running.

Postgres locks aren’t scary once you understand their behavior

Locking is one of the mechanisms that Postgres uses to safely implement concurrent ACID transactions. While most of the time you don’t even have to think about locking, you may occasionally see a transaction taking way longer than usual. Your first reaction might be: Why is it slow? But often the right question is: Why is it waiting for a lock? We hope this post will help you understand locking behaviors in Postgres and diagnose any database locking issues you might encounter.

Enjoy what you’re reading?

If you’re interested in reading more posts from our team, sign up for our monthly newsletter and get the latest content delivered straight to your inbox.

转载于:https://my.oschina.net/zhiyonghe/blog/3033787

PostgreSQL rocks, except when it blocks: Understanding locks相关推荐

  1. IOS Blocks 详细介绍

    block类似与函数指针,与函数指针的区别在于其可以捕获(capture)其定义时作用域内的变量,当在其他位置调用时仍然可以使用. block是一种Objective-C对象,其数据结构中存在isa指 ...

  2. RAC 中GES/GCS原理

    一.RAC的GES/GCS原理(1) 为了保证群集中的实例的同步,两个虚拟服务将被实现:全局排队服务(GES),它负责控制对锁的访问.全局内存服务(GCS),控制对数据块的访问. GES 是 分布式锁 ...

  3. RAC的GES/GCS原理

    一.RAC的GES/GCS原理(1) 为了保证群集中的实例的同步,两个虚拟服务将被实现:全局排队服务(GES),它负责控制对锁的访问.全局内存服务(GCS),控制对数据块的访问. GES 是 分布式锁 ...

  4. Oracle RAC的GES/GCS原理

    一.RAC的GES/GCS原理(1) 为了保证群集中的实例的同步,两个虚拟服务将被实现:全局排队服务(GES),它负责控制对锁的访问.全局内存服务(GCS),控制对数据块的访问. GES 是 分布式锁 ...

  5. poj 1383 Labyrinth

    题目连接 http://poj.org/problem?id=1383 Labyrinth Description The northern part of the Pyramid contains ...

  6. java需要记住什么英文_JAVA英语面试题(苦涩的记忆)

    JAVA英语面试题(苦涩的记忆) (2010-01-30 14:59:02) 标签: 杂谈 java 英语面试题 2008-11-13 11:051.Tell me a little about yo ...

  7. dbt2 mysql_DBT2-和DBT2相关的内容-阿里云开发者社区

    MySQL集群的flexAsynch测试工具的安装和使用 一.编译安装flexAsynch Oracle官方推荐的MySQL Cluster性能测试工具有DBT2,flexAsynch等.此处采用了f ...

  8. PostgreSQL DBA(81) - Locks(FOR UPDATE SKIP LOCKED)

    本节介绍了PostgreSQL中的FOR UPDATE SKIP LOCKED,通过该Option可以提高某些场景下的并发性能. Session 1希望从tbl中id < 100的记录中随机选择 ...

  9. PostgreSQL的高可用与数据复制方案

    2019独角兽企业重金招聘Python工程师标准>>> PostgreSQL是开源的,有多种高可用与数据复制方案,这里做个简单的比较. 一.高可用性.负载均衡.复制方案 共享磁盘失效 ...

  10. PostgreSQL高可用性、负载均衡、复制与集群方案介绍

    目录[-] 一.高可用性.负载均衡.复制的几个方案比较: 二.多节点集群方案比较 9.3官方文档(中文):http://58.58.27.50:8079/doc/html/9.3.1_zh/high- ...

最新文章

  1. 共享程序集和强命名程序集(3):强命名程序集的一些作用
  2. oracle数据库实现不同数据库之间的表格数据定期同步
  3. 合肥python培训-合肥Python测试开发全栈核心课程
  4. 电信充q币短信怎么发_王者荣耀充值中心Q币充值IOS系统游戏点券的办法_云奇付Q币寄售...
  5. Spring Boot笔记-使用RedirectAttributes重定向后也可以显示填写的信息
  6. jvm类加载过程_详解JVM类加载
  7. python/数据库操作补充—模板—Session
  8. java struts2 例子_java struts2入门学习实例--用户注册和用户登录整合
  9. C语言 execve()函数使用方法
  10. 群同态和群同构的区别_顾沛《抽象代数》1.4群的同态与同构习题解答
  11. VC++开发RTX拨打电话插件
  12. Hyperledger Explorer部署
  13. 直接杀向3G市场!华为两款新手机曝光
  14. jq ajax简单使用方法
  15. 2009年ACM-ICPC——瑞典取双
  16. 改HTML5里的input标签的required属性的提示为英文的
  17. python为什么运行不了_python为什么安装了运行不了?
  18. C#WinForm中在dataGridView中添加中文表头
  19. 快递扫地机器人被损坏_熬夜秒到的扫地机器人丢了 快递公司说最多赔几十元...
  20. WQ7033开发指南(基础篇)之1.3 如何进行单线烧录

热门文章

  1. Leetcode 76.最小覆盖子串
  2. Python正则表达式初识(七)
  3. 【剑指offer】29、顺时针打印矩阵
  4. javascript高级程序设计第二章知识点提炼
  5. ipython快捷键
  6. Nuxt.Js爬坑小记
  7. 用递归将嵌套的JSON对象遍历出来,转为二维数组 或一维数组
  8. Message no. C6015--No valuation variant found for valuation area xxxx
  9. python中的raw_input() 与 input() 比较
  10. mac无法充电解决办法!快来get下吧!