Wir haben eine Anwendung, die Daten in eine Tabelle einfügt. Leider bekommen wir Deadlocks und die Deadlocks kommen nur von Inserts. Wir sehen, dass die Einfügungen Schlüsselsperren in einer anderen Reihenfolge für einen nicht gruppierten Index annehmen, was das Problem verursacht.
Warum verhalten sich die Einsätze so und was sollten wir tun, um die Deadlocks zu lindern? Jede Hilfe oder Einsicht wird geschätzt.
Im folgenden Beispiel sind nur zwei Einfügungen beteiligt, es waren jedoch bis zu 4 verschiedene Einfügungen an einem Deadlock beteiligt.
Hier ist das Deadlock-Diagramm:
<deadlock>
<victim-list>
<victimProcess id="process3ab355868" />
</victim-list>
<process-list>
<process id="process3ab355868" taskpriority="0" logused="1184" waitresource="KEY: 5:72057594043629568 (6234ed5bf036)" waittime="7493" ownerId="92332106" transactionname="implicit_transaction" lasttranstarted="2014-10-13T12:37:43.060" XDES="0x123699668" lockMode="X" schedulerid="3" kpid="3540" status="suspended" spid="89" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2014-10-13T12:37:44.333" lastbatchcompleted="2014-10-13T12:37:44.333" lastattention="1900-01-01T00:00:00.333" clientapp="Microsoft JDBC Driver for SQL Server" hostname="" hostpid="0" loginname="" isolationlevel="read committed (2)" xactid="92332106" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="278" stmtend="818" sqlhandle="0x0200000053a65d302154b91e9fee55234669030a42479c050000000000000000000000000000000000000000">
INSERT INTO table (col1, col2, col3, col4, col5, col6, col7, col8, col9) VALUES (@P0, @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8)
</frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown
</frame>
</executionStack>
<inputbuf>
(@P0 datetime2,@P1 nvarchar(4000),@P2 nvarchar(4000),@P3 datetime2,@P4 nvarchar(4000),@P5 nvarchar(4000),@P6 decimal(38,1),@P7 int,@P8 int)INSERT INTO table (col1, col2, col3, col4, col5, col6, col7, col8, col9) VALUES (@P0, @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8) select SCOPE_IDENTITY() AS GENERATED_KEYS
</inputbuf>
</process>
<process id="process14b38c928" taskpriority="0" logused="2564" waitresource="KEY: 5:72057594043629568 (275232b7b238)" waittime="7491" ownerId="92325909" transactionname="implicit_transaction" lasttranstarted="2014-10-13T12:37:39.567" XDES="0x16b38b988" lockMode="X" schedulerid="3" kpid="3668" status="suspended" spid="65" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2014-10-13T12:37:44.337" lastbatchcompleted="2014-10-13T12:37:44.337" lastattention="1900-01-01T00:00:00.337" clientapp="Microsoft JDBC Driver for SQL Server" hostname="" hostpid="0" loginname="" isolationlevel="read committed (2)" xactid="92325909" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="278" stmtend="818" sqlhandle="0x0200000053a65d302154b91e9fee55234669030a42479c050000000000000000000000000000000000000000">
INSERT INTO table (col1, col2, col3, col4, col5, col6, col7, col8, col9) VALUES (@P0, @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8)
</frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown
</frame>
</executionStack>
<inputbuf>
(@P0 datetime2,@P1 nvarchar(4000),@P2 nvarchar(4000),@P3 datetime2,@P4 nvarchar(4000),@P5 nvarchar(4000),@P6 decimal(38,1),@P7 int,@P8 int)INSERT INTO table (col1, col2, col3, col4, col5, col6, col7, col8, col9) VALUES (@P0, @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8) select SCOPE_IDENTITY() AS GENERATED_KEYS
</inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594043629568" dbid="5" objectname="table1" indexname="unique_index" id="lock17bc3a480" mode="X" associatedObjectId="72057594043629568">
<owner-list>
<owner id="process14b38c928" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process3ab355868" mode="X" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594043629568" dbid="5" objectname="table1" indexname="unique_index" id="lock10735ce00" mode="X" associatedObjectId="72057594043629568">
<owner-list>
<owner id="process3ab355868" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process14b38c928" mode="X" requestType="wait" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
Hier ist die Tabelle DDL:
CREATE TABLE [table1](
[col0] [int] IDENTITY(1,1) NOT NULL,
[col1] [int] NOT NULL,
[col2] [int] NOT NULL,
[col3] [decimal](15, 4) NULL,
[col4] [datetime2](7) NOT NULL,
[col5] [varchar](8) NOT NULL,
[col6] [varchar](30) NOT NULL,
[col7] [datetime2](7) NOT NULL,
[col8] [varchar](8) NOT NULL,
[col9] [varchar](30) NOT NULL,
CONSTRAINT [PK] PRIMARY KEY CLUSTERED
(
[col0] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [unique_index] UNIQUE NONCLUSTERED
(
[col2] ASC,
[col1] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE table1 ADD DEFAULT (sysdatetime()) FOR [col4]
GO
ALTER TABLE table1 ADD DEFAULT (sysdatetime()) FOR [col7]
GO
sql-server
sql-server-2012
insert
deadlock
GoodwinSQL
quelle
quelle
Antworten:
Ich beantworte hier meine eigene Frage, weil wir das Problem endlich herausgefunden haben.
Kurzversion : Wir haben dem nicht gruppierten Index eine dritte Spalte hinzugefügt. Deadlocks verschwanden.
Lange Version:
Schauen Sie sich zuerst James Rowland-Jones 'Dynamit-Blog-Post über Lock-Hashing-Kollisionen an (meine Erklärung wird seiner Qualität bei weitem nicht nahe kommen).
Aus dem Blogbeitrag:
Die Sperren-Hash-Kollision tritt auf, wenn doppelte Hash-Werte generiert werden.
Nach einer eingehenderen Analyse vieler Deadlock-Diagramme stellten wir fest, dass viele der WAITRESOURCE-Schlüssel-Hashwerte (die Werte in Klammern) gleich waren. Ich fing an, eine kurze Liste zu erstellen, um den Überblick zu behalten:
Sicher genug, wir haben viele doppelte Hash-Werte aus verschiedenen Deadlock-Diagrammen erhalten. Ich beschloss, die Daten in den beiden Spalten (col2 & col1) des unique_index-Index (wo die Deadlocks auftraten) zu untersuchen. Die gesamte Tabelle DDL befindet sich oben in der Frage.
Die Spalte col2 hat immer einen Wert von 1-6 für einen einzelnen Wert in der Spalte col1. Das machte also Sinn. Für SQL war eine begrenzte Anzahl von Daten verfügbar, aus denen Hash-Werte generiert werden konnten. Dies erklärt, warum wir doppelte Hash-Werte erhalten haben.
Eines des Updates JRJ erwähnte im Blog war eine zusätzliche Spalte mit dem Index hinzuzufügen. Dies fügt den Daten eine gewisse Vielfalt hinzu und bietet mehr Optionen für den Hashing-Algorithmus. Glücklicherweise konnten wir dem Index eine create_timestamp-Spalte hinzufügen und die gleiche Eindeutigkeit beibehalten, die wir mit den beiden Spalten hatten. BOOM! Nach dem Hinzufügen der dritten Spalte zum Index verschwanden die Deadlocks.
Nebenbemerkung: In einem der Kommentare im Blog wurde vorgeschlagen, die Zeilensperrung für den Index zu deaktivieren. Wir haben es zuerst versucht. Es hat die Deadlocks beseitigt, aber zu mehr Sperren geführt und den Gesamtdurchsatz um etwa 40-50% gesenkt, sodass uns diese Option für unser System nicht gefallen hat. In einer Datenbank mit geringerer Arbeitslast funktioniert dies jedoch möglicherweise einwandfrei.
Hoffentlich macht das alles Sinn.
quelle
Ich bin mir nicht sicher, ob dies tatsächlich eine Antwort sein könnte, aber ich kann dies nicht als Kommentar posten
Wenn Sie Ihren Anwendungscode sehen, ist es ähnlich
Wenn Sie sehen , Sie haben erklärt ,
@p4,@p5...@P6
wie ,nvarchar()
aber wenn Sie Tabellendefinition sehenDie Spalte, in die sie Werte einfügen, wird als deklariert
varchar
und der Wert, den Sie übergeben, istnvarchar
. Keine Ihrer Tabellenspalten hat dennvarchar
Datentyp. Wenn dies zutrifft, erfolgt eine implizite Konvertierung, und es werden Index-Scans durchgeführt, anstatt zu versuchen, Sperren (insbesondere bei Aktualisierungen) für eine längere Dauer zu halten. Ich denke, dies könnte eine mögliche Ursache für den Deadlock sein.Es gibt auch trancount = 2, was bedeutet, dass nicht festgeschriebene Transaktionen und natürlich Bit vorliegen, wenn eine Abfrageoptimierung erforderlich ist.
Beide Transaktionen, die am Deadlock beteiligt waren, konkurrierten mit derselben Ressource
waitresource="KEY: 5:72057594043629568 (6234ed5bf036)"
quelle