再談Transaction——MySQL事務處理分析

MySQL 事務基礎概念/Definition of Transaction

事務(Transaction)是訪問和更新數據庫的程序執行單元;事務中可能包含一個或多個 sql 語句,這些語句要麼都執行,要麼都不執行php

事務處理在各類管理系統中都有着普遍的應用,好比人員管理系統,不少同步數據庫操做大都須要用到事務處理。好比說,在人員管理系統中,你刪除一我的員,你即須要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操做語句就構成一個事務!html

刪除的SQL語句
delete from userinfo where ~~~
delete from mail where ~~
delete from article where~~
~~
若是沒有事務處理,在你刪除的過程當中,假設出錯了,只執行了第一句,那麼其後果是不可思議的!

但用事務處理。若是刪除出錯,你只要rollback就能夠取消刪除操做(實際上是隻要你沒有commit你就沒有確實的執行該刪除操做)mysql

通常來講,在商務級的應用中,都必須考慮事務處理的linux

mysql邏輯架構和存儲引擎

MySQL 支持事務的存儲引擎有 InnoDB、NDB Cluster 等,其中 InnoDB 的使用最爲普遍;其餘存儲引擎不支持事務,如 MyIsam、Memory 等面試

事務的特徵

  • 一個最小的不可再分的工做單元;一般一個事務對應一個完整的業務(例如銀行帳戶轉帳業務,該業務就是一個最小的工做單元)sql

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

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

先來明確一下事務涉及的相關知識:bash

事務都應該具有ACID特徵

所謂ACID是Atomic(原子性)/Consistent(一致性)/Isolated(隔離性)/Durable(持續性)四個詞的首字母所寫。服務器

通常來講, 事務是必須知足4個條件(ACID)
  • 原子性(Autmic):事務是最小單位,不可再分。即:組成事務處理的語句造成了一個邏輯單元,不能只執行其中的一部分。事務在執行性,要作到"要麼不作,要麼全作!",就是說不容許事務部分得執行。即便由於故障而使事務不能完成,在rollback時也要消除對數據庫得影響!

    好比:銀行轉賬過程當中,必須同時從一個賬戶減去轉賬金額,並加到另外一個賬戶中,只改變一個賬戶是不合理的。

    原子性由 Undo log 保證。Undo Log 會保存每次變動以前的記錄,從而在發生錯誤時進行回滾。

  • 一致性(Consistency):在事務處理執行先後,數據庫是一致的。事務要求全部的DML語句操做的時候,必須保證同時成功或者同時失敗。事務得操做應該使使數據庫從一個一致狀態轉變倒另外一個一致得狀態!

    好比:網上購物,你只有即讓商品出庫,又讓商品進入顧客得購物籃才能構成事務!

    好比:銀行轉賬過程當中,要麼轉賬金額從一個賬戶轉入另外一個賬戶,要麼兩個賬戶都不變,沒有其餘的狀況。

    一致性由原子性,隔離性,持久性來保證

  • 隔離性(Isolation):一個事務處理對另外一個事務處理沒有影響。即:事務A和事務B之間具備隔離性。若是多個事務併發執行,應象各個事務獨立執行同樣!也就是說任何事務都不可能看到一個處在不完整狀態下的事務。

    隔離性追求的是併發情形下事務之間互不干擾。

    好比:銀行轉賬過程當中,在轉賬事務沒有提交以前,另外一個轉賬事務只能處於等待狀態。

    隔離性由 MVCC(Multi-Version Concurrency Control) 和 Lock(鎖機制) 保證

  • 持久性(Durability):事務處理的效果可以被永久保存下來。反過來講,事務應當可以承受全部的失敗,包括服務器、進程、通訊以及媒體失敗等等。一個成功執行得事務對數據庫得做用是持久得,即便數據庫應故障出錯,也應該可以恢復!持久性是事務的保證,事務終結的標誌(內存的數據持久到硬盤文件中)。

    好比:銀行轉賬過程當中,轉賬後賬戶的狀態要能被保存下來。

    持久性由 Redo Log 保證。每次真正修改數據以前,都會將記錄寫到 Redo Log 中,只有 Redo Log 寫入成功,纔會真正的寫入到 B+ 樹中,若是提交以前斷電,就能夠經過 Redo Log 恢復記錄

InnoDB存儲引擎兩種事務日誌:

  • redo log(重作日誌): 用於保證事務持久性

  • undo log(回滾日誌):事務原子性和隔離性實現的基礎

