MySQL之事務

 爲何要有事務?

        事務普遍的運用於訂單系統、銀行系統等多種場景。若是有如下一個場景:A用戶和B用戶是銀行的儲戶。如今A要給B轉帳500元。那麼須要作如下幾件事:mysql

            1. 檢查A的帳戶餘額>500元;sql

            2. A帳戶扣除500元;數據庫

            3. B帳戶增長500元;安全

        正常的流程走下來,A帳戶扣了500,B帳戶加了500,皆大歡喜。那若是A帳戶扣了錢以後,系統出故障了呢?A白白損失了500,而B也沒有收到本該屬於他的500。以上的案例中,隱藏着一個前提條件:A扣錢和B加錢,要麼同時成功,要麼同時失敗。事務的需求就在於此。服務器

  解決方案:當 A 帳戶的餘額減小以後,不要當即修改數據表,而是在確認 B 帳戶的餘額增長以後,同時修改數據表。併發

  事務安全,是一種保護連續操做同時實現(完成)的機制。事務安全的意義就是,保證數據操做的完整性。性能

1、事務的簡介:

  一、事務的定義atom

  事務(Transaction):一個最小的不可再分的工做單元;spa

  一般一個事務對應一個完整的業務(例如銀行帳戶轉帳業務,該業務就是一個最小的工做單元)。.net

  一個完整的業務須要批量的DML(insert、update、delete)語句共同聯合完成。

  事務只和DML語句有關,或者說DML語句纔有事務。這個和業務邏輯有關,業務邏輯不一樣,DML語句的個數不一樣。

  二、存儲引擎(爲何要提MySQL的存儲引擎,由於不是全部的數據庫存儲引擎都支持事務)。

  在實際工做中,選擇一個合適的存儲引擎是一個比較複雜的問題。每種存儲引擎都有本身的優缺點,不能籠統地說誰比誰好。

   

  InnoDB:支持事務處理,支持外鍵,支持崩潰修復能力和併發控制。若是須要對事務的完整性要求比較高(好比銀行),要求實現併發控制(好比售票),那選擇InnoDB有很大的優點。若是須要頻繁的更新、刪除操做的數據庫,也能夠選擇InnoDB,由於支持事務的提交(commit)和回滾(rollback)。 

  MyISAM:插入數據快,空間和內存使用比較低。若是表主要是用於插入新記錄和讀出記錄,那麼選擇MyISAM能實現處理高效率。若是應用的完整性、併發性要求比 較低,也可使用。

  MEMORY:全部的數據都在內存中,數據的處理速度快,可是安全性不高。若是須要很快的讀寫速度,對數據的安全性要求較低,能夠選擇MEMOEY。它對錶的大小有要求,不能創建太大的表。因此,這類數據庫只使用在相對較小的數據庫表。

  注意:同一個數據庫也可使用多種存儲引擎的表。若是一個表要求比較高的事務處理,能夠選擇InnoDB。這個數據庫中能夠將查詢要求比較高的表選擇MyISAM存儲。若是該數據庫須要一個用於查詢的臨時表,能夠選擇MEMORY存儲引擎。

  三、事務的四大特性

  •   A(atomicity) 原子性。一個事務的執行被視爲一個不可分割的最小單元。事務裏面的操做,要麼所有成功執行,要麼所有失敗回滾,不能夠只執行其中的一部分。
  •        C(consistency) 一致性。一個事務的執行不該該破壞數據庫的完整性約束。若是上述例子中第2個操做執行後系統崩潰,保證A和B的金錢總計是不會變的。
  •        I(isolation) 隔離性。一般來講,事務之間的行爲不該該互相影響。然而實際狀況中,事務相互影響的程度受到隔離級別的影響。文章後面會詳述。
  •        D(durability) 持久性。事務提交以後,須要將提交的事務持久化到磁盤。即便系統崩潰,提交的數據也不該該丟失。

  四、事務的併發訪問問題

