天天看點

MySQL事務篇

一、一條Insert語句

為了故事的順利發展,我們需要建立一個表

CREATE TABLE t (

id INT PRIMARY KEY,

c VARCHAR(100)

) Engine=InnoDB CHARSET=utf8;

然後向這個表裡插入一條資料:

現在表裡的資料就是這樣的:

二、執行流程

CREATE TABLE t (

id INT PRIMARY KEY,

c VARCHAR(100)

) Engine=InnoDB CHARSET=utf8;

1

2

3

4

1 INSERT INTO t VALUES(1, '劉備');

mysql> SELECT * FROM t;

+----+--------+

| id | c |

+----+--------+

| 1 | 劉備 |

+----+--------+

1 row in set (0.01 sec)

MySQL事務篇

二、事務介紹 

1、事務概述

MySQL是一個伺服器/用戶端架構的軟體,對于同一個伺服器來說,可以有若幹個用戶端與之連接配接,每 個用戶端與伺服器連接配接上之後,就可以稱之為一個會話(Session )。我們可以同時在不同的會話裡輸 入各種語句,這些語句可以作為事務的一部分進行處理。不同的會話可以同時發送請求,也就是說服務 器可能同時在處理多個事務,這樣子就會導緻不同的事務可能同時通路至IJ相同的記錄。我們前邊說過事 務有一個特性稱之為隔離性,理論上在某個事務對某個資料進行通路時,其他事務應該進行排隊,當該 事務送出之後,其他事務才可以繼續通路這個資料。但是這樣子的話對性能影響太大,是以設計資料庫 的大叔提出了各種隔離級别,來最大限度的提升系統并發處理事務的能力,但是這也是以犧牲一定的隔 離性來達到的。

事務是資料庫最為重要的機制之一,凡是使用過資料庫的人,都了解資料庫的事務機制,也對ACID四個 基本特性如數家珍。但是聊起事務或者acid的底層實作原理,往往言之不詳,不明是以。在MySQL中的 事務是由存儲引擎實作的,而且支援事務的存儲引擎不多,我們主要講解InnoDB存儲弓|擎中的事務。 是以,今天我們就一起來分析和探讨InnoDB的事務機制,希望能建立起對事務底層實作原理的具體了 解。

MySQL事務篇

資料庫事務具有ACID四大特性。ACID是以下4個詞的縮寫:

•原子性(atomicity):事務最小工作單元,要麼全成功,要麼全失敗。

・一緻性(consistency):事務開始和結束後,資料庫的完整性不會被破壞。

•隔離性(isolation):不同僚務之間互不影響,四種隔離級别為RU (讀未送出)、RC (讀已提 交)、RR (可重複讀)、SERIALIZABLE (串行化)。

•持久性(durability):事務送出後,對資料的修改是永久性的,即使系統故障也不會丢失。

2、隔離級别

1)未送出讀(READ UNCOMMITTED/RU)

髒讀:一個事務讀取到另一個事務未送出的資料。

如果一個事務讀到了另一個未送出事務修改過的資料,那麼這種隔離級别就稱之為未送出讀(英文名:

READ UNCOMMITTED )

2)已送出讀(READ COMMITTED/RC)

不可重複讀:一個事務因讀取到II另一個事務已送出的update。導緻對同一條記錄讀取兩次以上的 結果不一緻。

如果一個事務隻能讀到另一個已經送出的事務修改過的資料,并且其他事務每對該資料進行一次修改并 送出後,該事務都能查詢得到最新值,那麼這種隔離級别就稱之為已送出讀(英文名:READ COMMITTED )

3)可重複讀(REPEATABLE READ/RR)

幻讀:一個事務因讀取至II另一個事務闩送出的insert資料或者delete資料。導緻對同一張表讀取兩 次以上的結果不一緻。

在一些業務場景中,一個事務隻能讀到另一個已經送出的事務修改過的資料,但是第一次讀過某條記錄 後,即使其他事務修改了該記錄的值并且送出,該事務之後再讀該條記錄時,讀到的仍是第一次讀到的 值,而不是每次都讀到不同的資料。那麼這種隔離級别就稱之為可重複讀(英文名:REPEATABLE READ)

4)串行化(SERIALIZABLE)

以上3種隔離級^都允許對同一a記錄進行讀-讀、讀-寫、寫-讀的并發操作,如果我們不允許醫寫、 寫-讀的并發操作,

三、事務和MVCC底層原理詳解

1、思考:丢失更新

兩個事務針對同一資料都發生修改J菊作時,會存在丢失更新問題。

