MySQL 事務最全詳解

什么是事務?

mysql 官方的一句話來描述事務是什么?MySQL 事務主要用于處理操作量大,復雜度高的數據。那何為數據量大?何為復雜度高呢?我用我自己的理解來描述一下吧。事務其實就是 MySQL 中處理數據的一種方式,主要用在數據完整性高,數據之間依賴性大的情況下的一種數據處理方式。舉個例子,小張向小李的銀行卡打 200 塊錢,在小張點擊了確認轉賬的按鈕時,系統突然崩潰了。會出現這樣幾中不正確的情況:

1. 小張的錢打到小李的賬戶上,但是自己的賬戶上的錢沒被扣.

2. 小張的錢打沒到小李的賬戶上了,但是自己賬戶上的錢被扣.

這樣的業務場景就需要 MySQL 事務保持,即使機器出故障的情況下,數據仍然是正確的.

事務使用的條件

MySQL 要使用事務,需要 MySQL 中的存儲引擎支持。現目前 MySQL 內置的存儲引擎支持事務的有 InnoDB、NDB cluster, 第三方的存儲引擎有 PBXT 和 XtrDB.

事務有什么特點?

MySQL 中的事務有如下幾個特點 (ACID):

原子性 (atomicity):

一個事務必須被作為一個不可分割的最小工作單元,每個事務中的所有操作必須要么成功,或者要么失敗,永遠不可能一些操作失敗,一些操作成功,這就是所謂的原子性的概念.

一致性 (consistency):

一致性就像上面舉的一個例子一樣,當發生異常情況下,數據仍然是正確的。就是說當一個事務執行失敗了,數據之間是不會受異常的情況而影響,永遠保持著他的正確性.

隔離性 (isolation):

當一個事務還未提交,每個事務之間是相互隔離的,互補受到影響.

持久性 (durability):

當一個事務進行提交之后,發生的變化就會永遠保存在數據庫中.

事務的隔離級別

在談及到 MySQL 的隔離性的特點,就不得不說說隔離性的幾種級別。至于為什么會涉及到這一點,可以這樣簡單的理解:如果同一時刻,有兩個請求在執行事務的操作,并且這兩個事務是對同一條數據做操作,那么到底最終的結果是以誰的為準呢?不同的隔離級別導致的結果不一樣,因此事務的隔離級別也是一個非常重要的點.

隔離級別分為如下幾點:

1. 未提交讀 (READ UNCOMMITTED)

一個事務中對數據所做的修改,即使沒有提交,這個修改對其他的事務仍是可見的,這種情況下就容易出現臟讀,影響了數據的完整性.

舉例:小明在用支付寶支付時,查看了銀行卡的余額還有 300 塊,其實只有 100 塊,只是因為他女朋友正在向銀行卡存款了 200 塊,此時女朋友不想存了,點擊了回滾操作,小明進行支付卻失敗了.

2. 讀提交 (READ COMMITTED)

一個事務開始時,只能看見其他已經提交過的事務。這種情況下容易出現不可重復讀 (兩次讀的結果不一樣).

舉例:同樣用上面的例子舉例,當他女朋友在刷卡時卡里余額有 100 塊,但是在點擊最終支付時,提示余額不足,此時看卡里的錢沒了。這是因為小明女朋友在支付時,小明操作的事務還未提交,所以小明女朋友兩次看到的結果不一樣.

3. 可重復讀 (REPEATABLE READ)

多次讀取記錄的結果都是一致的,可重復讀可以解決上面的不可重復讀的情況。但是有這樣一種情況,當一個事務在讀取某個范圍的記錄時,另外一個事務在這個范圍內插入了一條新的數據,當事務再次進行讀取數據時,發現比第一次讀取記錄多了一條,這就是所謂的幻讀,兩次讀取的結果不一致.

舉例:小明女朋友在查看銀行卡的記錄時,看見有 5 條消費記錄,此時小明正在消費,這時候消費記錄里面記錄了這條消費記錄,當女朋友再次讀取記錄時,發現有 6 條記錄了.

4. 可串行 (SERIALIZABLE)

