MySQL 事務介紹及原理
阿新 • • 發佈:2019-02-20
1 為什麼要事務 事務是一組不可被分割執行的SQL語句集合,如果有必要,可以撤銷。銀行轉賬是經典的解釋事務的例子。使用者A給使用者B轉賬5000元主要步驟可以概括為如下兩步。 第一,賬戶A賬戶減去5000元; 第二,賬戶B賬戶增加5000元; 這兩步要麼成功,要麼全不成功,否則都會導致資料不一致。這就可以用到事務來保證,如果是不同銀行之間的轉賬還需要用到分散式事務。2 事務的性質 事務的機制通常被概括為“ACID”原則即原子性(A)、穩定性(C)、隔離性(I)和永續性(D)。 原子性:構成事務的的所有操作必須是一個邏輯單元,要麼全部執行,要麼全部不執行。 穩定性:資料庫在事務執行前後狀態都必須是穩定的。 隔離性:事務之間不會相互影響。 3 事務隔離性實現原理 資料庫事務會導致髒讀、不可重複讀和幻影讀等問題。 髒讀:事務還沒提交,他的修改已經被其他事務看到。 不可重複讀:同一事務中兩個相同SQL讀取的內容可能不同。兩次讀取之間其他事務提交了修改可能會造成讀取資料不一致。 幻影資料:同一個事務突然發現他以前沒發現的資料。和不可重複讀很類似,不過修改資料改成增加資料。針對可能的問題,InnoDB提供了四種不同級別的機制保證資料隔離性。 事務的隔離用是通過鎖機制實現的,不同於MyISAM使用表級別的鎖,InnoDB採用更細粒度的行級別鎖,提高了資料表的效能。InnoDB的鎖通過鎖定索引來實現,如果查詢條件中有主鍵則鎖定主鍵,如果有索引則先鎖定對應索引然後再鎖定對應的主鍵(可能造成死鎖),如果連索引都沒有則會鎖定整個資料表 3.1 READ UNCOMMIT未提交讀 READ UNCOMMIT允許某個事務看到其他事務並沒有提交的資料。可能會導致髒讀、不可重複讀、幻影資料。 原理:READ UNCOMMIT不會採用任何鎖。3.2 READ COMMIT提交讀 READ COMMIT允許某個事務看到其他事務已經提交的資料。可能會導致不可重複讀和幻影資料。 原理:資料的讀是不加鎖的,但是資料的寫入、修改、刪除加鎖,避免了髒讀。3.3 REPEATABLE READ可重複讀InnoDB預設級別 InnoDB中REPEATABLE READ級別同一個事務的兩次相同讀取肯定是一樣的,其他事務的提交 不會對本次事務有影響。 原理:資料的讀、寫都會加鎖,當前事務如果佔據了鎖,其他事務必須等待本次事務提交完成釋放鎖後才能對相同的資料行進行操作。3.4 SERIALIZABLE 序列讀完全序列化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞 SERIALIZABLE 級別在InnoDB中和REPEATABLE READ採用相同的實現。4 原子性、穩定性和永續性實現原理 原子性、穩定性和永續性是通過redo 和 undo 日誌檔案實現的,不管是redo還是undo檔案都會有一個快取我們稱之為redo_buf和undo_buf。同樣,資料庫檔案也會有快取稱之為data_buf。4.1 undo 日誌檔案 undo記錄了資料在事務開始之前的值,當事務執行失敗或者ROLLBACK時可以通過undo記錄的值來恢復資料。例如 AA和BB的初始值分別為3,5。A 事務開始B 記錄AA=3到undo_bufC 修改AA=1D 記錄BB=5到undo_bufE 修改BB=7F 將undo_buf寫到undo(磁碟)G 將data_buf寫到datafile(磁碟)H 事務提交 通過undo可以保證原子性、穩定性和永續性 如果事務在F之前崩潰由於資料還沒寫入磁碟,所以資料不會被破壞。 如果事務在G之前崩潰或者回滾則可以根據undo恢復到初始狀態。 資料在任務提交之前寫到磁碟保證了永續性。 但是單純使用undo保證原子性和永續性需要在事務提交之前將資料寫到磁碟,浪費大量I/O。4.2 redo/undo 日誌檔案引入redo日誌記錄資料修改後的值,可以避免資料在事務提交之前必須寫入到磁碟的需求,減少I/O。A 事務開始B 記錄AA=3到undo_bufC 修改AA=1 記錄redo_bufD 記錄BB=5到undo_bufE 修改BB=7 記錄redo_bufF 將redo_buf寫到redo(磁碟)G 事務提交 通過undo保證事務的原子性,redo保證永續性。 F之前崩潰由於所有資料都在記憶體,恢復後重新衝磁碟載入之前的資料,資料沒有被破壞。 FG之間的崩潰可以使用redo來恢復。 G之前的回滾都可以使用undo來完成。5 事務操作命令 如果需要使用事務就必須選用支援事務的資料庫引擎如InnoDB和Falcon,MyISAM並不支援事務。 在預設情況下MySQL開啟的是autocommit模式,也就是隱含的將每條語句當做一個事務處理,每條SQL都會被自動提交。當我們使用BEGIN或者START TRANSCATION時會把自動提交掛起,直到顯示的呼叫COMMIT。使用事務可以有如下兩種方法:
永續性:事務執行成功後必須全部寫入磁碟。
BEGIN; //開始事務,掛起自動提交
insert into t_cart_shopcart (user_id, sku_id, amount, shop_id, status) values(10001, 10001, 1, 10001, 0);
insert into t_cart_shopcart (user_id, sku_id, amount, shop_id, status) values(10001, 10002, 1, 10001, 0);
COMMIT; //提交事務,恢復自動提交
----------------------------------------------------------------------------------------------------------------------------
insert into t_cart_shopcart (user_id, sku_id, amount, shop_id, status) values(10001, 10001, 1, 10001, 0);
insert into t_cart_shopcart (user_id, sku_id, amount, shop_id, status) values(10001, 10002, 1, 10001, 0);
COMMIT; //提交事務
set autocommit = 1; //恢復自動提交
----------------------------------------------------------------------------------------------------------------------------
這兩種方式效果相同。
參考[4] 《MySQL技術內幕》