這種方案比較簡單粗暴,就是一個事務去讀取一a瓣的時候,就上鎖,不允許其他事務來操作。 MySQL加鎖之後就是目前讀。假如目前事務隻是加共享鎖,那麼其他事務就不能有排他鎖,也就是不能 修改資料;同段如目前事務需要加排他鎖,那麼其他事務就不能持有田可鎖。總而言之,能加鎖成功, 就確定了除了目前事務之外,其他事務不會對目前資料産生影響,是以自然而然的,目前事務讀取到的 瓣就隻能是最新的,而不會是快照資料

當然使用MVCC (MVCC多版本的并發控制,英文全稱:Multi Version Concurrency Control)機制可 以解決這個問題。查詢總額事務先讀取了使用者A的賬戶存款,然後轉賬事務會修改使用者A和使用者B賬戶存 款,查詢總額事務讀取使用者B存款時不會讀取轉賬事務修改後的資料,而是讀取本事務開始時的資料副 本(在REPEATABLE READ隔離等級下)。

MVCC使得資料庫讀不會對資料加鎖,普通的SELECT請求不會加鎖,提高了資料庫的并發處理能力。借助MVCC,資料庫可以實作READ COMMIXED, REPEATABLE READ等隔離級使用者可以檢視目前數 據的前一個或者前幾個曆史版本,保證了ACID中的I特性(隔離性)。

InnoDB的MVCC實作

我們首先來看一下wi ki上對MVCC的定義:

Multiversion concurrency control (MCC or MVCC), is a concurrency control method commonly used by database management systems to provide concurrent access to the database and in programming languages to implement transactional memory.

由定義可知,MVCC是用于資料庫提供并發通路控制的并發控制技術。與MVCC相對的是基于鎖的并發控 制,Lock-Based Concurrency Control。MVCC最大的好處,相信也是耳熟能詳:讀不加鎖,讀寫不沖突。在讀多寫少的OLTP應用中,讀寫不沖突是非常重要的,極大的增加了系統的并發性能,這也是為 什麼現階段,幾乎所有的RDBMS,都支援了MVCC。

多版本并發控制僅僅是一種技術概念,并沒有統一的實作标準,其核心理念就是資料快照,不同的事務 通路不同版本的資料快照,進而實作不同的事務隔離級别。雖然字面上是說具有多個版本的資料快照, 但這并不意味着資料庫必須拷貝資料,儲存多份資料檔案,這樣會浪費大量的存儲空間。InnoDB通過事 務的undo日志巧妙地實作了多版本的資料快照。

SELECT

InnoDB會根據以下兩個條件檢查每行記錄:

1. InnoDB隻查找版本早于目前事務版本的資料行(也就是,行的事務編号小于或等于目前事務的事 務編号),這樣可以確定事務讀取的行,要麼是在事務開始前已經存在的,要麼是事務自身插入或 者修改過的。

2. 删除的行要事務ID判斷,讀取到事務開始之前狀态的版本。

隻有符合上述兩個條件的記錄,才能傳回作為查詢結果。

INSERT

InnoDB為新插入的每一行儲存目前事務編号作為行版本号。

DELETE

InnoDB為删除的每一行儲存目前事務編号作為行删除辨別。

UPDATE

InnoDB為插入一行新記錄,儲存目前事務編号作為行版本号,同時儲存目前事務編号到原來的行作為行 删除辨別。

儲存這兩個額外事務編号,使大多數讀操作都可以不用加鎖。這樣設計使得讀瓣操作很簡單,性能很 好,并且也能保證隻我取到符合标準的行。不足之處是每行記謡隔要額外的存儲空間,需要噸多

MVCC隻在REPEATABLE READ和READ COMMITIED兩個隔離級§!]下工作。其他兩個隔離級§!]都和 MVCC不相容,因為READ UNCOMMITIED總是讀取最新的資料行,而不是符合目前事務版本的數 據行。而SERIALIZABLE則會對所有讀取的行都加鎖

2) undo log

根據行為的不同,undo log分為兩種:1 nsert undo log和update undo log

• insert undo log:

1 是在i nsert操作中産生的undo log:

2 因為insert操作的記錄隻對事務本身可見,對于其它事務此記錄是不可見的,是以insert undo log可以在事務送出後直接删除而不需要進行purge操作。

為了更好的支援并發,InnoDB的多版本一緻性讀是采用了基于復原段的的方式。另外,對于更新 和删除操作,InnoDB并不是真正的删除原來的記錄,而是設定記錄的delete mark為1。是以為了 解決資料Page和Undo Log膨脹的問題,需要引入purge機制進行回收。Undo log儲存了記錄修改 前的鏡像。在InnoDB存儲弓|擎中,undo log分為:

• insert undo log

• update undo log

insert undo log是指在insert操作中産生的undo log。由于insert操作的記錄,隻是對本事務可 見,其他事務不可見,是以undo log可以在事務送出後直接删除,而不需要purge操作。

叩date undo log是指在delete和up date操作中産生的undo log。該undo log會被後續用于MVCC 當中,是以不能送出的時候删除。送出後會放入undo log的連結清單,等待purge線程進行最後的删 除。

3) Readview

對于使用READ UNCOMMITTED隔離級的事務來說,直接讀取記錄的最新版本就好了。對于使用 SERIALIZABLE隔離級§!]的事務來說,使用加鎖的方式來通路記錄。對于使用READ COMMITTED和 REPEATABLE READ隔離級§啲事務來說,就需要用到我們上邊所說的版本鍊了。核心問題就是需要判 斷一下版本鍊中的哪個版本是目前事務可見的。是以設計innoDB的設計者提出了一個ReadView的概 念,這個Readview中主要包含目前系統中還有哪些活躍的讀寫事務,把它們的事務id放到一個清單 中,我們把這個清單命名為為m_ids。

這樣在通路某條記錄時,隻需要按照下邊的步驟判斷記錄的某個版本(版本鍊中的版本)是否可見:

•如果被通路版本的trx_i d屬性值小幹m_i ds清單中最小的事務d,表明生成該版本的事務在生成 Readview前已經送出,是以該版本可以誠目前事務通路。

•如果被通路版本的trx_i d屬性值大幹m_i ds清單中最大的事務d,表明生成該版本的事務在生成 Readview後才生成,是以該版本不可以被目前事務通路。

•如果誠通路版本的trx i d屬性值在m i ds清單中晶大的事務d和晶小事務id力間,那就霊要判斷 —下trx_id屬性值是不是在m_ids清單中,如果在,說明建立Readvi ew時生成該版本的事務還 是活躍的,該版本不可以被通路;如果不在,說明建立Readview時生成該版本的事務已經被提 交,該版本可以被通路。

如果某個版本的資料對目前事務不可見的話,那就順着版本鍊找到下一個版本的資料,繼續按照上 邊的步驟判斷可見性,依此類推,直到版本鍊中的最後一個版本,如果最後一個版本也不可見的 話,那麼就意味着該條記錄對該事務不可見,查詢結果就不包含該記錄。

在MySQL中,READ COMMITTED和REPEATABLE READ隔離級别的的一個非常大的差別就是它們生成

6、MVCC總結

從上邊的描述中我們可以看出來,所謂的MVCC(Multi-Version Concurrency Control,多版本并發控 制)指的就是在使用READ COMMITTD、REPEATABLE READ這兩種隔離級别的事務在執行普通的 SEELCT操作時通路記錄的版本鍊的過程,這樣子可以使不同僚務的讀-寫、寫-讀操作并發執行,進而 提升系統性能。

READ COMMITTD、REPEATABLE READ這兩個隔離級别的一個很大不同就是生成Readview的時機不 同,READ COMMITTD在每一次進行普通SELECT操作前都會生成一個Readview,而REPEATABLE READ隻在第一次進行普通SELECT操作前生成一個ReadVi ew,之後的查詢操作都重複這個ReadVi ew 就好了。

四、事務復原和資料恢複

事務的隔離性由多版本控制機制和鎖實作,而原子性,持久性和一緻性主要是通過red。log undo log 和Force Log at Commit機制機制來完成的。redo log用于在崩潰時恢複資料,undo log用于對事務的影 響進行撤銷,也可以用于多版本控制。而Force Log at Commit機制保證事務送出後redo log日志都已經 持久化。

開啟一個事務後,使用者可以使用COMMIT來送出,也可以用ROLLBACK來復原。其中COMMIT或者 ROLLBACK執行成功之後,資料一定是會被全部儲存或者全部復原到最初狀态的,這也展現了事務的原 子性。但是也會有很多的異常情況,比如說事務執行中途連接配接斷開,或者是執行COMMIT或者

我們先來看一下redo log的原理,redo log顧名思義,就是重做日志,每次資料庫的SQL操作導緻的資料 變化它都會記錄一下,具體來說,redo log是實體日志,記錄的是資料庫頁的實體修改操作。如果資料 發生了丢失,資料庫可以根據red。log進行資料恢複。