MySQL知識網絡
引擎
-
InnoDB
- 支持表鎖 、行鎖
- 支持事務
- *.frm 表結構文件
- *.idb 表數據和索引文件
-
MyISAM
- 支持表鎖
- *.frm 表結構文件
- *.MYD 表數據文件
- *.MYI表索引文件
-
MEMORY
內存表
-
CSV
csv形式
索引
-
BTREE
-
主鍵索引
主鍵索引就是聚簇索引
-
聚簇索引
聚簇索引就是主鍵索引
-
普通索引
數據端也存在主鍵 (回表)
-
聯合索引
最左前綴原則
-
-
HASH
- key:value
事務
事務是一組操作的集合
事務特性
-
原子性:滿足原子操作,對數據操作,要么全部成功,要么全部失敗。
-
一致性:數據開始和完成,數據都保持一致。
-
隔離性:事物之間是相互獨立的,中間狀態對外不可見。
-
持久性:數據的修改時永久的
隔離級別
1. 原因
多個事務並發執行,會出現幾個問題
-
臟讀:A事務未提交, B事務讀到A的結果(破壞了隔離性)
-
不可重復讀:A事務在本次事務中,對自己未操作的數據,進行多次讀取,出現了結果不一致,或者記錄不存在的情況。(破壞了一致性, update)
注:主要是MVCC、和鎖來解決這個問題
-
幻讀:A事務在本次事務中,對自己的數據進行多次讀取,第一次不存在, 第二次記錄出現了。(破壞了一致性, insert)
注:主要用next-key鎖來解決這個問題
2.解決辦法(制定標准)
為了權衡【隔離】和【並發】的矛盾,ISO定義了四個事務級別,每個級別的隔離程度不同,出現的情況也不同
-
Read uncommitted
存在問題:臟讀、不可重復讀、幻讀
實現原理:讀無鎖
改:啟用行級共享鎖
-
Read committed
存在問題:不可重復讀、幻讀
實現原理:讀行級共享鎖
改:行級排他鎖
-
Repeatable read
存在問題:幻讀
實現原理: 讀取共享鎖直到事務結束釋放 寫排它鎖直到事務結束釋放
-
Serializable
存在問題:不可並發
實現原理:讀取表級共享鎖直到事務結束釋放, 寫表級排它鎖直到事務結束釋放
3.實現(InnoDB)
-
鎖機制:阻止其他事物進行操作,各個隔離級別主要體現在讀取數據是加的鎖和釋放的時機。
RU: 事務讀取時, 不加鎖。
RC: 事務讀取時,加行級共享鎖(讀到才加鎖), 一旦讀完,立即釋放(並不是事務結束)。
RR: 事務讀取時,加行級共享鎖,直到事務結束才會釋放。
SE: 事務讀取時加表級共享鎖,直到事務結束時,才釋放
-
MVCC機制:生成一個數據快照,並用這個快照來提供一定級別的一致性讀取,也成為了多版本數據控制。(基於undo做的快照,將讀取數據頁變成讀快照來防止臟讀、幻讀、不可重復去的問題)
原理:
通過每行保存兩個隱藏列:trx_id(事務id)和 roll_pointer(回滾指針)兩個字段
每次操作都會生成一條undo log日志,回滾指針指向前一條數據
從最新記錄開始找:
如果當前記錄:事務id<未提交事務的最小id,則可讀(即:活躍事務最小id)
如果當前記錄: 未提交事務的最小id <事務id <= 未提交事務的最大id,則判斷是否存在未提交事務數組中,存在則不可讀(當然自己的事務也是可讀),不存在可讀
如果當前記錄:事務id>未提交事務的最大id, 則不可讀
注:可重復讀僅在事務開始創建一次快照,而讀已提交是每次執行語句時都要重新創建一次。
快照的規則:
- 事務內更新可讀到
- 版本未提交,不能讀到
- 版本已提交,但是在快照后創建的,不能讀到
- 版本已提交,且在快照創建前創建的,可以讀到
並發寫問題:
多個事務對同一條數據修改。更新前要先讀數據,這里的說的讀,是更新之前的讀叫做“當前讀”,總是當前版本的數據, 也就是多個版本中最新一次提交的那版。
-
實際就是【CAS版本控制】和【讀寫分離】思想
-
主要用於RC和RR級別
鎖
分類
MySQL鎖分為共享鎖和排它鎖, 也叫讀鎖和寫鎖
讀鎖是共享的,可以通過lock in share mode實現,這時候只能讀不能寫。
寫鎖是排他的,它會阻塞其他的寫鎖和讀鎖。從顆粒度來區分,可以分為表鎖和行鎖兩種。
表鎖會鎖定整張表並且阻塞其他用戶對該表的所有讀寫操作,比如alter修改表結構的時候會鎖表。
行鎖又可以分為樂觀鎖和悲觀鎖,悲觀鎖可以通過for update實現,樂觀鎖則通過版本號實現。
行鎖&表鎖
只有明確鎖定索引, 才會執行行鎖,否則執行表鎖
-
無鎖
# 主鍵不存在 select * from user where id=-1 for update;
-
行鎖
select * from user where id=1 for update; select * from user wehre id=1 and name='xxx' for update;
-
表鎖
# 主鍵不明確 select * from user where name='xxx' for update; select * from user where id <> 3 for update;
鎖算法(機制)
行鎖算法
Record Lock(普通行鎖)
-
鍵值存在條件范圍內
-
記錄存在
Gap Lock(間隙鎖)
- 對於鍵值不存在的條件范圍內,叫做“間隙”(GAP).引擎就會對這個“間隙”加鎖,這種機制就是Gap機制(InnoDB獨有的)
Next-Key Lock(行 & 間隙)
-
在鍵值范圍條件內,同時鍵值又不在條件范圍內 (注: Next-Key 鎖 用來解決RR中幻讀)
注: Next-Key 鎖 用來解決RR中幻讀
# id 只有1-50 select * from user id>49 for update;
表鎖算法
意向鎖(升級機制)
-
當一個事務帶着表鎖去訪問一個被加了行鎖的資源,那么, 此時, 這個行鎖就會升級成意向鎖,將表鎖住。
# 事務A select * from user where id=10 for update; # 事務B (B表鎖中的值包含 A行鎖 id=10) select * from user where name ='xxx' for update;
自增鎖
-
事務插入自增類型的列時,獲取自增鎖
如果一個事務正在往表中插入自增記錄,其他事物都必須等待
實現
共享鎖 & 排它鎖
行鎖和表鎖是粒度的概念, 共享鎖和它他鎖使他們的具體實現
共享鎖(s)
- 允許一個事務去讀一行,阻止其他事物去獲取該行的排它鎖; 都能讀,但是不能改
排他鎖(x):寫鎖
-
允許持有排它鎖的事務去讀數據,阻止其他事物去獲取該資源的共享鎖和排它鎖; 改的時候誰都不許操作
-
不能獲取任何鎖,不代表不能讀
注意
-
某個事務獲取數據的排它鎖,其他事務不能獲取該數據的任何鎖,並不代表其他事務不能無鎖讀取數據。
- 無鎖
select ... from ...
- 共享鎖
select ... lock in share mode
MySQL8.0以上,for share 代替了lock in share mode,但是任然支持lock in share mode;但是 nowait、skip locked,配合自旋鎖,可以高效的實現一個等待隊列。
- 排它鎖
update ... delete ... insert ... select ... for update
樂觀鎖&悲觀鎖
無論是什么鎖都需要加失敗重試
-
樂觀鎖
概念:總是假設最好的情況,每次拿數據的時候都認為別人不會修改,所以不會上鎖。但是在更新的時候判斷一下在此期間別人有沒有更新這個數據,一般通過版本號機制和CAS算法實現。樂觀鎖適用於寫比較少的情況下(多讀場景)
一般通過版本號進行更新, 同版本方可更新成功,不同版本則需要重試更新操作,直到成功
update user set name='xxx' where id=1 and version=1;
-
悲觀鎖
概念:總是假設最壞的情況,每次拿數據的時候都認為有人回修改,每次拿數據的時候都會上鎖,然后別人想拿數據就一直堵塞。多寫場景
排它鎖的實現
日志
InnoDB日志
redo log (重做日志)
redo log通常是物理日志,記錄的是數據頁的物理修改,而不是某一行或某幾行修改成怎樣怎樣,它用來恢復提交后的物理數據頁(恢復數據頁,且只能恢復到最后一次提交的位置)。
作用:
確保事務的持久性。防止在發生故障的時間點,當有臟頁未寫入磁盤,在重啟mysql服務的時候,根據redo log進行重做,從而達到事務的持久性這一特性。
undo log (回滾日志)
undo用來回滾行記錄到某個版本。undo log一般是邏輯日志,根據每行記錄進行記錄。
作用:
保存了事務發生之前的數據的一個版本,可以用於回滾,同時可以提供多版本並發控制下的讀(MVCC),也即非鎖定讀
MySQL server 日志
bin log(歸檔日志)
作用:
用於復制,在主從復制中,從庫利用主庫上的binlog進行重播,實現主從同步。
用於數據庫的基於時間點的還原。
redo log 和 binlog 區別
- redo log是屬於innoDB層面,binlog屬於MySQL Server層面的,這樣在數據庫用別的存儲引擎時可以達到一致性的要求。
- redo log是物理日志,記錄該數據頁更新的內容;binlog是邏輯日志,記錄的是這個更新語句的原始邏輯
- redo log是循環寫,日志空間大小固定;binlog是追加寫,是指一份寫到一定大小的時候會更換下一個文件,不會覆蓋。
- binlog可以作為恢復數據使用,主從復制搭建,redo log作為異常宕機或者介質故障后的數據恢復使用。
一條更新語句執行的順序
update T set c=c+1 where ID=2;
- 執行器先找引擎取 ID=2 這一行。ID 是主鍵,引擎直接用樹搜索找到這一行。如果 ID=2 這一行所在的數據頁本來就在內存中,就直接返回給執行器;否則,需要先從磁盤讀入內存,然后再返回。
- 執行器拿到引擎給的行數據,把這個值加上 1,比如原來是 N,現在就是 N+1,得到新的一行數據,再調用引擎接口寫入這行新數據。
- 引擎將這行新數據更新到內存中,同時將這個更新操作記錄到 redo log 里面,此時 redo log 處於 prepare 狀態。然后告知執行器執行完成了,隨時可以提交事務。
- 執行器生成這個操作的 binlog,並把 binlog 寫入磁盤。
- 執行器調用引擎的提交事務接口,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成。
SQL優化
常見面試題
- mysql主從同步怎么搞的?分哪幾個過程?如果有一台新機器要加到從機里,怎么個過程。
- 主mysql將日志寫入bin log
- 從mysql連上主mysql,獲取bin log
- 主mysql dump線程將bin log日志推送給從mysql
- 從mysql將得到的日志 寫入relay log
- 從mysql開一個sql線程 讀取relay log 並執行sql語句,完成同步
- 從mysql記錄自己的bin log
- binlog 日志是 master 推的還是 salve 來拉的?
剛剛連接的時候是 slave來拉 后面是master主動推過來
binlog 有哪幾種模式
row 記錄數據值,同時也會記錄關聯表的信息情況,優點不沖突 缺點日志大
statement 記錄sql邏輯, 優點數據小, 缺點使用function 可能導致主從數據不一致
mixed row和statement的結合
mysql有哪些日志
redo log、bin log、undo log、relay log、slow query log、error log、general log
mysql加鎖規則
加鎖規則 next-key lock
- 唯一索引等值查詢
- 記錄存在時,
next-key lock
退化成行鎖
- 記錄不存在時,
next-key lock
退化成間隙鎖
- 非唯一索引等值查詢
- 記錄存在時,加
next-key lock
外, 還額外加一個間隙所
, 也就是兩把鎖 - 當記錄不存在時,加
next-key lock
, next-key lock退化成間隙鎖