簡析Innodb Online DDL

源于最近有幾次對于一些大表進(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前,它是如何做DDL?主要分為兩種形式。

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_WRITEMDL_EXCLUSIVE。元數(shù)據(jù)鎖是一個跟隨事務(wù)結(jié)束而釋放的鎖,由MySQL控制,保護(hù)處于事務(wù)中的表元數(shù)據(jù)的一致性。

  • MDL_SHARED_READ,Queries時加鎖,屬于共享鎖
  • MDL_SHARED_WRITE,DML時加鎖,屬于共享鎖
  • MDL_EXCLUSIVEDDL時加鎖,屬于獨(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 DDLsql語句則采用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ū)分 INPLACECOPY,在于是否需要創(chuàng)建臨時表
  • 簡單區(qū)分 INPLACECOPYCOPY主要由Server支持,INPLACE主要由Innodb支持
  • INPLACE不是不需要額外的數(shù)據(jù)空間,取決是否為Only Modifies Metadata
  • Only Modifies Metadata不需要Rebuilds Table,不需要Rebuilds Table的不一定是Only Modifies Metadata
image.png

實(shí)現(xiàn)原理

Online DDL主要分為3個階段,PREPAREEXECUTE,COMMIT

  • PREPARE
    • 創(chuàng)建新的臨時frm文件
    • 持有MDL_EXCLUSIVE鎖,禁止讀寫
    • 根據(jù)alter類型,確定執(zhí)行方式(copy,rebuildnot-rebuild
    • 更新數(shù)據(jù)字典的內(nèi)存對象
    • 若是需要rebuild,分配row_log對象用于記錄增量
    • 若是需要rebuild,生成新的臨時ibd文件
  • 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
  • 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期間,QueriesDML操作在多數(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/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容