为什么 PostgreSQL 在 SELECT FOR UPDATE SKIP LOCKED 时抛出并发更新错误?

Why PostgreSQL throws concurrent update error while SELECT FOR UPDATE SKIP LOCKED?

我的 Java 应用程序通过 MyBatis 与 PostgreSQL 交互。

它从多个线程执行此请求

  select * 
  from v_packet_unread
  limit 1000
  for update skip locked

有时会 ERROR: could not serialize access due to concurrent update。 我记得这个错误发生在乐观更新的情况下,这里我只使用 SELECT,甚至没有更新,也无法解释发生了什么。

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

如果您 运行 在具有隔离级别 REPEATABLE READ 或更高级别的事务中,则可能会发生这种情况:如果您尝试锁定自事务开始以来已被不同事务同时修改的行,你会得到一个序列化错误。

为避免这种情况,请使用默认的 READ COMMITTED 隔离级别。