前言:html
作爲一個DBA,會常常執行Online DDL,可是在MySQL5.6以前,執行DDL會鎖表,致使大量的Waiting for meta data lock,DDL操做(好比CREATE,DROP,ALTER等)代價是很是高的,特別是在單表上千萬的狀況下,加個索引或改個列類型,就有可能堵塞整個表的讀寫。這多是不少DBA心中的痛。在MySQL5.6引入一個新特性Online DDL,這個新特性解決了執行DDL鎖表的問題,在表變動時,不會阻塞線上業務的讀寫,數據庫依然能夠正常對外提供訪問mysql
1、Online DDL的前世此生sql
1、MySQL5.6之前的Online DDL數據庫
在 MySQL 5.1 (帶InnoDB Plugin)和5.5中,有個新特性叫 Fast Index Creation(下稱 FIC),就是在添加或者刪除二級索引的時候,能夠不用複製原表。對於以前的版本對於索引的添加刪除這類DDL操做,MySQL數據庫的操做過程爲:1)首先新建Temp table,表結構是 ALTAR TABLE 新定義的結構緩存
2)而後把原表中數據導入到這個Temp table微信
3)刪除原表併發
4)最後把臨時表rename爲原來的表名app
爲了保持數據的一致性,中間複製數據(Copy Table)全程鎖表只讀,若是有寫請求進來將沒法提供服務,鏈接數爆張。less
引入FIC以後,建立二級索引時會對原表加上一個S鎖,建立過程不須要重建表(no-rebuild);刪除InnoDB二級索引只須要更新內部視圖,並標記這個索引的空間可用,去掉數據庫元數據上該索引的定義便可。這個過程也只容許讀操做,不能寫入,但大大加快了修改索引的速度(不含主鍵索引,InnoDB IOT的特性決定了修改主鍵依然須要 Copy Table )。性能
2、MySQL5.7的Online DDL
ALGORITHM=INPLACE,能夠避免重建錶帶來的IO和CPU消耗,保證ddl期間依然有良好的性能和併發。
ALGORITHM=COPY,須要拷貝原始表,因此不容許併發DML寫操做,可讀。這種copy方式的效率仍是不如 inplace ,由於前者須要記錄undo和redo log,並且由於臨時佔用buffer pool引發短期內性能受影響。
· In-Place爲Yes是優選項,說明該操做支持INPLACE
· Copies Table爲No是優選項,由於爲Yes須要重建表。大部分狀況與In-Place是相反的
· Allows Concurrent DML?爲Yes是優選項,說明ddl期間表依然可讀寫,能夠指定 LOCK=NONE(若是操做容許的話mysql自動就是NONE)
· Allows Concurrent Query?默認全部DDL操做期間都容許查詢請求,放在這只是便於參考
Notes會對前面幾列Yes/No帶*號的限制說明
http://dev.mysql.com/doc/refman/5.7/en/innodb-create-index-overview.html
2、在線DDL的限制
1)在alter table時,若是涉及到table copy操做,要確保datadir目錄有足夠的磁盤空間,可以放的下整張表,由於拷貝表的的操做是直接在數據目錄下進行的。
2)添加索引無需table copy,但要確保tmpdir目錄足夠存下索引一列的數據(若是是組合索引,當前臨時排序文件一合併到原表上就會刪除)
3)在主從環境下,主庫執行alter命令在完成以前是不會進入binlog記錄事件,若是容許dml操做則不影響記錄時間,因此期間不會致使延遲。然而,因爲從庫是單個SQL Thread按順序應用relay log,輪到ALTER語句時直到執行完才能下一條,因此從庫會在master ddl完成後開始產生延遲。(pt-osc能夠控制延遲時間,因此這種場景下它更合適)
4)During each online DDL ALTER TABLE statement, regardless of the LOCK clause, there are brief periods at the beginning and end requiring an exclusive lock on the table (the same kind of lock specified by the LOCK=EXCLUSIVE clause). Thus, an online DDL operation might wait before starting if there is a long-running transaction performing inserts, updates, deletes, or SELECT … FOR UPDATE on that table; and an online DDL operation might wait before finishing if a similar long-running transaction was started while the ALTER TABLE was in progress.
5)在執行一個容許併發DML在線 ALTER TABLE時,結束以前這個線程會應用 online log 記錄的增量修改,而這些修改是其它thread裏產生的,因此有可能會遇到重複鍵值錯誤(ERROR 1062 (23000): Duplicate entry)。
6)涉及到table copy時,目前尚未機制限制暫停ddl,或者限制IO閥值
7)在MySQL 5.7.6開始可以經過 performance_schema 觀察alter table的進度
通常來講,建議把多個alter語句合併在一塊兒進行,避免屢次table rebuild帶來的消耗。可是也要注意分組,好比須要copy table和只需inplace就能完成的,應該分兩個alter語句。
8)若是DDL執行時間很長,期間又產生了大量的dml操做,以致於超過了innodb_online_alter_log_max_size變量所指定的大小,會引發DB_ONLINE_LOG_TOO_BIG 錯誤。默認爲 128M,特別對於須要拷貝大表的alter操做,考慮臨時加大該值,以此得到更大的日誌緩存空間
9)執行完 ALTER TABLE 以後,最好 ANALYZE TABLE tb1 去更新索引統計信息
3、Online DDL的實現過程
online ddl主要包括3個階段,prepare階段,ddl執行階段,commit階段,rebuild方式比no-rebuild方式實質多了一個ddl執行階段,prepare階段和commit階段相似。下面將主要介紹ddl執行過程當中三個階段的流程。
Prepare階段:
建立新的臨時frm文件(與InnoDB無關)
持有EXCLUSIVE-MDL鎖,禁止讀寫
根據alter類型,肯定執行方式(copy,online-rebuild,online-norebuild)
假如是Add Index,則選擇online-norebuild即INPLACE方式
更新數據字典的內存對象
分配row_log對象記錄增量(僅rebuild類型須要)
生成新的臨時ibd文件(僅rebuild類型須要)
ddl執行階段:
降級EXCLUSIVE-MDL鎖,容許讀寫
掃描old_table的彙集索引每一條記錄rec
遍歷新表的彙集索引和二級索引,逐一處理
根據rec構造對應的索引項
將構造索引項插入sort_buffer塊排序
將sort_buffer塊更新到新的索引上
記錄ddl執行過程當中產生的增量(僅rebuild類型須要)
重放row_log中的操做到新索引上(no-rebuild數據是在原表上更新的)
重放row_log間產生dml操做append到row_log最後一個Block
commit階段:
當前Block爲row_log最後一個時,禁止讀寫,升級到EXCLUSIVE-MDL鎖
重作row_log中最後一部分增量
更新innodb的數據字典表
提交事務(刷事務的redo日誌)
修改統計信息
rename臨時idb文件,frm文件
變動完成
4、測試實驗
創建數據
root@localhost [test]>create table test01( id int(4) unsigned not null auto_increment, age int(4) unsigned not null default '0', `name` varchar(3) default null, primary key(id) )engine=innodb; Query OK, 0 rows affected (0.14 sec) root@localhost [test]>desc test01; +-------+-----------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------------+------+-----+---------+----------------+ | id | int(4) unsigned | NO | PRI | NULL | auto_increment | | age | int(4) unsigned | NO | | 0 | | | name | varchar(3) | YES | | NULL | | +-------+-----------------+------+-----+---------+----------------+ 3 rows in set (0.03 sec) root@localhost [test]>insert into test01 values(1,18,'小芳'); Query OK, 1 row affected (0.05 sec) root@localhost [test]>insert into test01 values(2,19,'小亞'); Query OK, 1 row affected (0.02 sec) root@localhost [test]>insert into test01 values(3,20,'小飛'); Query OK, 1 row affected (0.00 sec) root@localhost [test]>SET AUTOCOMMIT=0; Query OK, 0 rows affected (0.00 sec)
1、建立刪除二級索引測試
Session1
root@localhost [test]>alter table test01 add index idx_name(name); Query OK, 0 rows affected (0.16 sec) Records: 0 Duplicates: 0 Warnings: 0 root@localhost [test]>
Session2
root@localhost [test]>insert into test01 values(4,19,'小亞'); Query OK, 1 row affected (0.01 sec) root@localhost [test]>select * from test01; +----+-----+--------+ | id | age | name | +----+-----+--------+ | 1 | 18 | 小芳 | | 2 | 19 | 小亞 | | 3 | 20 | 小飛 | | 4 | 19 | 小亞 | +----+-----+--------+ 4 rows in set (0.00 sec) root@localhost [test]>alter table test01 drop index idx_name; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 root@localhost [test]>desc test01; +-------+-----------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------------+------+-----+---------+----------------+ | id | int(4) unsigned | NO | PRI | NULL | auto_increment | | age | int(4) unsigned | NO | | 0 | | | name | varchar(3) | YES | | NULL | | +-------+-----------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec) root@localhost [test]>desc test01; +-------+-----------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-----------------+------+-----+---------+----------------+ | id | int(4) unsigned | NO | PRI | NULL | auto_increment | | age | int(4) unsigned | NO | | 0 | | | name | varchar(3) | YES | | NULL | | +-------+-----------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)
因而可知,刪除、增長索引是自動提交的,不會copy表,不影響讀寫。
2、添加列
示例再也不粘貼,經本人測試,
添加、刪除一個普通列不會copy table。不影響讀寫。
刪除一個主鍵列會copy table。
添加一個主鍵列不會copy table。
改變表中列類型會copy table。不會影響查詢、寫入。
建議:
1)此中類型太多不一一列舉,詳細能夠到MySQL官方看一下,都清楚了。
地址:http://dev.mysql.com/doc/refman/5.7/en/innodb-create-index-overview.html
2)若是在alter以前有大事務在執行,會阻塞ddl以及後續的全部請求,所以DDL操做要在業務低峯期進行
參考文檔:
http://www.cnblogs.com/cchust/p/4639397.html
http://www.cnblogs.com/zengkefu/p/5671661.html
http://dev.mysql.com/doc/refman/5.7/en/innodb-create-index-overview.html
爲了方便你們交流,本人開通了微信公衆號,和QQ羣291519319。喜歡技術的一塊兒來交流吧