MySQL 基础 ————事务与隔离级别总结
引言
?
在處理并發讀或寫時,可以通過實現一個由兩種類型的鎖組成的鎖系統來解決問題:
共享鎖(shared lock)和排它鎖(exclusive lock),也叫讀鎖(read lock)和寫鎖(write lock)。
讀鎖是共享的,也就是互相不阻塞。多個客戶在同一時刻可以同時讀取同一個資源,互不干擾。
寫鎖是排他的,也就是說一個寫鎖會阻塞其他的寫鎖和讀鎖,只有這樣,才能保證在給定的時間里,只有一個用戶能執行寫入,并防止其他用戶讀取正在寫入的同一資源。?
在實際數據庫系統中,每時每刻都在發生鎖定,當某個用戶在修改某一部分數據時,MySQL 會通過鎖定防止其他用戶讀取同一數據。
一、事務的概念
事務主要針對查詢以外的其他幾項操作:插入、更新、刪除,將多條SQL合并到一個執行單元中,要么全部執行成功,要么全部回滾。MySQL的TCL 事務控制語言,就是為事務而設計,接下來我們來總結一下。
二、事務的 ACID 屬性
1、原子性
原子性是指事務是一個不可分割的工作單元,不可分割,意味著要么全部執行,要么全部回滾。
2、一致性
事務必須使數據庫從一個一致狀態,變換到另一個一致狀態。
3、隔離性
事務的隔離性是指一個事務的執行不能被其他事務干擾,即一個事務內部的操作及使用的數據對并發的其他事務是不可見的。并發的多個事務之間互不干擾。
4、持久性
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來的其他操作和數據庫故障不應該對其有任何影響。
三、事務操作的演示
3.1 事務提交變量:autocommit
MySQL控制事務提交的變量叫做'autocommit',可以通過如下語句進行查看:
SHOW VARIABLES LIKE 'autocommit';默認情況下是開啟的,即執行SQL后會自動提交事務。如果希望手動提交事務,需要將ON —> OFF。但這種方法只在當前會話中有效,只要新開一個連接或是MySQL會話,就會恢復自動提交事務:
SET autocommit = 0;3.2 開啟事務和提交事務
通過 set autocommit = 0 即表示開啟顯式事務(默認是隱式事務),其實開啟事務的語句是下面這句,不過一般都會省略:
START TRANSACTION;開啟事務后,我們就可以執行多條SQL,一般是 insert、update、delete 語句等,且不會自動提交。
當我們執行完這些SQL,想提交的時候,可以執行下面語句:
COMMIT; -- 提交事務 ROLLBACK; -- 回滾事務注意:事務中的語句僅支持 select、insert、update、delete,這四種,其他的如 create 等是不支持的。
3.3 案例演示
假設現在有兩個會話連接到MySQL數據庫,第一個連接關閉事務自動提交:
先查詢 emp 表的一條記錄,準備后續的更新操作:
將名稱更新為'張三豐':
切換到另一個會話,查看該條記錄,可以看到記錄還沒有任何變化:
切換回第一個會話,提交事務:
此時再切換到另一個事務查看該條記錄,已經變成了 “張三豐”:
四、并發事務與隔離級別
對于同時運行的多個事務,當這些事務訪問數據庫中相同的數據時,如果沒有采取必要的隔離機制,就會導致各種并發問題。
1、臟讀:對于任意兩個事務:T1、T2。T1讀取了已經被T2 更新但還沒有提交的字段之后,若 T2 回滾, T1 讀取到的內容是臨時且無效的。讀取了事務執行過程中的中間數據。
2、不可重復讀:對于兩個事務 T1、T2 。T1 讀取了一個字段,然后T2 更新了該字段之后,T1 再次讀取同一個字段,值就不同了。在同一個事務中,受其他事務更新影響,兩次讀取的數據不一致。
3、幻讀:對于兩個事務 T1、T2。T1 讀取一張表,然后 T2 在該表中插入了一些新的記錄。之后,如果 T1 再次讀取同一張表,就會多出幾行。在同一個事務中,受其他事務插入刪除影響,兩次讀取的記錄數量有變化。
4.1 隔離級別介紹
為了針對上述的三種并發問題,每種數據庫都會有自己的一套隔離級別,它描述了一個事務與其他事務隔離的程度。數據庫規定了多種事務隔離級別,不同的隔離級別對應不同的干擾程度,隔離級別越高,數據一致性就越好,但相應的,并發性就越差。
數據庫提供四種隔離級別:讀未提交、讀已提交、可重復讀(MySQL默認)、串行化。Oracle 僅支持讀已提交和串行化,默認是讀已提交。MySQL則四種全部支持,默認是可重復讀。
4.2 READ-UNCOMMITTED : 讀未提交
該隔離級別允許讀取未被其他事務提交的變更,臟讀、不可重復讀和幻讀的問題都會出現。
4.3 READ-COMMITTED: 讀已提交
只允許讀取已經被其他事務提交的變更,可避免臟讀,但不可重復讀和幻讀問題仍然會出現。
4.4 REPEATABLE-READ : 可重復讀
確保事務內可以多次從一個字段中讀取相同的值,在這個事務持續期間,禁止其他事務對這個字段進行更新,可以避免臟讀和不可重復度,但幻讀問題仍然存在。
4.5 SERIALIZABLE : 串行化事務
確保事務可以從一個表中讀取相同的行,在這個事務持續期間,禁止其他事務對該表執行插入、更新、刪除等操作,所有并發問題都可以解決,但性能非常低。
4.6 查看、設置隔離級別
通過 SELECT 子句,查看隔離級別:
SELECT @@tx_isolation;通過 SET 子句,設置隔離級別:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- SESSION 可省略注意,上述方法設置的隔離級別僅適用于當前連接,如果希望設置全局的隔離級別,可以使用 GLOBAL 關鍵字:
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;GLOBAL 關鍵字可以保證所有新的連接都會使用設置的隔離級別,但是舊的連接如果沒有關閉,依然會采用原來的隔離級別。另外,全局隔離級別設置僅針對本次MySQL服務有效,如果MySQL服務重啟,則依然是默認的隔離級別,除非在配置文件中改變MySQL默認隔離級別。
4.7 隔離級別與并發問題的關系
| 臟讀 | 不可重復讀 | 幻讀 | |
| READ-UNCOMMITTED | 出現 | 出現 | 出現 |
| READ-COMMITTED (Oracle默認) | 解決 | 出現 | 出現 |
| REPEATABLE-READ (MySQL默認) | 解決 | 解決 | 出現 |
| SERIALIZABLE | 解決 | 解決 | 解決 |
以上就是關于隔離級別與并發問題的總結和歸納。
重點是事務的ACID 屬性,三個并發問題的定義,四種隔離級別的定義,四種隔離級別與三個并發問題的關系,以及如何查看數據庫的隔離級別,設置數據庫的隔離級別。
4.8 事務日志
事務日志可以幫助提高事務的效率。
使用事務日志,存儲引擎在修改表的數據時只需要修改其內存拷貝,再把該修改行為記錄到硬盤上的事務日志中,而不用每次都將修改的數據本身持久到磁盤。
事務日志采用的是追加方式,因此寫日志的操作是磁盤上一小塊區域的順序IO,而不是存儲數據時的隨機IO,所以采用事務日志相對較快。
事務日志持久以后,內存中被修改的數據可以在后臺慢慢刷回到磁盤。
目前大多數存儲引擎都是這樣實現的,通常稱之為“預寫式日志(Write-Ahead Logging)”,修改數據需要寫兩次磁盤。
如果數據的修改已經記錄到事務日志并持久化,但數據本身還沒寫回磁盤,如果此時系統崩潰,那么存儲引擎在重啟時是可以自動恢復這部分修改的數據的。但具體的恢復方式可能不盡相同。
五、鎖粒度
一種提高共享資源并發性的的方式就是讓鎖定對象更有選擇性,盡量只鎖定需要修改的部分數據。更理想的方式是,只對會修改的數據片進行精確的鎖定。但是鎖的數量也會增大系統的開銷。
所謂鎖策略,就是在鎖的開銷和數據的安全性之間尋求平衡,這種平衡當然也會影響到性能。大多數商業數據庫沒有提供更多的選擇,一般都是在表上施加行級鎖,并以各種復雜的方式來實現,以便在鎖比較多的情況下盡可能的提供更好的性能。
MySQL則提供了多種選擇。每種MySQL存儲引擎都可以實現自己的鎖策略和鎖粒度。
在存儲引擎的設計中,鎖管理是個非常重要的決定。
5.1 表鎖
表鎖是MySQL最基本的鎖策略,并且是開銷最小的鎖策略。用戶在對表進行寫操作(插入、刪除、更新)前,需要先獲得寫鎖,這會阻塞其他用戶對該表的所有讀和寫操作。只有沒有寫鎖時,其它讀取的用戶才能獲得讀鎖,讀鎖之間是不相互阻塞的。
在特定情況下,表鎖也有良好的性能。例如,READ LOCAL表鎖支持某種類型的并發寫操作。
寫鎖比讀鎖有更高的優先級。一個寫鎖請求可以插入到鎖隊列中讀鎖的前面,反之讀鎖則不能插入到寫鎖的前面。
存儲引擎可以管理自己的鎖,如InnoDB的行鎖,但MySQL在某些情景下會使用表鎖,從而忽略存儲引擎的鎖機制。例如,MySQL會為ALTER TABLE之類的語句使用表鎖,忽略存儲引擎的鎖機制。
5.2 行鎖
行級鎖可以最大程度地支持并發處理,但同時鎖開銷也是最大的。行級鎖只在存儲引擎層實現,如InnoDB、XtraDB等,而MySQL服務器層沒有實現。
總結
以上是生活随笔為你收集整理的MySQL 基础 ————事务与隔离级别总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python变量类型有几种_python
- 下一篇: Java 写时复制容器 —— CopyO