NoteOfMySQL-13-事務與併發控制
一、事務簡介
儲存引擎如InnoDB、BDB才支援事務處理。
每個事務(transaction)的處理必須滿足ACID原則:
- 原子性(Atomicity):
- 原子性指每個事務都必須被認為是一個不可分割的單元,即一個事務要麼都執行,要麼都不執行。
- 一致性(Consistency)
- 一致性表示在事務開始之前和結束以後,資料庫的完整性沒有被破壞,保證了資料庫從不返回一個未處理完的事務。
- 隔離性(Isolation)
- 隔離性可以防止多個事務併發執行時由於交叉執行而導致資料的不一致。
- 永續性(Durability)
- 事務處理結束後,對資料的修改就是永久的,即便系統故障也不會丟失。在預設情況下,InnoDB表是100%持久的,MyISAM表在最後一個
flush tables
- 事務處理結束後,對資料的修改就是永久的,即便系統故障也不會丟失。在預設情況下,InnoDB表是100%持久的,MyISAM表在最後一個
二、MySQL事務
1. 事務控制
1.1 語法
語法 | 說明 |
---|---|
start commit | begin |
開始一項新的事務 |
commit [and [no] chain] [[no] release] |
提交事務 |
rollback [and [no] chain] [[no] release] |
回滾事務 |
set autocommit={0|1} |
設定是否自動提交事務 |
chain
表示提交或回滾一個事務後立即啟動一個新事務realease
表示提交或回滾一個事務後斷開與客戶端的連線
1.2 例子
2. 事務隔離級別
2.1 隔離級別瞭解
每一個事務都有一個隔離級,它定義了使用者之間隔離和互動的程度。顯然,在但使用者環境中,它起不到獨特的作用,但在多使用者環境中,若不保證事務隔離,資料基本上會受到其他事務的影響,這將導致不一致性。
基於ANSI/ISO SQL規範,MySQL定義了4種隔離級別,如下表所示,按照隔離級別逐漸降低排列。
隔離級別 | 名稱 | 說明 |
---|---|---|
serializable | 序列化 | 使用者之間會按順序執行事務 |
repeatable read | 可重複讀 | 事務不會被看成是序列,即同個事務執行多個select語句結果都是相同的 |
read committed | 提交讀 | 若多個事務修改相應的表,那麼同個事務執行多個select語句結果會不同 |
read uncommitted | 未提交讀 | 當前級別的事務可以讀取到為提交的資料 |
2.2 設定隔離級別
set [global | session] transaction isolation level
{serializable | repeatable read | read committed | read uncommitted};
global
表示定義的隔離級別適用於所有的使用者session
表示定義的隔離級別只適用於當前允許的會話和連線
2.3 查詢當前隔離級別
-- 方法一:
select @@tx_isolation;
-- 方法二:
show variables like 'tx_isolation';
三、併發簡介
1. 併發導致的問題
當多個使用者併發地存取資料庫的同一個資料時,若不加控制可能會導致存取和儲存不正確的資料。
1.1 丟失更新問題(lost update)
當兩個或多個事務同時更新同一行資料時,最後的更新會覆蓋其他事務所作的更新,這種問題就叫丟失更新問題。
在完成並提交一個事務之前,其他事務不能訪問統一檔案或資源,這樣可以避免這種問題的出現。
1.2 髒讀問題(dirty read)
一個事務正在對一條記錄做修改,在這個事務完成並提交之前,這條記錄的資料處於不一致的狀態,若這是另一個事務也讀取這條記錄的資料,則這種現象就稱為“髒讀”。
1.3 不可重複讀問題(unrepeatable read)
當一個事務訪問資料時,其他事務也訪問該資料並對其進行修改,這會導致第一個事務兩次讀取到的資料不一樣,這種情況就是不可重複讀問題。
1.4 幻讀問題(phantom read)
當一個事務對某行資料進行插入或刪除操作時,若其他事務也對這部分資料進行讀取操作,則這個事務前後讀取的內容可能會不同,這種情況就是幻讀問題。
2. 鎖
當用戶對資料庫併發訪問時,為了確保事務完整性和資料庫的一致性,需要對其進行鎖定,它是實現資料庫併發控制的主要手段。鎖是一種用來防止多個客戶端同時訪問資料時產生問題的機制。具體地說,鎖可以防止出現上述併發導致的四種問題。
對於MySQL資料庫而言,最顯著的特點是不同的儲存引擎支援的鎖機制不同。
鎖機制 | 儲存引擎 | 特點 |
---|---|---|
表級鎖(table-level locking) | MyISAM、MEMORY、BDB、InnoDB | 開銷小,加鎖快;不會出現死鎖;鎖定力度大,發生鎖衝突概率高,併發度低 |
頁面鎖(page-level locking) | BDB | 開銷中,加鎖中;會出現死鎖;鎖定力度中,併發度中 |
行級鎖(row-level locking) | InnoDB | 開銷大,加鎖慢;會出現死鎖;鎖定力度小,發生鎖衝突概率低,併發度高 |
3. 死鎖
通常來說,死鎖都是應用設計的問題,可通過調整業務流程、資料庫物件設計、事務大小及訪問資料庫的SQL語句。
四、MySQL併發控制
1. MyISAM表的表級鎖
- 對於讀操作(
select
),會自動給涉及的表加讀鎖。- 讀鎖不會阻塞其他程序對同一張表的讀請求,但會阻塞對同一張表的寫請求,需要等讀鎖釋放後,其他程序才能進行寫操作。
- 對於寫操作(
update
、delete
、insert
),會自動給涉及的表加寫鎖。- 寫鎖會阻塞其他程序對同一張表的讀和寫操作,需要等寫鎖釋放後,其他程序才能進行讀寫操作。
- 用於鎖是自動加的,所有不需要使用者使用
lock tables
命令顯式為表加鎖。
1.1 查看錶級鎖爭用情況
show status like 'table_locks%';
1.2 鎖模式
MySQL的表級鎖有兩種模式:表共享讀鎖(table read lock)、表獨佔寫鎖(table write lock)。
1.3 加鎖方法
lock tables
tb_name [[as] new_tb_name] read [local] | [low_priority] write
...
unlock tables
- 當用戶在一次查詢時多次使用到一個被鎖定的表,需要在鎖定表的時候用
as
子句為表定義一個別名new_tb_name
。 read
為讀鎖定,確保使用者可以讀表,但不能修改表。加上local
表示允許表鎖定後,使用者可以進行非衝突的insert操作。write
為寫鎖定,只有鎖定該表的使用者可以修改表,其他使用者無法訪問該表。加上low_priority
表示允許其他使用者讀表。- 表鎖定只用於防止其他客戶端進行不正當的讀寫操作。保持鎖定的客戶端依然可以進行表層級的操作,如
drop table
。 - 在事務表中,
autocommit
必須設定為0,否則在執行lock tables
之後就會立即釋放表鎖定,很容易形成死鎖。 - 在事務表中,鎖定表時會隱式提交所有事務;在開始一個事務時會隱式解開所有表鎖定。
2. InnoDB的行級鎖
InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這意味著,只有通過索引條件檢索詩句,InnoDB才使用行級鎖,否則,InnoDB使用表級鎖。與MySQL不同,Oracle是通過資料塊中對相應資料行加鎖來實現的。
- 對於讀操作(
select
),InnoDB不會加任何鎖。 - 對於寫操作(
update
、delete
、insert
),會自動給涉及的資料集加排他鎖(即寫鎖)。
2.1 檢視行級鎖爭用情況
show status like 'innoDB_row_lock%';
2.2 鎖模式
InnoDB有兩種型別的行鎖:
- 共享鎖(S, 即讀鎖):允許一個事務去讀一行,但會阻塞其他事務對相同資料集進行寫操作。
- 排他鎖(X, 即寫鎖):允許獲得排他鎖的事務更新資料,但會阻塞其他事務對相同資料集進行讀和寫操作。
另外,為了允許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖為表鎖。意向鎖是InnoDB自動加的,不需要使用者干預。
- 意向共享鎖(IS):事務打算給資料行加行共享鎖(S),在此之前必須先取得該表的IS鎖。
- 意向排他鎖(IX):事務打算給資料行加行排他鎖(X),在此之前必須先取得該表的IX鎖。
若一個事務請求的鎖模式與當前的鎖相容,InnoDB就將請求的鎖授予該事務,否則該事務需要等待相應的鎖釋放。
2.3 加鎖方法
-- 新增共享鎖(S)
select * from tb_name where子句 ... lock in share mode;
-- 新增排他鎖(X)
select * from tb_name where子句 ... for update;
- 若當前事務包含對資料的更新操作,應使用排他鎖而不是共享鎖,這樣可以避免死鎖。