undo log是現原子性的關鍵,是當事務回滾時可以撤銷全部已經成功執行的 sql 語句。

InnoDB 實現回滾,靠的是 undo log:

  • 當事務對數據庫進行修改時,InnoDB 會生成對應的 undo log。

  • 若是事務執行失敗或調用了 rollback,致使事務須要回滾,即可以利用 undo log 中的信息將數據回滾到修改以前的樣子。

undo log 屬於邏輯日誌,它記錄的是 sql 執行相關的信息。

當發生回滾時,InnoDB 會根據 undo log 的內容作與以前相反的工做:

  • 對於每一個 insert,回滾時會執行 delete。

  • 對於每一個 delete,回滾時會執行 insert。

  • 對於每一個 update,回滾時會執行一個相反的 update,把數據改回去。

以 update 操做爲例:當事務執行 update 時,其生成的 undo log 中會包含被修改行的主鍵(以便知道修改了哪些行)、修改了哪些列、這些列在修改先後的值等信息,回滾時即可以使用這些信息將數據還原到 update 以前的狀態。

redo log 存在的背景

InnoDB 做爲 MySQL 的存儲引擎,數據是存放在磁盤中的,但若是每次讀寫數據都須要磁盤 IO,效率會很低。

爲此,InnoDB 提供了緩存(Buffer Pool),Buffer Pool 中包含了磁盤中部分數據頁的映射,做爲訪問數據庫的緩衝:

  • 當從數據庫讀取數據時,會首先從 Buffer Pool 中讀取,若是 Buffer Pool 中沒有,則從磁盤讀取後放入 Buffer Pool。

  • 當向數據庫寫入數據時,會首先寫入 Buffer Pool,Buffer Pool 中修改的數據會按期刷新到磁盤中(這一過程稱爲刷髒)。

Buffer Pool 的使用大大提升了讀寫數據的效率,可是也帶來了新的問題:若是 MySQL 宕機,而此時 Buffer Pool 中修改的數據尚未刷新到磁盤,就會致使數據的丟失,事務的持久性沒法保證。

因而,redo log 被引入來解決這個問題:當數據修改時,除了修改 Buffer Pool 中的數據,還會在 redo log 記錄此次操做;當事務提交時,會調用 fsync 接口對 redo log 進行刷盤。

若是 MySQL 宕機,重啓時能夠讀取 redo log 中的數據,對數據庫進行恢復。

redo log 採用的是 WAL(Write-ahead logging,預寫式日誌),全部修改先寫入日誌,再更新到 Buffer Pool,保證了數據不會因 MySQL 宕機而丟失,從而知足了持久性要求。

既然 redo log 也須要在事務提交時將日誌寫入磁盤,爲何它比直接將 Buffer Pool 中修改的數據寫入磁盤(即刷髒)要快呢?

主要有如下兩方面的緣由:

  • 刷髒是隨機 IO,由於每次修改的數據位置隨機,但寫 redo log 是追加操做,屬於順序 IO。

  • 刷髒是以數據頁(Page)爲單位的,MySQL 默認頁大小是 16KB,一個 Page 上一個小修改都要整頁寫入;而 redo log 中只包含真正須要寫入的部分,無效 IO 大大減小。

redo log 與 binlog

咱們知道,在 MySQL 中還存在 binlog(二進制日誌)也能夠記錄寫操做並用於數據的恢復,但兩者是有着根本的不一樣的。

做用不一樣:

  • redo log 是用於 crash recovery 的,保證 MySQL 宕機也不會影響持久性;

  • binlog 是用於 point-in-time recovery 的,保證服務器能夠基於時間點恢復數據,此外 binlog 還用於主從複製。

層次不一樣:

  • redo log 是 InnoDB 存儲引擎實現的,

  • 而 binlog 是 MySQL 的服務器層實現的,同時支持 InnoDB 和其餘存儲引擎。

內容不一樣:

  • redo log 是物理日誌,內容基於磁盤的 Page。

  • binlog 是邏輯日誌,內容是一條條 sql。

寫入時機不一樣:

  • redo log 的寫入時機相對多元。前面曾提到,當事務提交時會調用 fsync 對 redo log 進行刷盤;這是默認狀況下的策略,修改 innodb_flush_log_at_trx_commit 參數能夠改變該策略,但事務的持久性將沒法保證。

