PostgreSQL的锁机制是数据库实现并发控制的核心组件,通过不同粒度和类型的锁协调多个事务对共享资源的访问,避免数据不一致问题,同时尽可能提升并发操作的执行效率。

PostgreSQL锁机制的核心特点
PostgreSQL的锁机制设计兼顾了数据一致性和并发性能,主要具备以下几个特点:
- 多粒度锁支持:支持从数据库、表、页到行不同粒度的锁,事务可以根据操作范围选择合适的锁粒度,减少锁冲突概率。
- 锁类型丰富:针对不同操作场景设计了多种锁类型,包括读锁、写锁、意向锁等,适配查询、更新、DDL等不同操作需求。
- 自动锁管理:大部分锁由数据库自动加锁和释放,开发者无需手动干预,仅在特殊场景下需要显式加锁。
- 等待策略灵活:支持阻塞等待、NOWAIT非阻塞、SKIP LOCKED跳过锁冲突记录等多种等待模式,适配不同业务场景。
- 死锁自动检测:内置死锁检测机制,发现死锁后会自动回滚其中一个事务,避免事务无限阻塞。
PostgreSQL常见锁类型
表级锁
表级锁作用于整张表,常见的表级锁类型及适用场景如下:
| 锁类型 | 适用操作 | 冲突说明 |
|---|---|---|
| ACCESS SHARE | SELECT查询 | 与ACCESS EXCLUSIVE锁冲突,不阻塞其他读操作 |
| ROW SHARE | SELECT FOR UPDATE/SHARE | 与EXCLUSIVE、ACCESS EXCLUSIVE锁冲突 |
| ROW EXCLUSIVE | INSERT、UPDATE、DELETE | 与SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE锁冲突 |
| SHARE | CREATE INDEX(非并发) | 与ROW EXCLUSIVE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE锁冲突 |
| ACCESS EXCLUSIVE | ALTER TABLE、DROP TABLE、TRUNCATE | 与所有其他表级锁冲突,是最严格的表级锁 |
行级锁
行级锁作用于表中的单行记录,仅在事务执行更新、删除或者显式加锁时触发,常见的行级锁有两种:
- FOR UPDATE:锁定选中的行,其他事务不能对这些行执行UPDATE、DELETE、SELECT FOR UPDATE/SHARE操作,直到当前事务提交。
- FOR SHARE:锁定选中的行,其他事务不能对这些行执行UPDATE、DELETE操作,但可以执行SELECT FOR SHARE。
行级锁的使用示例如下:
-- 对id为1的用户记录加行级更新锁 BEGIN; SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 执行更新操作 UPDATE users SET balance = balance - 100 WHERE id = 1; COMMIT;
意向锁
意向锁是表级锁的一种,用于表明事务稍后会对表中的行加哪种类型的锁,避免表级锁和行级锁之间的冲突检查需要遍历所有行。常见的意向锁包括意向共享锁(IS)和意向排他锁(IX),比如事务要对某行加FOR UPDATE锁时,会先对表加IX锁。
PostgreSQL锁等待策略
当事务请求的锁与已有锁冲突时,PostgreSQL提供了多种等待策略供开发者选择:
默认阻塞等待
如果不指定特殊策略,事务请求锁冲突时会一直阻塞,直到持有锁的事务释放锁或者超过锁等待超时时间。锁等待超时时间可以通过lock_timeout参数设置,默认值为0,表示不限制等待时间。
设置锁等待超时的示例如下:
-- 设置当前会话的锁等待超时时间为3秒 SET lock_timeout = 3000; -- 执行可能加锁的操作,超过3秒未获取到锁会报错 UPDATE users SET name = 'test' WHERE id = 1;
NOWAIT非阻塞策略
在加锁语句后添加NOWAIT关键字,如果请求的锁无法立即获取,事务会直接报错返回,不会阻塞等待。适合不允许阻塞的业务场景。
-- 尝试获取行锁,获取不到直接报错 BEGIN; SELECT * FROM users WHERE id = 1 FOR UPDATE NOWAIT; -- 如果获取锁成功再执行后续操作 UPDATE users SET balance = balance + 100 WHERE id = 1; COMMIT;
SKIP LOCKED跳过策略
在加锁语句后添加SKIP LOCKED关键字,查询时会自动跳过已经被其他事务锁定的行,只返回未被锁定的记录,适合消息队列、任务分配等场景,避免阻塞等待锁冲突的记录。
-- 从任务表中获取1条未被锁定的任务,跳过已被锁定的记录 BEGIN; SELECT * FROM task_queue WHERE status = 'pending' ORDER BY create_time LIMIT 1 FOR UPDATE SKIP LOCKED; -- 更新任务状态为处理中 UPDATE task_queue SET status = 'processing' WHERE id = 1; COMMIT;
锁相关注意事项
在使用PostgreSQL锁机制时,需要注意以下几点减少锁冲突:
- 尽量缩短事务执行时间,事务持有锁的时间越长,发生锁冲突的概率越高。
- 更新操作尽量按照相同的顺序访问表记录,减少死锁发生的可能性。
- 避免在事务中执行耗时操作,比如调用外部接口、处理大量业务逻辑,这些操作会延长锁持有时间。
- 定期监控数据库锁状态,可以通过
pg_locks系统视图查看当前锁信息,及时发现异常锁阻塞问题。
查看当前数据库锁状态的查询示例如下:
-- 查询当前所有锁信息,关联表名和事务信息
SELECT
a.pid,
a.usename,
a.application_name,
a.client_addr,
a.state,
b.relname AS table_name,
c.locktype,
c.mode,
c.granted
FROM pg_stat_activity a
JOIN pg_locks c ON a.pid = c.pid
LEFT JOIN pg_class b ON c.relation = b.oid
WHERE c.locktype != 'virtualxid'
ORDER BY a.pid;
PostgreSQL锁机制锁类型等待策略并发控制修改时间:2026-06-16 01:00:37