關於MySQL Online DDL

1. Online DDL

在 MySQL 5.1 (帶InnoDB Plugin)和5.5中,有個新特性叫 Fast Index Creation(下稱 FIC),就是在添加或者刪除二級索引的時候,能夠不用複製原表。對於以前的版本對於索引的添加刪除這類DDL操做,MySQL數據庫的操做過程爲以下:html

  1. 首先新建Temp table,表結構是 ALTAR TABLE 新定義的結構
  2. 而後把原表中數據導入到這個Temp table
  3. 刪除原表
  4. 最後把臨時表rename爲原來的表名

爲了保持數據的一致性,中間複製數據(Copy Table)全程鎖表只讀,若是有寫請求進來將沒法提供服務,鏈接數爆張。mysql

引入FIC以後,建立二級索引時會對原表加上一個S鎖,建立過程不須要重建表(no-rebuild);刪除InnoDB二級索引只須要更新內部視圖,並標記這個索引的空間可用,去掉數據庫元數據上該索引的定義便可。這個過程也只容許讀操做,不能寫入,但大大加快了修改索引的速度(不含主鍵索引,InnoDB IOT的特性決定了修改主鍵依然須要 Copy Table )。sql

FIC只對索引的建立刪除有效,MySQL 5.6 Online DDL把這種特性擴展到了添加列、刪除列、修改列類型、列重命名、設置默認值等等,實際效果要看所使用的選項和操做類別來定。數據庫

1.1 Online DDL選項

MySQL 在線DDL分爲 INPLACE 和 COPY 兩種方式,經過在ALTER語句的ALGORITHM參數指定。緩存

  • ALGORITHM=INPLACE,能夠避免重建錶帶來的IO和CPU消耗,保證ddl期間依然有良好的性能和併發。
  • ALGORITHM=COPY,須要拷貝原始表,因此不容許併發DML寫操做,可讀。這種copy方式的效率仍是不如 inplace ,由於前者須要記錄undo和redo log,並且由於臨時佔用buffer pool引發短期內性能受影響。

上面只是 Online DDL 內部的實現方式,此外還有 LOCK 選項控制是否鎖表,根據不一樣的DDL操做類型有不一樣的表現:默認mysql儘量不去鎖表,可是像修改主鍵這樣的昂貴操做不得不選擇鎖表。併發

  • LOCK=NONE,即DDL期間容許併發讀寫涉及的表,好比爲了保證 ALTER TABLE 時不影響用戶註冊或支付,能夠明確指定,好處是若是不幸該 alter語句不支持對該表的繼續寫入,則會提示失敗,而不會直接發到庫上執行。ALGORITHM=COPY默認LOCK級別
  • LOCK=SHARED,即DDL期間表上的寫操做會被阻塞,但不影響讀取。
  • LOCK=DEFAULT,讓mysql本身去判斷lock的模式,原則是mysql儘量不去鎖表
  • LOCK=EXCLUSIVE,即DDL期間該表不可用,堵塞任何讀寫請求。若是你想alter操做在最短的時間內完成,或者表短期內不可用能接受,能夠手動指定。

可是有一點須要說明,不管任何模式下,online ddl開始以前都須要一個短期排它鎖(exclusive)來準備環境,因此alter命令發出後,會首先等待該表上的其它操做完成,在alter命令以後的請求會出現等待waiting meta data lock。一樣在ddl結束以前,也要等待alter期間全部的事務完成,也會堵塞一小段時間。因此儘可能在ALTER TABLE以前確保沒有大事務在執行,不然同樣出現連環鎖表。app

1.2 考慮不一樣的DDL操做類別

從上面的介紹能夠看出,不是5.6支持在線ddl就能夠爲所欲爲的alter table,鎖不鎖表要看狀況:less

提示:下表根據官方 Summary of Online Status for DDL Operations 整理挑選的經常使用操做。性能

  • 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帶 * 號的限制說明
Operation In-Place? Copies Table? Allows Concurrent DML? Allows Concurrent Query? Notes
添加索引 Yes* No* Yes Yes 對全文索引的一些限制
刪除索引 Yes No Yes Yes 僅修改表的元數據
OPTIMIZE TABLE Yes Yes Yes Yes 從 5.6.17開始使用ALGORITHM=INPLACE,固然若是指定了old_alter_table=1或mysqld啓動帶--skip-new則將仍是COPY模式。若是表上有全文索引只支持COPY
對一列設置默認值 Yes No Yes Yes 僅修改表的元數據
對一列修改auto-increment 的值 Yes No Yes Yes 僅修改表的元數據
添加 foreign key constraint Yes* No* Yes Yes 爲了不拷貝表,在約束建立時會禁用foreign_key_checks
刪除 foreign key constraint Yes No Yes Yes foreign_key_checks 不影響
改變列名 Yes* No* Yes* Yes 爲了容許DML併發, 若是保持相同數據類型,僅改變列名
添加列 Yes* Yes* Yes* Yes 儘管容許 ALGORITHM=INPLACE ,但數據大幅重組,因此它仍然是一項昂貴的操做。當添加列是auto-increment,不容許DML併發
刪除列 Yes Yes* Yes Yes 儘管容許 ALGORITHM=INPLACE ,但數據大幅重組,因此它仍然是一項昂貴的操做
修改列數據類型 No Yes* No Yes 修改類型或添加長度,都會拷貝表,並且不容許更新操做
更改列順序 Yes Yes Yes Yes 儘管容許 ALGORITHM=INPLACE ,但數據大幅重組,因此它仍然是一項昂貴的操做
修改ROW_FORMAT 
和KEY_BLOCK_SIZE
Yes Yes Yes Yes 儘管容許 ALGORITHM=INPLACE ,但數據大幅重組,因此它仍然是一項昂貴的操做
設置列屬性NULL
或NOT NULL
Yes Yes Yes Yes 儘管容許 ALGORITHM=INPLACE ,但數據大幅重組,因此它仍然是一項昂貴的操做
添加主鍵 Yes* Yes Yes Yes 儘管容許 ALGORITHM=INPLACE ,但數據大幅重組,因此它仍然是一項昂貴的操做。
若是列定義必須轉化NOT NULL,則不容許INPLACE
刪除並添加主鍵 Yes Yes Yes Yes 在同一個 ALTER TABLE 語句刪除就主鍵、添加新主鍵時,才容許inplace;數據大幅重組,因此它仍然是一項昂貴的操做。
刪除主鍵 No Yes No Yes 不容許併發DML,要拷貝表,並且若是沒有在同一 ATLER TABLE 語句裏同時添加主鍵則會收到限制
變動表字符集 No Yes No Yes 若是新的字符集編碼不一樣,重建表

從表看出,In-Place爲No,DML必定是No,說明 ALGORITHM=COPY 必定會發生拷貝表,只讀。但 ALGORITHM=INPLACEE 也要可能發生拷貝表,但能夠併發DML:優化

  • 添加、刪除列,改變列順序
  • 添加或刪除主鍵
  • 改變行格式ROW_FORMAT和壓縮塊大小KEY_BLOCK_SIZE
  • 改變列NULL或NOT NULL
  • 優化表OPTIMIZE TABLE
  • 強制 rebuild 該表

不容許併發DML的狀況有:修改列數據類型、刪除主鍵、變動表字符集,即這些類型操做ddl是不能online的。

另外,更改主鍵索引與普通索引處理方式是不同的,主鍵即彙集索引,體現了表數據在物理磁盤上的排列,包含了數據行自己,須要拷貝表;而普通索引經過包含主鍵列來定位數據,因此普通索引的建立只須要一次掃描主鍵便可,並且是在已有數據的表上創建二級索引,更緊湊,未來查詢效率更高。

修改主鍵也就意味着要重建全部的普通索引。刪除二級索引更簡單,修改InnoDB系統表信息和數據字典,標記該因此不存在,標記所佔用的表空間能夠被新索引或數據行從新利用。

1.3 在線DDL的限制

  • 在alter table時,若是涉及到table copy操做,要確保 datadir 目錄有足夠的磁盤空間,可以放的下整張表,由於拷貝表的的操做是直接在數據目錄下進行的。
  • 添加索引無需table copy,但要確保 tmpdir 目錄足夠存下索引一列的數據(若是是組合索引,當前臨時排序文件一合併到原表上就會刪除)
  • 在主從環境下,主庫執行alter命令在完成以前是不會進入binlog記錄事件,若是容許dml操做則不影響記錄時間,因此期間不會致使延遲。然而,因爲從庫是單個SQL Thread按順序應用relay log,輪到ALTER語句時直到執行完才能下一條,因此從庫會在master ddl完成後開始產生延遲。(pt-osc能夠控制延遲時間,因此這種場景下它更合適)
  • 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.
  • 在執行一個容許併發DML在線 ALTER TABLE時,結束以前這個線程會應用 online log 記錄的增量修改,而這些修改是其它thread裏產生的,因此有可能會遇到重複鍵值錯誤 (ERROR 1062 (23000): Duplicate entry)
  • 涉及到table copy時,目前尚未機制限制暫停ddl,或者限制IO閥值
    在MySQL 5.7.6開始可以經過 performance_schema 觀察alter table的進度
  • 通常來講,建議把多個alter語句合併在一塊兒進行,避免屢次table rebuild帶來的消耗。可是也要注意分組,好比須要copy table和只需inplace就能完成的,應該分兩個alter語句。
  • 若是DDL執行時間很長,期間又產生了大量的dml操做,以致於超過了 innodb_online_alter_log_max_size 變量所指定的大小,會引發 DB_ONLINE_LOG_TOO_BIG 錯誤。默認爲 128M,特別對於須要拷貝大表的alter操做,考慮臨時加大該值,以此得到更大的日誌緩存空間
  • 執行完 ALTER TABLE 以後,最好 ANALYZE TABLE tb1 去更新索引統計信息