除了事務提交時,還有其餘刷盤時機:如 master thread 每秒刷盤一次 redo log 等,這樣的好處是不必定要等到 commit 時刷盤,commit 速度大大加快。

  • binlog 在事務提交時寫入。

事務的隔離四種級別

ACID四大特徵中,最難理解的不是一致性,而是事務的隔離性,數據庫權威專家針對事務的隔離性研究出來了事務的隔離四種級別,四種事務隔離級別就是爲了解決數據在高併發下產生的問題(髒讀、不可重複讀、幻讀)

併發一致性問題

  • 髒讀:好比有兩個事務並行執行操做同一條數據庫記錄,A事務能讀取到B事務未提交的數據。好比:事務B操做了數據庫可是沒有提交事務,此時A讀取到了B沒有提交事務的數據。這就是髒讀的體現。

  • 不可重複讀:好比事務A在同一事務中屢次讀取同一記錄,此時事務B修改了事務A正在讀的數據而且提交了事務,可是事務A讀取到了事務B所提交的數據,致使兩次讀取數據不一致。

  • 幻讀:事務A將數據表中全部數據都設置爲100,此時事務B插入了一條值爲200的數據並提交事務,當事務A修改完成提交事務後,發現還有一條數據的值不爲100.這就是幻讀的一種體現方式。

髒讀示例不可重複讀示例幻讀示例

髒讀狀況不可重複讀(Non-Repeatable Reads)幻讀流傳展現

"髒讀"、"不可重複讀"和"幻讀",其實都是數據庫讀一致性問題,必須由數據庫提供必定的事務隔離機制來解決

事務隔離級別

  • 未提交讀(RU),一個事務還沒提交時,它作的變動就能被別的事務看到。

    發生髒讀的緣由:RU 原理是對每一個更新語句的行記錄進行加鎖,而不是對整個事務進行加鎖,因此會發生髒讀。而 RC 和 RR 會對整個事務加鎖。

  • 已提交讀(RC),一個事務提交以後,它作的變動纔會被其餘事務看到。

    不能重複讀的緣由:RC 每次執行 SQL 語句都會生成一個新的 Read View,每次讀到的都是不一樣的。而 RR 的事務從始至終都是使用同一個 Read View。

  • 可重複讀(RR), 一個事務執行過程當中看到的數據,老是跟這個事務在啓動時看到的數據是一致的。固然在可重複讀隔離級別下,未提交變動對其餘事務也是不可見的。

    默認是 MVCC 機制(「一致性非鎖定讀」)保證 RR 級別的隔離正確性,是不上鎖的。能夠選擇手動上鎖:select xxxx for update (排他鎖); select xxxx lock in share mode(共享鎖),稱之爲「一致性鎖定讀」。使用鎖以後,就能在 RR 級別下,避免幻讀。固然,默認的 MVCC 讀,也能避免幻讀。

  • 串行化(serializable),全部事務一個接着一個的執行,這樣能夠避免幻讀 (phantom read),對於基於鎖來實現併發控制的數據庫來講,串行化要求在執行範圍查詢的時候,須要獲取範圍鎖,若是不是基於鎖實現併發控制的數據庫,則檢查到有違反串行操做的事務時,需回滾該事務。

    顧名思義是對於同一行記錄,「寫」會加「寫鎖」,「讀」會加「讀鎖」。當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成,才能繼續執行。

數據庫權威專家針對事務的隔離性研究出來了事務的隔離四種級別

事務隔離級別越嚴格,越消耗計算機性能,效率也越低

數據庫的事務隔離越嚴格,併發反作用越小,但付出的代價也就越大,由於事務隔離實質上就是使事務在必定程度上 「串行化」進行,這顯然與「併發」是矛盾的。同時,不一樣的應用對讀一致性和事務隔離程度的要求也是不一樣的,好比許多應用對「不可重複讀」和「幻讀」並不敏感,可能更關心數據併發訪問的能力。

Mysql默認使用的數據隔離級別是REPEATABLE READ(可重複讀,容許幻讀)。MySql 的 REPEATABLE READ 級別不會致使幻讀。

REPEATABLE READ級別會致使幻讀,可是,因爲 Mysql 的優化,在使用默認的 select 時,MySql 使用 MVCC 機制保證不會幻讀;

也可使用鎖,在使用鎖時,例如 for update(X 鎖),lock in share mode(S 鎖),MySql 會使用 Next-Key Lock 來保證不會發生幻讀。