串行就像一個隊列一個樣,每個事務都是排隊等候著執行,只有前一個事務提交之后,下一個事務才能進行操作。這種情況雖然可以解決上面的幻讀,但是他會在每一條數據上加一個鎖,容易導致大量的鎖超時和鎖競爭,特別不適用在一些高并發的業務場景下.

舉例:我們在銀行排隊存錢,只有前一個人全部操作完,下一個人才可以進行辦理。中間的人是不可以插隊的,只能一個一個的排對,事務的串行就是這樣的一個概念,其實所謂的串行模式都是這樣的一個概念.

MySQL 事務最全詳解

MySQL 事務最全詳解

隔離性總結

通過上面的舉例,我們不難發現。臟讀和不可重復讀重在更新數據,然后幻讀重在插入數據.

多種存儲引擎時事務的處理方式

根據上面事務使用的條件,我們可以得知有的存儲引擎是不支持事務的,例如 MyISAM 存儲引擎就不支持。那如果在一個事務中使用了事務性的存儲引擎和非事務性的存儲,提交是可以正常進行,但是回滾非事務性的存儲引擎則會顯示響應的錯誤信息,具體信息和存儲引擎有關.

如何使用事務

MySQL 中事務隱式開啟的,也就是說,一個 sql 語句就是一個事務,當 sql 語句執行完畢,事務就提交了。在演示的過程中,我們顯式開啟.

MySQL 中的自動提交

上面提到了 MySQL 中事務是隱式開啟的,則代表我們每一個 sql 是自動提交的,需要關閉則需要設置 autocommit 選項.

//?查看autocommit配置值(1或者ON則表示開啟) mysql?root@127.0.0.1:(none)>?show?variables?like?'%autocommit%'; +---------------+-------+ |?Variable_name?|?Value?| +---------------+-------+ |?autocommit?|?ON?| +---------------+-------+ 1?row?in?set Time:?0.018s //?設置autocommit配置值 mysql?root@127.0.0.1:(none)>?set?autocommit?=?0; Query?OK,?0?rows?affected Time:?0.000s mysql?root@127.0.0.1:(none)>?show?variables?like?'%autocommit%'; +---------------+-------+ |?Variable_name?|?Value?| +---------------+-------+ |?autocommit?|?OFF?| +---------------+-------+ 1?row?in?set Time:?0.013s

1. 表結構如下

mysql?root@127.0.0.1:test&gt;?desc?user; +-------+--------------+------+-----+---------+----------------+ |?Field?|?Type?|?Null?|?Key?|?Default?|?Extra?| +-------+--------------+------+-----+---------+----------------+ |?id?|?int(11)?|?NO?|?PRI?|?<null>?|?auto_increment?| |?name?|?varchar(255)?|?YES?|?|?<null>?|?| |?age?|?int(2)?|?YES?|?|?<null>?|?| +-------+--------------+------+-----+---------+----------------+ 3?rows?in?set Time:?0.013s</null></null></null>

SQL 語句

CREATE?TABLE?`test`.`Untitled`?( `id`?int(11)?NOT?NULL?AUTO_INCREMENT, `name`?varchar(255)?CHARACTER?SET?utf8mb4?COLLATE?utf8mb4_general_ci?NULL?DEFAULT?NULL, `age`?int(2)?NULL?DEFAULT?NULL, PRIMARY?KEY?(`id`)?USING?BTREE )?ENGINE?=?InnoDB?AUTO_INCREMENT?=?3?CHARACTER?SET?=?utf8mb4?COLLATE?=?utf8mb4_general_ci?ROW_FORMAT?=?Dynamic;

2. 使用事務

MySQL 實現事務

下面的代碼,我們主要做了如下幾個操作

a. 開啟事務

b. 修改數據

c. 查詢數據是否改變

d. 數據回滾

e. 再次查詢數據,發現數據變回修改之前的狀態

f. 修改數據

g. 事務提交

h. 查詢數據,發現數據變為最后一次修改的狀態

i. 嘗試事務回滾

j. 查詢驗證是否被回滾了,發現數據還是為最后一次修改的狀態,事務回滾失敗

