從事軟件領域研發,只有涉及到數據領域,或者跟數據庫打交道,或者NoSQL或者大數據領域下,都離不開事務的應用場景,通過對應的鎖設計思路來解決併發的問題。
首先關注數據庫的事務級別
1:Read unommitted(也稱之爲髒讀)
事務隔離的最低級別,比如A修改了一個數據,還沒有提交,而另外一個用戶B就讀取都到了。
這種場景沒有實際的應用價值,因爲沒有做到ACID原則的Consistent reads(一致性)
2:Read committed
這個級別是Oracle默認的支持的隔離級別,起始也是保證數據一致性的基本。
用於B讀取到的數據,必須是其它用於已經提交的數據。
在這種隔離級別下,可能會出現這種場景:
A用戶讀取數據D, B用戶準備修改數據D,準備修改爲D1
在B修改提交之前,A讀到了數據D
然後B提交了修改結果,修改爲D1
然後A再一次讀取D,就得到了結果D1
該種場景爲:Non-Repeatable Reads(不可重複讀)
另外一種可能的場景爲Phantom Reads(幻想讀)
這種隔離級別在Mysql的InnoDB的存儲引擎中,由於支持的爲行級別鎖定,如果通過
select ... for update或者select ... Lock in Share Mode.
此時如果僅僅通過鎖定索引記錄而不鎖定之前的間隙,會允許鎖定記錄後,仍然可以自由的插入新紀錄。
此時如果通過索引不能夠準確定位到需要鎖定的記錄,就必須依賴升級爲表級別的鎖定,或者設置next-key或gap-locks來阻塞其他用戶的空隙插入。
幻想讀就會產生。
3:REPAETABLE READ
而這個級別爲InnoDB的默認事務隔離級別
類似SELECT ... FOR UPDATE,SELEC ... LOCK IN SHARE MODE,UPDATE ,DELETE
如果能夠以唯一條件或者唯一索引,只鎖定所需的記錄時,而不鎖定該索引之前的間隙。
否則的話,就通過next-key或者gap locks來鎖定所需要的資源,來阻塞其他用戶的新建插入。
該事務級別中:同一事務的所有的Consistent Reads均讀取第一次讀取時已確定的快照。
該事務級別:不會出現髒讀、不可重複讀,但還是會有幻想讀。
4:SERIALIZABLE
最高級別事務隔離,事務中的任何時刻,所看到的數據都是事務啓動時的狀態。
在此級別,不會出現幻想讀。InnoDB也支持到了。
接下來需要思考:
事務的目的是爲了保證對共享數據的併發訪問能顧保持一致性好完整性,理想情況下,事務級別越高越好;
但是針對系統的開銷也越大,爲了實現高級別的事務隔離,數據庫需要通過一系列的鎖機制來保證併發處理數據的操作不能夠相互
覆蓋;
這裏自然需要關聯到數據庫的鎖的類型,鎖的類型跟數據庫的模型有關;
比如MYSQL支持的三種類型的鎖:
表級鎖:
粗粒度,對整個表上鎖,MyISAM存儲引擎的支持規則(還包括Memory,CSV),支持併發度低。
行級鎖:
細粒度,對行上鎖,InnoDB的支持(NDB cluster),支持併發度高。
頁級鎖:
粒度適中,爲BerkeleyDB存儲引擎支持。
對於表級的鎖定,可以分爲:
讀鎖、寫鎖
內部通過
Curren readLock Queue和Pending ReadLock Queue
Curren writeLock Queue和Pending writeLock Queue
來維持。
針對列級的鎖定,除了讀寫之外,還可以細分:
共享鎖、排他鎖、意向共享鎖、意向排他鎖
而InnerDB作爲實現行鎖,鎖定的粒度在行級別,實現機制對比一下;
oracle:通過在需要鎖定的某行記錄所在的物理Block上的事務槽上面添加鎖定信息
InnoDB:通過在指向數據記錄的索引鍵之前和最後一個索引鍵之後的空域空間標記鎖定信息來實現。
實現原理的差別:可能爲導致InnoDB如果在通過索引無法定位記錄或者無法鎖定範圍時,會升級爲表級鎖定。
同時還有考慮:這種間隙鎖(Next-key locking)的弱點:
當鎖定在一定範圍時,某些不存在的鍵值也會被無辜鎖定,即導致鎖定時,無法插入鎖定範圍內的任何數據。
InnoDB的解釋可以爲:阻止幻想讀。
前面引入了鎖,如果出現死鎖,數據庫如何快速識別死鎖,並且如何解決死鎖的問題
當然這個沒有一個很好的快速解決方式,需要綜合考慮;
MyISAM引擎提供的表鎖定:由於粒度過大,降低了併發處理能力,如何優化呢:
a:儘可能減少業務的鎖定表的時間,來減少死鎖或者鎖等待的時間
b:分類頁的讀寫,支持並行操作
MyISAM引擎支持一個特徵爲Concurrency Insert,當配置爲1,2時,支持併發寫入。
針對conurrency_insert=1的規則爲:當存儲引擎表數據文件中間不存在空閒空間時,可以從文件尾部進行Concurrency Insert
conurrency_insert=2的規則爲:無論當存儲引擎表數據文件中間是否不存在空閒空間時,可以從文件尾部進行Concurrency Insert
c:合理利用讀寫優先級,針對讀和寫鎖還可以設置對應的優先級,可以根據系統特徵,優先保障寫鎖定完成,或者優先保障讀鎖定完成。
可以同table_locks_immediate和table_locks_waited來查看
而針對InnoDB的行級別鎖定,如何優化呢:
a:由於數據的鎖定機制依賴索引,索引數據的查找儘可能通過索引來完成
b:注意索引的設計,避免由於無法通過索引定位數據,而上升爲表級別鎖定
c:當然也避免間隙鎖帶來的阻止幻想讀
d:控制事務力度不要過大,減少所等待時間
e:合理設計業務應用,儘可能不要產生死鎖
f:針對事務的鎖定,一次性鎖定所需的所有資源
可以通過innodb_row_lock_current_waits
innodb_row_lock_time
innodb_row_lock_time_avg
innodb_row_lock_time_max
innodb_row_lock_waits來查看。