前者稱爲快照讀,後者稱爲當前讀。

Mysql默認使用REPEATABLE READ理由

Mysql在5.0這個版本之前,binlog只支持STATEMENT這種格式!而這種格式在讀已提交(Read Commited)這個隔離級別下主從複製是有bug的,所以Mysql將可重複讀(Repeatable Read)做爲默認的隔離級別!

有什麼bug呢?具體閱讀《互聯網項目中mysql應該選什麼事務隔離級別

mysql爲何不採默認用串行化(Serializable)?由於每一個次讀操做都會加鎖,快照讀失效,通常是使用mysql自帶分佈式事務功能時才使用該隔離級別!

主從複製,是基於什麼複製的? 是基於binlog複製的!

binlog有幾種格式?

  1. statement:記錄的是修改SQL語句

  2. row:記錄的是每行實際數據的變動

  3. mixed:statement和row模式的混合


事務開啓的標誌?事務結束的標誌?

開啓標誌:任何一條DML語句(insert、update、delete)執行,標誌事務的開啓

結束標誌(提交或者回滾)

提交:成功的結束,將全部的DML語句操做歷史記錄和底層硬盤數據來一次同步

回滾:失敗的結束,將全部的DML語句操做歷史記錄所有清空

事物與數據庫底層數據:

在事物進行過程當中,未結束以前,DML語句是不會更改底層數據,只是將歷史操做記錄一下,在內存中完成記錄。只有在事物結束的時候,並且是成功的結束的時候,纔會修改底層硬盤文件中的數據。

什麼是transaction及其控制?

Transaction包含了一系列的任務操做。這些操做多是建立更新刪除等等操做,是做爲一個特定的結果來表現的。要麼成功,要麼不成功。

transaction有4種控制,一種是commit,也就是提交。一種是rollback,也就是回調。再一種是set transaction,對這個事務設定一個名字。再一種是save point。這個控制是用來設定某個點用來回退的。

MVCC解析

MVCC 全稱 Multi-Version Concurrency Control,即多版本的併發控制協議。

一致性非鎖定讀

一致性非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎經過多版本控制(MVCC)的方式來讀取當前執行時間數據庫中行的數據。若是讀取的這行正在執行DELETE或UPDATE操做,這時讀取操做不會向XS鎖同樣去等待鎖釋放,而是會去讀一個快照數據。

MVCC相關的知識

在事務隔離級別RC和RR下,InnoDB存儲引擎使用非鎖定的一致性讀。然而對於快照數據的定義卻不一樣,在RC級別下,對於快照數據,非一致性讀老是讀取被鎖定行的最新一份快照數據。而在RR級別下,對於快照數據,非一致性讀老是讀取事務開始時的行數據版本。

MVCC過程圖示

能夠看到,第1步和第2步是很是容易理解的,而在第3步事務B插入一條新的數據後,在第4步事務A仍是查不到,也就是利用了MVCC的特性來實現。當事務B提交後,第5步的查詢在RC和RR隔離級別下的輸出是不一樣的,這個的緣由在另外一篇博客中也說到了,是由於他們建立ReadView的時機不一樣。

可是很詭異的是在第6步的時候,事務A更新了一條它看不見的記錄,而後查詢就可以查詢出來了。這裏不少人容易迷惑,不可見不表明記錄不存在,它只是利用了可見性判斷忽略了而已。更新成功以後,事務A順其天然的記錄了這條記錄的Undo log,在隨後的查詢中,由於它可以看見本身的改動這一個可見性的判斷,天然就可以查詢出來了。這裏不少名詞須要去深刻讀一下此文:談談MySQL InnoDB存儲引擎事務的ACID特性

RR 解決髒讀、不可重複讀、幻讀等問題,使用的是 MVCC

高性能MySQL MVCC


MVCC是個 行級鎖 的變種,它在 普通讀狀況下避免了加鎖操做,所以開銷更低 。雖然實現不一樣,但一般都是實現非阻塞讀,寫操做只鎖定必要的行。

MVCC 的特色:在同一時刻,不一樣的事務讀取到的數據多是不一樣的(即多版本)

MVCC 最大的優勢是讀不加鎖,所以讀寫不衝突,併發性能好。InnoDB 實現 MVCC,多個版本的數據能夠共存,主要是依靠數據的隱藏列(也能夠稱之爲標記位)和 undo log。

