1. 程式人生 > >MySQL InnoDB 事務

MySQL InnoDB 事務

MySQL 原理篇

MySQL 索引機制

MySQL 體系結構及儲存引擎

MySQL 語句執行過程詳解

MySQL 執行計劃詳解

MySQL InnoDB 緩衝池

MySQL InnoDB 事務

MySQL InnoDB 鎖

MySQL InnoDB MVCC

MySQL InnoDB 實現高併發原理

MySQL InnoDB 快照讀在RR和RC下有何差異

MySQL5.5 及以後版本的預設儲存引擎使用的是 InnoDB,接下來針對事務的講解都是基於 InnoDB 儲存引擎的。

事務的定義

事務:資料庫操作的最小工作單元,是作為單個邏輯工作單元執行的一系列操作; 事務是一組不可再分割的操作集合(工作邏輯單元)。

典型事務使用場景:轉賬

update user_account set balance = balance - 1000 where userID = 3;
update user_account set balance = balance + 1000 where userID = 1;

MySQL 開啟事務:

/*
  BEGIN / START TRANSACTION        --手工
  COMMIT / ROLLBACK                --事務提交或回滾
  SET SESSION autocommit = ON/OFF  --設定會話級別事務是否自動開啟
*/

MySQL 預設是開啟事務的,通過 SHOW VARIABLES like 'autocommit';

可以檢視 MySQL 的事務開啟情況。

  • 在 autocommit = ON(自動提交事務)的情況下,可以執行 BEGIN; 或者 START TRANSACTION; 命令,改為手動提交事務,執行完 SQL 語句後,需要通過 COMMIT 命令提交事務,或者通過 ROLLBACK 命令回滾事務。
  • 在 autocommit = OFF(手動提交事務)的情況下,執行完 SQL 語句後,需要通過 COMMIT 命令提交事務,或者通過 ROLLBACK 命令回滾事務。

JDBC 程式設計:

connection.setAutoCommit(boolean);

測試命令:

SHOW VARIABLES like 'autocommit';

/* autocommit為ON */
update teacher set name ='seven' where id =1;
insert teacher (name,age) value ('james',22);
delete from teacher where name = 'james';

/* autocommit為ON如何開啟事務 */
BEGIN;
START TRANSACTION;

update teacher set name ='seven' where id =1;
insert teacher (name,age) value ('james',22);
delete from teacher where name = 'james';

COMMIT;
ROLLBACK;

/* 將autocommit改成OFF */
set session autocommit = OFF;
update teacher set name ='seven' where id =1;
insert teacher (name,age) value ('james',22);
delete from teacher where name = 'james';

COMMIT;
ROLLBACK;

事務 ACID 特性

原子性(Atomicity):最小的工作單元,整個工作單元要麼一起提交成功,要麼全部失敗回滾。

一致性(Consistency):事務中操作的資料及狀態改變是一致的,即寫入資料的結果必須完全符合預設的規則,不會因為出現系統意外等原因導致狀態的不一致。

隔離型(Isolation):一個事務所操作的資料在提交之前,對其他事務的可見性設定(一般設定為不可見)。

永續性(Durability):事務所做的修改就會永久儲存,不會因為系統意外導致資料的丟失。

參考(原子性和一致性的區別是什麼?)這篇博文,講了一下原子性和一致性的區別。

事務併發帶來了哪些問題

髒讀

比如 user 表中有一條使用者資料,執行了如下操作:

  1. 事務B更新 id=1 的資料,age 更新為18,不提交事務
  2. 事務A查詢 id=1 的資料
  3. 事務B回滾剛才的更新操作

這個時候,事務A中查詢出的 id=1 的資料,age 的值是16還是18?

不可重複讀

 

比如 user 表中有一條使用者資料,執行了如下操作:

  1. 事務A查詢 id=1 的資料
  2. 事務B更新 id=1 的資料,age 更新為18,並提交事務
  3. 事務A再次查詢 id=1 的資料

這個時候,事務A兩次查詢出的 id=1 的資料,age 的值是16還是18?

幻讀

比如在 user 表中執行了如下操作:

  1. 事務A查詢 age>15 的資料
  2. 事務B新增一條資料,age=22,並提交事務
  3. 事務A再次查詢 age>15 的資料

這個時候,事務A兩次查詢出的資料,數量是1條還是2條?

事務四種隔離級別

SQL92 ANSI/ISO標準:http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt

Read Uncommitted(未提交讀) --未解決併發問題

事務未提交對其他事務也是可見的,髒讀(dirty read)

Read Committed(提交讀) --解決髒讀問題

一個事務開始之後,只能看到自己提交的事務所做的修改,不可重複讀(non repeatable read)

Repeatable Read(可重複讀) --解決不可重複讀問題

在同一個事務中多次讀取同樣的資料結果是一樣的,這種隔離級別未定義解決幻讀的問題

Serializable(序列化) --解決所有問題

最高的隔離級別,通過強制事務的序列執行

InnoDB 引擎對隔離級別的支援程度

事務隔離級別

髒讀

不可重複讀

幻讀

未提交讀(Read Uncommitted)

可能

可能

可能

已提交讀(Read Committed)

不可能

可能

可能

可重複讀(Repeatable Read)

不可能

不可能

對 InnoDB 不可能

序列化(Serializable)

不可能

不可能

不可能

事務隔離級別的併發能力:未提交讀 > 已提交讀 > 可重複讀 > 序列化

InnDB 引擎預設的事務隔離級別是可重複讀(Repeatable Read),在該級別中它把幻讀的問題也解決了。InnDB 中事務隔離級別通過鎖、MVCC 實現。

通過如下語句檢視 InnoDB 的預設事務隔離級別:

/* 全域性、當前會話的隔離級別 */
SELECT @@global.tx_isolation, @@tx_isolation;

通過如下語句設定 InnoDB 的事務隔離級別:

/* 設定全域性隔離級別 */
set global transaction isolation level read committed;
/* 設定當前會話的隔離級別 */
set session transaction isolation level read committed;

接下來我們來測試一下 InnoDB 的預設事務隔離級別(Repeatable Read)是否解決了髒讀、不可重複讀、幻讀的問題。

資料準備:

CREATE TABLE `user` (
  `id` int(11) NOT NULL,
  `name` varchar(32) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into `user` (`id`, `name`, `age`) values('1','Ada','16');

是否解決了髒讀的問題?

開啟兩個 MySQL 客戶端,模擬事務A和事務B的操作,執行步驟如下:

  1. 事務B開啟手動事務,更新 id=1 的資料,age 更新為18,不提交事務
  2. 事務A查詢 id=1 的資料
  3. 事務B回滾剛才的更新操作

圖中的數字是執行步驟,通過下圖可以看出事務A的執行結果是16,InnDB 的預設事務隔離級別完美的解決了髒讀的問題。

是否解決了不可重複讀的問題?

開啟兩個 MySQL 客戶端,模擬事務A和事務B的操作,執行步驟如下:

  1. 事務A開啟手動事務,查詢 id=1 的資料
  2. 事務B更新 id=1 的資料,age 更新為18,並提交事務
  3. 事務A再次查詢 id=1 的資料

圖中的數字是執行步驟,通過下圖可以看出事務A兩次的執行結果都是16,沒有受事務B更新操作的影響,InnDB 的預設事務隔離級別完美的解決了不可重複讀的問題。

是否解決了幻讀的問題?

開啟兩個 MySQL 客戶端,模擬事務A和事務B的操作,執行步驟如下:

  1. 事務A開啟手動事務,查詢 age>15 的資料
  2. 事務B新增一條資料,age=22,並提交事務
  3. 事務A再次查詢 age>15 的資料

圖中的數字是執行步驟,通過下圖可以看出事務A兩次的執行結果都是一條資料,沒有受事務B新增操作的影響,InnDB 的預設事務隔離級別完美的解決了幻讀的問題。