1)髒讀:髒讀就是指當一個事務正在訪問數據,而且對數據進行了修改,而這種修改尚未提交到數據庫中,這時,另一個事務也訪問這個數據,而後使用了這個數據。
例如:
  張三的工資爲5000,事務A中把他的工資改成8000,但事務A還沒有提交。
  與此同時,
  事務B正在讀取張三的工資,讀取到張三的工資爲8000。
  隨後,
  事務A發生異常,而回滾了事務。張三的工資又回滾爲5000。
  最後,
  事務B讀取到的張三工資爲8000的數據即爲髒數據,事務B作了一次髒讀。

2)不可重複讀:是指在一個事務內,屢次讀同一數據。在這個事務尚未結束時,另一個事務也訪問該同一數據。那麼,在第一個事務中的兩次讀數據之間,因爲第二個事務的修改,那麼第一個事務兩次讀到的的數據多是不同的。這樣就發生了在一個事務內兩次讀到的數據是不同的,所以稱爲是不可重複讀。
例如:
  在事務A中,讀取到張三的工資爲5000,操做沒有完成,事務還沒提交。
  與此同時,
  事務B把張三的工資改成8000,並提交了事務。
  隨後,
  在事務A中,再次讀取張三的工資,此時工資變爲8000。在一個事務中先後兩次讀取的結果並不致,致使了不可重複讀。
3)幻讀:是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的所有數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,之後就會發生操做第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺同樣。
例如:
  目前工資爲5000的員工有10人,事務A讀取全部工資爲5000的人數爲10人。
  此時,
  事務B插入一條工資也爲5000的記錄。
  這是,事務A再次讀取工資爲5000的員工,記錄爲11人。此時產生了幻讀。

  五、事務的隔離級別

  事務的隔離性受到隔離級別的影響。那麼事務的隔離級別是什麼呢?事務的隔離級別能夠認爲是事務的"自私"程度,它定義了事務之間的可見性。隔離級別分爲如下幾種:

          1).READ UNCOMMITTED(未提交讀)。在RU的隔離級別下,事務A對數據作的修改,即便沒有提交,對於事務B來講也是可見的,這種問題叫髒讀。這是隔離程度較低的一種隔離級別,在實際運用中會引發不少問題,所以通常不經常使用。

          2).READ COMMITTED(提交讀)。在RC的隔離級別下,不會出現髒讀的問題。事務A對數據作的修改,提交以後會對事務B可見,舉例,事務B開啓時讀到數據1,接下來事務A開啓,把這個數據改爲2,提交,B再次讀取這個數據,會讀到最新的數據2。在RC的隔離級別下,會出現不可重複讀的問題。這個隔離級別是許多數據庫的默認隔離級別。

          3).REPEATABLE READ(可重複讀)。在RR的隔離級別下,不會出現不可重複讀的問題。事務A對數據作的修改,提交以後,對於先於事務A開啓的事務是不可見的。舉例,事務B開啓時讀到數據1,接下來事務A開啓,把這個數據改爲2,提交,B再次讀取這個數據,仍然只能讀到1。在RR的隔離級別下,會出現幻讀的問題。幻讀的意思是,當某個事務在讀取某個範圍內的值的時候,另一個事務在這個範圍內插入了新記錄,那麼以前的事務再次讀取這個範圍的值,會讀取到新插入的數據。Mysql默認的隔離級別是RR,然而mysql的innoDB引擎間隙鎖成功解決了幻讀的問題。

          4).SERIALIZABLE(可串行化)。可串行化是最高的隔離級別。這種隔離級別強制要求全部事物串行執行,在這種隔離級別下,讀取的每行數據都加鎖,會致使大量的鎖徵用問題,性能最差。

   設置事務的隔離級別:

  方式一:能夠在my.ini文件中使用transaction-isolation選項來設置服務器的缺省事務隔離級別

  該選項值能夠是:

  – READ-UNCOMMITTED

  – READ-COMMITTED

  – REPEATABLE-READ

  – SERIALIZABLE

  • 例如: [mysqld] transaction-isolation = READ-COMMITTED

  方式二:經過命令動態設置隔離級別 

  隔離級別也能夠在運行的服務器中動態設置,應使用SET TRANSACTION ISOLATION LEVEL語句。 

  其語法模式爲:

  SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL <isolation-level>
  其中的<isolation-level>能夠是:
  – READ UNCOMMITTED
  – READ COMMITTED
  – REPEATABLE READ
  – SERIALIZABLE
  • 例如: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

 六、事務的一些術語及兩條重要的SQL語句
  • 開啓事務:Start Transaction
  • 事務結束:End Transaction
  • 提交事務:Commit Transaction
  • 回滾事務:Rollback Transaction

