为啥 SELECT FOR UPDATE SKIP LOCKED 时 PostgreSQL 会抛出并发更新错误?
Posted
技术标签:
【中文标题】为啥 SELECT FOR UPDATE SKIP LOCKED 时 PostgreSQL 会抛出并发更新错误?【英文标题】:Why PostgreSQL throws concurrent update error while SELECT FOR UPDATE SKIP LOCKED?为什么 SELECT FOR UPDATE SKIP LOCKED 时 PostgreSQL 会抛出并发更新错误? 【发布时间】:2021-09-25 00:20:45 【问题描述】:我的 Java 应用程序通过 MyBatis 与 PostgreSQL 交互。
它从多个线程执行这个请求
select *
from v_packet_unread
limit 1000
for update skip locked
有时会得到ERROR: could not serialize access due to concurrent update
。
我记得这个错误发生在乐观更新的情况下,在这里我只使用 SELECT,甚至没有 UPDATE,并且无法解释发生了什么。
v_packet_unread
- 是一个简单的视图,它连接了两个小表(每个 2 列),没有任何隐藏效果(如函数调用的触发器)。
您能帮我找出这种行为的原因以及如何避免这种行为吗?
例外:
2021-07-16 06:31:39.278 [validator-exec-5 ] [ERROR] r.c.p.Operators - Operator called default onErrorDropped
reactor.core.Exceptions$ErrorCallbackNotImplemented: org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: could not serialize access due to concurrent update
### The error may exist in database/schemas/receiver/map/PacketMapper.xml
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select * from v_packet_unread limit ? for update skip locked
### Cause: org.postgresql.util.PSQLException: ERROR: could not serialize access due to concurrent update
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: could not serialize access due to concurrent update
### The error may exist in database/schemas/receiver/map/PacketMapper.xml
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select * from v_packet_unread limit ? for update skip locked
### Cause: org.postgresql.util.PSQLException: ERROR: could not serialize access due to concurrent update
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:153)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:145)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
at jdk.proxy2/jdk.proxy2.$Proxy65.selectUnread(Unknown Source)
at ...
Caused by: org.postgresql.util.PSQLException: ERROR: could not serialize access due to concurrent update
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323)
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401)
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164)
at org.postgresql.jdbc.PgPreparedStatement.execute(PgPreparedStatement.java:153)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
at org.apache.ibatis.executor.BatchExecutor.doQuery(BatchExecutor.java:92)
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:325)
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:89)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:151)
... 25 common frames omitted
版本:
PostgreSQL 12.5 on x86_64-redhat-linux-gnu,
compiled by gcc (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5), 64-bit
dependencies:
org.mybatis:mybatis:3.5.7
org.postgresql:postgresql:42.2.20
【问题讨论】:
【参考方案1】:如果您在隔离级别为REPEATABLE READ
或更高级别的事务中运行,则可能会发生这种情况:如果您尝试锁定自事务启动以来已被不同事务同时修改的行,您将收到序列化错误。
为避免这种情况,请使用默认的READ COMMITTED
隔离级别。
【讨论】:
以上是关于为啥 SELECT FOR UPDATE SKIP LOCKED 时 PostgreSQL 会抛出并发更新错误?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我们在 Oracle SQL 中需要 SELECT FOR UPDATE?