MySQL事務處理特性的實現原理

摘要:事務這個詞來自於英語中的transactional這個詞的翻譯,這個詞的含義更多的是指 「交易」。在數據庫系統或者軟件系統中咱們一般 稱 transactional 爲事務html

本文分享自華爲雲社區《【數據庫事務與鎖機制】- 事務的隔離級別》,原文做者:技術火炬手 。mysql

事務這個詞來自於英語中的transactional這個詞的翻譯,這個詞的含義更多的是指 「交易」。在數據庫系統或者軟件系統中咱們一般 稱 transactional 爲事務。算法

數據庫事務的四個特性 ACID:分別是 原子性、一致性、隔離性、持久性。數據庫事務的這四大特性來源於 ISO標準的 ISO/IEC 10026-1:1992/COR 1:1996,它定義了事務須要具有以上四個特性。那麼在InnoDB中是如何實現這些特徵的呢?下面內容咱們討論 MySQL (下指InnoDB引擎)對事務特性的支持是怎麼實現的。sql

討論MySQL的事務處理特性的實現原理以前咱們須要先了解下MySQL對MVCC的支持,關於MVCC 維基百科有以下解釋。數據庫

多版本併發控制(Multiversion concurrency control, MCC 或 MVCC),是數據庫管理系統經常使用的一種併發控制,也用於程序設計語言實現事務內存。MVCC意圖解決讀寫鎖形成的多個、長時間的讀操做餓死寫操做問題。每一個事務讀到的數據項都是一個歷史快照(snapshot)並依賴於實現的隔離級別。寫操做不覆蓋已有數據項,而是建立一個新的版本,直至所在操做提交時才變爲可見。快照隔離使得事物看到它啓動時的數據狀態session

數據庫事務的隔離級別

爲了實現事務的隔離性,ISO 標準組織對事務鎖須要實現的隔離級別有四種定義,下面咱們先對四種事務隔離的級別簡單闡述一下。併發

READ UNCOMMITTED 讀未提交

RU(READ UNCOMMITTED) 被稱爲讀未提交,有些資料稱之爲瀏覽穩定(browse access)可是正確的翻譯應該是未提交讀。RU是最低標準的隔離,未提交讀的意思就是在事務併發的狀況下,能夠允許一個事務在沒有提交修改的的狀況下被另一個事務讀取到這個修改,這就就會產生髒讀的狀況。下面這個表格是各個事務隔離級別對於髒讀、幻讀、可重複讀的抑制狀況,事實上RU不但會產生髒讀的狀況並且其餘兩種讀的狀況都會發生。性能

 

首先咱們有必要澄清一下以上三種數據讀問題的概念,對於數據庫事務來講咱們簡單的認識是一系列的數據庫操做在一個事務中,這個事務要不所有成功要不所有失敗,可是要知道數據庫在實際使用的過程當中不是串行的,它是併發的,串行場景下咱們事先事務就很是簡單了,就是一個一個操做嘛,你們排隊執行。可是在併發事務的場景下就會出現對同一個數據的競爭問題,簡單的理解就是你也要讀寫這個數據,我也要讀寫這個數據,那麼你們多個事務操做一個數據的時候怎麼保證數據的一致和完整?這個時候就會出現數據的髒讀、幻讀、重複讀問題。url

髒讀

當一個事務容許讀取另一個事務修改但未提交的數據時,就可能發生髒讀(dirty reads)spa

髒讀是指多個事務同時讀寫一個數據,當事務1中修改和讀取數據A時,事務2對數據A作了修改,而後這個修改反映到了事務A中。

咱們試想有這樣的場景,假如兩個事務都在操做金額表中的同一條記錄,事務A須要得到到當前金額值而後給他作加3的操做(用於買黃瓜),原來這個金額的值是5,可是此時事務B將這條數據的金額修改爲了8,而後這個修改被事務A拿到而後在8的基礎上加了3等於11。可是萬萬沒想到在A事務作完這個操做之後B事務回滾了(反悔了,香蕉的錢沒給)。這個時候A事務完成之後帳戶的金額莫名其妙的變成了11,可是事實上應該是8。這也就是髒讀的狀況。

不可重複讀

在一次事務中,當一行數據獲取兩遍獲得不一樣的結果表示發生了不可重複讀(non-repeatable reads)

在理解不可重複讀以前先理解什麼是可重複讀,可重複讀的意思就是在一個事務中對同一個數據的屢次讀取其結果應該是相同的(在這個事務中沒有修改它的值)。那麼反過來的意思就是在一個事務中對一個數據的屢次讀取的值是不同的,什麼狀況下會出現不可重複讀呢?