在MySQL中,默認狀況下,事務是自動提交的,也就是說,只要執行一條DML語句就開啓了事物,而且提交了事務(固然,自動提交機制也是能夠關閉的)。

2、事務操做實例

  事務操做,分爲兩種:自動事務(默認的),手動事務。

  以銀行帳戶的餘額增減爲例,咱們來了解手動事務的操做流程。

  執行以下 SQL 語句,建立銀行帳戶表並插入數據:

-- 建立銀行帳戶表
create table bank_account(
    id int primary key auto_increment,
    cardno varchar(16) not null unique comment 'bank card number',
    name varchar(20) not null,
    money decimal(10,2) default 0.0 comment 'account balance' 
)charset utf8;

-- 插入數據
insert into bank_account values
(null, '0000000000000001', 'Charies', 8000),
(null, '0000000000000002', 'Gavin', 6000);

 

  操做事務:

 

第 1 步:開啓事務,告訴系統如下全部操做,不要直接寫入數據庫,先存到事務日誌。

  • 基本語法:start transaction;

執行如上 SQL 語句,開啓事務:

 

第 2 步:減小 Charies 帳戶的餘額

-- 更新 Charies 帳戶餘額
update bank_account set money = money - 1000 where id = 1;
-- 查詢 bank_account 表數據
select * from bank_account;

如圖所示:

  Charies 帳戶的餘額顯示減小1000,但實際上,因爲咱們開啓了事務,數據表真實的數據,並無同步更新。爲了驗證這個論斷,咱們從新打開一個數據庫客戶端,查詢bank_account表的數據:

如上圖所示:

  顯然數據庫的事務安全機制起了做用,當咱們開啓(手動)事務以後,其後一系列操做並無直接寫入數據庫,而是存入了事務日誌。在這裏,咱們並無打開數據庫事務的日誌進行驗證,由於事務日誌存儲的是通過編譯以後的字節碼文件。

 

第 3 步:增長 Gavin 帳戶的餘額

-- 更新 Gavin 帳戶餘額
update bank_account set money = money + 1000 where id = 2;
-- 查詢 bank_account 表數據
select * from bank_account;

如上圖所示:

  Gavin 帳戶的餘額顯示增長1000,可是,因爲咱們開啓了事務,數據表真實的數據,仍然沒有同步更新。

 

第 4 步:提交事務或回滾事務

  •   提交事務基本語法:commit;
  •   回滾事務基本語法:rollback;

若是咱們選擇提交事務,則將事務日誌存儲的記錄直接更新到數據庫,並清除事務日誌;若是咱們選擇回滾事務,則直接將事務日誌清除,全部在開啓事務至回滾事務之間的操做失效,保持原有的數據庫記錄不變。在這裏,咱們以提交事務爲例:

-- 提交事務
commit;
-- 查詢 bank_account 表數據
select * from bank_account;

如上圖所示:

  當咱們提交事務以後,數據庫的真實記錄更新,兩個客戶端的數據一致。

值得咱們注意的是:

  當咱們提交事務以後,在進行回滾事務是不起做用的,由於事務日誌在提交事務的同時已經被清除了!

 

 參考博客:https://blog.csdn.net/qq_35246620/article/details/78305872

相關文章
相關標籤/搜索