是數據庫區別于文件系統的重要特性之一,用于保證數據庫的完整性,數據庫會從一種一致性狀態轉換為另一種一致性狀態。
1、四大特性
(1) 原子性
數據庫的每個事務都是不可分割的單元,只有事務中的所有 SQL 語句都執行成功,才算整個事務成功,如果事務中有一個 SQL 語句執行失敗,整個事務都將回滾。
(2) 一致性
數據庫從一種一致性狀態轉換為另一種一致性狀態,即數據庫的完整性約束沒有被破壞,要么都是新數據,要么都是老數據。
(3) 隔離性
一個事務的影響在該事務提交之前對其他事務都是不可見的,通過鎖機制來實現。
(4) 持久性
事務處理結束后,對數據的修改就是永久的,即便系統故障也不會丟失。
2、語法:
在 MySQL 命令行默認設置下,事務是自動提交的,即執行了 SQL 語句之后馬上會執行 commit 操作,可以通過 set autocommit = 0 來禁用當前會話的自動提交。
(1) 開始事務
begin 或 start transaction
(2) 提交事務
commit,即 commit [work]
(3) 事務回滾
rollback,即 rollback [work]
(4) 定義保存點
savepoint xxx,一個事務中可以有多個保存點
(5) 刪除保存點
release savepoint xxx,保存點不存在時會拋出異常
(6) 回滾到某個保存點
rollback to [savepoint] xxx
回滾到某個保存點并不能讓事務結束,要提交事務或回滾事務。
3、InnoDB 的實現
隔離性由鎖機制實現,原子性、一致性、持久性都是通過數據庫的 redo 日志和 undo 日志來完成。
redo 日志:重做日志,它記錄了事務的行為;
undo 日志:對數據庫進行修改時會產生 undo 日志,也會產生 redo 日志,使用 rollback 請求回滾時通過 undo 日志將數據回滾到修改前的樣子。
InnoDB 存儲引擎回滾時,它實際上做的是與之前相反的工作,insert 對應 delete,delete 對應insert,update 對應相反的 update。
ps:
1、commit 和 commit work 的區別
commit work 可以控制事務接收后的行為,可以用 select @@completion_type 查看值,默認是0。
completion_type = 0,兩者等價。
completion_type = 1,commit work 等價于 commit and chain,表示馬上開啟一個相同隔離級別的事務。
completion_type = 2,commit work 等價于 commit and release,表示事務提交后自動斷開與服務器的連接。
2、隱式提交
有些語句會造成隱式提交,主要有3類:
(1) DDL 語句:create event、create index、alter table、create database、truncate 等等,所有 DDL 語句都是不能回滾的。
(2) 權限操作語句:create user、drop user、grant、rename user 等等。
(3) 管理語句:analyze table、check table 等等。
3、隔離級別
通過鎖機制實現,大部分數據庫都沒有提供真正意義上的隔離性。
數據庫制造商在標準和性能之間進行了權衡,MySQL 還是比較貼近該標準的,ISO 和 ANSI SQL 標準了四種事務隔離級別的標準。
可以用 select @@tx_isolation 來查看它的值。
(1) read uncommitted
隔離級別最低,開銷也是最小,目前沒有數據庫會設置它為默認隔離級別,因為會出現臟讀。
臟讀:讀到未提交的數據(臟數據),n 事務中讀到 m 事務中修改了,但未提交的數據,即在不同事務下可以讀到其它事務未提交的數據。
嚴格來說,它已經破壞了事務的隔離性。
(2) read committed
它不允許在別的事務未提交時讀取到別的事務的修改數據,容易出現不可重復讀,但是可以被人們接受,因為事務已經提交,數據已經持久化到磁盤了,隔離級別越低,事務請求的鎖就越少,或者保持鎖定的時間就越短,這也是大多數數據庫默認的事務隔離級別都是 read committed 的原因。
不可重復讀:在一個事務中多次讀同一個數據,由于其他事務在第一個事務還沒結束時修改了數據,造成第一個事務中兩次讀到的數據不一致。
臟讀與不可重復讀:臟讀讀取的是未提交的數據,不可重復讀讀到的是已提交的數據。
(3) repeatable read
InnoDB 存儲引擎默認支持的隔離級別。不會出現臟讀、不可重復讀和幻讀(InnoDB 避免了該問題)。
(4) serializable
SQL 和 SQL2 標準的默認事務隔離級別,它是真正意義上的隔離,當然性能也是最差。
設置事務隔離級別:
set [global|session] transaction isolation level {read uncommitted|...};
比如:set tx_isolation='read-committed';