事務在一個數據庫中的地位尤其重要,尤爲是高併發的場合。保證數據庫操做的原子性和錯誤出現狀況下的回滾,對數據的安全性和可靠性提供了保障。事務有四大原則,即ACID原則。網上關於這個問題的文章有不少,讀者能夠到網上看看相關的文章,我這裏就不贅述了。可是須要注意的是,MySQL默認是不開啓事務的,默認狀況是autocommit自動提交,而若是想開啓事務,須要數據庫管理員或者開發者手動輸入begin來開啓事務。php
本文主要介紹四大原則中的I原則,即隔離級別。並在講述I原則的時候,順帶討論MVCC。由於MVCC一般和隔離級別是討論到一塊的。html
事務的隔離級別實際上是SQL語言的標準,這裏我就以本身比較經常使用的MySQL數據庫爲例進行介紹。mysql
客戶端輸入命令:sql
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL數據庫
{READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}安全
小提示:也可使用小寫,MySQL是不分大小寫的。服務器
在MySQL Workbench輸入命令查詢系統默認的隔離狀態網絡
MySQL默認狀況下是repeatable read。session
設置當前會話隔離級別併發
設置當前會話(客戶端)的隔離級別,當客戶端關閉後從新進入,隔離級別會恢復到系統的全局隔離狀態。
理解‘當前會話’:
MySQL中認爲,打開一個客戶端就是開啓一個會話。其實質是創建一個網絡鏈接。這個網絡鏈接是有狀態性的,一旦關閉,此會話級別中設置的變量就會恢復爲系統的變量。
設置全局隔離級別
設置全局隔離級別的時候,只對全局進行修改,當前會話仍是會保持原來的設置,退出從新登陸纔會遵循全局的設置。
在MySQL Workbench中輸入命令:
或者在終端輸入
mysql> show variables like '%isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | READ-COMMITTED | | tx_isolation | READ-COMMITTED | +---------------- ------+----------------+ 2 rows in set, 1 warning (0.00 sec)
另一種方法:
在ini文件(Linux爲conf文件)設置全局的隔離級別
[mysqld]
添加:
transaction-isolation = READ-COMMITTED
不過這種狀況須要重啓服務器,因此不太建議使用這種方式,除非是幾乎沒有請求的深夜時候進行。
C:\WINDOWS\system32> net stop mysql MySQL 服務正在中止.. MySQL 服務已成功中止。 C:\WINDOWS\system32> net start mysql MySQL 服務正在啓動 . MySQL 服務已經啓動成功。
關於以上命令,有一些須要理解的點:
1)隔離級別優先級順序 read uncommittd < read committed < repeatable read < serializable
個人理解是每個級別都是在上一級別的基礎上增長了表操做的限制。讀者往下看就能夠體會到這種限制的增強。
2)session和global
1. 默認行爲(不帶session和global)是爲下一個(未鏈接)會話設置隔離級別。
2. 若是使用GLOBAL關鍵字,此命令語句在全局中對從那點開始建立的全部新鏈接設置事務級別。
3. 使用SESSION 關鍵字爲當前鏈接上執行的事務設置默認事務級別。
4. 任何客戶端在任什麼時候候都能自由改變會話/全局隔離級別(甚至在事務的中間),或者爲下一個事務設置隔離級別。
實際測試中,因爲是本地鏈接,一些狀況沒法模擬出來,並且手動也很難作一些模擬高併發的情形,能夠藉助一些測試工具來進行。例如sysbench,《高性能MySQL》中也有一章專門寫了基準測試。
下面是對四個隔離級別的介紹
1. Read Uncommitted 讀取未提交的事務
一個數據庫鏈接實例中,它的未提交事務執行了對數據庫的增刪改操做,這些操做對數據的更改是沒有提交到服務器磁盤的,可是在高併發的狀況下,本鏈接的事務會不斷更新數據庫以獲取其餘鏈接的事務對數據庫進行的最新的更改。那麼第一個事務查詢操做讀取的數據多是不真實的,人家都尚未提交!因此這種狀況下就會出現了所謂的髒讀。通常,在現實中不多使用這個級別,除非是對數據的真實性要求不高,只想得到最新的數據的狀況。想一想在一些金融領域的電子現金系統或者商城購物車系統使用這個級別會是怎樣一個情形?
2. Read Committed 讀取已經提交的事務
此級別在本會話中對數據庫進行查詢,會讀取已經提交(commit)了SQL語句的事務結果,而未提交的事務的操做對數據庫所形成的影響是不會讀取的。能夠這樣理解,這個級別更像是‘事務級別’,即不一樣事務間內部的操做互不影響,只有提交了事務纔會對其餘事務產生影響。
Read committed級別同時也是nonrepeatable read級別(是相對下面的Repeatable Read而言的),即不要求重複讀。一個事務先後兩次的讀取內容能夠是不一樣的,即容許在本事務查詢的過程當中其餘事務對本事務查詢的數據進行其餘操做,這樣就會出現先後兩次讀取的數據的不一致性。此處涉及到MVCC和樂觀鎖、悲觀鎖的概念,後面會講述。咱們先討論這種機制的運行邏輯。
想象一下下面這種情形:高併發狀況下,多個數據庫鏈接同時對數據庫提交操做。本事務因爲SQL語句比較多,執行起來比較慢。而在這期間(有時多是1s內),有另外兩個鏈接同時在執行操做,一個鏈接對一行數據的某一列進行增長2操做,而另一個鏈接對同一行數據的這一列數據進行減3操做,它們在對數據庫的實現上可能相差的時間很小,可能僅在毫秒之間這個時候就會出現兩個vesion版本的結果,一個是執行了一個事務的版本,另外一個是執行了兩個事務的版本。那麼本事務究竟讀取哪個版本呢?答案是指讀取執行了兩條事務的版本。由於在本事務內,會不斷去‘查看’其餘事務的提交狀況,並將最新的提交狀況‘反饋’在本事務中。
3. Repeatable Read 可重複讀
Repeatable Read正好與上面的Read Committed相反,本事務會讀取第一個version的數據。由於在進行第一次查詢操做的時候,可能第一個事務已經提交了,而第二個事務還沒提交。而不管後面第二個事務甚至更多個事務提交了,但本事務中再次讀取的數據時只會讀取是第一讀取的相同數據,即‘無視’其餘事務對此行數據的更改。能夠理解,在這裏是悲觀鎖發揮了做用,即鎖住了本事務,其餘事務的數據的更改就沒法影響到本事務。而在Read Committed級別中,發揮做用的是樂觀鎖,其實就是什麼也不鎖定,只在更新數據的時候鎖定(下面有更詳細描述)。因此在本事務的先後兩次範圍查詢中會出現數據不一致的幻讀現象。
4. Serilizable 串行化
數據庫最高級別的隔離限制,事務與事務之間串行化的。即一個事務在執行任何操做的時候,都不容許其餘事務對本事務查詢範圍內的數據有任何操做,這裏會引入一個共享鎖的概念,即本事務的查詢操道別讀取的數據鎖住了,必須等本事務完成以後才容許其餘事務獲取這個共享鎖進行其餘操做,就比如如排着隊一個個執行。
MVCC
MVCC, Multiversion Concurrency Control多版本併發控制。MVCC是行級鎖的一個變種,可是它在不少狀況下避免了加鎖操做, 所以服務器的開銷更低(減小了鎖的生產和分配)。雖然實現機制有所不一樣, 但大都實現了非阻塞的讀操做,寫操做也只鎖定必要的行。
在實現上,MySQL經過三個列實現對版本的控制,即6字節的事務ID(DB_TRX_ID)字段,7字節的回滾指針(DB_ROLL_PTR)字段 ,6字節的DB_ROW_ID字段。更多相關內容推薦看這篇文章,裏面講得很詳細 https://juejin.im/entry/5a4b52eef265da431120954b
而實際上InnoDB並不是徹底意義上的MVCC,由於沒有實現多版本並存。關鍵在於並存兩字,即在事務執行對數據操做時同時存在多個版本。而不管如何MySQL在事務執行的時候是串行化的,即便這兩個事務幾乎同時發生。其實這正是對數據安全性的一個保障。
MVCC只在 READ COMMITTED 和 REPEATABLE READ 兩個隔離級別下工做。其餘兩個隔離級別和MVCC不兼容。
悲觀鎖:
悲觀鎖的特色是先獲取鎖,再進行業務操做,即「悲觀」的認爲獲取鎖是很是有可能失敗的,所以要先確保獲取鎖成功再進行業務操做。
樂觀鎖:
樂觀鎖的特色先進行業務操做,不到萬不得已不去拿鎖。即「樂觀」的認爲拿鎖多半是會成功的,所以在進行完事務操做須要實際更新數據的最後一步再去拿一下鎖就好。
在實戰中怎麼使用,仍是要看具體的數據量和業務邏輯來進行選擇。
更新說明:隨着本身對MySQL數據庫隔離級別的更多理解,本文在以前的內容基礎上進行從新整理,而且添加上了MVCC的內容。18.12.25
參考文章:
https://www.cnblogs.com/phpper/p/7345332.html
https://juejin.im/entry/5a4b52eef265da431120954b