一文精通MySQL事務(wù)規(guī)則
點(diǎn)擊上方“框架師”,選擇“置頂公眾號(hào)”
我們一起學(xué)習(xí)進(jìn)步!


引言:
每個(gè)程序員學(xué)習(xí)數(shù)據(jù)庫(kù)離不開(kāi)MySQL,而學(xué)習(xí)MySQL就必學(xué)事務(wù),今天我們就來(lái)一起學(xué)習(xí)一下MySQL的事務(wù);
事務(wù)的概念
在實(shí)際的業(yè)務(wù)開(kāi)發(fā)中,有些業(yè)務(wù)操作要多次訪問(wèn)數(shù)據(jù)庫(kù)。一個(gè)業(yè)務(wù)要發(fā)送多條SQL語(yǔ)句給數(shù)據(jù)庫(kù)執(zhí)行。由多條SQL語(yǔ)句組成一個(gè)功能(事務(wù)).需要將多次訪問(wèn)數(shù)據(jù)庫(kù)的操作視為一個(gè)整體來(lái)執(zhí)行,要么所有的SQL語(yǔ)句全部執(zhí)行成功。如果其中有一條SQL語(yǔ)句失敗,就進(jìn)行事務(wù)的回滾,所有的SQL語(yǔ)句全部執(zhí)行失敗。
事務(wù)應(yīng)用場(chǎng)景
最通俗易懂的就是銀行轉(zhuǎn)賬的案例,例如張三給李四轉(zhuǎn)錢(qián),張三賬號(hào)減去相應(yīng)數(shù)額資金,李四賬號(hào)添加相應(yīng)數(shù)額資金;
案例演示:
-- 創(chuàng)建account數(shù)據(jù)表
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
balance DOUBLE
);
-- 添加數(shù)據(jù)
INSERT INTO account (NAME, balance) VALUES ('張三', 1000), ('李四', 1000);模擬張三給李四轉(zhuǎn)500元錢(qián),一個(gè)轉(zhuǎn)賬的業(yè)務(wù)操作最少要執(zhí)行下面的2條sql語(yǔ)句:
張三賬號(hào)-500
-- 1. 張三賬號(hào)-500
UPDATE account SET balance = balance - 500 WHERE id=1;李四賬號(hào)+500
-- 2. 李四賬號(hào)+500
UPDATE account SET balance = balance + 500 WHERE id=2;結(jié)果如下

假設(shè)當(dāng)張三賬號(hào)上-500元,服務(wù)器崩潰了。李四的賬號(hào)并沒(méi)有+500元,數(shù)據(jù)就出現(xiàn)問(wèn)題了。我們需要保證其中一條SQL語(yǔ)句出現(xiàn)問(wèn)題,整個(gè)轉(zhuǎn)賬就算失敗。只有兩條SQL都成功了轉(zhuǎn)賬才算成功。這個(gè)時(shí)候就需要用到事務(wù)。
事務(wù)的四大特性(A,C,I,D)
| 事務(wù)特性 | 描述 |
| 原子性(Atomicity) | 事務(wù)是一個(gè)不可分割的工作單位,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生 |
| 一致性(Consistency) | 事務(wù)前后數(shù)據(jù)的完整性必須保持一致 |
| 隔離性(Isolation) | 如多個(gè)用戶并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí),一個(gè)用戶的事務(wù)不可以被其它用戶的事務(wù)所干擾,多個(gè)用戶并發(fā)事務(wù)之間數(shù)據(jù)要相互隔離,不可以互相影響 |
| 持久性(Durability) | 如一個(gè)事務(wù)一旦被提交,它對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)改變就是永久性的,就算未來(lái)數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)數(shù)據(jù)有所影響 |
手動(dòng)提交事務(wù)
在MySQL數(shù)據(jù)庫(kù)中有兩種提交事務(wù)的規(guī)則,一種是手動(dòng)提交事務(wù),一種是自動(dòng)提交事務(wù),其中自動(dòng)提交事務(wù)是默認(rèn)的,下面先看一下手動(dòng)提交事務(wù)的案例,MySQL中有專門(mén)用于事務(wù)的SQL,如下
| SQL | 描述 |
| start transaction | 開(kāi)始事務(wù) |
| commit | 提交事務(wù) |
| rollback | 回滾事務(wù) |
| End Transaction | 事務(wù)結(jié)束 |
手動(dòng)提交事務(wù)步驟
?手動(dòng)提交事務(wù)分別有兩種情況,如下圖

第1種情況:開(kāi)啟事務(wù) -> 執(zhí)行SQL語(yǔ)句 -> 成功 -> 提交事務(wù)
目前數(shù)據(jù)庫(kù)內(nèi)容如下

使用DOS控制臺(tái)進(jìn)入MySQL
執(zhí)行以下SQL語(yǔ)句:1.開(kāi)啟事務(wù), 2.張三賬號(hào)-500, 3.李四賬號(hào)+500
START TRANSACTION;
UPDATE account SET balance = balance - 500 WHERE id=1;
UPDATE account SET balance = balance + 500 WHERE id=2;

使用Navicat查看數(shù)據(jù)庫(kù)數(shù)據(jù),發(fā)現(xiàn)數(shù)據(jù)庫(kù)并沒(méi)有變

在控制臺(tái)執(zhí)行
commit提交任務(wù):

使用Navicat查看數(shù)據(jù)庫(kù):發(fā)現(xiàn)數(shù)據(jù)改變

第2種情況:開(kāi)啟事務(wù) -> 執(zhí)行SQL語(yǔ)句 -> 失敗 -> 回滾事務(wù)
模擬張三給李四轉(zhuǎn)500元錢(qián)(失?。┠壳皵?shù)據(jù)庫(kù)數(shù)據(jù)如下:

在控制臺(tái)執(zhí)行以下SQL語(yǔ)句:1.開(kāi)啟事務(wù), 2.張三賬號(hào)-500
START TRANSACTION;
UPDATE account SET balance = balance - 500 WHERE id=1;
使用Navicat查看數(shù)據(jù)庫(kù):發(fā)現(xiàn)數(shù)據(jù)并沒(méi)有改變

在控制臺(tái)執(zhí)行rollback回滾事務(wù):

使用Navicat查看數(shù)據(jù)庫(kù):發(fā)現(xiàn)數(shù)據(jù)沒(méi)有改變

自動(dòng)提交事務(wù)
MySQL的每一條DML(增刪改)語(yǔ)句都是一個(gè)單獨(dú)的事務(wù),每條語(yǔ)句都會(huì)自動(dòng)開(kāi)啟一個(gè)事務(wù),執(zhí)行完畢自動(dòng)提交事務(wù),MySQL默認(rèn)開(kāi)始自動(dòng)提交事務(wù)。
將張三和李四金額重置為1000

執(zhí)行以下SQL語(yǔ)句
UPDATE account SET balance = balance - 500 WHERE id=1;
使用Navicat查看數(shù)據(jù)庫(kù):發(fā)現(xiàn)數(shù)據(jù)已經(jīng)改變

?我們可以使用SQL語(yǔ)句查看MySQL是否開(kāi)啟自動(dòng)提交事務(wù)
show variables like '%commit%';
-- 或
SELECT @@autocommit;
通過(guò)修改MySQL全局變量"autocommit",取消自動(dòng)提交事務(wù)

0:OFF(關(guān)閉自動(dòng)提交)
1:ON(開(kāi)啟自動(dòng)提交)
取消自動(dòng)提交事務(wù),設(shè)置自動(dòng)提交的參數(shù)為OFF,執(zhí)行SQL語(yǔ)句:set autocommit = 0;

在控制臺(tái)執(zhí)行以下SQL語(yǔ)句:張三-500
UPDATE account SET balance = balance - 500 WHERE id=1;
使用Navicat查看數(shù)據(jù)庫(kù),發(fā)現(xiàn)數(shù)據(jù)并沒(méi)有改變

在控制臺(tái)執(zhí)行commit提交任務(wù)

使用Navicat查看數(shù)據(jù)庫(kù),發(fā)現(xiàn)數(shù)據(jù)改變

MySQL事務(wù)的原理
事務(wù)開(kāi)啟之后, 所有的操作都會(huì)臨時(shí)保存到事務(wù)日志, 事務(wù)日志只有在得到commit命令才會(huì)同步到數(shù)據(jù)表中,其他任何情況都會(huì)清空事務(wù)日志(rollback,斷開(kāi)連接),如下圖

事務(wù)的隔離級(jí)別
事務(wù)在操作時(shí)的理想狀態(tài):多個(gè)事務(wù)之間互不影響,如果隔離級(jí)別設(shè)置不當(dāng)就可能引發(fā)并發(fā)訪問(wèn)問(wèn)題。
| 并發(fā)訪問(wèn)的問(wèn)題 | 含義 |
| 臟讀 | 一個(gè)事務(wù)讀取到了另一個(gè)事務(wù)尚未提交的數(shù)據(jù) |
| 不可重復(fù)讀 | 一個(gè)事務(wù)中兩次讀取的數(shù)據(jù)內(nèi)容不一致 |
| 幻讀 | 一個(gè)事務(wù)讀取到了別的事務(wù)插入的數(shù)據(jù),導(dǎo)致前后讀取記錄行數(shù)不同 |
臟讀演示

不可重復(fù)讀演示

幻讀演示

MySQL數(shù)據(jù)庫(kù)有四種隔離級(jí)別:上面的級(jí)別最低,下面的級(jí)別最高?!笆恰北硎緯?huì)出現(xiàn)這種問(wèn)題,“否”表示不會(huì)出現(xiàn)這種問(wèn)題。
| 級(jí)別 | 名稱 | 隔離級(jí)別 | 臟讀 | 不可重復(fù)讀 | 幻讀 | 數(shù)據(jù)庫(kù)默認(rèn)隔離級(jí)別 |
| 1 | 讀未提交 | read uncommitted | 是 | 是 | 是 | |
| 2 | 讀已提交 | read committed | 否 | 是 | 是 | Oracle和SQL Server |
| 3 | 可重復(fù)讀 | repeatable read | 否 | 否 | 是 | MySQL |
| 4 | 串行化 | serializable | 否 | 否 | 否 |

下面給大家推薦幾篇之前的文章,之前有小伙伴問(wèn)我要過(guò)博客源碼,現(xiàn)在已經(jīng)將源碼開(kāi)源在碼云了,需要的小伙伴點(diǎn)擊閱讀原文進(jìn)入我博客找零基礎(chǔ)搭建博客那篇文章,首條就是碼云地址,如果這篇文章有幫助到你,請(qǐng)多多轉(zhuǎn)發(fā),點(diǎn)再看,這是我的動(dòng)力
one more thing!如果大家想要第一時(shí)間看到墨白更新的文章,可以掃描下方的二維碼,關(guān)注我的公眾號(hào)。我們下篇文章見(jiàn)!
