提问者:小点点

具有隔离级别的悲观写锁可在postgresql序列化


当我使用具有SERIALIZABLE隔离级别的悲观写锁时,我在某些情况下遇到了一些错误。

这是MappingEntity类:

@Data
@Entity(name = "asset_type_mapping")
@Table(
    name = "asset_type_mapping",
    uniqueConstraints =
            @UniqueConstraint(
                name = "UQ_MappingEntity",
                columnNames = {
                    Constants.DATA_TYPE_VALUE,
                    Constants.DATA_TYPE_NAMESPACE_INDEX,
                    Constants.TENANT_ID,
                    Constants.ASSET_TYPE_NAME
                }
            )
)
public class MappingEntity {
    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    private String id;

    @Column(name = Constants.DATA_TYPE_VALUE)
    private long dataTypeValue;

    @Column(name = Constants.DATA_TYPE_NAMESPACE_INDEX)
    private int dataTypeNamespaceIndex;

    @Column(name = Constants.ASSET_TYPE_NAME)
    private String assetTypeName;

    @Column(name = Constants.TENANT_ID)
    private String tenantId;
}

这是MappingRepository类:

public interface MappingRepository extends JpaRepository<MappingEntity, String> {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    MappingEntity findMappingEntityWithLockByTenantIdAndAssetTypeName(
            String tenantId, String assetTypeName);
}

这是我收到错误的服务代码块。

@Transactional(isolation = Isolation.SERIALIZABLE)
    public void deleteAspectType(String tenantId, String aspectTypeId) {

MappingEntity mappingEntity = mappingRepository.findMappingEntityWithLockByTenantIdAndAssetTypeName(tenantId, assetTypeName);

mappingRepository.delete(mappingEntity);

}

现在让我解释一下我的步骤。在我的数据库中,MappingEntity Table中有两行,我想同时删除这两行。为此,我同时发送两个请求。两个线程接受这个请求并同时调用deleteAbooType()方法。这两个线程首先使用lock运行select query,然后删除。但是其中一个线程无法删除,并抛出异常:

ERROR: could not serialize access due to read/write dependencies among transactions
  Detail: Reason code: Canceled on identification as a pivot, during write.
  Hint: The transaction might succeed if retried.

我很困惑。线程检索不同的行并锁定它们。当我更改为隔离级别为已提交读取时,我不接受此异常并正常工作。或者当我使用tenantId和assetTypeName添加索引此表时,隔离级别仍为SERIALIZABLE,我也不接受此异常。

请有人解释我,为什么我得到这个异常时,隔离级别SERIALIZABLE没有索引,为什么这段代码工作正常时,隔离级别读提交或隔离级别SERIALIZABLE与索引?


共1个答案

匿名用户

这种行为可能是因为如果使用索引或使用表顺序扫描,事务不会读取相同的行。Doc说

为了保证真正的可串行性,PostgreSQL使用谓词锁,这意味着它保留锁,允许它确定写入何时会对先前从并发事务读取的结果产生影响,如果它先运行的话。在PostgreSQL中,这些锁不会导致任何阻塞,因此不会在导致死锁方面发挥任何作用。它们用于识别和标记并发Serializable事务之间的依赖关系,这些事务在某些组合中可能导致序列化异常。

和:

顺序扫描总是需要关系级谓词锁。这可能导致序列化失败率增加。通过减少random_page_cost和/或增加cpu_tuple_cost来鼓励使用索引扫描可能会有所帮助。请务必权衡事务回滚和重新启动的任何减少与查询执行时间的任何整体更改。