如何在PostgreSQL事务中为SELECT FOR UPDATE设置超时时间?
在PostgreSQL中为SELECT FOR UPDATE设置3秒超时的方法
首先,我们有一个product表,结构如下:
| id | name | stock |
|---|---|---|
| 1 | Apple | 10 |
| 2 | Orange | 20 |
默认情况下,SELECT FOR UPDATE没有超时机制,当T1持有行锁时,T2会一直等待直到T1释放锁,流程如下:
| 流程 | 事务1(T1) | 事务2(T2) | 说明 |
|---|---|---|---|
| 步骤1 | BEGIN; | T1启动。 | |
| 步骤2 | BEGIN; | T2启动。 | |
| 步骤3 | SELECT stock FROM product WHERE id = 2 FOR UPDATE;返回结果:20 | T1通过SELECT FOR UPDATE读取到stock值,并持有该行的排他锁。 | |
| 步骤4 | SELECT stock FROM product WHERE id = 2 FOR UPDATE; | T2尝试获取行锁,进入等待状态。 | |
| 步骤5-97 | 等待中... | T2持续等待T1释放锁。 | |
| 步骤98 | COMMIT; | 等待中... | T1提交事务,释放行锁。 |
| 步骤99 | SELECT stock FROM product WHERE id = 2 FOR UPDATE;返回结果:20 | T2获取到行锁,成功读取数据。 | |
| 步骤100 | COMMIT; | T2提交事务。 |
要实现T2等待3秒后自动回滚,可通过以下方法:
设置lock_timeout参数
lock_timeout是PostgreSQL提供的会话级参数,用于控制等待锁的最长时间,超时后会抛出lock timeout错误,此时事务会终止,需执行回滚操作。
修改后的T2执行流程:
| 流程 | 事务1(T1) | 事务2(T2) | 说明 |
|---|---|---|---|
| 步骤1 | BEGIN; | T1启动。 | |
| 步骤2 | BEGIN; | T2启动。 | |
| 步骤3 | SELECT stock FROM product WHERE id = 2 FOR UPDATE;返回结果:20 | T1获取行锁并读取数据。 | |
| 步骤4 | SET lock_timeout = '3s'; | T2设置锁等待超时为3秒。 | |
| 步骤5 | SELECT stock FROM product WHERE id = 2 FOR UPDATE; | T2尝试获取行锁,进入等待状态。 | |
| 步骤6 | 等待中... | 等待时间未超过3秒。 | |
| 步骤7 | 抛出错误:ERROR: lock timeout | 等待时间超过3秒,触发锁超时错误。 | |
| 步骤8 | ROLLBACK; | T2执行回滚,结束事务。 | |
| 步骤100 | COMMIT; | T1提交事务,释放行锁。 |
注意事项:
lock_timeout是会话级设置,仅影响当前会话的后续操作,若需全局生效可在postgresql.conf中配置,通常建议在事务内按需设置。- 另外还有
SELECT FOR UPDATE NOWAIT(无法立即获取锁时直接报错)、SELECT FOR UPDATE SKIP LOCKED(跳过已锁定行)两种方式,但不符合等待3秒再回滚的需求。
内容的提问来源于stack exchange,提问作者Super Kai - Kazuya Ito
相关产品推荐
相关产品推荐

