我正在使用Azure SQL + Java + Spring Boot2。目前,我正在尝试了解发生死锁的原因。在事务中,我正在执行插入操作,稍后在同一张表上进行更新,但行不同。据我了解,SQL Server默认情况下使用行锁和read_commited隔离。
这是死锁的详细信息:
<deadlock>
<victim-list>
<victimProcess id="process2b8618644e8"/>
</victim-list>
<process-list>
<process id="process2b8618644e8" taskpriority="0" logused="352"
waitresource="KEY: 16:72057594043760640 (5f601e0257f1)" waittime="1321" ownerId="9246067"
transactionname="implicit_transaction" lasttranstarted="2019-02-17T13:03:49.683" XDES="0x2b868778458"
lockMode="U" schedulerid="2" kpid="288932" status="suspended" spid="118" sbid="0" ecid="0" priority="0"
trancount="2" lastbatchstarted="2019-02-17T13:03:49.793" lastbatchcompleted="2019-02-17T13:03:49.760"
lastattention="1900-01-01T00:00:00.760" clientapp="Microsoft JDBC Driver for SQL Server"
hostname="TZ-MacBook-Pro.local" hostpid="0" loginname="master" isolationlevel="read committed (2)"
xactid="9246067" currentdb="16" currentdbname="test" lockTimeout="4294967295" clientoption1="671088672"
clientoption2="128058">
<executionStack>
<frame procname="unknown" queryhash="0x431f2517c2d3feb8" queryplanhash="0x3a793ad664472011" line="1"
stmtstart="110" stmtend="238"
sqlhandle="0x02000000060c530e608cef3b3e4e4712f48c40d2efd9b04d0000000000000000000000000000000000000000">
unknown
</frame>
<frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1"
sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown
</frame>
</executionStack>
<inputbuf>
(@P0 int,@P1 nvarchar(4000),@P2 nvarchar(4000),@P3 int)update bug set lock=@P0, status=@P1 where
bug_id=@P2 and lock=@P3
</inputbuf>
</process>
<process id="process2b84f702108" taskpriority="0" logused="700"
waitresource="KEY: 16:72057594043760640 (185e9bfc7320)" waittime="1287" ownerId="9246090"
transactionname="implicit_transaction" lasttranstarted="2019-02-17T13:03:49.727" XDES="0x2b868764458"
lockMode="U" schedulerid="1" kpid="334812" status="suspended" spid="115" sbid="0" ecid="0" priority="0"
trancount="2" lastbatchstarted="2019-02-17T13:03:49.830" lastbatchcompleted="2019-02-17T13:03:49.790"
lastattention="1900-01-01T00:00:00.790" clientapp="Microsoft JDBC Driver for SQL Server"
hostname="TZ-MacBook-Pro.local" hostpid="0" loginname="master" isolationlevel="read committed (2)"
xactid="9246090" currentdb="16" currentdbname="test" lockTimeout="4294967295" clientoption1="671088672"
clientoption2="128058">
<executionStack>
<frame procname="unknown" queryhash="0x431f2517c2d3feb8" queryplanhash="0x3a793ad664472011" line="1"
stmtstart="110" stmtend="238"
sqlhandle="0x02000000060c530e608cef3b3e4e4712f48c40d2efd9b04d0000000000000000000000000000000000000000">
unknown
</frame>
<frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1"
sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown
</frame>
</executionStack>
<inputbuf>
(@P0 int,@P1 nvarchar(4000),@P2 nvarchar(4000),@P3 int)update bug set lock=@P0, status=@P1 where
bug_id=@P2 and lock=@P3
</inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594043760640" dbid="16" objectname="b51ce3c4-02f0-4dfa-89dd-3ab2f45beac7.dbo.bug"
indexname="bug_pk" id="lock2b8658c6f00" mode="X" associatedObjectId="72057594043760640">
<owner-list>
<owner id="process2b84f702108" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process2b8618644e8" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594043760640" dbid="16" objectname="b51ce3c4-02f0-4dfa-89dd-3ab2f45beac7.dbo.bug"
indexname="bug_pk" id="lock2b856aa7f80" mode="X" associatedObjectId="72057594043760640">
<owner-list>
<owner id="process2b8618644e8" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process2b84f702108" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
</deadlock>
我也在id列上使用了聚集索引,并且随机操作顺序没有问题。而且,当我只在事务内插入时,一切正常,没有死锁。 如果我切换到PostgreSql,则没有死锁问题。
那我为什么在Azure SQL中陷入僵局?
这是Java源代码:
@Transactional
public void bug() {
BugEntity bugEntity = bugRepo.findById("1234")
.orElseGet(() -> createBugEntity());
if (bugEntity.getStatus().equals("SomeStatus")) {
bugEntity.setStatus("NewStatus");
bugRepo.save(bugEntity);
}
}
private BugEntity createBugEntity() {
BugEntity bugEntity = BugEntity.builder()
.bugId(UUID.randomUUID().toString())
.status("SomeStatus")
.data("SomeData")
.build();
return bugRepo.save(bugEntity);
}
实体类:
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@Entity(name = "bug")
@DynamicUpdate
public class BugEntity {
@Id
String bugId;
String status;
String data;
LocalDateTime dateTime;
@Version
int lock;
}
为了重现死锁,我正在使用带有几个并行请求的加特林模拟。
先谢谢!
答案 0 :(得分:0)
导致死锁的常见原因是需要查询和索引调整的查询所涉及的数据量超出了必要。
死锁跟踪显示参数正在作为nvarchar(4000)
数据类型传递。这可能会导致varchar
列上的索引无法有效使用,因为nvarchar
的数据类型优先级比varchar
高。从应用程序数据类型推断参数数据类型时,此问题尤其常见,因为字符串通常是Unicode(例如Java和.NET语言),因此无论基础列数据类型如何,都将产生nvarchar
参数。
解决方案是对字符串使用varchar参数而不是nvarchar,除非基础列为nvarchar
。如果您不在数据库中使用nvarchar
,请按照this answer中的详细说明指定JDBC连接字符串参数sendStringParametersAsUnicode
。或者,更改应用程序代码或框架配置,以对varchar
列使用varchar
参数类型。
最佳做法是确保参数与基础数据库列的类型匹配。这将提高性能和并发性,并具有其他好处。