事務:
事務指的是邏輯上的一組操作,組成這組操作的各個單元要麼全都成功,要麼全都失敗。
作用:保證在一個事務中多次操作資料庫表中資料時,要麼全部成功,要麼全部失敗。
事務的特性(ACID)
資料庫的事務必須具備ACID特性,ACID是指 Atomic(原子性)、Consistensy(一緻性)、Isolation(隔離型)和Durability(持久性)的英文縮寫。
1、原子性(Atomicity)
事務包裝的一組sql,要麼都執行成功,要麼都失敗。這些操作是不可分割的。
2、一緻性(Consistency)
資料庫的資料狀态是一緻的。
事務的成功與失敗,最終資料庫的資料都是符合實際生活的業務邏輯。一緻性絕大多數依賴業務邏輯和原子性。
3、持久性:(Durability)
事務成功送出之後,對于資料庫的改變是永久的。哪怕資料庫發生異常,重新開機之後資料亦然存在。
4、隔離性(Isolation)
一個事務的成功或者失敗對于其他的事務是沒有影響。2個事務應該互相獨立。
事務問題:
髒讀:讀正在編輯中的資料(不能容忍)
髒資料(正在編輯中的資料) dirty data
Clean data : 編輯完成的資料(儲存,撤銷)
幻讀(虛讀):事務A首先根據條件索引得到N條資料,然後事務B改變了這N條資料之外的M條或者增添了M條符合事務A搜尋條件的資料,導緻事務A再次搜尋發現有N+M條資料了,就産生了幻讀。
也就是說,目前事務讀第一次取到的資料比後來讀取到資料條目少。
不可重複讀:同一事務中,前後多次讀到的資料不一樣
在金融/銀行類機構,這個問題一定要解決
幻讀與不可重複讀的差別:
兩者有些相似,但是前者針對的是insert,後者針對的update或delete。
spring(資料庫)事務隔離級别分為四種(級别遞減):
1、Serializable (串行化):最嚴格的級别,事務串行執行,資源消耗最大;
2、REPEATABLE READ(重複讀) :保證了一個事務不會修改已經由另一個事務讀取但未送出(復原)的資料。避免了“髒讀取”和“不可重複讀取”的情況,但不能避免“幻讀”,但是帶來了更多的性能損失。
3、READ COMMITTED (送出讀):大多數主流資料庫的預設事務等級,保證了一個事務不會讀到另一個并行事務已修改但未送出的資料,避免了“髒讀取”,但不能避免“幻讀”和“不可重複讀取”。該級别适用于大多數系統。
4、Read Uncommitted(未送出讀) :事務中的修改,即使沒有送出,其他事務也可以看得到,會導緻“髒讀”、“幻讀”和“不可重複讀取”。
隔離級别與解決問題
資料庫規範規定了4種隔離級别,分别用于描述兩個事務并發的所有情況。
read uncommitted 讀未送出,一個事務讀到另一個事務沒有送出的資料。
a)存在:3個問題(髒讀、不可重複讀、虛讀)。
b)解決:0個問題
read committed 讀已送出,一個事務讀到另一個事務已經送出的資料。
a)存在:2個問題(不可重複讀、虛讀)。
b)解決:1個問題(髒讀)
repeatable read(): 可重複讀,在一個事務中讀到的資料始終保持一緻,無論另一個事務是否送出。
a)存在:1個問題(虛讀)。
b)解決:2個問題(髒讀、不可重複讀)
serializable 串行化,同時隻能執行一個事務,相當于事務中的單線程。
a)存在:0個問題。
b)解決:3個問題(髒讀、不可重複讀、虛讀)
安全和性能對比
安全性:serializable > repeatable read > read committed > read uncommitted
性能 : serializable < repeatable read < read committed < read uncommitted
常見資料庫的預設隔離級别:
MySql:repeatable read
Oracle:read committed
事務在解決生活中的問題時,邏輯是這樣的:
MySQL事務操作
sql語句——描述
start transaction;——開啟事務 (禁止了自動送出)
commit;——送出事務 (手動)
rollback;——復原事務
準備資料:
操作:MySQL中可以有兩種方式進行事務管理
- 手動送出:先開啟,再送出
- 自動送出:MySQL預設自動送出。即執行一條sql語句送出一次事務。
方式一、手動
(事務送出之後,sql語句對資料庫産生的操作才會被永久的儲存)
事務是如何處理異常的?
a=1000 b=1000
開啟事務(start transaction;)
update t_account set money = money -100 where name=‘a’; a=900
出現異常
update t_account set money = money +100 where name=‘b’;
事務的復原(rollback;)(撤銷已經成功執行的sql語句,回到開啟事務之前的狀态)
a=1000 b=1000;
注意:隻要送出事務,那麼資料就會長久儲存了,就不能復原事務了。即送出或者復原事務都是代表結束目前事務的操作。
方式二、自動送出,通過修改mysql全局變量“autocommit”進行控制。
通過以下指令可以檢視目前autocommit模式:
show variables like ‘%commit%’;
設定自動送出的參數為OFF
set autocommit = 0; – 0:OFF 1:ON
說明:
1)MySql預設自動送出。即執行一條sql語句送出一次事務。
2)設定autocommit為off狀态,隻是臨時性的,下次重新啟動mysql,autocommit依然變為on狀态。
3)如果設定autocommit為off狀态,那麼當我們執行一條sql語句,就不會自動送出事務,重新啟動可視化工具,資料并沒有改變。
4)如果設定autocommit為on狀态,如果我們先執行 start transaction; 然後在執行修改資料庫的語句:
update account set money = money-100 where name=‘a’;
update account set money = money+100 where name=‘b’;
那麼此時就表示上述修改資料庫的sql語句都在同一個事務中,此時必須手動送出事務,即commit;
換句話說,如果我們手動開啟事務 start transaction; 那麼此時mysql就不會自動送出事務,必須手動送出事務。
5)如果設定autocommit為on狀态,如果我們不執行 start transaction; 直接執行修改資料庫的語句:
update account set money = money-100 where name=‘a’;
update account set money = money+100 where name=‘b’;
那麼此時mysql就會自動送出事務。即上述每條sql語句就是一個事務。
JDBC事務操作
Connection對象的方法名——描述
conn.setAutoCommit(false)——開啟事務(禁止自動送出)
conn.commit()——送出事務
conn.rollback()——復原事務
注意:在jdbc事務操作中,事務的控制都是通過Connection對象完成的,當一個完整的業務操作前,我們首先使用conn.setAutoCommit(false)來開啟事務。預設情況下是true的,表示關閉事務,那麼一條sql語句就是一個事務,預設送出事務。如果設定為false,那麼表示開啟事務,所有的sql語句就會都在一個事務中。
當業務操作完成之後,如果整個操作沒有問題,我們需要使用conn.commit()來送出事務。當然了,如果出現了異常,我們需要使用conn.rollback()撤銷所有的操作,是以出現異常,需要進行事務的復原。
常見事務問題之詳解
髒讀:隻一個事務讀取了另外一個事務未送出的資料
set session transaction isolation level read uncommitted;
不可重複讀:在一個事務内多次讀取表中資料,多次讀取的結果不同。(和髒讀的差別:不可重複讀是讀取已送出的資料)
set session transaction isolation level read committed;
幻讀(虛讀):
set session transaction isolation level repeatable read;
指在一個事務中 讀取 另一個事務 插入或删除 資料記錄,導緻目前事務 讀取資料前後不一緻。
存款100元但未送出,這時銀行做報表 統計總額為500元,丙将事務送出,銀行再統計就變成了 600元,兩次統計結果不一緻,銀行便會不知所措。
一個事務 讀取 另一個事務 已經送出的資料,強調的是 記錄數 的變化,常有sql類型為 insert和 delete。
注意: 在mysql資料庫中,底層對于幻讀做了優化,示範不了。
serializable串行化
可以避免所有的問題。資料庫執行這個事務,其他事務必須等待目前事務執行完畢,才能被執行。
串行為什麼可以解決所有問題?因為上述問題都是由并行執行引起的,是以改成所有事務一次執行(串行),就可以解決所有問題。