//?我們先查看表中的數據,id為1的age字段是12 mysql?root@127.0.0.1:test&gt;?select?*?from?user; +----+------+-----+ |?id?|?name?|?age?| +----+------+-----+ |?1?|?張三?|?12?| |?2?|?李四?|?15?| +----+------+-----+ 2?rows?in?set Time:?0.013s //?開啟事務 mysql?root@127.0.0.1:test&gt;?begin; Query?OK,?0?rows?affected Time:?0.001s //?將id為1的age字段改為10 mysql?root@127.0.0.1:test&gt;?update?user?set?age=10?where?id=1; Query?OK,?1?row?affected Time:?0.001s //?再次查詢數據時,發現數據改為修改后的值 mysql?root@127.0.0.1:test&gt;?select?*?from?user; +----+------+-----+ |?id?|?name?|?age?| +----+------+-----+ |?1?|?張三?|?10?| |?2?|?李四?|?15?| +----+------+-----+ 2?rows?in?set Time:?0.012s //?此時我們進行回滾操作 mysql?root@127.0.0.1:test&gt;?rollback; Query?OK,?0?rows?affected Time:?0.001s //?再次查詢發現數據回到最初狀態 mysql?root@127.0.0.1:test&gt;?select?*?from?user; +----+------+-----+ |?id?|?name?|?age?| +----+------+-----+ |?1?|?張三?|?12?| |?2?|?李四?|?15?| +----+------+-----+ 2?rows?in?set Time:?0.019s //?我們再次對數據進行修改 mysql?root@127.0.0.1:test&gt;?update?user?set?age=15?where?id=1; Query?OK,?1?row?affected Time:?0.001s //?此時將事務進行提交 mysql?root@127.0.0.1:test&gt;?commit; Query?OK,?0?rows?affected Time:?0.000s //?發現此時的數據變為我們最終提交的值 mysql?root@127.0.0.1:test&gt;?select?*?from?user; +----+------+-----+ |?id?|?name?|?age?| +----+------+-----+ |?1?|?張三?|?15?| |?2?|?李四?|?15?| +----+------+-----+ 2?rows?in?set Time:?0.012s //?我們嘗試用剛才回滾的方式進行還原數據 mysql?root@127.0.0.1:test&gt;?rollback; Query?OK,?0?rows?affected Time:?0.000s //?發現數據無法回退了,仍然是提交后的數據 mysql?root@127.0.0.1:test&gt;?select?*?from?user; +----+------+-----+ |?id?|?name?|?age?| +----+------+-----+ |?1?|?張三?|?15?| |?2?|?李四?|?15?| +----+------+-----+ 2?rows?in?set Time:?0.017s

php 實現事務實例代碼

<?php // 連接MySQL $mysqli = new mysqli(&#39;127.0.0.1&#39;, &#39;root&#39;, &#39;123456&#39;, &#39;test&#39;, 3306); // 關閉事務自動提交 $mysqli->autocommit(false); //?1.開啟事務 $mysqli-&gt;begin_transaction(); //?2.修改數據 $mysqli-&gt;query("update?user?set?age=10?where?id=1"); //?3.查看數據 $mysqli-&gt;query("select?*?from?user"); //?4.事務回滾 $mysqli-&gt;rollback(); //?5.查看數據 $mysqli-&gt;query("select?*?from?user"); //?7.修改數據 $mysqli-&gt;query("update?user?set?age=15?where?id=1"); //?8.事務提交 $mysqli-&gt;commit(); //?9.事務回滾 $mysqli-&gt;rollback(); //?10.查看數據 $mysqli-&gt;query("select?*?from?user");

如何設置事務的隔離級別

//?查看當前的事務隔離級別 mysql?root@127.0.0.1:test&gt;?select?@@tx_isolation; +-----------------+ |?@@tx_isolation?| +-----------------+ |?REPEATABLE-READ?| +-----------------+ 1?row?in?set Time:?0.015s //?設置隔離級別 set?session?transaction?isolation?level?隔離級別(上面事務隔離級別中的英文單詞);

? 版權聲明
THE END
喜歡就支持一下吧
點贊5 分享