其中數據的隱藏列包括了該行數據的版本號、刪除時間、指向 undo log 的指針等等。

當讀取數據時,MySQL 能夠經過隱藏列判斷是否須要回滾並找到回滾須要的 undo log,從而實現 MVCC;隱藏列的詳細格式再也不展開。

一致性鎖定讀

前面說到,在默認隔離級別RR下,InnoDB存儲引擎的SELECT操做使用一致性非鎖定讀。可是在某些狀況下,用戶須要顯式地對數據庫讀取操做進行加鎖以保證數據邏輯的一致性。InnoDB存儲引擎對於SELECT語句支持兩種一致性的鎖定讀(locking read)操做。

  • SELECT … FOR UPDATE (X鎖)

  • SELECT … LOCK IN SHARE MODE (S鎖)

MySQL InnoDB 髒讀 不可重複讀 幻讀

髒讀

髒讀情景分析

當事務 A 在 T3 時間節點讀取 zhangsan 的餘額時,會發現數據已被其餘事務修改,且狀態爲未提交。

此時事務 A 讀取最新數據後,根據數據的 undo log 執行回滾操做,獲得事務 B 修改前的數據,從而避免了髒讀。

不可重複讀

不可重複讀情景分析

當事務 A 在 T2 節點第一次讀取數據時,會記錄該數據的版本號(數據的版本號是以 row 爲單位記錄的),假設版本號爲 1;當事務 B 提交時,該行記錄的版本號增長,假設版本號爲 2。

當事務 A 在 T5 再一次讀取數據時,發現數據的版本號(2)大於第一次讀取時記錄的版本號(1),所以會根據 undo log 執行回滾操做,獲得版本號爲 1 時的數據,從而實現了可重複讀。

幻讀

InnoDB 實現的 RR 經過 next-keylock 機制避免了幻讀現象。

next-keylock 是行鎖的一種,實現至關於 record lock(記錄鎖) + gap lock(間隙鎖);其特色是不只會鎖住記錄自己(record lock 的功能),還會鎖定一個範圍(gap lock 的功能)。

固然,這裏咱們討論的是不加鎖讀:此時的 next-key lock 並非真的加鎖,只是爲讀取的數據增長了標記(標記內容包括數據的版本號等);準確起見姑且稱之爲類 next-key lock 機制。

幻讀情景分析

這樣當 T5 時刻再次讀取 0

小結:歸納來講,InnoDB 實現的 RR,經過鎖機制、數據的隱藏列、undo log 和類 next-key lock,實現了必定程度的隔離性,能夠知足大多數場景的須要。

不過須要說明的是,RR 雖然避免了幻讀問題,可是畢竟不是 Serializable,不能保證徹底的隔離


MYSQL的事務處理主要的兩種方法

用begin,rollback,commit來實現

  • begin 開始一個事務
  • rollback 事務回滾
  • commit 事務確認

改變mysql的自動提交模式

MYSQL默認事務是自動提交的,也就是你提交一個QUERY,它就直接執行!也就是說,只要執行一條DML語句就開啓了事物,而且提交了事務。

MySQL自動提交模式

咱們能夠經過設置autocommit來實現事務的處理。

  • set autocommit=0 禁止自動提交

  • set autocommit=1 開啓自動提交

但注意當你用 set autocommit=0 的時候,你之後全部的SQL都將作爲事務處理,直到你用commit確認或rollback結束,注意當你結束這個事務的同時也開啓了個新的事務!按第一種方法只將當前的做爲一個事務!
我的推薦使用第一種方法!
MYSQL中只有INNODB和BDB類型的數據表才能支持事務處理!其餘的類型是不支持的!(切記!)

再來看看哪些問題會用到事務處理:

先假設一下問題的背景:網上購書,某書(數據庫編號爲123)只剩最後一本,而這個時候,兩個用戶對這本書幾乎同時發出了購買請求,讓咱們看看整個過程:

在具體分析以前,先來看看數據表的定義:

-------------------------------------------------------------------------------

create table book
(
book_id unsigned int(10) not null auto_increment,
book_name varchar(100) not null,
book_price float(5, 2) not null, #我假設每本書的價格不會超過999.99元
book_number int(10) not null,
primary key (book_id)
)
type = innodb; #engine = innodb也行

-------------------------------------------------------------------------------

對於用戶甲來講,他的動做稍微比乙快一點點,其購買過程所觸發的動做大體是這樣的:

-------------------------------------------------------------------------------

1. SELECT book_number FROM book WHERE book_id = 123;

book_number大於零,確認購買行爲並更新book_number

2. UPDATE book SET book_number = book_number - 1 WHERE book_id = 123;

購書成功

-------------------------------------------------------------------------------

而對於用戶乙來講,他的動做稍微比甲慢一點點,其購買過程所觸發的動做和甲相同:

-------------------------------------------------------------------------------

1. SELECT book_number FROM book WHERE book_id = 123;

這個時候,甲剛剛進行完第一步的操做,還沒來得及作第二步操做,因此book_number必定大於零

2. UPDATE book SET book_number = book_number - 1 WHERE book_id = 123;

購書成功

-------------------------------------------------------------------------------

表面上看甲乙的操做都成功了,他們都買到了書,可是庫存只有一本,他們怎麼可能都成功呢?再看看數據表裏book_number的內容,已經變成 -1了,這固然是不能容許的(實際上,聲明這樣的列類型應該加上unsigned的屬性,以保證其不能爲負,這裏是爲了說明問題因此沒有這樣設置)

好了,問題陳述清楚了,再來看看怎麼利用事務來解決這個問題,打開MySQL手冊,能夠看到想用事務來保護你的SQL正確執行其實很簡單,基本就是三個語句:開始,提交,回滾。

  • 開始:START TRANSACTION或BEGIN語句能夠開始一項新的事務

  • 提交:COMMIT能夠提交當前事務,是變動成爲永久變動

  • 回滾:ROLLBACK能夠回滾當前事務,取消其變動

此外,SET AUTOCOMMIT = {0 | 1}能夠禁用或啓用默認的autocommit模式,用於當前鏈接。

-------------------------------------------------------------------------------

那是否是隻要用事務語句包一下咱們的SQL語句就能保證正確了呢?好比下面代碼:

-------------------------------------------------------------------------------

BEGIN;

SELECT book_number FROM book WHERE book_id = 123;

// ...

UPDATE book SET book_number = book_number - 1 WHERE book_id = 123;

COMMIT;

-------------------------------------------------------------------------------

答案是否認了,這樣依然不能避免問題的發生,若是想避免這樣的狀況,實際應該以下:

-------------------------------------------------------------------------------

BEGIN;

SELECT book_number FROM book WHERE book_id = 123 FOR UPDATE ;

// ...

UPDATE book SET book_number = book_number - 1 WHERE book_id = 123;

COMMIT;

-------------------------------------------------------------------------------

因爲加入了FOR UPDATE,因此會在此條記錄上加上一個行鎖,若是此事務沒有徹底結束,那麼其餘的事務在使用SELECT ... FOR UPDATE請求的時候就會處於等待狀態,直到上一個事務結束,它才能繼續,從而避免了問題的發生,須要注意的是,若是你其餘的事務使用的是不帶FOR UPDATE的SELECT語句,將得不到這種保護。

MySQL事務操做

mysql> use RUNOOB;
Database changed
mysql> CREATE TABLE runoob_transaction_test( id int(5)) engine=innodb;  # 建立數據表
Query OK, 0 rows affected (0.04 sec)

mysql> select * from runoob_transaction_test;
Empty set (0.01 sec)
 
mysql> begin;  # 開始事務
Query OK, 0 rows affected (0.00 sec)
 
mysql> insert into runoob_transaction_test value(5);
Query OK, 1 rows affected (0.01 sec)
 
mysql> insert into runoob_transaction_test value(6);
Query OK, 1 rows affected (0.00 sec)
 
mysql> commit; # 提交事務
Query OK, 0 rows affected (0.01 sec)
 
mysql>  select * from runoob_transaction_test;
+------+
| id   |
+------+
| 5    |
| 6    |
+------+
2 rows in set (0.01 sec)
 
mysql> begin;    # 開始事務
Query OK, 0 rows affected (0.00 sec)
 
mysql>  insert into runoob_transaction_test values(7);
Query OK, 1 rows affected (0.00 sec)
 
mysql> rollback;   # 回滾
Query OK, 0 rows affected (0.00 sec)
 
mysql>   select * from runoob_transaction_test;   # 由於回滾因此數據沒有插入
+------+
| id   |
+------+
| 5    |
| 6    |
+------+
2 rows in set (0.01 sec)複製代碼

PHP + MySQL事務操做的代碼演示:

