源于最近有幾次對于一些大表進(jìn)行DDL
操作,但可能因?yàn)閷?code>InnoDB引擎的Online DDL
的原理所知甚少,以至于有一些同學(xué)一開口就是,"執(zhí)行DDL
時會鎖表,如果數(shù)據(jù)量太大鎖表時間太長,會阻塞到線上業(yè)務(wù)",為確保以后進(jìn)行DDL
操作時能夠做到心中有數(shù),對Online DDL
做一些總結(jié)。
前身
MySQL
于5.6版本推出Online DDL
,那么在沒有Online DDL
前,它是如何做DD
L?主要分為兩種形式。
copy table
-
create temp table
,創(chuàng)建臨時表結(jié)構(gòu),保持與原表結(jié)構(gòu)一致 -
lock original table
,不允許對原表進(jìn)行DML
操作,僅允許Queries
-
ddl on temp table
,在臨時表上執(zhí)行DDL (lock) -
copy original table data into temp table
,將原表數(shù)據(jù)復(fù)制到臨時表 (lock) -
rename temp tablename to oringal tablename
,對原表加鎖并進(jìn)行rename
操作,不允許DML & Queries
,直至整個過程完成。
從整個執(zhí)行過程來分析,為了保證執(zhí)行期間表數(shù)據(jù)的一致性,必須通過加鎖來阻止DML
操作,只允許Queries
。當(dāng)表數(shù)據(jù)量過大時,在復(fù)制階段所需要的時間過長導(dǎo)致整個語句在執(zhí)行期間加鎖阻塞其他事務(wù)的DML
語句,故而導(dǎo)致應(yīng)用連接數(shù)撐爆,大量事務(wù)超時等問題。
inplace
inplace
又稱為fast index creation
,僅支持索引的創(chuàng)建。
-
create frm
, 創(chuàng)建數(shù)據(jù)字典 -
lock original table
,不允許對原表進(jìn)行DML
操作,僅允許Queries
-
copy table
,按照聚簇索引順序讀取數(shù)據(jù),構(gòu)造新的索引項(xiàng),順序插入新的索引頁 -
lock original table
,鎖表,不允許DML & Queries
-
rename
,替換frm
文件,完成DDL
過程
inplace
方式比起需要copy table
自然是更優(yōu)的,因?yàn)橹恍枰獜木鄞厮饕x取新索引的列項(xiàng)出來構(gòu)造新索引頁,其他索引不受影響。但是,無論是copy table
還是 inplace
在執(zhí)行操作的過程中都需要長時間鎖表,阻塞DML
語句,毫無疑問對于服務(wù)運(yùn)行而言簡直是毀滅式的行為,因此online ddl
迫在眉睫。
在討論online ddl
前,有必要先了解幾個點(diǎn),其中包括 Innodb
索引組織形式,LOCK CAUSE
,METADATA LOCK
。
索引組織形式
關(guān)于索引的官方文檔
開發(fā)同學(xué)在構(gòu)建數(shù)據(jù)庫表時,都會根據(jù)業(yè)務(wù)特點(diǎn)選擇合適的索引,因?yàn)樗饕哪康氖羌铀偎阉?。從官網(wǎng)文檔中可以看到Innodb存儲引擎將索引分為Clustered Index
以及Secondary Index
,稱為聚集索引與二級索引(也稱聚簇索引,非聚簇索引)。
二者如何區(qū)分?Innodb只會有一個聚簇索引,聚簇索引的葉子節(jié)點(diǎn)上存儲的是Row Data
,一般情況下是表定義中的PRIMARY KEY
,如果沒有則選擇表定義中第一個非空UNIQUE KEY
,如果表定義中即沒有PK,也沒有UK,則會通過Innodb隱藏的一個ROW ID
作為索引。而處理聚簇索引之外的其他索引,皆稱為二級索引。二級索引的葉子節(jié)點(diǎn)上存儲的是聚簇索引的值。
正因?yàn)檫@個特征,很多企業(yè)定義的SQL規(guī)范中都會有類似:
- PK盡量選擇長整型,且趨勢遞增,推薦使用
AUTO INCREMENT
- PK不適合較長的字符串
- ...
LOCK CAUSE
關(guān)于LOCK CAUSE
的官方文檔
默認(rèn)情況下,MySQL
會選擇盡可能的輕量的鎖來完成DDL
操作,在某些特定情況下可以更加嚴(yán)格
的鎖來完成操作。比如前文提到,DDL
過程中完成不允許DML,僅允許Queries
,也可以不允許DML & Queries
等。
-
LOCK=NONE
Permits concurrent queries and DML. -
LOCK=SHARED
Permits concurrent queries but blocks DML. -
LOCK=DEFAULT
Permits as much concurrency as possible (concurrent queries, DML, or both). Omitting the LOCK clause is the same as specifying LOCK=DEFAULT. -
LOCK=EXCLUSIVE
Blocks concurrent queries and DML
METADATA LOCK
關(guān)于METADATA LOCK
的官方文檔
關(guān)于METADATA LOCK
的補(bǔ)充文檔
元數(shù)據(jù)鎖,是MySQL
的表鎖之一,屬于隱式鎖(另一種是顯式鎖,通過lock table ... with read/write
指定)。在補(bǔ)充文檔中可以了解到元數(shù)據(jù)鎖的由來,也對其進(jìn)行了解釋。補(bǔ)充文檔中提及元數(shù)據(jù)鎖有11種類型,常用的有MDL_SHARED_READ
,MDL_SHARED_WRITE
及MDL_EXCLUSIVE
。元數(shù)據(jù)鎖是一個跟隨事務(wù)結(jié)束而釋放的鎖,由MySQL
控制,保護(hù)處于事務(wù)中的表元數(shù)據(jù)的一致性。
-
MDL_SHARED_READ
,Queries
時加鎖,屬于共享鎖 -
MDL_SHARED_WRITE
,DML
時加鎖,屬于共享鎖 -
MDL_EXCLUSIVE
,DDL
時加鎖,屬于獨(dú)占鎖
Online DDL
簡述
MySQL
在5.6.7版本推了Online DDL
能力,主要是在基于原有的fast index creation
上增強(qiáng)實(shí)現(xiàn)。其次,假定前提,衡量DDL
是否足夠Online
在于是否長時間允許DML
。
目前Online DDL
支持兩種形式
COPY
INPLACE
MySQL
如何選擇該兩種形式呢?
- 支持指定,即在執(zhí)行語句上指定
ALGORITHM=INPLACE / COPY
。 - 默認(rèn)對于不支持
Online DDL
的sql
語句則采用COPY
,相反則采用INPLACE
。
INPLACE
會根據(jù)是否涉及到修改行記錄格式分為三種情形
-
Rebuilds Table
,修改了行記錄格式,比如修改列類類型、增減列等 -
Not Rebuilds Table & Not Only Modifies Metadata
,不需要重建表但是也不僅僅只是修改元數(shù)據(jù),比如增加索引。 -
Only Modifies Metadata
,僅修改元數(shù)據(jù),比如刪除索引、設(shè)置列默認(rèn)值,重命名列名等
Online DDL
支持選項(xiàng)
ALGORITHM={COPY|INPLACE}
-
LOCK={NONE|SHARED|DEFAULT|EXCLUSIVE}
,參照前文LOCK CAUSE
可以從官方文檔查看Online DDL
支持情況。
有幾個點(diǎn)需要特意說明
-
INPLACE
并不表示絕對支持并行DML
,但是COPY
絕對不支持并行DML
。 - 簡單區(qū)分
INPLACE
與COPY
,在于是否需要創(chuàng)建臨時表 - 簡單區(qū)分
INPLACE
與COPY
,COPY
主要由Server
支持,INPLACE
主要由Innodb
支持 -
INPLACE
不是不需要額外的數(shù)據(jù)空間,取決是否為Only Modifies Metadata
-
Only Modifies Metadata
不需要Rebuilds Table
,不需要Rebuilds Table
的不一定是Only Modifies Metadata
實(shí)現(xiàn)原理
Online DDL
主要分為3個階段,PREPARE
,EXECUTE
,COMMIT
- PREPARE
- 創(chuàng)建新的臨時
frm
文件 - 持有
MDL_EXCLUSIVE
鎖,禁止讀寫 - 根據(jù)
alter
類型,確定執(zhí)行方式(copy
,rebuild
,not-rebuild
) - 更新數(shù)據(jù)字典的內(nèi)存對象
- 若是需要
rebuild
,分配row_log
對象用于記錄增量 - 若是需要
rebuild
,生成新的臨時ibd
文件
- 創(chuàng)建新的臨時
- EXECUTE
- 如果是僅修改元數(shù)據(jù):
- 這部分無操作
- 其他,則是:
- 降低
MDL_EXCLUSIVE
鎖,允許DML & Queries(copy 不允許寫) - 記錄
DDL
執(zhí)行過程中產(chǎn)生的增量row-log
(非only modify metadata類型需要) - 掃描
old_table
的聚集索引每一條記錄record
- 遍歷新表的聚集索引和二級索引,逐一處理
- 根據(jù)
record
構(gòu)造對應(yīng)的索引項(xiàng) - 將構(gòu)造索引項(xiàng)插入
sort_buffer
塊 - 將
sort_buffer
塊插入新的索引 - 把
row_log
中的操作應(yīng)用到新臨時表中,應(yīng)用到最后一個Block
- 降低
- 如果是僅修改元數(shù)據(jù):
- COMMIT
- 升級到
MDL_EXECLUSIVE
鎖,禁止讀寫 - 重做最后一部分的
row_log
增量 - 更新
innodb
的數(shù)據(jù)字典表 - 提交事務(wù),寫
redo
日志 - 修改統(tǒng)計信息
-
rename
臨時的ibd
文件、frm
文件 -
DDL
完成
- 升級到
在整個
Online DDL
的過程中,并非是完全的Permits Concurrent DML
,但是由于整個過程中持有MDL_EXCLUSIVE
鎖的時間較短,所以近似的認(rèn)為整個過程是Permits Concurrent DML
Online DDL帶來的優(yōu)勢
-
Online DDL
期間,Queries
和DML
操作在多數(shù)情況下可以正常執(zhí)行,鎖表時間大大減少。 - 允許
INPLACE
操作的DDL
,避免COPY
方式的磁盤IO及CPU資源,減少對數(shù)據(jù)庫的整體負(fù)荷,使得在DDL
期間,能夠維持?jǐn)?shù)據(jù)庫的高性能及高吞吐量; - 允許
INPLACE
操作的DDL
,比需要COPY
到臨時表的操作要更少占用buffer pool
,避免以往DDL
過程中性能的臨時下降,因?yàn)樾枰截悢?shù)據(jù)到臨時表,這個過程會占用到buffer pool
,導(dǎo)致內(nèi)存中的部分頻繁訪問的數(shù)據(jù)會被清理出去。
資料
https://www.cnblogs.com/cchust/p/4639397.html
https://www.cnblogs.com/xinysu/p/6732646.html
https://www.cnblogs.com/dbabd/p/10381942.html
http://mysql.taobao.org/monthly/2021/03/06/