当我使用具有 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 表中有两行,我想同时删除这两行。为此,我同时发送两个请求。两个线程接受这个请求并同时调用 deleteAspectType() 方法。这两个线程首先运行带锁的选择查询然后删除。但是其中一个线程无法删除,并抛出异常:
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 没有索引时我会得到这个异常,为什么当隔离级别 Read Committed 或隔离级别 SERIALIZABLE 有索引时这段代码工作正常?