正文html
MySQL Online DDL這個新特性是在MySQL5.6.7開始支持的,更早期版本的MySQL進行DDL對於DBA來講是很是痛苦的。如今主流版本都集中在5.6與5.7,爲了更好的理解Online DDL的工做原理與機制,本文就對Online DDL的實現方式進行總結。mysql
本文使用的MySQL版本爲官方社區版 5.7.24
。sql
(root@localhost) [test] > select version(); +------------+ | version() | +------------+ | 5.7.24-log | +------------+ 1 row in set (0.00 sec)
主要說明
Online DDL這個新特性解決了早期版本MySQL進行DDL操做同時帶來鎖表的問題,在DDL執行的過程中依然能夠保證讀寫狀態,不影響數據庫對外提供服務,大大提升了數據庫和表維護的效率。數據庫
- 早期實現方式(MySQL5.6.7以前版本)
早期版本MySQL執行DDL語句時主要經過如下方式進行:服務器
COPY方式:
這是InnoDB最先期支持的方式,主要實現步驟:併發
- 建立與原表結構定義一致的臨時表;
- 對原表加鎖,不容許執行DML,但容許查詢;
- 在臨時表上執行DDL語句;
- 逐行拷貝原表數據到臨時表;
- 原表與臨時表進行RENAME操做,此時會升級原表上的鎖,不容許讀寫,直至完成DDL操做;
INPLACE方式:
INPLACE方式也稱爲InnoDB fast index creation,是MySQL5.5及以後版本爲了提升建立二級索引效率的方式,因此INPLACE方式僅限於二級索引的建立跟刪除,關於fast index creation能夠參考官方文檔:InnoDB fast index creation,主要實現步驟:性能
- 建立臨時的frm文件;
- 對原表加鎖,不容許執行DML,但容許查詢;
- 根據彙集索引的順序,構造新的索引項,按照順序插入新索引頁;
- 升級原表上的鎖,不容許讀寫操做;
- 進行RENAME操做,替換原表的frm文件,完成DDL操做。
相對於COPY方式,INPLACE方式在原表上進行,不會生成臨時表,也不會拷貝原表數據,減小了不少系統I/O資源佔用,但仍是沒法進行DML操做,也只適用於索引的建立與刪除,並不適用於其餘類型的DDL語句。學習
- 當前實現方式(MySQL5.6.7及以後版本)
在MySQL5.6.7及以後版本中推出了新的特性:優化
Online DDL方式:
Online DDL特性是基於MySQL5.5的InnoDB fast index creation上改進加強的。Online DDL一樣包含兩種方式:ui
- COPY方式;
- INPLACE方式。
其中,某些DDL語句不支持Online DDL的就採用COPY方式,支持Online DDL的則採用INPLACE方式,由於Online DDL是對早期INPLACE方式的增長,因此INPLACE方式根據是否涉及到記錄格式的修改又分爲以下兩種情形:
- Rebuilds Table;
- No-Rebuilds Table。
Rebuilds Table操做是由於DDL有涉及到行記錄格格式的修改,如字段的增、刪、類型修改等;
No-Rebuilds Table則不涉及行記錄格式的修改,如索引刪除、字段名修改等。
Online DDL選項
ALGORITHM={COPY|INPLACE}
指定DDL執行時對錶的操做方式。首選是INPLACE,但並不是全部的語句都支持INPLACE,須要根據DDL語句類型決定。- LOCK={NONE|SHARED|DEFAULT|EXCLUSIVE}
指定DDL執行時對錶鎖定方式。默認狀況下MySQL在表執行DDL時會盡可能使用最少的鎖定,LOCK選項能夠爲DDL語句指定執行更爲嚴格的鎖定方式,一旦指定的鎖級別低於DDL語句執行所需的鎖級別,則DDL語句會執行失敗。- NONE:容許併發查詢和DML操做;
- SHARED:容許併發查詢,但不容許DML操做;
- DEFAULT:容許儘量多的併發查詢或DML操做(或二者都容許),沒指定LOCK選項默認就爲DEFAULT;
- EXCLUSIVE:不容許併發查詢和DML操做。
Online DDL類型
根據官方文檔Online DDL Operations的描述,結合常見的表DDL執行語句,MySQL5.7支持的Online DDL操做類型主要有如下種類:
- 索引操做
- 字段操做
- 組合字段操做
- 主鍵操做
- 外鍵操做
- 表操做
- 表分區操做
- 表空間操做
索引操做
索引操做類型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容許併發DML操做(Permits Concurrent DML) | 僅修改元數據(Only Modifies Metadata) |
---|---|---|---|---|
建立添加二級索引 | YES | NO | YES | NO |
重命名索引 | YES | NO | YES | YES |
刪除索引 | YES | NO | YES | YES |
建立全文索引 | YES | NO | NO | NO |
建立空間索引 | YES | NO | NO | NO |
修改索引類別 | YES | NO | YES | YES |
由以上表格能夠看出涉及索引的DDL操做均可以使用INPLACE方式來完成,除了建立全文索引與空間索引以外都容許DML操做,並不會鎖表。
-- 建立添加二級索引 create index index_name on table_name (column[,column]..); 或 alter table table_name add index index_name (column[,column]..); -- 刪除索引 drop index index_name on table_name; 或 alter table table_name drop index index_name; -- 重命名索引 alter table table_name rename index old_index_name to new_index_name, algorithm=inplace, lock=none; -- 建立全文索引 create fulltext index index_name on table_name(column[,column]..); -- 建立空間索引 create table geom (g geometry not null); alter table geom add spatial index(g), algorithm=inplace, lock=none; -- 修改索引類型 alter table table_name drop index index_name, add index index_name(column[,column]..) using btree, algorithm=inplace;
字段操做
字段操做類型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容許併發DML操做(Permits Concurrent DML) | 僅修改元數據(Only Modifies Metadata) |
---|---|---|---|---|
添加字段 | YES | YES | YES | NO |
刪除字段 | YES | YES | YES | NO |
重命名字段 | YES | NO | YES | YES |
重排序字段 | YES | YES | YES | NO |
字段指定默認值 | YES | NO | YES | YES |
字段修改類型 | NO | YES | NO | NO |
擴展VARCHAR字段大小 | YES | NO | YES | YES |
刪除字段默認值 | YES | NO | YES | YES |
修改自增值 | YES | NO | YES | NO |
字段指定NULL | YES | YES | YES | NO |
字段指定NOT NULL | YES | YES | YES | NO |
修改枚舉(ENUM OR SET)定義值 | YES | NO | YES | YES |
從以上表格能夠看出,除了修改字段類型的DDL語句沒法使用INPLACE方式外,其餘均可以使用到INPLACE方式執行DDL語句。
-- 添加字段,若是添加的是自增列,仍是不容許DML操做 alter table table_name add column column_name(column_definition), algorithm=inplace, lock=none; -- 刪除字段 alter table table_name drop column column_name, algorithm=inplace, lock=none; -- 重命名字段 /* 爲了容許併發DML操做,需保持重命名後字段類型一致,只修改字段名; 若是重命名的字段在外鍵定義中,外鍵定義也會自動更新爲新字段名。 */ alter table table_name change old_column_name new_column_name data_type, algorithm=inplace, lock=none; -- 重排序字段,使用first或after after table table_name modify column column_name column_definition first, algorithm=inplace, lock=none; -- 字段修改類型,只支持COPY方式 alter table table_name change column column column_definition, algorithm=copy; -- 擴展VARCHAR字段大小 /* 當varchar字節長度爲0~255時,須要額外一個字節進行編碼; 當varchar字節長度大於255時,則須要額外兩個字節進行編碼; 當varchar字節長度在0~255之間時,而且需從小變大的狀況,支持INPLACE方式; 當varchar字節長度字節編碼數從1變爲2或者從2變爲1時,則須要用COPY方式。 */ alter table table_name change column column column_definition, algorithm=inplace, lock=none; -- 例表: t(c1 varchar(20)) alter table t change c1 c1 varchar(85), algorithm=inplace, lock=none; alter table t change c1 c1 varchar(10), algorithm=copy; alter table t change c1 c1 varchar(100), algorithm=copy; -- 字段指定默認值 alter table table_name alter column column_name set default literal, algorithm=inplace, lock=none; -- 刪除字段默認值 alter table table_name alter column column_name drop default, algorithm=inplace, lock=none; -- 修改自增列值 alter table table_name auto_increment=next_value, algorithm=inplace, lock=none; -- 字段指定NULL alter table table_name modify column column_name data_type NULL, algorithm=inplace, lock=none; -- 字段指定NOT NULL -- sql_mode中需包含選項STRICT_TRANS_TABLES和STRICT_ALL_TABLES才能使用INPLACE,不然需使用COPY alter table table_name modify column column_name data_type NOT NULL, algorithm=inplace, lock=none; -- 修改枚舉(ENUM OR SET)定義值 -- 例表: t (c2 enum('a','b','c')) alter table t modify column c2 enum('a','b','c','d'), algorithm=inplace, lock=none;
組合字段操做
組合字段操做類型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容許併發DML操做(Permits Concurrent DML) | 僅修改元數據(Only Modifies Metadata) |
---|---|---|---|---|
添加存儲(STORED)組合字段 | NO | YES | NO | NO |
刪除存儲(STORED)組合字段 | YES | YES | YES | NO |
修改存儲(STORED)組合字段順序 | NO | YES | NO | NO |
添加虛擬(VIRTUAL)組合字段 | YES | NO | YES | YES |
刪除虛擬(VIRTUAL)組合字段 | YES | NO | YES | YES |
修改虛擬(VIRTUAL)組合字段順序 | NO | YES | NO | NO |
-- 例表: t (c1 int,c2 varchar(20)) -- 添加存儲組合字段 alter table t add column (c3 int generated always as (c1 + 2) stored), algorithm=copy; -- 刪除存儲組合字段 alter table t drop column c3, algorithm=inplace, lock=none; -- 修改存儲組合字段順序 alter table t modify column c3 int generated always as (c1 + 1) stored first, algorithm=copy; -- 添加虛擬組合字段 -- 對於非分區表纔可使用INPLACE方式,不能與其餘的alter table語句合併使用 alter table t add column (c3 int generated always as (c1 + 1) virtual), algorithm=inplace, lock=none; -- 刪除虛擬組合字段 -- 對於非分區表纔可使用INPLACE方式,不能與其餘的alter table語句合併使用 alter table t drop column c3, algorithm=inplace, lock=none; -- 修改虛擬組合字段順序 alter table t modify column c3 int generated always as (c1 + 1) virtual first, algorithm=copy;
主鍵操做
主鍵操做類型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容許併發DML操做(Permits Concurrent DML) | 僅修改元數據(Only Modifies Metadata) |
---|---|---|---|---|
添加主鍵 | YES | YES | YES | NO |
刪除主鍵 | NO | YES | NO | NO |
刪除主鍵並添加新主鍵 | YES | YES | YES | NO |
-- 例表: t (c1 int,c2 varchar(20)) -- 添加主鍵 alter table t add primray key (c1),algorithm=inplace, lock=none; 採用INPLACE方式進行表數據重構,若是添加主鍵涉及字段沒有NOT NULL屬性時,則沒法使用INPLACE方式,只能使用COPY方式。InnoDB表爲索引組織表,當涉及到彙集索引的從新構建時須要對錶中數據進行拷貝,爲了減小性能開銷,最好在建表時就指定主鍵。 由於InnoDB表的特殊性,DDL操做主鍵必定會涉及到錶行數據的拷貝操做,但經過INPLACE方式添加比COPY方式添加主要有以下優點: 1.不須要記錄undo和redo日誌,記錄日誌會提高性能開銷; 2.二級索引數據行是預先排序的,能夠按順序加載; 3.無需使用到change buffer,由於沒有隨機數據插入二級索引當中。 -- 刪除主鍵 alter table t drop primary key,algorithm=copy; -- 刪除主鍵並添加新主鍵 alter table t drop primary key,add primary key(c1,c2), algorithm=inplace, lock=none;
外鍵操做
外鍵操做類型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容許併發DML操做(Permits Concurrent DML) | 僅修改元數據(Only Modifies Metadata) |
---|---|---|---|---|
添加外鍵 | YES | NO | YES | YES |
刪除外鍵 | NO | NO | YES | YES |
-- 添加外鍵 當系統參數foreign_key_checks = 0時,可使用INPLACE方式,不然,只能使用COPY方式。 alter table t1 add constraint fk_name foreign key index(col1) references t2(col2) referential_actions; -- 刪除外鍵 alter table t drop foreign key fk_name;
表操做
表操做類型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容許併發DML操做(Permits Concurrent DML) | 僅修改元數據(Only Modifies Metadata) |
---|---|---|---|---|
修改行格式 | YES | YES | YES | NO |
修改索引鍵塊大小 | YES | YES | YES | NO |
設置永久表統計信息 | YES | NO | YES | YES |
指定字符集 | YES | YES | NO | NO |
轉換字符集 | NO | YES | NO | NO |
優化表格 | YES | YES | YES | NO |
使用FORCE選項重建表 | YES | YES | YES | NO |
使用NULL重建表 | YES | YES | YES | NO |
重命名錶 | YES | NO | YES | YES |
-- 修改行格式 alter table table_name row_format = format, algorithm=inplace, lock=none; -- 修改索引鍵塊大小 alter table table_name key_block_size = value, algorithm=inplace, lock=none; -- 設置永久表統計信息選項 alter table table_name stats_persistent = 0, stats_sample_pages = 20, stats_auto_recalc = 1, algorithm=inplace, lock=none; -- 優化表 -- 若是表中有全文索引,則不能使用INPLACE方式,不能使用algorithm和lock子句。 optimize table table_name; -- 使用FORCE選項重建表 -- 若是表中有全文索引,則不能使用INPLACE方式。 alter table table_name force, algorithm=inplace, lock=none; -- 使用NULL重建表 -- 若是表中有全文索引,則不能使用INPLACE方式。 alter table table_name engine = InnoDB, algorithm=inplace, lock=none; -- 重命名錶 alter table table_name rename to new_table_name, algorithm=inplace, lock=none;
表分區操做
表分區操做類型以下表所示:
分區子句(Partitioning Clause) | 原表操做(In Place) | 容許DML操做(Permits DML) | 說明(Notes) |
---|---|---|---|
PARTITION BY | NO | NO | 只容許algorithm=copy,lock={default |
ADD PARTITION | NO | NO | 只容許algorithm=default,lock=default 執行期間對於已採用RANGE或LIST分區的數據不進行拷貝,對於已採用HASH或LIST分區的數據容許併發查詢。在須要拷貝數據時持有共享鎖。 |
DROP PARTITION | NO | NO | 只容許algorithm=default,lock=default 只容許algorithm=default,lock=default 只容許algorithm=default,lock=default 執行期間對於已採用RANGE或LIST分區的數據不進行拷貝。 |
DISCARD PARTITION | NO | NO | 只容許algorithm=default,lock=default |
IMPORT PARTITION | NO | NO | 只容許algorithm=default,lock=default |
TRUNCATE PARTITION | YES | YES | 不會對錶中現有數據進行拷貝,僅僅刪除分區數據行,不會改會表和表分區的定義。 |
COALESCE PARTITION | NO | NO | 只容許algorithm=default,lock=default 對於已採用HASH或LIST分區的數據容許併發查詢。在須要拷貝數據時持有共享鎖。 |
REORGANIZE PARTITION | NO | NO | 只容許algorithm=default,lock=default 對於已採用LINEAR HASH或LIST分區的數據容許併發查詢。在從受影響分區拷貝數據時持有MDL鎖。 |
EXCHANGE PARTITION | YES | YES | |
ANALYZE PARTITION | YES | YES | |
CHECK PARTITION | YES | YES | |
OPTIMIZE PARTITION | NO | NO | algorithm和lock子句被忽略。 |
REBUILD PARTITION | NO | NO | 只容許algorithm=default,lock=default 對於已採用LINEAR HASH或LIST分區的數據容許併發查詢。在從受影響分區拷貝數據時持有MDL鎖。 |
REPAIR PARTITION | YES | YES | |
REMOVE PARTITIONING | NO | NO | 只容許algorithm=copy,lock={default |
表空間操做
表空間操做類型以下表所示:
操做(Operation) | 原表操做(In Place) | 重建表操做(Rebuilds Table) | 容許併發DML操做(Permits Concurrent DML) | 僅修改元數據(Only Modifies Metadata) |
---|---|---|---|---|
開啓或禁止獨立表空間加密 | NO | YES | NO | NO |
主要涉及獨立表空間加密的Online DDL操做:
alter table table_name encryption='Y', algorithm=copy;
Online DDL過程
Online DDL主要有PREPARE(準備)、EXECUTE(執行)和COMMIT(提交)三個階段,以下:
- PREPARE:
- 建立新的臨時frm文件;
- 持有EXCLUSIVE-MDL鎖,禁止讀寫操做;
- 根據ALTER類型,肯定執行方式(copy,Online-Rebuilds,Online-No-Rebuilds);
- 更新數據字典的內存對象;
- 分配row_log對象記錄增量(Rebuilds須要);
- 生成新的臨時ibd文件(Rebuilds須要)。
- EXECUTE:
- 降級EXCLUSIVE-MDL鎖,容許讀寫;
- 記錄執行期間產生的DML增量到row_log中(Rebuilds須要);
- 掃描old_table的彙集索引中每一條記錄record;
- 遍歷新表的彙集索引和二級索引,逐一處理;
- 根據record構造對應的索引項;
- 將構造的索引項插入sort_buffer塊中;
- 將sort_buffer塊插入到新的索引中;
- 將row_log中的記錄應用到新臨時表中,應用到最後一個block;
- COMMIT:
- 升級到EXECLUSIVE-MDL鎖,禁止讀寫;
- 重作row_log中最後一部分的增量;
- 更新InnoDB的數據字典表;
- 提交事務,寫InnoDB redo日誌;
- 修改統計信息;
- RENAME臨時的ibd和frm文件;
- 執行變動完成。
row_log記錄了DDL執行期間產生的DML操做,這保證了變動期間表的併發性,經過以上過程能夠看出在EXECUTE(執行)階段表容許讀寫操做,操做記錄在row_log中,在最後階段應用到新表當中,保證了數據的完整性。
Online DDL涉及參數
- old_alter_table
屬性(Property) | 值(Value) |
---|---|
命令行格式(Command-Line Format) | --old-alter-table |
系統變量格式(System Variable) | old_alter_table |
做用範圍(Scope) | 全局、會話 |
動態參數(Dynamic) | 是 |
類型(Type) | 布爾型 |
默認值(Default Value) | OFF |
指定是否使用早期版本的DDL方式,默認爲OFF,爲動態參數,能夠全局和會話級別修改。指定表DDL的執行過程中採用COPY方式生成臨時表複製數據。
- innodb_online_alter_log_max_size
屬性(Property) | 值(Value) |
---|---|
命令行格式(Command-Line Format) | --innodb-online-alter-log-max-size=# |
系統變量格式(System Variable) | innodb_online_alter_log_max_size |
做用範圍(Scope) | 全局 |
動態參數(Dynamic) | 是 |
類型(Type) | 數值型 |
默認值(Default Value) | 134217728 |
最小值(Minimum Value) | 65536 |
最大值(Maximum Value) | 2**64-1 |
指定Online DDL執行期間產生臨時日誌文件的最大大小,單位字節,默認大小爲128MB。日誌文件記錄的是表在DDL期間的數據插入、更新和刪除信息(DML操做),一旦日誌文件超過該參數指定值時,DDL執行就會失敗並回滾全部未提交的當前DML操做,因此,當執行DDL期間有大量DML操做時能夠提升該參數值,但同時也會增長DDL執行完成時應用日誌時鎖定表的時間。
Online DDL注意事項
對於線上環境的MySQL來講,任何類型的DDL都要十分謹慎,最好在語句執行以前能夠分析下語句所使用的方式以及預估判斷下影響的時長,儘可能選擇在業務訪問的低峯期進行操做,主要有如下幾點須要注意:
- 空間需求
- 臨時日誌文件大小(innodb_online_alter_log_max_size)
當DDL執行過程中容許併發執行DML操做時的日誌大小需求。 - 臨時排序文件大小(tmpdir)
當DDL執行過程當中表須要rebuild時臨時排序文件是放在tmpdir指定的路徑下的,須要保證該路徑下的磁盤空間充足。臨時排序文件都足夠容納全部二級索引以及聚簇索引的主鍵列,最終合併到新表或索引後,臨時排序文件會被刪除。在MySQL5.7.11及以後版本當中新增系統參數innodb_tmpdir專門用來指定Online DDL產生排序文件的路徑。 - 臨時中間表文件大小
當有些DDL執行過程當中表須要rebuild時會在當前表所在路徑下產生臨時中間表文件,臨時中間表文件大小可能須要與原表大小一致,在DDL執行過程中產生。
- 臨時日誌文件大小(innodb_online_alter_log_max_size)
合併拆分同表的DDL操做
早期不支持Online DDL時一般將同一張表中的多個DDL合併一塊兒執行,以便減小屢次rebuild錶帶來的性能消耗;
如今Online DDL特性出現以後,能夠經過COPY方式和INPLACE方式來進行分類併合並分組。其中INPLACE方式又能夠根據是否rebuild表來進行分組合並,儘可能減小DDL對系統的CPU、I/O資源的影響。- 對於一些大表進行Online DDL並須要重建表的操做
- 如今尚未機制能夠作到暫停Online DDL的操做或者限制Online DDL對服務器CPU、I/O資源的使用;
- 若是Online DDL執行失敗,則回滾有可能會是一項昂貴的操做;
- 執行時間過長的Online DDL可能會致使主從複製的延遲。由於主庫在執行DDL時可能容許DML併發操做,而在從庫只能在執行完DDL語句以後再進行應用DML語句操做。
總結
總之,對於線上環境的DDL語句執行一樣也須要保持敬畏之心,不管執行的DDL語句是複雜仍是簡單,最好能夠評估下執行成本,還有須要選擇在業務低峯期進行操做。
參考
https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl.html
https://dev.mysql.com/doc/refman/5.5/en/innodb-create-index.html
http://www.cnblogs.com/cchust/p/4639397.html
http://www.cnblogs.com/xinysu/p/6732646.html
http://hedengcheng.com/?p=421
☆〖本人水平有限,文中若有錯誤還請留言批評指正!〗☆