1. 程式人生 > >MySQL(十三)之MySQL事務

MySQL(十三)之MySQL事務

發出 最簡 -i 更改 讀取數據 兩種方法 mysql ont 之間

前言

  這段時間自己會把之前學的東西都總結一遍,希望對自己以後的工作中有幫助。其實現在每天的狀態都是很累的,但是我要堅持!

  進入我們今天的正題:

  為什麽MySQL要 有事務呢?事務到底是用來幹什麽的?我們通過一個例子來說明:

  事務廣泛的運用於訂單系統、銀行系統等多種場景。如果有以下一個場景:A用戶和B用戶是銀行的儲戶。現在A要給B轉賬500元。那麽需要做以下幾件事:

    1)檢查A的賬戶余額>500元;
    2)A賬戶扣除500元;
    3)賬戶增加500元;

  正常的流程走下來,A賬戶扣了500,B賬戶加了500,皆大歡喜。那如果A賬戶扣了錢之後,系統出故障了呢?A白白損失了500,而B也沒有收到本該屬於他的500。

  以上的案例中,隱藏著一個前提條件:A扣錢和B加錢,要麽同時成功,要麽同時失敗。事務的需求就在於此!

一、MySQL事務概述

1.1、MySQL事務簡介

  MySQL事務是訪問並更新數據庫中各種數據項的一個程序執行單元。在事務中的操作,要麽都執行修改,要麽都不執行,這就是事務的目的,也是事務模型區別於文件系統的重要特征之一。

  MySQL事務主要用於處理操作量大,復雜度高的數據。比如說,在員工管理系統中,刪除一個員工,既需要刪除員工的基本資料,也要刪除和該員工相關的其他信息。這些數據庫操作語句就構成一個事務。

  在MySQL中只有使用了InnoDB數據庫引擎的數據庫或者表才支持事務。

  事務處理可以用來維護數據庫的完整性,保證成批的 SQL 語句要麽全部執行,要麽全部不執行

  事務用來管理 insert,update,delete 語句

1.2、事務的四大特性(ACID特性)

  嚴格上來說,事務必須同時滿足4個特性,即通常所說事務的ACID特性。雖然理論上定義了嚴格的事務要求,但是數據庫廠商出於各種目的並沒有嚴格滿足事務的ACID標準。

  例如,對於MYSQL的NDB Cluster引擎,雖然支持事務,但是不滿足D的要求,即持久性的要求。對於Oracle數據庫來說,其默認的事務隔離級別為READ COMMITTED,不滿足I的要求,即隔離性的要求。

  對於InnoDB存儲引擎而言,默認的事務隔離級別是READ REPRATABLE,完全遵循和滿足事務的ACID特性。

  1)原子性(Atomicity):指事務包含的所有操作要麽全部成功,要麽全部失敗回滾,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。

  2)一致性(Consistency):指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態

    (例如:拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那麽不管A和B之間如何轉賬,轉幾次賬,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。)

  3)隔離性(Isolation):指當多個用戶並發訪問數據庫操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所幹擾,多個並發事務之間要相互隔離

  4)持久性(Durability):指一個事務一旦被提交了,那麽對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。

  註意:事務的(ACID)特性是由關系數據庫管理系統(RDBMS,數據庫系統)來實現的。數據庫管理系統采用日誌來保證事務的原子性、一致性和持久性。日誌記錄了事務對數據庫所做的更新,

     如果某個事務在執行過程中發生錯誤,就可以根據日誌,撤銷事務對數據庫已做的更新,使數據庫退回到執行事務前的初始狀態。數據庫管理系統采用鎖機制來實現事務的隔離性。

     當多個事務同時更新數據庫中相同的數據時,只允許持有鎖的事務能更新該數據,其他事務必須等待,直到前一個事務釋放了鎖,其他事務才有機會更新該數據。

1.3、事務分類

  扁平事務:最簡單,使用最頻繁的事務。在扁平事務中,所有的操作都處於一個層次,其有BEGIN WORK開始,有COMMIT WORK或ROLLBACK WORK結束。處於之間的操作是原子的,要麽全部執行,要麽全部回滾。

  帶有保存點的扁平事務:除了扁平事務支持的操作外,允許在事務執行過程中回滾到同一事務中較早的一個狀態,這是因為可能有些事務在執行過程中出現的錯誤並不會對有的操作都無效,

            放棄整個事務不合乎要求,開銷也太大。保存點用來通知系統應該記住事務當前的狀態,以便以後發生錯誤時,事務能回到該狀態。

  鏈事務:可視為保存點模式的一個變種。

  嵌套事務:一個層次結構框架。

  分布式事務

二、事務控制語句

  在MYSQL命令行的默認設置下,事務都是自動提交的,即執行SQL語句後就會馬上執行COMMIT操作。

  因此要顯示的開啟一個事務必須使用命令BEGIN和START TRANSACTION,或者執行命令SET AUTOCOMMIT = 0,以禁用當前會話的自動提交

  1)START TRANSACTION | BEGIN

    顯示的開啟一個事務。在存儲過程中,MYSQL數據庫的分析器會自動將BEGIN識別為BEGIN...END,因此在存儲過程中只能使用START TRANSACTION語句來開啟一個事務。

  2)COMMIT

    要想使用這個語句的最簡形式,只需發出COMMIT。COMMIT會提交事務,並使已對數據庫進行的所有修改成為永久性的。COMMIT和COMMIT WORK語句基本上是一致的,都是用來提交事務。

    不同的是COMMIT WORK用來控制事務結束後的行為是CHAIN還是RELEASE的。如果是CHAIN方式,那麽事務就變成了鏈事務。用戶可以通過參數completion_type來進行控制,默認該參數是0,表示沒有任何操作。

    在這種設置下,COMMIT和COMMIT WORK是完全等價的。當參數值為1時,COMMIT WORK等價於COMMIT AND CHAIN,表示馬上自動開啟一個相同隔離級別的事務。

    當參數值為1時,COMMIT WORK等價於COMMIT AND RELEASE。當提交事務後會自動斷開與服務器連接。

  3)ROLLBACK

    回滾會結束用戶的事務,並撤銷正在進行的所有未提交的修改。

  4)SAVEPOINT identifiter

    SAVEPOINT允許用戶在事務中創建一個保存點,一個事務可以有很多個保存點

  5)RELEASE SAVEPOINT identifier

    刪除一個事務的保存點,當沒有一個保存點執行這語句時,會拋出一個異常。

  6)ROLLBACK to [SAVEPOINT] identifier

    這個語句與SAVEPOINT命令一起使用。可以把事務回滾到標記點,而不回滾到此標記點之前的任何工作

    註意:雖然有ROLLBACK,但是它並沒有真正的結束一個事務,因此即使執行了ROLLBACK TO SAVEPOINT,之後也需要顯示的運行COMMIT或ROLLBACK命令。

  7)SET TRANSACTION

    這個語句用來設置事務的隔離級別。InnoDB存儲引擎提供的事務隔離級別有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE

三、MySQL事務處理的兩種方法

1)用 BEGIN,ROLLBACK,COMMIT來實現

  BEGIN 開始一個事務

  ROLLBACK 事務回滾

  COMMIT 事務確認

2)直接用SET來改變MySQL的自動提交模式

  SET AUTOCOMMIT=0 禁止自動提交

  SET AUTOCOMMIT=1 開啟自動提交

四、MySQL中事務的隔離級別

