Innodb加索引,這個時候會鎖表嗎?
在 MySQL 5.6 之前,InnoDB 在索引構建期間會對表進行排它鎖定,這意味著其他會話無法讀取或修改表中的數據,從而導致長時間阻塞和性能問題。
自 MySQL 5.6 起,InnoDB 開始采用一種名為“Online DDL”的技術,允許在不阻塞其他會話的情況下創建或刪除索引。Online DDL 針對不同的操作提供了多種實現方式,包括 COPY、INSTANT 和 INPLACE。
由于 DDL 涉及多種操作,如索引創建、字段增加和外鍵添加等,因此不同操作的支持方式也各不相同。具體支持方式可參考 MySQL 官方文檔(https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html)。
以索引創建為例:
圖片
從上文可見,當我們創建、刪除或重命名索引時,會采用“in place”的模式。
需要注意的是,盡管 Online DDL 能夠減少鎖定時間和對性能的影響,但在索引構建期間仍可能出現鎖定和阻塞情況。例如,在添加索引時,如果表中存在大量未提交的事務,則需要等待這些事務提交后才能開始索引構建。因此,建議在非高峰時段進行此類操作,以避免影響用戶的正常使用。在執行任何 DDL 操作之前,最好進行充分的測試和規劃,并確保有備份和回滾計劃,以應對意外情況。
擴展知識
什么是 Online DDL
DDL,即數據定義語言(Data Definition Language),用于定義數據庫結構的操作。DDL 操作包括創建、修改和刪除數據庫中的表、索引、視圖、約束等數據庫對象,而不涉及實際數據的操作。以下是一些常見的 DDL 操作:
- CREATE(創建)
- ALTER(修改)
- DROP(刪除)
- TRUNCATE(截斷)
相對應的是 DML,即數據操作語言(Data Manipulation Language),用于操作數據。包括我們常用的 INSERT、DELETE 和 UPDATE 等操作。
在 MySQL 5.6 之前,所有的 ALTER 操作實際上都會阻塞 DML 操作,例如添加或刪除字段、添加或刪除索引等,都會導致表被鎖定。
然而,在 MySQL 5.6 中引入了 Online DDL,它是 MySQL 5.6 提出的一種加速 DDL 的方案,旨在盡可能保證 DDL 期間不會阻塞 DML 操作。但需要注意的是,并非所有的 DDL 語句都會利用 Online DDL 進行加速。
Online DDL 的優點在于可以減少阻塞,它是 MySQL 內置的一種優化手段。但需注意的是,在 DDL 開始和結束階段,都需要獲取 MDL 鎖,如果在獲取鎖時存在未提交的事務,則 DDL 可能因為鎖定失敗而被阻塞,從而影響性能。
此外,如果 Online DDL 操作失敗,其回滾操作可能會造成較高的成本。長時間運行的 Online DDL 操作也可能導致主從同步的延遲。
DDL 算法
在 MySQL 5.6 支持 Online DDL 之前,存在兩種 DDL 算法,分別是 COPY 和 INPLACE。
我們可以使用以下 SQL 來指定 DDL 算法:
ALTER TABLE paidaxing_ddl_test ADD PRIMARY KEY (id) ,ALGORITHM=INPLACE,LOCK=NONE
copy 算法原理
- 創建一張臨時表。
- 對原表加共享 MDL 鎖,阻止對原表的寫操作,僅允許查詢操作。
- 逐行將原表數據拷貝到臨時表中,且無需進行排序。
- 數據拷貝完成后,將原表鎖升級為排他 MDL 鎖,阻止對原表的讀寫操作。
- 對臨時表進行重命名操作,并創建索引,完成 DDL 操作。
INPLACE 算法原理
INPLACE 算法是在 MySQL 5.5 中引入的,旨在優化索引的創建和刪除過程的效率。其原理是盡可能地使用原地算法進行 DDL 操作,而不是重新創建或復制表。
- 創建索引數據字典。
- 對原表加共享 MDL 鎖,阻止對原表的寫操作,只允許查詢操作。
- 根據聚集索引的順序,查詢表中的數據,并提取所需的索引列數據。將提取的索引數據進行排序,并插入到新的索引頁中。
- 等待當前表的所有只讀事務提交。
- 索引創建完成。
MySQL 中的 INPLACE 算法實際上分為兩種:
- inplace-no-rebuild:對二級索引的增刪改查、修改變長字段長度(例如:varchar)、重命名列名等操作都不需要重建原表。
- inplace-rebuild:修改主鍵索引、增加或刪除列、修改字符集、創建全文索引等操作需要重建原表。
OnlineDDL 算法
前面提到,ALGORITHM 可以指定 DDL 操作的算法,目前主要支持以下幾種:
- COPY 算法
- INPLACE 算法
- INSTANT 算法:MySQL 8.0.12 引入的新算法,目前只支持添加列等少量操作。它利用了 8.0 新的表結構設計,可以直接修改表的元數據,省去了重建原表的過程,從而極大地縮短了 DDL 語句的執行時間。對于其他類型的改表語句,默認使用 inplace 算法。關于 INSTANT 支持的場景可參考官方文檔 [Online DDL Operations]:https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html。
- DEFAULT:如果不指定 ALGORITHM,MySQL 會自行選擇默認算法。它優先考慮 INSTANT,其次是 INPLACE,然后是 COPY。
以下是 MySQL 官網上給出的 Online DDL 對索引操作的支持情況:
圖片
OnlineDDL 的原理
以下是 Online DDL 的整體步驟,主要分為 Prepare 階段、DDL 執行階段以及 Commit 階段。
Prepare 階段:
- 創建臨時 frm 文件。
- 加 EXCLUSIVE-MDL 鎖,阻止讀寫操作。
- 根據 ALTER 類型,確定執行方式(copy/online-rebuild/online-norebuild)。需要注意,如果使用 copy 算法,則不是 Online DDL。
- 更新數據字典的內存對象。
- 分配 row_log 對象,記錄 Online DDL 過程中增量的 DML。
- 生成新的臨時 idb 文件。
Execute 階段:
- 降級 EXCLUSIVE-MDL 鎖為 SHARED-MDL 鎖,允許讀寫操作。
- 掃描原表聚集索引的每一條記錄。
- 遍歷新表的聚集索引和二級索引,逐一處理。
- 根據原表中的記錄構造對應的索引項。
- 將構造的索引項插入 sort_buffer 塊排序。
- 將 sort_buffer 塊更新到新表的索引上。
- 記錄 Online DDL 執行過程中產生的增量(online-rebuild)。
- 重放 row_log 中的操作到新表的索引上(online-not-rebuild 數據是在原表上更新)。
- 重放 row_log 中的 DML 操作到新表的數據行上。
Commit 階段:
- 升級到 EXCLUSIVE-MDL 鎖,阻止讀寫操作。
- 重做 row_log 中最后一部分增量。
- 更新 InnoDB 的數據字典表。
- 提交事務,寫 redo log。
- 修改統計信息。
- 重命名臨時 ibd 文件,frm 文件。
- 變更完成,釋放 EXCLUSIVE-MDL 鎖。
盡管 Prepare 階段和 Commit 階段也加了 EXCLUSIVE-MDL 鎖,但操作非常輕量,因此耗時較低。Execute 階段允許讀寫操作,并通過 row_log 記錄期間的變更數據記錄,最終應用這些變更到新表中,從而實現 Online DDL 的效果。