2. 實現過程

online ddl主要包括3個階段,prepare階段,ddl執行階段,commit階段,rebuild方式比no-rebuild方式實質多了一個ddl執行階段,prepare階段和commit階段相似。下面將主要介紹ddl執行過程當中三個階段的流程。

  • Prepare階段 :

    1. 建立新的臨時frm文件(與InnoDB無關)
    2. 持有EXCLUSIVE-MDL鎖,禁止讀寫
    3. 根據alter類型,肯定執行方式(copy,online-rebuild,online-norebuild)
      假如是Add Index,則選擇online-norebuild即INPLACE方式
    4. 更新數據字典的內存對象
    5. 分配row_log對象記錄增量(僅rebuild類型須要)
    6. 生成新的臨時ibd文件(僅rebuild類型須要)
  • ddl執行階段 :

    1. 降級EXCLUSIVE-MDL鎖,容許讀寫
    2. 掃描old_table的彙集索引每一條記錄rec
    3. 遍歷新表的彙集索引和二級索引,逐一處理
    4. 根據rec構造對應的索引項
    5. 將構造索引項插入sort_buffer塊排序
    6. 將sort_buffer塊更新到新的索引上
    7. 記錄ddl執行過程當中產生的增量(僅rebuild類型須要)
    8. 重放row_log中的操做到新索引上(no-rebuild數據是在原表上更新的)
    9. 重放row_log間產生dml操做append到row_log最後一個Block
    • commit階段 :

      1. 當前Block爲row_log最後一個時,禁止讀寫,升級到EXCLUSIVE-MDL鎖
      2. 重作row_log中最後一部分增量
      3. 更新innodb的數據字典表
      4. 提交事務(刷事務的redo日誌)
      5. 修改統計信息
      6. rename臨時idb文件,frm文件
      7. 變動完成

3. Online DDL 的思惟導向圖

4. Online DDL 注意事項:

  • 磁盤空間
    • rebuild 的時候,datadir空間是否足夠
      • 由於會拷貝ibd文件,因此要確保空間足夠
    • rebuild 的時候,innodb_online_alter_log_max_size是否足夠
      • rebuild過程當中,產生的DML涉及到行記錄變動日誌,是否足夠存儲
    • 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,不會存在衝突,其建立的臨時idb及frm文件會自動刪除
  • copy table ,inplace下宕機
    • 這兩種狀況下宕機後,沒有完成的DDL語句不會繼續執行
    • 可是,其生成的frm跟idb臨時文件不會被刪除,能夠手動刪除,也能夠不手動刪除,即便不刪除,也不會影響再次執行DDL
    • 但建議mysql服務後,刪除無用的臨時文件
  • 同個表格多個DDL語句,不要一個個執行
    • 請按照是否支持inplace及是否須要rebuild分類合併執行

5. 5.7 的版本Online DDL 能夠經過P_S 查看進度

1、打開功能

mysql> UPDATE setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'stage/innodb/alter%'; Query OK, 7 rows affected (0.00 sec) Rows matched: 7 Changed: 7 Warnings: 0 mysql> UPDATE setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE '%stages%'; Query OK, 3 rows affected (0.00 sec) Rows matched: 3 Changed: 3 Warnings: 0 

2、執行改表操做

mysql> ALTER TABLE employees.employees ADD COLUMN middle_name varchar(14) AFTER first_name; Query OK, 0 rows affected (9.27 sec) Records: 0 Duplicates: 0 Warnings: 0 

3、查看改表進度

mysql> SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED FROM events_stages_current; +------------------------------------------------------+----------------+----------------+ | EVENT_NAME | WORK_COMPLETED | WORK_ESTIMATED | +------------------------------------------------------+----------------+----------------+ | stage/innodb/alter table (read PK and internal sort) | 280 | 1245 | +------------------------------------------------------+----------------+----------------+ 1 row in set (0.01 sec)
相關文章
相關標籤/搜索