Innodb早期支持經過copy table跟inplace的方式來執行DDL語句,其原理以下:mysql
copy table方式sql
新建跟原表格一致的臨時表,並在該臨時表上執行DDL語句數據庫
鎖原表,不容許DML,容許查詢服務器
逐行數據從原表拷貝到臨時表中(這個過程是沒有排序的)多線程
拷貝結束後,原表禁止讀操做,也就是原表此時不提供讀寫服務併發
進行rename操做,完成DDL過程ide
inplace方式(fast index creation,僅針對索引的建立跟刪除)性能
新建frm臨時文件測試
鎖原表,不容許DML,容許查詢優化
按照彙集索引的順序,查詢數據,找到須要的索引列數據,排序後插入到新的索引頁中
原表禁止讀操做,也就是原表此時不提供讀寫服務
進行rename操做,替換frm文件,完成DDL過程
inplace在copy table的基礎上作了一個較大的改進,則是不須要copy整個表格,只須要在原來的ibd文件上,新建所須要的索引頁,這個過程比copy table節約極大的IO資源佔用 且 DDL SQL執行速度大大提升,減小了該表格不提供寫服務的時長。可是inplace僅支持索引的建立於刪除,不支持其餘的DDL操做,其餘的DDL操做,仍然是copy table方式執行。
對於一個線上業務數據庫,不管是copy table方式仍是inplace方式,這裏仍然有一個明顯的弊端:操做期間涉及表格不提供寫服務!沒法對涉及到表格至下INSERT,UPDATE,DELETE操做,僅支持SELECT。
當表格發生DDL操做,可能會出現該表格數分鐘甚至數小時不可訪問,性能及響應異常,爲了有效改善這個狀況,MySQL 在5.6.7版本推出了Online DDL。(本文參考官網5.7版本的文檔整理及測試)。
在online DDL中,也包含了copy跟inplace兩種方式,對於不支持Online DDL的DDL SQL,則採用COPY方式;對於支持Online DDL的DDL SQL,則採用Inplace方式,這裏的Inplace又區分爲2類:是否須要rebuild表格,判斷標準爲:是否修改行記錄格式。若是修改了行記錄格式,則須要rebuild表格,好比修改列類型、增減列等;若是沒有修改行記錄格式,僅修改表的元數據,則不須要rebuild表格,僅修改元數據 metadata,好比刪除索引、設置默認值及重命名列名等。詳細可見下圖,具體語法狀況見`第4部分`。
那麼,新增的Online DDL內部是怎樣一個實現原理呢?(此處參考:http://blog.itpub.net/22664653/viewspace-2056953/)
有3個階段:prepare、execute、commit。
PREPARE
建立新的臨時frm文件
持有EXCLUSIVE_MDL鎖,禁止讀寫
根據alter類型,肯定執行方式(copy,rebuild,no-rebuild)
更新數據字典的內存對象
如果須要rebuild,分配row_log對象記錄的增量
如果須要rebuild, 生成新的臨時ibd文件
EXECUTE
下降EXCLUSIVE-MDL鎖,容許讀寫(copy 不容許寫)
記錄ddl執行過程當中產生的增量row-log(僅rebuild類型須要)
掃描old_table的彙集索引每一條記錄record
遍歷新表的彙集索引和二級索引,逐一處理
根據record構造對應的索引項
將構造索引項插入sort_buffer塊
將sort_buffer塊插入新的索引
把row-log中的操做應用到新臨時表中,應用到最後一個Block
這部分無操做
若是是僅修改元數據:
其餘,則是:
COMMIT
升級到EXECLUSIVE-MDL鎖,禁止讀寫
重作最後一部分的row_log增量
更新innodb的數據字典表
提交事務,寫redo日誌
修改統計信息
rename 臨時的ibd文件、frm文件
DDL完成
這裏注意下row-log,它是記錄 DDL在執行過程當中表格發生數據變動的操做,這樣就能夠保證執行DDL表格的併發性,在EXCUTE階段能夠正常提供寫服務,不發生堵塞,最後把row-log應用到新的表格上便可。
在5.7.17版本上測試的時候,發現,支持inplace且須要rebuild的DDL,在DDL期間,若是數據發生修改,都是直接刷新到原來的ibd文件上,在測試環境中,給大表testddl刪除一個字段,這個過程當中INSERT 100w行記錄,能夠看到原有ibd文件增加了1G左右。
這裏有個疑問,未肯定:row-log應該不是記錄行記錄的修改格式,由於這樣效率過慢,初步推測應該是僅記錄主鍵,而後根據主鍵查找應用到新表上。
Online DDL能夠有效改善DDL期間對數據庫的影響:
Online DDL期間,查詢和DML操做在多數狀況下能夠正常執行,對錶格的鎖時間也會大大減小,儘量的保證數據庫的可擴展性;
容許 in-place 操做的 DDL,避免重建表格佔用過多磁盤IO及CPU資源,減小對數據庫的總體負荷,使得在DDL期間,可以維持數據庫的高性能及高吞吐量;
容許 in-place 操做的 DDL,比須要COPY到臨時文件的操做要更少佔用buffer pool,避免以往DDL過程當中性能的臨時降低,由於之前須要拷貝數據到臨時表,這個過程會佔用到buffer pool ,致使內存中的部分頻繁訪問的數據會被清理出去。
online ddl過程當中發生DML時,會把數據修改狀況記錄到row-log中,而row-log的大小,則由 innodb_online_alter_log_max_size設定,默認爲128M,當表格較大且操做頻繁時,作DDL過程,可調大該參數,避免出現1799錯誤:
Alter table …. , ALGORITHM [=] {DEFAULT|INPLACE|COPY}, LOCK [=] { DEFAULT| NONE| SHARED| EXCLUSIVE }
該選項用於調整DDL加鎖的方式,一共有4個選項。
LOCK=EXCLUSIVE
對整個表格添加獨佔鎖(x鎖),不容許查詢跟修改操做
LOCK=SHARED
對整個表格添加(s鎖),容許查詢操做,可是不支持數據變動操做
LOCK=NONE
不添加鎖,既容許查詢操做,也支持數據庫變動操做,該模式下併發最好
LOCK=DEFAULT
沒有指定LOCK的時候,則是默認爲這個選項
根據DDL的操做類型,最小程度的加鎖,儘量支持查詢及0DML操做
首先判斷當前操做是否可使用NONE模式,若是不能,判斷是否可使用SHARED模式,若是不能,判斷是否可使用EXCLUSIVE模式
DDL對數據庫性能的影響,很大程度受操做方式影響,好比是不是容許in-place,是否請求COPY操做,是否重建整個表格。好比某個表格,修改或者添加默認值,並不會影響到表格內部的數據,因此1s內就能夠完成;添加1個索引,須要幾十秒,應爲須要新增索引數據頁跟修改frm文件,可是不用rebuild表格數據;而修改列的數據類型是,可能須要幾分鐘甚至更多時間,由於其須要從新Rebuild整個表格,執行期間對CPU,IO及buffer pool大量申請資源。
由DDL引發的INPLACE,COPY,REBUILD,能夠經過指定ALGORITHM來選擇(注意並不是全部DDL都支持in-place,詳見第4部分)
ALGORITHM=INPLACE
ALGORITHM=COPY
這兩個選項中,INPLACE要比COPY性能好,由於INPLACE既不會記錄UNDO LOG,也不寫REDO LOG,同時執行期間提供DML操做。
Online DDL對不一樣的DDL語句具備不一樣的執行規則,下面的表格將詳細描述各個語法對Online DDL的支持狀況。
列說明:
In-Place?
說明: 是否支持 ALGORITHM=INPLACE
Rebuilds Table?
說明:是否會重建表格
重建表格分爲兩種方式:INPLACE跟COPY (原地修改或者複製到臨時文件修改)
若是支持 ALGORITHM=INPLACE,那麼則是原地修改 INPLACE(淡×××標記)
若是不支持 ALGORITHM=INPLACE,那麼則是COPY,拷貝到臨時文件修改,而且不支持UPDATE DELETE INSERT操做(深褐色標記)
Permits Concurrent DM
說明: 是否支持在DDL期間併發對該表格操做DML SQL
新增空間索引及全文索引時,不支持DML操做
當容許時,能夠經過LOCK選項來控制是否要提供查詢或者修改操做
LOCK=NONE,支持查詢跟UPDATE INSERT DELETE操做
LOCK=SHARED,僅支持查詢
Only Modifies Metadata?
是否只修改元數據
針對是否支持INPLACE、是否須要REBUILD及是否僅修改metadata來分類,選取每類一個DDL SQL來測試,見下圖:
考慮到varchar變化長度的問題,這裏加測多這一項。
測試DB環境:表格名 tbddl,表格大小:1G ,500W行記錄
測試流程:開啓事務查詢,不提交 => 執行DDL => 提交查詢事務 => 執行DML =>開啓事務,執行DML不提交 =>提交DML
測試DDL SQL
ALTER TABLE tbddl MODIFY COLUMN ItemId VARCHAR(20);
ALTER TABLE tbddl ADD xinysu int;
CREATE INDEX IX_PROID ON tbddl (providerid);
ALTER TABLE tbddl ALTER COLUMN xinysu SET DEFAULT 123456;
ALTER TABLE tbddl ALTER COLUMN ItemId VARCHAR(50); #UTF8字符集,3個字節一個字符,50個字符則是150個字節,小於256bytes
ALTER TABLE tbddl ALTER COLUMN ItemId VARCHAR(100); #UTF8字符集,3個字節一個字符,100個字符則是300個字節,大於256bytes
測試關注點
啓動與關閉 old_alter_table
prepare,commit階段的鎖是怎麼樣的
excute階段的鎖是怎麼樣的
執行期間服務器的性能狀況(zabbix監控)
執行期間數據庫的併發狀況(sysbench壓測)
測試過程當中的截圖,不在此描述,直接粘貼測試結果,感興趣的筒子們,能夠自行測試。
VARCHAR按字符存儲,每一個字符按照字符集來計算字節,UTF8是3個字節一個字符,當VARCHAR的字節數<256byte時,只須要1個byte來存儲實際長度,當VARCHAR字節數>=256時,則須要2個byte來存儲實際長度。舉例,UTF8字符集下的VARCHAR(10),假設存儲 N (0<=N<=10),則其佔用的字節數爲:N*3+1;UTF8字符集下的VARCHAR(100),假設存儲 N (0<=N<=100),則其佔用的字節數爲:N*3+2。
理解了這一點後,就能夠理解 增加或縮短列的長度這列DDL的處理方式,假設列 VARCHAR(M)須要增大或縮小到VARCHAR(N),字符集是UTF8:
當 3M<256,3N<256,存儲長度的字節不須要變化,都爲1,則不須要變更行記錄,僅須要修改元數據;
當 3M>256,3N>256,存儲長度的字節不須要變化,都爲2, 則不須要變更行記錄,僅須要修改元數據;
當 3M<256,3N>256,存儲長度的字節須要變化,由1變2, 則須要變更行記錄,Online DDL使用COPY TABLE方式;
當 3M>256,3N>256,存儲長度的字節須要變化,由2變1,則須要變更行記錄,Online DDL使用COPY TABLE方式
在Online DDL以前,都會習慣性的把同個表格的全部DDL語句合併爲一個SQL語句,避免重複Rebuild、屢次加鎖致使不提供DML時長增長等弊端。
可是,引入Online DDL後,須要有2點改觀:
除了個別不支持inplace的DDL語句,其餘DDL語句在執行期間是不會加X鎖的,也就是表格仍然提供DML操做
鎖的粒度,同個DDL語句中,按照最高級別的鎖處理
維護的方便性
這裏建議按照3類來處理(測試後的我的建議,僅供參考),見下圖。
爲啥copy table單獨出來呢?
由於這一類操做過程當中是不容許DML操做的,建議把這一類的合成單獨一條DDL SQL執行,不與IPLACE的DDL SQL合併;
爲啥iplace的要分爲2類呢?
方便維護
僅元數據修改的DDL較快執行結束,爲了方便管理維護,不至於全部SQL貼一堆,僅元數據修改的DDL語句歸一類
須要REBUILD的歸一類,避免重複rebuild,浪費磁盤IO跟CPU資源。
舉個例子,如今上線項目,須要對錶格tbddl,1個字段由INT修改成VARCHAR,新增3個字段,2個索引,2個默認值,2個列增加長度,單獨的SQL 爲:
alter table tbddl alter column ItemId varchar(20);
ALTER TABLE tbddl ADD su int;
ALTER TABLE tbddl ADD xin varchar(40);
ALTER TABLE tbddl ADD yu int;
CREATE INDEX IX_SU ON tbddl(SU);
CREATE INDEX IX_yu ON tbddl(yu);
ALTER TABLE tbddl ALTER COLUMN CreatedById SET DEFAULT 123456;
ALTER TABLE tbddl ALTER COLUMN ItemID SET DEFAULT 654321;
ALTER TABLE tbddl ALTER COLUMN CreatedByName VARCHAR(70);
ALTER TABLE tbddl ALTER COLUMN ModifiedByName VARCHAR(100);
測試建議如下執行方式:
alter table tbddl alter column ItemId varchar(20);
ALTER TABLE tbddl ADD su int ,ADD xin varchar(40) ,ADD yu int,ALTER COLUMN ModifiedByName VARCHAR(100),add index ix_su(SU), add index ix_yu(yu);
ALTER TABLE tbddl ALTER COLUMN CreatedById SET DEFAULT 123456,ALTER COLUMN ItemID SET DEFAULT 654321,ALTER COLUMN CreatedByName VARCHAR(70);
執行DDL期間,須要密切留意數據庫服務器的CPU及IO狀況,查看數據庫的鏈接池、慢查詢狀況,若是期間發生了異常,應該如何處理呢?
假設如今給大表tbddl新增一列,新增的過程當中,發現影響到線上業務,須要緊急中止,能夠經過如下步驟操做:
show processlist;
kill 進程id;
具體見截圖。
DDL期間,若是發生宕機狀況,會對數據庫的恢復啓動形成什麼影響呢?臨時文件還存在嗎? 恢復過程當中會自動執行未完成的DDL操做嗎?若是會,是怎麼處理?若是不會,再次手動建立會有影響嗎?
在5.7.17版本上,測試了4類DDL SQL,當DDL執行過程當中,數據庫發生宕機,該DDL不會影響到數據庫的恢復啓動,同時,這個未完成的DDL語句不回自動執行,因爲宕機過程當中來不及清理臨時文件,因此數據庫恢復後,臨時文件依舊存在。DDL沒有commit,也就覺得這數據庫的數據字典和表格的元數據沒有發生修改,再次手動執行DDL語句,並不會報衝突。(這點跟部分博文的分析有些出入,本次測試版本是5.7.17版本)
測試過程,這裏不作過多描述,直接貼上結論,感興趣的筒子們能夠自行測試,歡迎討論。
DDL期間,假設該SQL執行的時間須要10h,除去waitting metadata lock的時間,rebuild或者inplace的時間須要5小時,那麼在從庫是單線程SQL THREAD應用relay log的狀況,須要考慮從庫滯後的影響。
DDL在主庫執行狀況,因爲DDL語句沒有提交,因此不會同步到從庫上,從庫能夠正常同步其餘數據修改操做,這個環節沒有問題,可是當DDL在主庫提交後,該binlog日誌經過IO_THREAD傳送到從庫的RELAY LOG上,從庫的SQL_Thread是單線程工做,應用RELAY log的時候,至少須要5個小時,也就是這5個小時都用來執行RELAY LOG,沒法同步主庫幾個小時內產生的BIN LOG,那麼,從庫就會發生嚴重的滯後狀況,這個問題是否在可接受範圍內,須要歸入到DDL執行形成的影響範圍內。
若是不能接受從庫這麼大的滯後,有什麼法子能夠處理呢?
能夠經過這個思路來,從庫啓動並行複製。啓動並行複製,須要注意這幾個問題:
使用注意
在從庫嚴重落後主庫的狀況下,能夠開啓該參數實現多線程並行執行
在業務量低的數據庫,不建議開啓,從庫同步性能反而會比拖累
配置注意
database,不一樣庫的事務,觸發從庫並行回放
logical_clock,組提交事務,按照組提交設置,從庫並行回放,若是是爲了改善DDL的滯後狀況,應使用這個配置。
注意 master_info_repository relay_log_info_repository 設置爲 table,默認是寫入mater_info.log 及 relay_info.log ,刷下這兩個文件的頻率帶來的性能影響比較大,據 姜承驍姜老師 壓測,性能相差 20-50%間
slave_parallel_workers 建議設置爲從庫 核心 數
slave_parallel_type
磁盤空間
rebuild過程當中,產生的DML涉及到行記錄變動日誌,是否足夠存儲
由於會拷貝ibd文件,因此要確保空間足夠
rebuild 的時候,datadir空間是否足夠
rebuild 的時候,innodb_online_alter_log_max_size是否足夠
inplace的時候,考慮tmpdir空間是否足夠
ddl對從庫延遲的影響是否能夠接受
主庫online DDL的過程當中,因爲沒有commit,因此其餘併發操做能夠正常同步到從庫
主庫commit後,DDL同步到從庫
因爲從庫是單線程執行SQL_THREAD,假設DDL執行過程須要1個小時,那麼從庫將會滯後1小時+
是否容許從庫的滯後,若是不容許,能夠經過並行複製來優化處理
row-log會檢查重複值或者修改衝突嗎?
會根據主鍵及惟一約束來檢查
copy table ,inplace下如何暫停DDL操做
show full processlist;
kill id; #( DDL SQL的id號)
這裏kill完後,仍然能夠再次正常執行DDL,不會存在衝突,其建立的臨時ibd及frm文件會自動刪除
copy table ,inplace下宕機
這兩種狀況下宕機後,沒有完成的DDL語句不會繼續執行
可是,其生成的frm跟 ibd 臨時文件不會被刪除,能夠手動刪除,也能夠不手動刪除,即便不刪除,也不會影響再次執行DDL
但建議mysql服務後,刪除無用的臨時文件
同個表格多個DDL語句,不要一個個執行
請按照是否支持inplace及是否須要rebuild分類合併執行
如何查看ddl進度(未解決)
若是有rebuild,則是經過ibd文件的增加來評估;可是若是是inplace,如何查看呢?有沒有什麼比較好的方式查看?performance_schema是否有提供相應的查詢方式?