仍是上面的例子,假如事務A在作加3操做以前先讀取了原來的值也就是5,而後繼續其餘操做,這個時候事務B對這條記錄進行了加3的操做而後提交了,當事務A再次讀這個值的時候發現當前值變成了8,這個時候先後兩次的值徹底不同,這也就是不可重複讀。

不可重複讀是針對單個事務來講的,也就是在一個事務中是否能夠對一條數據作重複的讀取,若是不能,那麼也就意味着不知足可重複讀的要求。

不可重複讀和髒讀很是相似,可是二者是有區別的髒讀是指事務2沒有提交這個修改就被事務1獲取到了修改後的值,而不可重複讀是指提交了修改之後產生了不一致的狀況。

幻讀

在事務執行過程當中,當兩個徹底相同的查詢語句執行獲得不一樣的結果集。這種現象稱爲幻讀(phantom read)

幻讀其實是不可重讀的一種場景,好比在事務1中,第一次按照某個條件讀取到了3條數據,可是此時事務2在這個表中添加了一條知足此條件的數據,在事務1第二次讀的時候發現多了一條數據(反過來就是少了一條數據),這時候對於事務1來講就有點莫名其妙了,貌似產生了幻覺(發多貨了),因此稱之爲幻讀。


因此針對未提交讀這種隔離級別,這三種讀問題都有可能產生,因此它是級別最低的事務隔離。

READ COMMITTED 讀提交

RC(READ COMMITTED) 讀提交是指在提交之後能夠讀,有些資料稱之爲提交讀(國內翻譯也是醉了)。提交讀主要針對的場景是UPDATE語句,就是針對更新只有提交了之後才能讀,試想一下在上面介紹髒讀的時候,若是事務2在修改完金額之後提交了這個值而不是回滾,那麼久沒有髒讀的狀況。

這也就是爲何提交讀只能解決髒讀的問題而不能解決其餘兩種讀的問題。由於很顯然就算事務2提交了此次修改,那麼對於事務1來講先後兩次的讀取都是不一致的(不可重複讀),固然幻讀的場景更是存在了,由於幻讀原本就是不可重複讀的特殊場景。

REPEATABLE READS 可重複讀

RR(REPEATABLE READS)可重複讀是僅次於SERIALIZABLE(串行化)的一種事務隔離級別,一般可重複讀是經過鎖實現的,它避免不了幻讀的產生。在InnoDB中默認採用RR這種事務隔離級別,可是和其餘數據庫不一樣的是InnoDB在在RR的事務隔離級別下采用了NKL的鎖算法(Next-Key Lock),避免了幻讀的產生。這與其餘數據庫不一樣,因此在InnoDB中RR的事務隔離級別達到了串行化的事務隔離標準。

NKL是指鎖定一個範圍和數據自己,而不是隻單單鎖定數據自己,這樣可以避免幻讀的產生,官方文檔

SERIALIZABLE 可串行化

是最高級別的事務隔離,按照定義是指全部事務都按照串行化進行執行,也就是沒有併發事務的產生,這樣就避免了全部讀問題,可是這對於數據庫來講是不可能的,由於任何一個數據庫都不能忍受這種狀況,因此大多數人認爲採用這種事務隔離級別會對性能產生很是大的影響,可是有些論文經過實驗得出串行化並不會對性能產生太大的影響。

關於串行化是否是對性能產生影響,這取決於數據庫對這種事務隔離級別的實現,不能徹底說串行就必定慢,反正我是不知道是否是真的對性能影響很大。

MySQL數據庫事務隔離級別查詢和修改

查詢事務隔離級別

在MySQL中咱們能夠經過如下方式查詢數據庫採用的事務隔離級別

show variables like '%tx_isolation%';
 
# 查詢回話的事務隔離級別
SELECT @@session.tx_isolation;
#查看全局的隔離級別
SELECT @@global.tx_isolation;

修改事務隔離級別

MySQL 提供了 SET TRANSACTION 語句,該語句能夠改變單個會話或全局的事務隔離級別。語法格式以下:

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

其中,SESSION 和 GLOBAL 關鍵字用來指定修改的事務隔離級別的範圍

  • SESSION:表示修改的事務隔離級別將應用於當前 session(當前 cmd 窗口)內的全部事務;
  • GLOBAL:表示修改的事務隔離級別將應用於全部 session(全局)中的全部事務,且當前已經存在的 session 不受影響;

若是省略 SESSION 和 GLOBAL,表示修改的事務隔離級別將應用於當前 session 內的下一個還未開始的事務。

任何用戶都能改變會話的事務隔離級別,可是隻有擁有 SUPER 權限的用戶才能改變全局的事務隔離級別

JDBC 修改當前鏈接的隔離級別

connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

相關文章
相關標籤/搜索