4.1、在MySQL中如果不考慮事務的隔離性,會發生的幾個問題

  1)臟讀

  指在一個事務處理過程裏讀取了另一個未提交的事務中的數據。

  當一個事務正在多次修改某個數據,而在這個事務中這多次的修改都還未提交,這時一個並發的事務來訪問該數據,就會造成兩個事務得到的數據不一致。

  舉例:

    用戶A向用戶B轉賬100元,對應SQL命令如下:

    update account set money=money+100 where name=’B’;(此時A通知B)

    update account set money=money-100 where name=’A’;

    當只執行第一條SQL時,A通知B查看賬戶,B發現確實錢已到賬(此時即發生了臟讀),而之後無論第二條SQL是否執行,只要該事務不提交,則所有操作都將回滾,那麽當B以後再次查看賬戶時就會發現錢其實並沒有轉。 

  2)不可重復讀

  指在對於數據庫中的某個數據,一個事務範圍內多次查詢卻返回了不同的數據值,這是由於在查詢的時候,被另一個事務修改並提交了。

  舉例:

    事務T1在讀取某一數據,而事務T2立馬修改了這個數據並且提交事務給數據庫,事務T1再次讀取該數據就得到了不同的結果,發送了不可重復讀。

  不可重復讀和臟讀的區別是,臟讀是某一事務讀取了另一個事務未提交的臟數據,而不可重復讀則是讀取了前一事務提交的數據。

  在某些情況下,不可重復讀並不是問題,比如我們多次查詢某個數據當然以最後查詢得到的結果為主。但在另一些情況下就有可能發生問題,例如對於同一個數據A和B依次查詢就可能不同,A和B就可能打起來了。 

  3)虛讀(幻讀)

  幻讀是事務非獨立執行時發生的一種現象。

  舉例:

    事務T1對一個表中所有的行的某個數據項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值還是為“1”並且提交給數據庫。

    而操作事務T1的用戶如果再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀。

  幻讀和不可重復讀都是讀取了另一條已經提交的事務(這點就臟讀不同),所不同的是不可重復讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)。

4.2、MySQL數據庫的四種隔離界別

   InnoDB存儲引擎提供事務的隔離級別有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。       

  1)Serializable (串行化):可避免臟讀、不可重復讀、幻讀的發生。

    它通過強制事務串行執行,避免了前面說的幻讀的問題。
  2)Repeatable read (可重復讀):可避免臟讀、不可重復讀的發生。

    解決了臟讀,也保證了在同一個事務中多次讀取同樣記錄的結果是一致的。但是理論上,可重讀讀隔離級別還是無法解決另外一個幻讀的問題,

    指的是當某個事務在讀取某個範圍內的記錄時,另外一個事務也在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍內的記錄時,會產生幻行。
  3)Read committed (讀已提交):可避免臟讀的發生。

    一個事務從開始直到提交之前,所做的任何修改對其他事務都是不可見的。

    這個級別有時候也叫做不可重復讀,因為兩次執行相同的查詢,可能會得到不一樣的結果。因為在這2次讀之間可能有其他事務更改這個數據,每次讀到的數據都是已經提交的。
  4)Read uncommitted (讀未提交):最低級別,任何情況都無法保證

    事務中的修改,即使沒有提交,在其他事務也都是可見的。事務可以讀取未提交的數據,這也被稱為臟讀。

  分析:

    1)以上四種隔離級別最高的是Serializable級別,最低的是Read uncommitted級別,當然級別越高,執行效率就越低。像Serializable這樣的級別,

     就是以鎖表的方式(類似於Java多線程中的鎖)使得其他的線程只能在鎖外等待,所以平時選用何種隔離級別應該根據實際情況。在MySQL數據庫中默認的隔離級別為Repeatable read (可重復讀)。

    2)在MySQL數據庫中,支持上面四種隔離級別,默認的為Repeatable read (可重復讀)。

     而在Oracle數據庫中,只支持Serializable (串行化)級別和Read committed (讀已提交)這兩種級別,其中默認的為Read committed級別。

    3)Repeatable read是MySQL的默認事務隔離級別,它確保同一事務的多個實例在並發讀取數據時,會看到同樣的數據行。

     不過理論上,這會導致另一個棘手的問題:幻讀 (Phantom Read)。簡單的說,幻讀指當用戶讀取某一範圍的數據行時,另一個事務又在該範圍內插入了新行,

     當用戶再讀取該範圍的數據行時,會發現有新的“幻影” 行。InnoDB和Falcon存儲引擎通過多版本並發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。

    4)在MySQL數據庫中查看當前事務的隔離級別:select @@tx_isolation;

    5)在MySQL數據庫中設置事務的隔離級別

      set [glogal|session] transaction isolation level 隔離級別名稱;
      set tx_isolation=’隔離級別名稱’;

     如果想啟動時就設置事務的默認隔離級別,修改MYSQL的配置文件,在[mysqld]中添加如下行:

      [mysqld]

      transaction-isolation = READ-COMMITTED

    註意:設置數據庫的隔離級別一定要是在開啟事務之前!  

  

MySQL(十三)之MySQL事務