死結 (Deadlock) 的發生與資料庫的鎖定 (Lock) 有關,這篇文章將介紹資料庫為什麼需要鎖定、MS SQL 的鎖定擴大 (Lock Escalation) 機制,以及減少死結的方法。
鎖定與鎖定擴大
MS SQL 資料庫在存取時,會執行資料鎖定。為什麼要鎖定資料 ? 主要是避免以下情形:
- 避免兩個更新作業同時進行,導致其中一方的更新資料遺失的問題。
- 不要讀取到未被認可 (Commit) 的資料。
- 避免讀取過的資料已經被修改過。
- 當同一次交易 (Transcation) 重複多次選取同樣資料時,避免每次的選取有不一樣的情形。
鎖定的層級有許多種,例如層級較低的 Row Lock (資料列鎖定)、Page Lock (資料頁鎖定),層級較高的 Table Lock (資料表鎖定) 等。
鎖定擴大 (Lock Escalation) 則是將多個較低層級的鎖定升級至較高的鎖定,以節省記憶體空間 (因為每個鎖定都要額外耗費記憶體)。不過由於一口氣鎖定的更多的資料,較容易造成在其他程式存取時,發生阻擋的現象。
當二或多個程式互相等待鎖定釋放時,就會造成死結,這時會導致程式逾時,而且程式中的 CRUD 的動作也無法完成。
死結的避免
要減少死結狀況,常見的做法是避免同時操作相同的資料表,特別是大量的更新/插入/刪除資料,避免發生鎖定擴大現象,可參考拙作 SQL 的鎖定擴大 (Lock Escalation)。
此外,也有以下幾種方法可以減少死結的發生:
- 在不同的程式中,以相同的順序存取資料。
- 避免在交易中的使用者互動。
- 保持批次作業中的交易簡短,避免批次同時作業時,互相阻擋其他交易的動作。
- 使用較低的隔離層級 (Isolation Level)。
- 使用基於資料列的隔離層級。
- 使用 Bound Connection。