跳到主要内容

锁

  • 当多个事务并发访问或修改同一份数据时,保证数据一致性,避免重复修改、丢失更新、或者发生其他并发问题

数据库锁通常由数据库自动管理,开发中主要需要关注:

  • 锁住什么范围;
  • 锁的读写权限;
  • 锁什么时候释放;
  • 是否会产生阻塞和死锁;
  • 是否真的需要显式加锁

数据库锁分类​

  • 锁的权限:共享锁、排他锁
    • 共享锁和排他锁由数据库自动管理,不需要你手动配置
  • 并发控制策略:乐观锁
  • 锁的生命周期:事务锁、会话级锁
  • 锁的范围:行锁、表锁

最常见的锁

  • 排他锁 X Lock
    • 只要执行修改语句,数据库通常会自动加排他锁:
  • 行锁

排他锁​

  • 排他锁主要用于修改
  • 当前事务修改这条数据时,其他事务不能同时修改它。
  • 一般不需要手动配置,数据库自动获取,并在事务提交或回滚后释放
sql
# 当前事务正在修改,其他事务不能同时修改。
UPDATE jobs
SET status = 'processing'
WHERE id = 100;

共享锁​

  • 多个事务可以一起持有,主要用于读取

行锁​

  • 锁一行或几行,只锁住表中的某一条或几条记录
sql
BEGIN;

# 数据库就会锁住 jobs 表中 id = 100 的这一行
UPDATE jobs
SET status = 'processing'
WHERE id = 100;

COMMIT;

表锁​

  • 锁住整张表
  • 不适用于并发场景,在业务场景种很少使用
sql
BEGIN;

# 锁住后,其他事务对 jobs 表的很多操作都可能等待
LOCK TABLE jobs IN EXCLUSIVE MODE;

COMMIT;

数据库锁如何配置​

数据库会自动选择和管理锁。开发中主要配置:

  • 锁等待时间
  • 事务超时时间
  • 死锁检测
  • 隔离级别
  • 数据库连接超时
  • 长事务限制

事务锁的典型场景​

  • 使用 PostgreSQL 任务表时,多个 Worker 并发领取任务。

常见锁配置​

锁语句​

语法含义常见程度
FOR UPDATE查询并锁住,准备修改非常常用
FOR SHARE查询并共享锁定较少使用
NOWAIT获取不到锁立即报错特定场景
SKIP LOCKED跳过已锁记录Worker 队列常用

理解语句​

text
FOR UPDATE
= 查询并锁住,准备修改

NOWAIT
= 锁不到就立即失败

SKIP LOCKED
= 被别人锁住就跳过

FOR UPDATE语句​

  • 查询后准备修改,先锁住这些记录,这是开发中最常见的显式加锁方式
sql
BEGIN;

SELECT *
FROM jobs
WHERE id = 100
FOR UPDATE;

UPDATE jobs
SET status = 'processing'
WHERE id = 100;

COMMIT;

常用语法组合速查​

语法含义
BEGIN开启事务
COMMIT提交事务并释放锁
ROLLBACK回滚事务并释放锁
SELECT ... FOR UPDATE查询并加行级修改锁
SELECT ... FOR SHARE查询并加共享锁
FOR UPDATE NOWAIT获取不到锁立即失败
FOR UPDATE SKIP LOCKED跳过已锁记录
UPDATE ...自动获取写锁
DELETE ...自动获取删除相关锁
LOCK TABLE ...手动锁表
SAVEPOINT创建事务保存点
SET TRANSACTION ISOLATION LEVEL设置事务隔离级别