天天看點

mysql事物隔離

項目問題

最近工作内容需要向一張表裡面寫入資料,有兩個實作方法,每種方法會運作得到一份結果,兩個結果的key會有大部分重複,後面跟的value會有不同。

表格中隻允許兩個結果中其中的一個key存在,二者選其一,隻能更新替代。在往表裡寫的程式是沒有錯誤的,但最終從表裡查詢的時候,發現很多key有兩份資料結果,思考其原因,可能跟事物隔離有關系,這裡講解下事物及事物隔離。

Mysql事物

MySQL 事務主要用于處理操作量大,複雜度高的資料。比如說,在人員管理系統中,你删除一個人員,你即需要删除人員的基本資料,也要删除和該人員相關的資訊,如信箱,文章等等,這樣,這些資料庫操作語句就構成一個事務!

  • 在 MySQL 中隻有使用了 Innodb 資料庫引擎的資料庫或表才支援事務。原生的 MyISAM 引擎就不支援事務。
  • 事務處理可以用來維護資料庫的完整性,保證成批的 SQL 語句要麼全部執行,要麼全部不執行。
  • 事務用來管理 insert,update,delete 語句。

一般來說,事務是必須滿足4個條件(ACID)::原子性(Atomicity,或稱不可分割性)、一緻性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。

  • 原子性:一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被復原(Rollback)到事務開始前的狀态,就像這個事務從來沒有執行過一樣。
  • 一緻性:在事務開始之前和事務結束以後,資料庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精确度、串聯性以及後續資料庫可以自發性地完成預定的工作。
  • 隔離性:資料庫允許多個并發事務同時對其資料進行讀寫和修改的能力,隔離性可以防止多個事務并發執行時由于交叉執行而導緻資料的不一緻。事務隔離分為不同級别,包括讀未送出(Read uncommitted)、讀送出(read committed)、可重複讀(repeatable read)和串行化(Serializable)。
  • 持久性:事務處理結束後,對資料的修改就是永久的,即便系統故障也不會丢失。

事物控制語句

事物用來管理更新,删除,插入語句。常用的事物控制語句有:

  • BEGIN 顯式地開啟一個事務;
  • COMMIT 會送出事務,并使已對資料庫進行的所有修改成為永久性的;
  • ROLLBACK 復原會結束使用者的事務,并撤銷正在進行的所有未送出的修改;
  • SAVEPOINT identifier,SAVEPOINT 允許在事務中建立一個儲存點,一個事務中可以有多個 SAVEPOINT;
  • RELEASE SAVEPOINT identifier 删除一個事務的儲存點,當沒有指定的儲存點時,執行該語句會抛出一個異常;
  • ROLLBACK TO identifier 把事務復原到标記點;
  • SET TRANSACTION 用來設定事務的隔離級别。InnoDB 存儲引擎提供事務的隔離級别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE 這四種方式。

常用的事物處理語句:

  • BEGIN 開始一個事務;
  • ROLLBACK 事務復原;
  • COMMIT 事務确認;
  • SET AUTOCOMMIT=0 禁止自動送出;
  • SET AUTOCOMMIT=1 開啟自動送出;

這裡注意一點,根據自身項目需要,将AUTOCOMMIT設定為0或1。

事物隔離

回到文章剛開始的問題,在表中出現了一個key具有兩種結果,初步估計是事物隔離的問題。上面簡單介紹了事物,以及事物隔離的四個類别,這裡詳細介紹。

