TypeORM操作PostgreSQL时SERIALIZABLE隔离级别下insert操作不锁定问题
问题原因与解决方案
1. SERIALIZABLE隔离级别下insert不阻塞的原因
PostgreSQL的SERIALIZABLE隔离级别基于可串行化快照隔离(SSI)实现,和你观察到的update/delete行为差异源于底层实现逻辑:
- update、delete操作针对已存在的行执行,执行时会先获取对应行的行锁,当多个事务操作同一行时会在执行阶段直接触发锁冲突,出现阻塞现象,冲突严重时会直接抛出
could not serialize access due to read/write dependencies among transactions错误。 - insert操作是新增数据行,不会和其他事务已持有行锁的已有行产生冲突,因此执行阶段不会被阻塞。SSI不会在insert执行时做冲突校验,而是在事务提交阶段统一跟踪所有事务的读写依赖,只有当多个事务的操作序列无法被串行化时,才会回滚其中一个事务并抛出序列化错误。
你当前测试的两个事务分别操作新插入的行和id=1的已有行,没有产生违反可串行化的依赖,因此不会触发报错或阻塞。如果调整第二个事务为查询全表、或者插入和第一个事务唯一键重复的行,就会在提交阶段触发冲突检测。
2. 悲观锁对insert不生效的原因
TypeORM提供的pessimistic_read、pessimistic_write悲观锁,本质是将查询语句转为SELECT ... FOR SHARE、SELECT ... FOR UPDATE执行,这类行锁只会作用于查询匹配到的已存在的数据行。你要插入的行在查询阶段还不存在,自然不会被行锁覆盖,因此悲观锁无法限制insert操作,这也是常规行锁无法解决幻读问题的核心原因。
可选解决方案
如果需要控制insert操作的并发冲突,可以采用以下方案:
- 对插入数据的唯一标识字段添加数据库唯一约束,靠唯一索引冲突避免重复插入,捕获冲突错误后做业务处理或重试即可。
- 针对特定业务范围的插入操作,使用PostgreSQL的advisory锁(自定义业务锁)控制并发。
- 直接复用SERIALIZABLE隔离级别的提交校验逻辑,捕获序列化错误后做事务重试,这也是SSI机制下的标准处理方式。
内容的提问来源于stack exchange,提问作者Vital O
相关产品推荐
相关产品推荐