<?php
$dbhost = 'localhost:3306';  // mysql服務器主機地址
$dbuser = 'root';            // mysql用戶名
$dbpass = '123456';          // mysql用戶名密碼
$conn = mysqli_connect($dbhost, $dbuser, $dbpass);
if(! $conn )
{
    die('鏈接失敗: ' . mysqli_error($conn));
}
// 設置編碼,防止中文亂碼
mysqli_query($conn, "set names utf8");
mysqli_select_db( $conn, 'RUNOOB' );
mysqli_query($conn, "SET AUTOCOMMIT=0"); // 設置爲不自動提交,由於MYSQL默認當即執行
mysqli_begin_transaction($conn);            // 開始事務定義

if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(8)"))
{
    mysqli_query($conn, "ROLLBACK");     // 判斷當執行失敗時回滾
}

if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(9)"))
{
    mysqli_query($conn, "ROLLBACK");      // 判斷執行失敗時回滾
}
mysqli_commit($conn);            //執行事務
mysqli_close($conn);
?>複製代碼


PHP使用AdoDB操做MySQL事務的代碼演示:

實際LAMP應用中,通常PHP使用AdoDB操做MySQL,下面給出AdoDB相應的代碼方便你們查閱:

-------------------------------------------------------------------------------

// ...
$adodb -> startTrans ();

//實際,getOne所調用的查詢也能夠直接放到rowLock來進行,這裏只是爲了演示效果能更明顯些。
$adodb -> rowLock ( 'book' , 'book_id = 123' );
$bookNumber = $adodb -> getOne ( "SELECT book_number FROM book WHERE book_id = 123" );
$adodb -> execute ( "UPDATE book SET book_number = book_number - 1 WHERE book_id = 123" );
$adodb -> completeTrans ();
// ...
?>

-------------------------------------------------------------------------------

其中,rowLock的方法就是調用的FOR UPDATE來實現的行鎖,你可能會想把&"FOR UPDATE&直接寫到$adodb->getOne()調用的那條SQL語句裏面去實現行鎖的功能,不錯,那樣確實能夠,可是並非全部的數據庫 都使用&"FOR UPDATE&"語法來實現行鎖功能,好比Sybase使用&"HOLDLOCK&"的語法來實現行鎖功能,因此爲了你的數據庫抽象層保持可移植性,我仍是勸你 用rowLock來實現行鎖功能,至於可移植性就交給AdoDB好了,嗯,有點扯遠了,今兒就說到這裏了。

-------------------------------------------------------------------------------

附:

AdoDB中存在一個setTransactionMode()方法,可以設置事務的隔離級別,以下:

SetTransactionMode allows you to pass in the transaction mode to use for all subsequent transactions for that connection session. Note: if you have persistent connections and using mysql or mssql, you might have to explicitly reset your transaction mode at the beginning of each page request. This is only supported in postgresql, mssql, mysql with InnoDB and oci8 currently. For example:

$db->SetTransactionMode("SERIALIZABLE");
$db->BeginTrans();
$db->Execute(...); $db->Execute(...);
$db->CommiTrans();

$db->SetTransactionMode(""); // restore to default
$db->StartTrans();
$db->Execute(...); $db->Execute(...);
$db->CompleteTrans();

Supported values to pass in:

* READ UNCOMMITTED (allows dirty reads, but fastest)
* READ COMMITTED (default postgres, mssql and oci8)
* REPEATABLE READ (default mysql)
* SERIALIZABLE (slowest and most restrictive)

You can also pass in database specific values such as 'SNAPSHOT' for mssql or 'READ ONLY' for oci8/postgres.

原文連接:再談Transaction--MySQL事務處理分析 - mysql - 周陸軍的我的網站,更新將在源站進行,文有不妥之處,請留言告知,以備更正。多謝!

參考連接:

MySQL——事務(Transaction)詳解 blog.csdn.net/w_linux/art…

MySql 三大知識點——索引、鎖、事務 zhuanlan.zhihu.com/p/59764376

面試官問你:MYSQL事務和隔離級別,該如何回答 zhuanlan.zhihu.com/p/70701037

談談MySQL的鎖 zhuanlan.zhihu.com/p/65721606

一文說盡MySQL事務及ACID特性的實現原理 zhuanlan.zhihu.com/p/56874694

MySQL 事務 https://www.runoob.com/mysql/mysql-transaction.html

相關文章
相關標籤/搜索