當資料庫上有多個事務同時執行的時候,就可能出現髒讀(dirty read)、不可重複讀(non-repeatable read)、幻讀(phantom read)的問題,為了解決這些問題,就有了“隔離級别”的概念。

  • 髒讀:髒讀就是指當一個事務正在通路資料,并且對資料進行了修改,而這種修改還沒有送出到資料庫中,這時,另外一個事務也通路這個資料,然後使用了這個資料;
  • 不可重複讀:是指在一個事務内,多次讀同一資料。在這個事務還沒有結束時,另外一個事務也通路該同一資料。那麼,在第一個事務中的兩 次讀資料之間,由于第二個事務的修改,那麼第一個事務兩次讀到的的資料可能是不一樣的。這樣就發生了在一個事務内兩次讀到的資料是不一樣的,是以稱為是不可重複讀。例如,一個編輯人員兩次讀取同一文檔,但在兩次讀取之間,作者重寫了該文檔。當編輯人員第二次讀取文檔時,文檔已更改。原始讀取不可重複。如果隻有在作者全部完成編寫後編輯人員才可以讀取文檔,則可以避免該問題。
  • 幻讀: 是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,這種修改涉及到表中的全部資料行。 同時,第二個事務也修改這個表中的資料,這種修改是向表中插入一行新資料。那麼,以後就會發生操作第一個事務的使用者發現表中還有沒有修改的資料行,就好象 發生了幻覺一樣。例如,一個編輯人員更改作者送出的文檔,但當生産部門将其更改内容合并到該文檔的主複本時,發現作者已将未編輯的新材料添加到該文檔中。 如果在編輯人員和生産部門完成對原始文檔的處理之前,任何人都不能将新材料添加到文檔中,則可以避免該問題。

在談隔離級别之前,首先要知道,隔離得越嚴實,效率就會越低。是以很多時候,需要在二者之間尋找一個平衡點。SQL标準的事務隔離級别包括:讀未送出(read uncommitted)、讀送出(read committed)、可重複讀(repeatable read)和串行化(serializable ):

  • 讀未送出:一個事務還沒送出時,它做的變更就能被别的事務看到;
  • 讀送出: 一個事務送出之後,它做的變更才會被其他事務看到;
  • 可重複讀:一個事務執行過程中看到的資料,總是跟這個事務在啟動時看到的資料是一緻的。當然在可重複讀隔離級别下,未送出變更對其他事務也是不可見的;
  • 串行化:對于同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,後通路的事務必須等前一個事務執行完成,才能繼續執行;

是以,查詢寫錯表的事物隔離類型:

mysql> SELECT @@tx_isolation tableName           

結果為:

READ-COMMITTED           

可以看到,該表的隔離類型為讀送出, 即需要送出後其變化才能被另外的事物看見。造成這種情況出現的問題是兩個結果都在往表裡寫入,當其中一個結果中的某個key寫入後沒有送出時,第二個結果也從其中查詢是否可以插入或更新的條件,沒有查詢到,就直接結果插入到表中了,這樣造成了重複性的插入。解決辦法就是兩個結果按照順序進行寫表,寫完第一個,再寫第二個,這樣就不會出現這種問題了。

總結

  • 1、事務的特性:原子性、一緻性、隔離性、持久性;
  • 2、多事務同時執行的時候,可能會出現的問題:髒讀、不可重複讀、幻讀;
  • 3、事務隔離級别:讀未送出、讀送出、可重複讀、串行化;
  • 4、不同僚務隔離級别的差別:
    • 讀未送出:一個事務還未送出,它所做的變更就可以被别的事務看到;
    • 讀送出:一個事務送出之後,它所做的變更才可以被别的事務看到;
    • 可重複讀:一個事務執行過程中看到的資料是一緻的。未送出的更改對其他事務是不可見的;
    • 串行化:對應一個記錄會加讀寫鎖,出現沖突的時候,後通路的事務必須等前一個事務執行完成才能繼續執行;
  • 5、配置方法:啟動參數transaction-isolation;
  • 6、事務隔離的實作:每條記錄在更新的時候都會同時記錄一條復原操作。同一條記錄在系統中可以存在多個版本,這就是資料庫的多版本并發控制(MVCC);
  • 7、事務啟動方式:一、顯式啟動事務語句,begin或者start transaction,送出commit,復原rollback;二、set autocommit=0,該指令會把這個線程的自動送出關掉。這樣隻要執行一個select語句,事務就啟動,并不會自動送出,直到主動執行commit或rollback或斷開連接配接;
  • 8、如果考慮多一次互動問題,可以使用commit work and chain文法。在autocommit=1的情況下用begin顯式啟動事務,如果執行commit則送出事務。如果執行commit work and chain則送出事務并自動啟動下一個事務;

參考