1. 程式人生 > >我對資料庫事務的理解(MYSQL中)

我對資料庫事務的理解(MYSQL中)

-- 設定資料庫事務為手動的提交
SET @@AUTOCOMMIT = 0;

-- 檢視是否被修改
SELECT @@autocommit;

-- 檢視當前的編碼格式
SELECT @@character_set_results;




-- 賬戶表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
balance INT
);


INSERT INTO account(NAME,balance) VALUES('mzy',5000);
INSERT INTO account(NAME,balance) VALUES('jacky',5000);


SELECT * FROM account;
UPDATE account SET balance=5000;
DELETE FROM account;

-- 設定為手動提交之後,必須手動commit
COMMIT;


-- 關於事務的四種特性中的隔離性的理解:
-- 1.首先調整資料庫事務隔離性的級別
-- 隔離級別髒讀不可重複讀幻讀
-- Serializable         否          否           否
-- Repeatable Read      否          否           是
-- Read Committed       否          是           是
-- Read Uncommitted     是          是           是


-- mysql預設的話: Repeatable Read
-- oracle預設的話:Read Committed










-- 修改事務的等級:最低的級別Read Uncommitted:
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 1. 測試髒讀
-- 檢視當前的級別:
SELECT @@global.tx_isolation;


髒讀的理解:
一個事務讀到了另一個事務未提交更新的資料!
假如這個事務未commit,只是當前操作可以看見,
但是未提交事務!

例如具體業務中:
買家點選貨物(假定價格100元),進入付款的介面了,
但是沒有執行最後的提交付款(COMMIT),但是也沒有
取消訂單(ROLLBACK)【注意是相當於保持這個操作】。
但是通知賣家檢視餘額的時候,就會發現餘額多了100元,
賣家一發貨,就相當於損失了100元。


SET @@AUTOCOMMIT = 0; -- 首先設定事務提交為手動


-- 我先給賣家jacky打2000,讓他發貨
UPDATE account SET balance=balance-2000 WHERE NAME='mzy';
UPDATE account SET balance=balance+2000 WHERE NAME='jacky';


-- jacky查餘額增加了2000
SELECT * FROM account WHERE NAME='jacky';


-- 然後我取消訂單進行混滾了
ROLLBACK;












-- 修改事務的等級:倒數第二級別Read Committed:
-- read committed 比 read uncommitted 多了防止髒讀
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 2. 測試不可重複讀
-- 檢視當前的級別:
SELECT @@global.tx_isolation;


不可重複讀:
一個事務得到了另一個事務已提交更新(update)的資料!

需要和上面的髒讀區分出來的是,我們的髒讀是站在使用者的角度來看的;
但是不可重複讀,是站在銀行的角度來講的,在銀行的需求中,當前的
報備和日誌需要統一!!
什麼意思呢?
比如在一次銀行對使用者的存款進行查賬的操作中,操作的資料
保持一致!
例如:我們當前的正在查A使用者,查到A使用者的存款有3000元,
但是在我們開始記錄的那一個時刻點,A使用者又進行存款的操作;
A使用者又存了2000元;如果沒有預防不可重複讀的話,那麼在一次
事務的操作中,如果需要多次記錄結果:第一次記錄結果就為3000元;
第二次記錄結果就為5000元,造成兩個記錄的結果不同;

意思就是,兩個併發進行的事務不能相互影響,這種修改的結果,只能在
當前事務結束了之後,存入,修改!

髒讀是必須避免的,但是不可重複讀,有些時候也是需要的;

-- 不可重複讀的測試應該是兩個窗口才能進行:不可重複讀原理是併發並行的事務;


-- 模擬:視窗一
-- jacky存入2000元
UPDATE account SET balance = banlance + 2000 WHERE account.`NAME` = 'jacky';




-- 模擬:視窗二(在視窗一的update操作前開啟)
-- 在沒有防止不可重複讀的時候,這裡的結果應該是修改了之後的結果
-- 在防止了不可重複讀之後,這裡的結果應該是修改前的,
-- 當退出當前事務之後,再次進入事務,才是修改後的結果。
SELECT account.`balance` FROM account WHERE account.`NAME` = 'jacky';







-- 修改事務的等級:倒數第三級別REPEATABLE READ:
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 3.測試幻讀
-- 檢視當前的級別:
SELECT @@global.tx_isolation;


幻讀:
一個事務讀到另一個事務,新插入(insert)的資料。
mysql中看不到幻讀,但是能防止幻讀?
只能看最高級別防止了幻讀後倒推!

SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;


-- 假如,現在銀行中新加了一個使用者,也是併發的事務;
-- 其中如果是顯示新增前的mzy和jacky兩個賬戶就是防止了幻讀
-- 如果計算上了新加的使用者,就是幻讀


-- 視窗一:
SELECT COUNT(account.`NAME`) FROM account;




-- 視窗二:
-- mysql中看不到幻讀
INSERT INTO account VALUES(....); -- 直接卡住,不會讓你插入
-- 因為如果你插入的話,就會影響到上面的查詢




-- 倒推:如果沒有防止幻讀的話,級別不是SERIALIZABLE,而是REPEATABLE READ
-- 就會在一次查詢中併發的兩個事務,產生幻讀首先查詢count(*)為2
-- 在insert之後,查詢就為3


-- 和防止可重複讀不同的是,可重複讀執行使用者修改(update),但是另一個事務當前不會
-- 發生變化,在重新開啟的時候才會發生變化。


-- 防止幻讀就很簡單粗暴,當在查詢的時候,就禁止使用者開戶(insert)


-- 隔離等級:但是安全性往往會帶來效能上的缺失
-- 我覺得在一般的業務中防止髒讀就可以了,可重複讀和幻讀都是可以接受的。