mysql隔离级别与悲观锁、乐观锁
2019獨角獸企業重金招聘Python工程師標準>>>
1、什么是悲觀鎖,樂觀鎖
悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨占性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。而樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基于數據版本( Version )記錄機制實現。何謂數據版本?即為數據增加一個版本標識,在基于數據庫表的版本解決方案中,一般是通過為數據庫表增加一個 “version” 字段來實現。讀取出數據時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大于數據庫表當前版本號,則予以更新,否則認為是過期數據。
?
悲觀鎖:假定會發生并發沖突,屏蔽一切可能違反數據完整性的操作。[1]
樂觀鎖:假設不會發生并發沖突,只在提交操作時檢查是否違反數據完整性。[1]樂觀鎖不能解決臟讀的問題。
樂觀鎖應用
1.?????使用自增長的整數表示數據版本號。更新時檢查版本號是否一致,比如數據庫中數據版本為6,更新提交時version=6+1,使用該version值(=7)與數據庫version+1(=7)作比較,如果相等,則可以更新,如果不等則有可能其他程序已更新該記錄,所以返回錯誤。
2.?????使用時間戳來實現.
注:對于以上兩種方式,Hibernate自帶實現方式:在使用樂觀鎖的字段前加annotation: @Version, Hibernate在更新時自動校驗該字段。
悲觀鎖應用
需要使用數據庫的鎖機制,比如SQL SERVER 的TABLOCKX(排它表鎖)?此選項被選中時,SQL? Server? 將在整個表上置排它鎖直至該命令或事務結束。這將防止其他進程讀取或修改表中的數據。
數據庫鎖機制:
?????? 共享鎖:由讀表操作加上的鎖,加鎖后其他用戶只能獲取該表或行的共享鎖,不能獲取排它鎖,也就是說只能讀不能寫。
?? 排它鎖:由寫表操作加上的鎖,加鎖后其他用戶不能獲取該表或行的任何鎖。
鎖的范圍:
?? 行鎖:?對某行記錄加上鎖
?? 表鎖:?對整個表加上鎖
這樣組合起來就有,行級共享鎖,表級共享鎖,行級排他鎖,表級排他鎖。
?
結論
在實際生產環境里邊,如果并發量不大且不允許臟讀,可以使用悲觀鎖解決并發問題;但如果系統的并發非常大的話,悲觀鎖定會帶來非常大的性能問題,所以我們就要選擇樂觀鎖定的方法.
?
2、事務并發會產生什么問題
1)第一類丟失更新:在沒有事務隔離的情況下,兩個事務都同時更新一行數據,但是第二個事務卻中途失敗退出,?導致對數據的兩個修改都失效了。
例如:
張三的工資為5000,事務A中獲取工資為5000,事務B獲取工資為5000,匯入100,并提交數據庫,工資變為5100,
隨后事務A發生異常,回滾了,恢復張三的工資為5000,這樣就導致事務B的更新丟失了。
2)臟讀:臟讀就是指當一個事務正在訪問數據,并且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問這個數據,然后使用了這個數據。
例如:
張三的工資為5000,事務A中把他的工資改為8000,但事務A尚未提交。
與此同時,
事務B正在讀取張三的工資,讀取到張三的工資為8000。
隨后,
事務A發生異常,而回滾了事務。張三的工資又回滾為5000。
最后,
事務B讀取到的張三工資為8000的數據即為臟數據,事務B做了一次臟讀。
3)不可重復讀:是指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那么,在第一個事務中的兩次讀數據之間,由于第二個事務的修改,那么第一個事務兩次讀到的的數據可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱為是不可重復讀。
例如:
在事務A中,讀取到張三的工資為5000,操作沒有完成,事務還沒提交。
與此同時,
事務B把張三的工資改為8000,并提交了事務。
隨后,
在事務A中,再次讀取張三的工資,此時工資變為8000。在一個事務中前后兩次讀取的結果并不致,導致了不可重復讀。
4)第二類丟失更新:不可重復讀的特例。有兩個并發事務同時讀取同一行數據,然后其中一個對它進行修改提交,而另一個也進行了修改提交。這就會造成第一次寫操作失效。?
例如:
在事務A中,讀取到張三的存款為5000,操作沒有完成,事務還沒提交。
與此同時,
事務B,存儲1000,把張三的存款改為6000,并提交了事務。
隨后,
在事務A中,存儲500,把張三的存款改為5500,并提交了事務,這樣事務A的更新覆蓋了事務B的更新。
5)幻讀:是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。
例如:
目前工資為5000的員工有10人,事務A讀取所有工資為5000的人數為10人。
此時,
事務B插入一條工資也為5000的記錄。
這是,事務A再次讀取工資為5000的員工,記錄為11人。此時產生了幻讀。
提醒:
不可重復讀的重點是修改:
同樣的條件,你讀取過的數據,再次讀取出來發現值不一樣了
幻讀的重點在于新增或者刪除:
同樣的條件,第 1 次和第 2 次讀出來的記錄數不一樣
3、事務隔離級別,解決什么并發問題
(1)READ_UNCOMMITTED
這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的數據。
會出現臟讀、不可重復讀、幻讀 (隔離級別最低,并發性能高)。
(2)READ_COMMITTED
保證一個事務修改的數據提交后才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。
可以避免臟讀,但會出現不可重復讀、幻讀問題(鎖定正在讀取的行)。
(3)REPEATABLE_READ
可以防止臟讀、不可重復讀,但會出幻讀(鎖定所讀取的所有行)。
(4)SERIALIZABLE
這是花費最高代價但是最可靠的事務隔離級別,事務被處理為順序執行。
保證所有的情況不會發生(鎖表)。
詳情見下表:
提醒:
Mysql默認的事務隔離級別為repeatable_read
4、悲觀鎖和樂觀鎖與數據庫隔離級別的關系
關系總結,事務隔離級別是并發控制的整體解決方案,其實際上是綜合利用各種類型的鎖和行版本控制,來解決并發問題。鎖是數據庫并發控制的內部機制,是基礎。對用戶來說,只有當事務隔離級別無法解決一些并發問題和需求時,才有必要在語句中手動設置鎖。
5、悲觀鎖和樂觀鎖的使用
1)悲觀鎖:
要使用悲觀鎖,我們必須關閉mysql數據庫的自動提交屬性,因為MySQL默認使用autocommit模式,也就是說,當你執行一個更新操作后,MySQL會立刻將結果進行提交。
使用mybatis或者jdbc api實現時:
sql語句:select status from t_goods where id=1 for update;,update t_goods set status=2;,這時id為1的記錄會被鎖住
使用hibernate實現時,
Criteria.setLockMode?
Query.setLockMode?
Session.lock?
注意,只有在查詢開始之前(也就是Hiberate 生成SQL 之前)設定加鎖,才會 真正通過數據庫的鎖機制進行加鎖處理,否則,數據已經通過不包含for update 子句的Select SQL加載進來,所謂數據庫加鎖也就無從談起。
2)樂觀鎖:
樂觀鎖是系統層面的實現,推薦的實現方案是表中新增一個version字段,然后基于version字段的更新來判斷事務是否有效。
使用mybatis或者jdbc api實現時:
select (status,status,version) from t_goods where id=#{id}
update t_goods?
set status=2,version=version+1
where id=#{id} and version=#{version};
更新操作是基于version的,當事務A更新了記錄之后,version字段會加1,事務B再根據之前的version去查找記錄的時候,數據庫沒有相應的記錄就會出現異常,這時系統捕捉異常進行處理,即可避免并發的問題。
使用hibernate實現時:
Hibernate中必須指定optimistic-lock屬性對version描述符指定。
轉載于:https://my.oschina.net/u/2277088/blog/1630187
總結
以上是生活随笔為你收集整理的mysql隔离级别与悲观锁、乐观锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS三栏自适应布局,左中右,上中下
- 下一篇: mongo索引命令