前言:
MySQL中DDL語句,即數據定義語言,用於創建、刪除、修改、庫或表結構,對數據庫或表的結構操作。常見的有create,alter,drop等。這類語句通常會耗費很大代價,特別是對於大表做表結構變更。本篇文章會揭露各類DDL語句執行的詳細情況。
1.Online DDL簡介
在MySQL的早期版本中,DDL操作因爲鎖表會和DML操作發生鎖衝突,大大降低併發性。在早期版本中,大部分DDL操作的執行原理就是通過重建表的方式,因爲要複製原表數據,所以會長時間鎖表,只能讀不能寫,DDL操作和DML操作有很嚴重的衝突。從MySQL5.6開始,很多DDL操作過程都進行了改進,出現了Online DDL,用於支持DDL執行期間DML語句的並行操作,提高數據庫的吞吐量。
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語句不支持對該表的繼續寫入,則會提示失敗,而不會直接發到庫上執行。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之前確保沒有大事務在執行,否則一樣出現連環鎖表。
2.不同類DDL操作詳情
不同種類DDL語句具體的執行情況是不同的,下表列舉出常見DDL語句具體的執行詳情,包括是否允許讀寫及是否鎖表。這個表格希望大家可以詳細對比看下,特別要關注下需要copy table的DDL操作。
操作 | 支持方式 | Allow R/W | 說明 |
---|---|---|---|
add/create index | online | 允許讀寫 | 當表上有FULLTEXT索引除外,需要鎖表,阻塞寫 |
drop index | online | 允許讀寫 | 操作元數據,不涉及表數據。所以很快,可以放心操作 |
optimize table | online | 允許讀寫 | 當帶有fulltext index的表用copy table方式並且阻塞寫 |
alter table...engine=innodb | online | 允許讀寫 | 當帶有fulltext index的表用copy table方式並且阻塞寫 |
add column | online | 允許讀寫(增加自增列除外) | 1、添加auto_increment列要鎖表,阻塞寫;2、雖採用online方式,但是表數據需要重新組織,所以增加列依然是昂貴的操作 |
drop column | online | 允許讀寫(增加自增列除外) | 同add column,重新組織表數據,,昂貴的操作 |
Rename a column | online | 允許讀寫 | 操作元數據;不能改列的類型,否則就鎖表 |
Reorder columns | online | 允許讀寫 | 重新組織表數據,昂貴的操作 |
Make column NOT NULL | online | 允許讀寫 | 重新組織表數據,昂貴的操作 |
Change data type of column | copy table | 僅支持讀,阻塞寫 | 創建臨時表,複製表數據,昂貴的操作 |
Set default value for a column | online | 允許讀寫 | 操作元數據,因爲default value存儲在frm文件中,不涉及表數據。所以很快,可以放心操作 |
alter table xxx auto_increment=xx | online | 允許讀寫 | 操作元數據,不涉及表數據。所以很快,可以放心操作 |
Add primary key | online | 允許讀寫 | 昂貴的操作 |
Convert character set | copy table | 僅支持讀,阻塞寫 | 如果新字符集不同,需要重建表,昂貴的操作 |
3.DDL最佳實踐
雖然MySQL 5.6和5.7版本提供了Online DDL操作,但Online DDL仍存在以下問題:
- 主從複製延遲,只有主庫上DDL執行成功纔會寫入到binlog中,而DDL操作在從庫上不能併發執行,因此即使主庫執行DDL時允許併發DML操作,對於大表操作,仍會引發嚴重的複製延遲。
- 主庫執行Online DDL時,不能根據負載暫停DDL操作。
- 使用Inplace方式執行的DDL,發生錯誤或被KILL時,需要一定時間的回滾期,執行時間越長,回滾時間越長。
- 使用Copy方式執行的DDL,需要記錄過程中的undo和redo日誌,同時會消耗buffer pool的資源,效率較低,優點是可以快速停止。
- Online DDL並不是所有時間段的Online,在特定時間段需要加元數據鎖或其他鎖。
- 允許併發DML的DDL,可能會導致Duplicate entry問題。
針對DDL,下面整理下幾點乾貨建議,之後執行DDL語句時可以參考下:
- 執行DDL前查看下該表有沒有被事務佔用,防止出現MDL鎖。
- 執行DDL前確保datadir,tmpdir磁盤空間足夠。
- 能業務低峯期操作的DDL,都儘量安排在業務低峯期進行。
- 對於大表和較大表,如果對複製延遲和主庫性能敏感,建議改爲gh-ost或pt-osc工具。
- 對於併發操作較高的表,無論表數據量多少,不能在業務高峯期操作。
- 同個表的多個DDL語句可以合併在一起進行,避免多次table rebuild帶來的消耗。但是也要注意分組,比如需要copy table和只需inplace就能完成的,應該分兩個alter語句。
參考: