Mysql中的事務


1、什么是事務:數據庫中的事務是指邏輯上的一組操作,這組操作要么都執行成功,要么都不執行成功。
2、事務的管理:默認情況下Mysql會自動管理事務,一條SQL語句獨占一個事務。
也可以使用start transaction、rollback和commit人為方式管理。
在start transaction之后的多條語句就是一個事務,事務commit之前可以rollback。
3、在JDBC中管理事務:
connection.setAutoCommit(false);
connection.rollback();
connection.commit();
多語句時可以創建回滾點:
SavePoint sp = connection.setSavePoint();
connection.rollback(sp);
4、事務的四大特性:
(1)原子性:是指事務是一個不可分割的整體,事務中的操作要么全部發生,要么一個都不發生。
(2)一致性:事務處理前后數據的完整性必須保持一致。
完整性是指一個數據在某個時間點完全滿足數據庫中約束的要求。
(3)隔離性:是指多個用戶訪問同一個數據庫時,一個用戶的事務處理不能被其他用戶的事務干擾,多個並發事務之間的數據要相互隔離。
(4)持久性:是指一個事務一旦被提交,它對數據庫中的數據改變是永久的。
5、一些概念:
(1)臟讀:一個事務讀到另一個事務未提交的數據
(2)不可重復讀:在一個事務中多次讀取某一數據同的結果(一個事務讀到另一個事務已經提交的數據)
(3)虛讀幻讀:是指一個事務內讀到其他事務插入的數據,導致前后讀取不一致(一個事務讀到另一個事務已經提交的數據)
6、隔離級別:
查看當前隔離級別:select @@tx_isolation
設置隔離級別:set [session/global] transaction isolation level read uncommitted
session:只針對當前會話有效,默認為session
global:修改數據庫默認隔離級別
Mysql默認隔離級別:repeatable read
(1)read uncommitted -- 數據庫會出現臟讀、不可重復讀、虛讀幻讀問題
(2)read committed -- 數據庫防止出現臟讀,不防止不可重復讀和虛讀幻讀問題
(3)repeatable read -- 數據庫防止臟讀、不可重復讀問題,但不能防止虛讀幻讀問題
(4)serializable -- 數據庫運行在單線程模式,所有的問題都會防止
7、鎖
(1)排他鎖:排他鎖和任何鎖都不能共存,在任何隔離級別下進行修改都會加排他鎖
(2)共享鎖:共享鎖只能和共享鎖共存,在非serializable級別下查詢不加任何鎖,在serializable級別下查詢加共享鎖。
8、線程安全問題
多個用戶同時對同一數據進行並發修改操作時,就會導致線程安全問題的發生,使用鎖機制保證同一時間只有一個線程能訪問數據,這樣雖然解決了線程安全問題,但由於同一時間只能有一個線程訪問該數據,使得數據庫的效率非常低。為了找到合適的解決方案,得先對事務的處理有個更深的認識。
(1)如果多個線程同時對同一數據做修改,一定會有線程安全問題,必須解決。
任何隔離級別下做修改操作,都會加排他鎖,保證同一時間只能有一個線程訪問數據。
(2)如果多個線程同時對同一數據做查詢操作,不會產生線程安全問題,無需處理。
(3)如果多個線程都未運行在serializable級別,則查詢都不加鎖,不會排斥。
(4)如果多個線程都運行在serializable級別,則都加共享鎖,不會排斥。
(5)如果多個線程中有serializable,也有其他,則serializable加共享鎖,其他不加,不會排斥。
(6)如果一個線程修改數據,其他線程查詢時,可能出現臟讀、不可重復讀、虛讀幻讀。但不是任何時候都會出現問題,只在某些應用場景才可能造成問題。
(7)在serializable隔離級別下查詢加共享鎖,共他客戶端無論是什么隔離級別只要做修改操作,就加排他鎖,只能等待,從而解決虛讀幻讀問題。
9、更新丟失問題
兩個查詢對同一個查詢結果做修改,后修改會忽略先修改的結果,從而造成先修改的結果丟失。
解決方案一:將數據庫隔離級別設置為serializable可避免更新結果丟失,但這樣使得數據庫效率低下,所以不會這么做。
解決方案二:鎖機制
(1)樂觀鎖:假定每次都不會造成更新丟失,在數據庫中增加一版本字段,每次更新都基於上一版本進行。
(2)悲觀鎖:假定每次都會造成更新丟失,在查詢時手動加排他鎖(select ... for update)。
如果查詢多,修改少應使用樂觀鎖,減少鎖表操作;
如果修改多,查詢少應使用悲觀鎖,減少修改失敗。
10、演示不同隔離級別下的並發問題:
假定有如下表,模擬銀行轉賬的操作。
CREATE TABLE `card` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(20) default NULL,
`money` double default NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `card` VALUES(NULL,'john',1000);
INSERT INTO `card` VALUES(NULL,'mary',1000);

set transaction isolation level 設置事務隔離級別
select @@tx_isolation 查詢當前事務隔離級別

(1)當把事務的隔離級別設置為read uncommitted時,會引發臟讀、不可重復讀和虛讀

A窗口
set transaction isolation level read uncommitted;
start transaction;
select * from card;
-----發現a帳戶是1000元,轉到b窗口

B窗口
start transaction;
update card set money=money+100 where name='john';
-----不要提交,轉到a窗口查詢

select * from card
-----發現john多了100元,這時候a讀到了b未提交的數據(臟讀)

(2)當把事務的隔離級別設置為read committed時,會引發不可重復讀和虛讀,但避免了臟讀

A窗口
set transaction isolation level read committed;
start transaction;
select * from card;
-----發現john帳戶是1000元,轉到b窗口
B窗口
start transaction;
update card set money=money+100 where name='john';
commit;
-----轉到a窗口

select * from card;
-----發現john帳戶多了100,這時候,a讀到了別的事務提交的數據,兩次讀取john帳戶讀到的是不同的結果(不可重復讀)

(3)當把事務的隔離級別設置為repeatable read(mysql默認級別)時,會引發虛讀,但避免了臟讀、不可重復讀

A窗口
set transaction isolation level repeatable read;
start transaction;
select * from card;
----發現表有2個記錄,轉到b窗口

B窗口
start transaction;
insert into card values(null,'harry',1000);
commit;
-----轉到 a窗口

select * from card;
----可能發現表有3條記錄,這時候發生了a讀取到另外一個事務插入的數據(虛讀)

(4)當把事務的隔離級別設置為Serializable時,會避免所有問題
A窗口
set transaction isolation level Serializable;
start transaction;
select * from card;
-----轉到b窗口

B窗口
start transaction;
insert into card values(null,'harry',1000);
-----發現不能插入,只能等待a結束事務才能插入


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM