事務(wù)是什么
事務(wù)(TRANSACTION)是單個邏輯工作單元執(zhí)行的一系列操作,這些操作作為一個整體一起向系統(tǒng)提交,要么都執(zhí)行、要么都不執(zhí)行 。事務(wù)是一個不可分割的工作邏輯單元。也就是說,如果某個工作單元執(zhí)行的多條SQL語句當中,有一條執(zhí)行失敗或者返回錯誤,那么整個單元將會回滾,將所有數(shù)據(jù)回滾到事務(wù)開始之前的狀態(tài)。
- 在 MySQL 中只有使用了 Innodb 數(shù)據(jù)庫引擎的數(shù)據(jù)庫或表才支持事務(wù)。
事務(wù)的ACID屬性
- 原子性 Atomicity
事務(wù)是一個完整的操作,各部分操作是不可分的,要么全執(zhí)行,要么全不執(zhí)行。 - 一致性 Consistency
事務(wù)執(zhí)行前后數(shù)據(jù)必須處于一致狀態(tài)。 - 隔離性 Isolation
一個事務(wù)的執(zhí)行不能被其他事務(wù)干擾,多個并發(fā)事務(wù)之間相互隔離。事務(wù)A和事務(wù)B如果都要操作一個數(shù)據(jù)的話,在A執(zhí)行結(jié)束之前B是不能訪問的。(一般達不到) - 持久性 Durability
持久性是指一個事務(wù)一旦被提交,它對數(shù)據(jù)庫的數(shù)據(jù)的改變是永久性的。
事務(wù)的創(chuàng)建
隱式事務(wù):
事務(wù)沒有開啟和結(jié)束的標記。
insert,update,delete語句就是隱式事務(wù)。一條insert或update或delete就是一個事務(wù),而且會自動提交。
也就是說如果 我們連續(xù)寫兩條update語句的話,執(zhí)行之后是按兩個事務(wù)來處理的。如果我們的本意是讓這兩條update語句處在一個事務(wù)當中,那么就需要設(shè)置事務(wù)的自動提交標記為0。
set autocommit=0;
顯式事務(wù)
事務(wù)具有開始和結(jié)束的標記。
需要先設(shè)置事務(wù)的自動提交標記為0。
//啟用事務(wù)執(zhí)行
set autocommit=0;
START TRANSACTION;
UPDATE account set balance=500 where username='TT';
UPDATE account set balance=1500 where username='XX';
COMMIT;
事務(wù)的并發(fā)會造成的問題
臟讀
若有兩個事務(wù)A,B,事務(wù)A讀取了B更新的數(shù)據(jù),但是B執(zhí)行了回滾操作,那么A讀到的數(shù)據(jù)就是臟數(shù)據(jù)。這種現(xiàn)象叫做臟讀。
不可重復(fù)讀
兩個事務(wù)A和B,事務(wù)A多次讀取同一個數(shù)據(jù),但是同時事務(wù)B更新了這個數(shù)據(jù),此時事務(wù)A再去讀取這個數(shù)據(jù),同一事務(wù)中前后兩次得到的數(shù)據(jù)不一致。
幻讀
兩個事務(wù)A和B,事務(wù)A讀取了一個字段,事務(wù)B在這個字段中插入或刪除了行,當事務(wù)A再去讀這個字段中的值時,就會多出幾行或少幾行。同樣的條件查詢同樣的字段,得到的記錄數(shù)不一樣,而臟讀則是數(shù)據(jù)本身的數(shù)值不同。
事務(wù)的隔離級別
read uncommitted
顧名思義,可以一個事務(wù)可以讀到另外一個事務(wù)修改但是未提交的數(shù)據(jù)。
可能出現(xiàn)臟讀,不可重復(fù)讀,幻讀的情況。
read committed
一個事務(wù)只能讀到另外一個事務(wù)修改并且提交后的數(shù)據(jù)。
解決了臟讀的出現(xiàn),可能出現(xiàn)不可重復(fù)讀,幻讀的情況。
repeatable-read
為了防止不可重復(fù)讀的情況出現(xiàn),SELECT 的結(jié)果是事務(wù)開始時時間點的狀態(tài),因此,同樣的 SELECT 操作讀到的結(jié)果會是一致的。但是,會有幻讀現(xiàn)象。
Serializable
事務(wù)由并行變?yōu)榇?,不會出現(xiàn)數(shù)據(jù)沖突,但是會極大的降低性能。
InnoDB架構(gòu)
redo log buffer
InnoDB采用了WAL策略。WAL的全稱是Write Ahead Log (預(yù)寫日志)。即數(shù)據(jù)中的某一條數(shù)據(jù)需要更新時,先寫到日志當中,再擇時寫磁盤。
- 為什么要有這種機制
如果每次update一次,就更新一次磁盤中的數(shù)據(jù)。需要從磁盤中找到這條要更新的數(shù)據(jù)并且更新,IO成本太高。WAL策略提高了效率。同時保證了數(shù)據(jù)庫中數(shù)據(jù)的可靠性。因為如果數(shù)據(jù)還沒有被寫入磁盤中但是此時數(shù)據(jù)庫崩了,那么數(shù)據(jù)庫仍然可以從redo log中恢復(fù)數(shù)據(jù)。
事務(wù)一旦被提交,必須先將事務(wù)的所有日志寫入redo log中。寫入成功則提交成功。
如果數(shù)據(jù)被寫入了磁盤里,redo log 就會清空。
若 redo log 已經(jīng)滿了但是數(shù)據(jù)還沒有被寫進磁盤,那么數(shù)據(jù)會先被寫進磁盤然后redo log清空。
rodo log 的存儲結(jié)構(gòu)采用循環(huán)寫的方式,類似于循環(huán)鏈表,有一個用于記錄清空位置的標記點(CheckPoint),有一個用于記錄存儲位置的標記點(WritePos)。

redo log的落盤機制
當事務(wù)被提交時,所有日志會立刻被存儲到redo log buffer中,然后再被寫入磁盤中的 logfile。事務(wù)才算提交完成。
什么時候?qū)懭氪疟P,InnoDB有三種落盤機制。
第一種是提交之后寫入redo log buffer,每隔一段時間從redo log buffer寫到操作系統(tǒng)緩存中然后刷新到磁盤上。
第二種是事務(wù)提交之后立刻寫入到系統(tǒng)緩存中,并刷新到磁盤上。
第三種是提交之后寫入系統(tǒng)緩存當中,然后每隔一段時間刷新到磁盤。
InnoDB默認使用第二種,因為這種方式保證了事務(wù)的一致性。
Double Write 雙寫機制
在對緩沖池的臟頁進行刷新時,并不直接寫磁盤,而是通過memcpy函數(shù)將臟頁先復(fù)制到內(nèi)存中的double write buffer區(qū)域,之后通過double write buffer每次1MB順序地寫入共享表空間的物理磁盤上,然后馬上調(diào)用fsync函數(shù)(操作系統(tǒng)自帶),同步磁盤,避免操作系統(tǒng)緩沖寫帶來的問題。在完成double write頁的寫入后,再將double wiritebuffer中的頁寫入各個表空間文件中。如果操作系統(tǒng)在將頁寫入磁盤的過程中發(fā)生了崩潰,在恢復(fù)過程中,InnoDB存儲引擎可以從共享表空間中的double write中找到該頁的一個副本,將其復(fù)制到表空間文件中。
CheckPoint
檢查點,臟頁中的數(shù)據(jù)寫入磁盤中的時機。
1、sharp checkpoint:
完全檢查點,數(shù)據(jù)庫正常關(guān)閉時,會觸發(fā)把所有的臟頁都寫入到磁盤上
2、fuzzy checkpoint:
正常使用時 模糊檢查點,部分頁寫入磁盤。
master thread checkpoint : 以每秒或每十秒的速度從緩沖池的臟頁列表中刷新一定比例的頁回磁
盤,這個過程是異步的,
flush_lru_list checkpoint : 讀取lru (Least Recently Used) list,找到臟頁,寫入磁盤。 最近最少使用
async/sync flush checkpoint : redo log file快滿了,會批量的觸發(fā)數(shù)據(jù)頁回寫,這個事件觸發(fā)的時候
又分為異步和同步,不可被覆蓋的redolog占log file的比值:75%--->異步、90%--->同步。
dirty page too much checkpoint : 默認是臟頁占比75%的時候,就會觸發(fā)刷盤,將臟頁寫入磁盤