Το τελευταίο καιρό ασχολούμαι με την υποδομή ενός μεγάλου πελάτη με σκοπό το optimization και φυσικά τo performance.
Μέσα στο διάστημα αυτό έχουμε κάνει αρκετές αλλαγές που οδήγησαν τα πράγματα στο σωστό δρόμο. Μια όμως από αυτές τις αλλαγές ενώ αρχικά δούλευε όπως είχε σχεδιαστεί μετά από μερικές μέρες άρχισε να εμφανίζει σε τυχαία διαστήματα συμπτώματα deadlock.
Εννοείται ότι κάτι τέτοιο δεν το αφήνεις να περάσει χωρίς να το ερευνήσεις.
Analyzing deadlock graph
Βλέποντας το deadlock graph (δείτε το άρθρο μου αυτό για το πως μπορείτε να πάρετε εύκολα το deadlock graph) είχα την παρακάτω εικόνα που ξεφεύγει από τα συνηθισμένα.
To graph έδειχνε αρκετά αλλά παρόλα αυτά όμως μου ήταν δύσκολο να καταλάβω ακριβώς τι συμβαίνει. Οπότε κατέφυγα στο διάβασμα του xml που παράγει το deadlock graph και το οποίο περιέχει περισσότερες πληροφορίες και αφού πρώτα έκανα ένα φρεσκάρισμα διαβάζοντας από τα BOL το τι δίνει το xml deadlock report.
Από αυτό δείχνω παρακάτω μόνο αυτά που έχουν σημασία για την κατανόηση του προβλήματος
<deadlock>
<victim-list>
<victimProcess id="process314c074cf8" />
</victim-list>
<process-list>
<process id="process314c074cf8" waitresource="PAGE: 9:1:810905 " schedulerid="1" kpid="14688" status="suspended" spid="229" sbid="0" ecid="5" ...
<process id="process456be51868" waitresource="PAGE: 9:1:810905 " schedulerid="10" kpid="27244" status="suspended" spid="96" sbid="0" ecid="7" ...
<process id="process314c074928" schedulerid="1" kpid="19956" status="suspended" spid="229" sbid="0" ecid="9" ...
<process id="process2dbbc38" schedulerid="1" kpid="17532" status="suspended" spid="229" sbid="0" ecid="0" ...
</process-list>
<resource-list>
<pagelock fileid="1" pageid="810905" dbid="9" subresource="FULL" objectname="XXXXXXXX" id="lock15f43f0b00" mode="U" associatedObjectId="72057630202331136">
<owner-list>
<owner id="process456be51868" mode="U" requestType="wait" />
</owner-list>
<waiter-list>
<waiter id="process314c074cf8" mode="U" requestType="wait" />
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="810905" dbid="9" subresource="FULL" objectname="XXXX" id="lock15f43f0b00" mode="U" associatedObjectId="72057630202331136">
<owner-list>
<owner id="process2dbbc38" mode="U" />
</owner-list>
<waiter-list>
<waiter id="process456be51868" mode="U" requestType="wait" />
</waiter-list>
</pagelock>
<exchangeEvent id="Pipe39b93bfa00" WaitType="e_waitPipeGetRow" nodeId="35">
<owner-list>
<owner id="process314c074cf8" />
</owner-list>
<waiter-list>
<waiter id="process314c074928" />
</waiter-list>
</exchangeEvent>
<exchangeEvent id="Porta3c7ef1500" WaitType="e_waitPortOpen" nodeId="7">
<owner-list>
<owner id="process314c074928" />
</owner-list>
<waiter-list>
<waiter id="process2dbbc38" />
</waiter-list>
</exchangeEvent>
</resource-list>
</deadlock>
Αν παρατηρήσουμε στο process-list τα processes θα δούμε ότι τρία από αυτά έχουν το ίδιο session id (spid="229").
Αυτά έχουν διαφορετικό schedulerid το ίδιο system batch id (sbid="0") και διαφορετικό execution context id (ecid) πράγμα που σημαίνει ότι το συγκεκριμένο statement (update με join στην περίπτωση μου) έχει παραλληλίσει για αυτό και υπάρχουν τα exchange events στο deadlock graph.
Αναλύοντας το section του resource-list επιβεβαίωσα αυτό που αρχικά δεν είχα δει με την πρώτη ματιά και που ήταν στην ουσία ότι το συγκεκριμένο statement αυτό-μπλοκαριζόταν καθώς κάποιο από τα threads που είχαν δημιουργηθεί εξαιτίας του παραλληλισμού διάβαζε περισσότερα data σε σχέση με τα άλλα με αποτέλεσμα το deadlock.
Σε αυτές τι περιπτώσεις ο SQL Server βγάζει το εξής λάθος
Msg 8650, Level 13, State 1, Line 1
Intra-query parallelism caused your server command (process ID #??) to deadlock. Rerun the query without intra-query parallelism by using the query hint option (maxdop 1).
Το οποίο όμως δυστυχώς δεν το κάνει log στα SQL Server Logs αλλά το δείχνει μόνο στο application το οποίο όμως δεν το έκανε σωστά log και έτσι το χάναμε.
My solution
Για την επίλυση του προβλήματος όμως δεν είναι πάντα ιδανικό αυτό που προτείνει το συγκεκριμένο error message καθώς θα καθυστερεί περισσότερο η διαδικασία. Αναλύοντας το statement αποφάσισα ότι ένας POC Index στα πεδία που εμπλέκονται στο statement ήταν η καλύτερη λύση και έτσι πλέον δεν παραλληλίζει το update μου εξαιτίας του join με τους άλλους πίνακες που εμπλέκονταν σε αυτό.
//antonch