溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

MySQL?DDL執(zhí)行方式Online?DDL實例分析

發(fā)布時間:2022-09-23 09:40:37 來源:億速云 閱讀:114 作者:iii 欄目:開發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“MySQL DDL執(zhí)行方式Online DDL實例分析”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“MySQL DDL執(zhí)行方式Online DDL實例分析”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。

    1 引言

    一般來說MySQL分為DDL(定義)和DML(操作)。

    • DDL:Data Definition Language,即數(shù)據(jù)定義語言,那相關(guān)的定義操作就是DDL,包括:新建、修改、刪除等;相關(guān)的命令有:CREATE,ALTER,DROP,TRUNCATE截斷表內(nèi)容(開發(fā)期,還是挺常用的),COMMENT 為數(shù)據(jù)字典添加備注。

    • DML:Data Manipulation Language,即數(shù)據(jù)操作語言,即處理數(shù)據(jù)庫中數(shù)據(jù)的操作就是DML,包括:選取,插入,更新,刪除等;相關(guān)的命令有:SELECT,INSERT,UPDATE,DELETE,還有 LOCK TABLE,以及不常用的CALL – 調(diào)用一個PL/SQL或Java子程序,EXPLAIN PLAN – 解析分析數(shù)據(jù)訪問路徑。

    我們可以認(rèn)為:

    • CREATE,ALTER ,DROP,TRUNCATE,定義相關(guān)的命令就是DDL;

    • SELECT,INSERT,UPDATE,DELETE,操作處理數(shù)據(jù)的命令就是DML;

    DDL、DML區(qū)別:

    • DML操作是可以手動控制事務(wù)的開啟、提交和回滾的。

    • DDL操作是隱性提交的,不能rollback,一定要謹(jǐn)慎哦!

    日常開發(fā)我們對一條DML語句較為熟悉,很多開發(fā)人員都了解sql的執(zhí)行過程,比較熟悉,但是DDL是如何執(zhí)行的呢,大部分開發(fā)人員可能不太關(guān)心,也認(rèn)為沒必要了解,都交給DBA吧。 其實不然,了解一些能盡量避開一些ddl的坑,那么下面帶大家一起了解一下DDL執(zhí)行的方式,也算拋磚引玉吧。如有錯誤,還請各位大佬們指正。

    2 概述

    在MySQL使用過程中,根據(jù)業(yè)務(wù)的需求對表結(jié)構(gòu)進(jìn)行變更是個普遍的運維操作,這些稱為DDL操作。常見的DDL操作有在表上增加新列或給某個列添加索引。

    我們常用的易維平臺提供了兩種方式可執(zhí)行DDL,包括MySQL原生在線DDL(online DDL)以及一種第三方工具pt-osc。

    下圖是執(zhí)行方式的性能對比及說明:

    MySQL?DDL執(zhí)行方式Online?DDL實例分析

    3 介紹

    MySQL Online DDL 功能從 5.6 版本開始正式引入,發(fā)展到現(xiàn)在的 8.0 版本,經(jīng)歷了多次的調(diào)整和完善。其實早在 MySQL 5.5 版本中就加入了 INPLACE DDL 方式,但是因為實現(xiàn)的問題,依然會阻塞 INSERT、UPDATE、DELETE 操作,這也是 MySQL 早期版本長期被吐槽的原因之一。

    在MySQL 5.6版本以前,最昂貴的數(shù)據(jù)庫操作之一就是執(zhí)行DDL語句,特別是ALTER語句,因為在修改表時,MySQL會阻塞整個表的讀寫操作。

    例如,對表 A 進(jìn)行 DDL 的具體過程如下:

    • 按照表 A 的定義新建一個表 B

    • 對表 A 加寫鎖

    • 在表 B 上執(zhí)行 DDL 指定的操作

    • 將 A 中的數(shù)據(jù)拷貝到 B

    • 釋放 A 的寫鎖

    • 刪除表 A

    • 將表 B 重命名為 A

    在以上 2-4 的過程中,如果表 A 數(shù)據(jù)量比較大,拷貝到表 B 的過程會消耗大量時間,并占用額外的存儲空間。此外,由于 DDL 操作占用了表 A 的寫鎖,所以表 A 上的 DDL 和 DML 都將阻塞無法提供服務(wù)。

    如果遇到巨大的表,可能需要幾個小時才能執(zhí)行完成,勢必會影響應(yīng)用程序,因此需要對這些操作進(jìn)行良好的規(guī)劃,以避免在高峰時段執(zhí)行這些更改。對于那些要提供全天候服務(wù)(24*7)或維護(hù)時間有限的人來說,在大表上執(zhí)行DDL無疑是一場真正的噩夢。

    因此,MySQL官方不斷對DDL語句進(jìn)行增強(qiáng),自MySQL 5.6 起,開始支持更多的 ALTER TABLE 類型操作來避免數(shù)據(jù)拷貝,同時支持了在線上 DDL 的過程中不阻塞 DML 操作,真正意義上的實現(xiàn)了 Online DDL,即在執(zhí)行 DDL 期間允許在不中斷數(shù)據(jù)庫服務(wù)的情況下執(zhí)行DML(insert、update、delete)。然而并不是所有的DDL操作都支持在線操作。到了 MySQL 5.7,在 5.6 的基礎(chǔ)上又增加了一些新的特性,比如:增加了重命名索引支持,支持了數(shù)值類型長度的增大和減小,支持了 VARCHAR 類型的在線增大等。但是基本的實現(xiàn)邏輯和限制條件相比 5.6 并沒有大的變化。

    4 用法

    ALTER TABLE tbl_name ADD PRIMARY KEY (column), ALGORITHM=INPLACE, LOCK=NONE;

    ALTER 語句中可以指定參數(shù) ALGORITHM 和 LOCK 分別指定 DDL 執(zhí)行的算法模式和 DDL 期間 DML 的鎖控制模式。

    • ALGORITHM=INPLACE 表示執(zhí)行DDL的過程中不發(fā)生表拷貝,過程中允許并發(fā)執(zhí)行DML(INPLACE不需要像COPY一樣占用大量的磁盤I/O和CPU,減少了數(shù)據(jù)庫負(fù)載。同時減少了buffer pool的使用,避免 buffer pool 中原有的查詢緩存被大量刪除而導(dǎo)致的性能問題)。

    • 如果設(shè)置 ALGORITHM=COPY,DDL 就會按 MySQL 5.6 之前的方式,采用表拷貝的方式進(jìn)行,過程中會阻塞所有的DML。另外也可以設(shè)置 ALGORITHEM=DAFAULT,讓 MySQL 以盡量保證 DML 并發(fā)操作的原則選擇執(zhí)行方式。

    • LOCK=NONE 表示對 DML 操作不加鎖,DDL 過程中允許所有的 DML 操作。此外還有 EXCLUSIVE(持有排它鎖,阻塞所有的請求,適用于需要盡快完成DDL或者服務(wù)庫空閑的場景)、SHARED(允許SELECT,但是阻塞INSERT UPDATE DELETE,適用于數(shù)據(jù)倉庫等可以允許數(shù)據(jù)寫入延遲的場景)和 DEFAULT(根據(jù)DDL的類型,在保證最大并發(fā)的原則下來選擇LOCK的取值)。

    5 兩種算法

    第一種 Copy

    • 按照原表定義創(chuàng)建一個新的臨時表;

    • 對原表加寫鎖(禁止DML,允許select);

    • 在步驟1 建立的臨時表執(zhí)行 DDL;

    • 將原表中的數(shù)據(jù) copy 到臨時表;

    • 釋放原表的寫鎖;

    • 將原表刪除,并將臨時表重命名為原表。

    • 從上可見,采用 copy 方式期間需要鎖表,禁止DML,因此是非Online的。比如:刪除主鍵、修改列類型、修改字符集,這些操作會導(dǎo)致行記錄格式發(fā)生變化(無法通過全量 + 增量實現(xiàn) Online)。

    第二種 Inplace

    在原表上進(jìn)行更改,不需要生成臨時表,不需要進(jìn)行數(shù)據(jù)copy的過程。根據(jù)是否行記錄格式,又可分為兩類:

    • rebuild:需要重建表(重新組織聚簇索引)。比如 optimize table、添加索引、添加/刪除列、修改列 NULL/NOT NULL 屬性等;

    • no-rebuild:不需要重建表,只需要修改表的元數(shù)據(jù),比如刪除索引、修改列名、修改列默認(rèn)值、修改列自增值等。

    對于 rebuild 方式實現(xiàn) Online 是通過緩存 DDL 期間的 DML,待 DDL 完成之后,將 DML 應(yīng)用到表上來實現(xiàn)的。例如,執(zhí)行一個 alter table A engine=InnoDB;

    重建表的 DDL 其大致流程如下:

    • 建立一個臨時文件,掃描表 A 主鍵的所有數(shù)據(jù)頁;

    • 用數(shù)據(jù)頁中表 A 的記錄生成 B+ 樹,存儲到臨時文件中;

    • 生成臨時文件的過程中,將所有對 A 的操作記錄在一個日志文件(row log)中;

    • 臨時文件生成后,將日志文件中的操作應(yīng)用到臨時文件,得到一個邏輯數(shù)據(jù)上與表 A 相同的數(shù)據(jù)文件;

    • 用臨時文件替換表 A 的數(shù)據(jù)文件。

    說明:

    • 在 copy 數(shù)據(jù)到新表期間,在原表上是加的 MDL 讀鎖(允許 DML,禁止 DDL);

    • 在應(yīng)用增量期間對原表加 MDL 寫鎖(禁止 DML 和 DDL);

    • 根據(jù)表 A 重建出來的數(shù)據(jù)是放在 tmp_file 里的,這個臨時文件是 InnoDB 在內(nèi)部創(chuàng)建出來的,整個 DDL 過程都在 InnoDB 內(nèi)部完成。對于 server 層來說,沒有把數(shù)據(jù)挪動到臨時表,是一個原地操作,這就是”inplace”名稱的來源。

    使用Inplace方式執(zhí)行的DDL,發(fā)生錯誤或被kill時,需要一定時間的回滾期,執(zhí)行時間越長,回滾時間越長。

    使用Copy方式執(zhí)行的DDL,需要記錄過程中的undo和redo日志,同時會消耗buffer pool的資源,效率較低,優(yōu)點是可以快速停止。

    不過并不是所有的 DDL 操作都能用 INPLACE 的方式執(zhí)行,具體的支持情況可以在(在線 DDL 操作) 中查看。

    以下是常見DDL操作:

    MySQL?DDL執(zhí)行方式Online?DDL實例分析

    官網(wǎng)支持列表:

    MySQL?DDL執(zhí)行方式Online?DDL實例分析

    6 執(zhí)行過程

    Online DDL主要包括3個階段,prepare階段,ddl執(zhí)行階段,commit階段。下面將主要介紹ddl執(zhí)行過程中三個階段的流程。

    1)Prepare階段:初始化階段會根據(jù)存儲引擎、用戶指定的操作、用戶指定的 ALGORITHM 和 LOCK 計算 DDL 過程中允許的并發(fā)量,這個過程中會獲取一個 shared metadata lock,用來保護(hù)表的結(jié)構(gòu)定義。

    • 創(chuàng)建新的臨時frm文件(與InnoDB無關(guān))。

    • 持有EXCLUSIVE-MDL鎖,禁止讀寫。

    • 根據(jù)alter類型,確定執(zhí)行方式(copy,online-rebuild,online-norebuild)。假如是Add Index,則選擇online-norebuild即INPLACE方式。

    • 更新數(shù)據(jù)字典的內(nèi)存對象。

    • 分配row_log對象來記錄增量(僅rebuild類型需要)。

    • 生成新的臨時ibd文件(僅rebuild類型需要) 。

    • 數(shù)據(jù)字典上提交事務(wù)、釋放鎖。

    注:Row log是一種獨占結(jié)構(gòu),它不是redo log。它以Block的方式管理DML記錄的存放,一個Block的大小為由參數(shù)innodb_sort_buffer_size控制,默認(rèn)大小為1M,初始化階段會申請兩個Block。

    2)DDL執(zhí)行階段:執(zhí)行期間的 shared metadata lock 保證了不會同時執(zhí)行其他的 DDL,但 DML 能可以正常執(zhí)行。

    • 降級EXCLUSIVE-MDL鎖,允許讀寫(copy不可寫)。

    • 掃描old_table的聚集索引每一條記錄rec。

    • 遍歷新表的聚集索引和二級索引,逐一處理。

    • 根據(jù)rec構(gòu)造對應(yīng)的索引項

    • 將構(gòu)造索引項插入sort_buffer塊排序。

    • 將sort_buffer塊更新到新的索引上。

    • 記錄ddl執(zhí)行過程中產(chǎn)生的增量(僅rebuild類型需要)

    • 重放row_log中的操作到新索引上(no-rebuild數(shù)據(jù)是在原表上更新的)。

    • 重放row_log間產(chǎn)生dml操作append到row_log最后一個Block。

    3)Commit階段:將 shared metadata lock 升級為 exclusive metadata lock,禁止DML,然后刪除舊的表定義,提交新的表定義。

    • 當(dāng)前Block為row_log最后一個時,禁止讀寫,升級到EXCLUSIVE-MDL鎖。

    • 重做row_log中最后一部分增量。

    • 更新innodb的數(shù)據(jù)字典表。

    • 提交事務(wù)(刷事務(wù)的redo日志)。

    • 修改統(tǒng)計信息。

    • rename臨時idb文件,frm文件。

    • 變更完成。

    MySQL?DDL執(zhí)行方式Online?DDL實例分析

    Online DDL 過程中占用 exclusive MDL 的步驟執(zhí)行很快,所以幾乎不會阻塞 DML 語句。
    不過,在 DDL 執(zhí)行前或執(zhí)行時,其他事務(wù)可以獲取 MDL。由于需要用到 exclusive MDL,所以必須要等到其他占有 metadata lock 的事務(wù)提交或回滾后才能執(zhí)行上面兩個涉及到 MDL 的地方。

    7 踩坑

    前面提到 Online DDL 執(zhí)行過程中需要獲取 MDL,MDL (metadata lock) 是 MySQL 5.5 引入的表級鎖,在訪問一個表的時候會被自動加上,以保證讀寫的正確性。當(dāng)對一個表做 DML 操作的時候,加 MDL 讀鎖;當(dāng)做 DDL 操作時候,加 MDL 寫鎖。

    為了在大表執(zhí)行 DDL 的過程中同時保證 DML 能并發(fā)執(zhí)行,前面使用了 ALGORITHM=INPLACE 的 Online DDL,但這里仍然存在死鎖的風(fēng)險,問題就出在 Online DDL 過程中需要 exclusive MDL 的地方。

    例如,Session 1 在事務(wù)中執(zhí)行 SELECT 操作,此時會獲取 shared MDL。由于是在事務(wù)中執(zhí)行,所以這個 shared MDL 只有在事務(wù)結(jié)束后才會被釋放。

    # Session 1> START TRANSACTION;> SELECT * FROM tbl_name;# 正常執(zhí)行

    這時 Session 2 想要執(zhí)行 DML 操作也只需要獲取 shared MDL,仍然可以正常執(zhí)行。

    # Session 2> SELECT * FROM tbl_name;# 正常執(zhí)行

    但如果 Session 3 想執(zhí)行 DDL 操作就會阻塞,因為此時 Session 1 已經(jīng)占用了 shared MDL,而 DDL 的執(zhí)行需要先獲取 exclusive MDL,因此無法正常執(zhí)行。

    # Session 3> ALTER TABLE tbl_name ADD COLUMN n INT;# 阻塞

    通過 show processlist 可以看到 ALTER 操作正在等待 MDL。

    +----+-----------------+------------------+------+---------+------+---------------------------------+-----------------+
    | Id | User            | Host             | db   | Command | Time | State                           | Info            |│----+-----------------+------------------+------+---------+------+---------------------------------+-----------------+
    | 11 | root            | 172.17.0.1:53048 | demo | Query   |    3 | Waiting for table metadata lock | alter table ... |+----+-----------------+------------------+------+---------+------+---------------------------------+-----------------+

    由于 exclusive MDL 的獲取優(yōu)先于 shared MDL,后續(xù)嘗試獲取 shared MDL 的操作也將會全部阻塞

    # Session 4> SELECT * FROM tbl_name;# 阻塞

    到這一步,后續(xù)無論是 DML 和 DDL 都將阻塞,直到 Session 1 提交或者回滾,Session 1 占用的 shared MDL 被釋放,后面的操作才能繼續(xù)執(zhí)行。

    上面這個問題主要有兩個原因:

    • Session 1 中的事務(wù)沒有及時提交,因此阻塞了 Session 3 的 DDL

    • Session 3 Online DDL 阻塞了后續(xù)的 DML 和 DDL

    對于問題 1,有些ORM框架默認(rèn)將用戶語句封裝成事務(wù)執(zhí)行,如果客戶端程序中斷退出,還沒來得及提交或者回滾事務(wù),就會出現(xiàn) Session 1 中的情況。那么此時可以在 infomation_schema.innodb_trx 中找出未完成的事務(wù)對應(yīng)的線程,并強(qiáng)制退出。

    > SELECT * FROM information_schema.innodb_trx\G*************************** 1. row ***************************trx_id: 421564480355704trx_state: RUNNINGtrx_started: 2022-05-01 014:49:41trx_requested_lock_id: NULLtrx_wait_started: NULLtrx_weight: 0trx_mysql_thread_id: 9trx_query: NULLtrx_operation_state: NULLtrx_tables_in_use: 0trx_tables_locked: 0trx_lock_structs: 0trx_lock_memory_bytes: 1136trx_rows_locked: 0trx_rows_modified: 0trx_concurrency_tickets: 0trx_isolation_level: REPEATABLE READtrx_unique_checks: 1trx_foreign_key_checks: 1trx_last_foreign_key_error: NULLtrx_adaptive_hash_latched: 0trx_adaptive_hash_timeout: 0trx_is_read_only: 0trx_autocommit_non_locking: 0trx_schedule_weight: NULL1 row in set (0.0025 sec)

    可以看到 Session 1 正在執(zhí)行的事務(wù)對應(yīng)的 trx_mysql_thread_id 為 9,然后執(zhí)行 KILL 9 即可中斷 Session 1 中的事務(wù)。
    對于問題 2,在查詢很多的情況下,會導(dǎo)致阻塞的 session 迅速增多,對于這種情況,可以先中斷 DDL 操作,防止對服務(wù)造成過大的影響。也可以嘗試在從庫上修改表結(jié)構(gòu)后進(jìn)行主從切換或者使用 pt-osc 等第三方工具。

    8 限制

    • 僅適用于InnoDB(語法上它可以與其他存儲引擎一起使用,如MyISAM,但MyISAM只允許algorithm = copy,與傳統(tǒng)方法相同);

    • 無論使用何種鎖(NONE,共享或排它),在開始和結(jié)束時都需要一個短暫的時間來鎖表(排它鎖);

    • 在添加/刪除外鍵時,應(yīng)該禁用 foreign_key_checks 以避免表復(fù)制;

    • 仍然有一些 alter 操作需要 copy 或 lock 表(老方法),有關(guān)哪些表更改需要表復(fù)制或表鎖定,請查看官網(wǎng);

    • 如果在表上有 ON … CASCADE 或 ON … SET NULL 約束,則在 alter table 語句中不允許LOCK = NONE;

    • Online DDL會被復(fù)制到從庫(同主庫一樣,如果 LOCK = NONE,從庫也不會加鎖),但復(fù)制本身將被阻止,因為 alter 在從庫以單線程執(zhí)行,這將導(dǎo)致主從延遲問題。

    讀到這里,這篇“MySQL DDL執(zhí)行方式Online DDL實例分析”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI