事務 (transaction) 其實指的就是一組操做裏面包含許多單一的邏輯,要麼就是全部邏輯都成功 (提交),只要有一個邏輯沒有成功,那麼這一組操做就算失敗,全部的數據都回歸到最初的狀態(回滾)。mysql
現有以下帳戶表,有 id 爲 1 和 2 的兩個帳戶:sql
假如咱們要讓 id 爲 1 的帳戶給 id 爲 2 的帳戶轉 100 塊,咱們要執行的 sql 就是:數據庫
update account set money=money-100 where id=1; update account set money=money+100 where id=2;
執行完以後:服務器
在上面示例中,其實 mysql 默認就幫咱們提交了事務,只是它把每一條執行的 sql 語句當成了一組操做,也就是執行一條 sql 事務就自動提交。session
而 mysql 事務的自動提交是能夠經過 autocommit 變量設置的:併發
能夠看到它默認是開啓狀態,如今咱們將它關閉:測試
再次執行上面的兩條 sql:spa
這時候會發現表數據好像是正常修改了。其實這只是此時內存中的數據,並無持久化保存到 db,咱們能夠經過 navicat 查看一下表數據:3d
此時咱們再執行一下 commit 提交事務:code
再次使用 navicat 查看錶數據:
此時上述的更新操做就持久化到了 db,這就是事務的提交 (commit) 操做。
假如咱們不執行 commit,執行 rollback:
能夠看到數據又回到更新前的初始狀態,這就是事務的回滾 (rollback) 操做。
set [session] autocommit=[0|1]; // 默認 設置會話級事務自動提交 set global autocommit=[0|1]; // 全局級事務自動提交
除了如方式一修改 mysql 事務的自動提交這種方式,mysql 還爲咱們提供了手動開始事務的方式,以下:
可經過 start transaction 開啓事務:
提交和回滾事務也都是經過 commit 和 rollback 。
原子性 (Atomicity):事務是最小單位,不可再分。
一致性 (Consistency):事務要求全部的 DML 語句操做的時候,必須保證同時成功或者同時失敗。
隔離性 (Isolation):事務 A 和事務 B 之間具備隔離性。
持久性 (Durability):是事務的保證,事務終結的標誌(內存的數據持久到硬盤文件中)。
事務 A 和事務 B 之間具備必定的隔離性,有 4 個隔離級別。
- 事務 A 和事務 B,事務 A 未提交的數據,事務 B 能夠讀取到。
- 這裏讀取到的數據叫作「髒數據」。
- 這種隔離級別最低,這種級別通常是在理論上存在,數據庫隔離級別通常都高於該級別。
- 事務 A 和事務 B ,事務A提交的數據,事務 B 才能讀取到。
- 這種隔離級別高於讀未提交。
- 換句話說,對方事務提交以後的數據,我當前事務才能讀取到。
- 這種級別能夠避免「髒數據」。
- 這種隔離級別會致使「不可重複讀取」。
- Oracle 默認隔離級別。
- 事務 A 和事務 B,事務 A 提交以後的數據,事務 B 讀取不到。
- 事務 B 是可重複讀取數據。
- 這種隔離級別高於讀已提交。
- 換句話說,對方提交以後的數據,我仍是讀取不到。
- 這種隔離級別能夠避免「不可重複讀取」,達到可重複讀取。
- 好比 1 點和 2 點讀到數據是同一個。
- Mysql 默認級別。
- 雖然能夠達到可重複讀取,可是會致使「幻像讀」。
- 事務 A 和事務 B,事務 A 在操做數據庫時,事務 B 只能排隊等待。
- 這種隔離級別不多使用,吞吐量過低,用戶體驗差。
- 這種級別能夠避免「幻像讀」,每一次讀取的都是數據庫中真實存在數據,事務 A 與事務 B 串行,而不併發。
- 髒讀:一個事務讀到了另外一個事務未提交的數據。
- 不可重複讀:一個事務讀到了另外一個事務已提交的數據,形成先後兩次查詢結果不一致。
- 幻讀:一個事務讀到了另外一個事務 insert 的數據,形成先後兩次查詢結果不一致(mysql 爲 innoDB 引擎時不存在這個問題)。
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交 | 可能 | 可能 | 可能 |
讀已提交 | 不可能 | 可能 | 可能 |
可重複讀 | 不可能 | 不可能 | 對 InnoDB 不可能 |
串行化 | 不可能 | 不可能 | 不可能 |
mysql 能夠在 my.ini 文件中使用 transaction-isolation 選項來設置服務器的缺省事務隔離級別。
值能夠是:
– READ-UNCOMMITTED – READ-COMMITTED – REPEATABLE-READ – SERIALIZABLE 例: [mysqld] transaction-isolation = READ-COMMITTED
語法:
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL <isolation-level>
[GLOBAL]:全局級設置,對全部會話有效。
[SESSION]:默認級別,會話級設置,只對當前會話有效。 <isolation-level>: – READ UNCOMMITTED – READ COMMITTED – REPEATABLE READ – SERIALIZABLE 例:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
要查看當前事務隔離級別,執行 SELECT @@tx_isolation; 便可:
假若有以下帳戶表:
分析:如今同時有 A 和 B 兩個事務操做,A 要修改 id 爲 1 的 name 爲 "zhangsansan",B 要修改 id 爲 1 的 money 爲 "2000"。此時必然有一個事務是先提交的:
假如 A 先提交,此時 id 爲 1 的 name 已修改成 "zhangsansan"。此時問題就出現了,由於 B 的修改信息如今還在內存中,B 這裏的 name 仍是以前的 "zhangsan",提交後就會覆蓋 A 的修改內容,最後將數據修改成:
id money name 1 2000 zhangsan
此時,A 的更新內容就丟失了。
用 mysql 測試上面示例,咱們會發現並無出現上述分析狀況,緣由就是排他鎖。
A 窗口,開啓事務,更新數據,但不提交:
B 窗口,開啓事務,執行更新操做,會發現阻塞住了:
當 A 窗口事務提交,B 窗口的阻塞會立馬消失,接着執行完畢。這是由於在 mysql 的事務中執行更新操做時會給要更新的數據加上排他鎖,若是當前更新事務未提交,那麼此時其它事務對該數據的更新操做就會被阻塞至當前事務提交(排他鎖釋放),當前事務提交後,其它事務就能拿到最新的數據在最新數據的基礎上更新,就不會出現分析中丟失更新的狀況。
select * from account where id=1 for update;
與悲觀鎖不一樣,悲觀鎖是由數據庫機制提供,而樂觀鎖是須要開發者手動控制的。修改表結構,給 account 表添加一個 version 字段:
只是要使用樂觀鎖的方式咱們就須要作一些額外的操做,例:
在事務提交以前,須要先檢查當前內存行數據 version 和對應 db 實際行數據 version 是否相同,若是相同,則提交更新,若是當前 version 小於實際 version,就將當前數據更新到實際 version 對應的數據,而後在該數據的基礎上執行咱們本身的更新操做,而且將 version 自增 1 後提交。
依然以上面示例說明,在 A 事務將 id 爲 1 的 name 改成 "zhangsansan" 提交後,該條數據 version 版本此時就爲 1。B 事務接着提交,當它以上述方式檢查當前內存 version 時,會發現當前內存 version 爲 0,而實際對應數據 version 爲 1,它就要將內存數據更新爲 version 爲 1 對應的這個版本了。即 name 同步爲 "zhangsan",接着在這個基礎上執行本身的修改操做,將 money 修改成 2000,因此更新後的結果爲:
id money name 1 2000 zhangsansan
這種方式也不會丟失更新,究其根底其實它的原理仍是和悲觀鎖相同:就是保證事務能在最新的數據基礎上更新。