首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL CREATE TABLE 內核流程源碼級解析:從 SQL 解析到存儲引擎落盤

     編輯:MySQL 綜合教學

前言

CREATE TABLE 看似簡單,實則觸發 MySQL Server 層與存儲引擎層複雜協作:語法解析、權限檢查、鎖管理、FRM/IBD 文件創建、數據字典註冊、索引構建、外鍵處理。本文基於 MySQL 5.1/5.5/5.7/8.0 核心調用鏈,深度解析三種創建模式 (CREATE TABLE、CREATE TABLE ... LIKE、CREATE TABLE ... SELECT) 的內核流程差異,為內核研發、極致優化、故障診斷提供源碼級視角。


一、 通用調用棧 (MySQL 5.7/8.0 核心路徑)


do_command() (sql_parse.cc)
  → dispatch_command()
    → mysql_parse()                      -- 語法解析、AST 生成
      → mysql_execute_command()
        → mysql_create_table() (sql_table.cc)
          → mysql_create_table_no_lock() -- 核心邏輯入口
            → ha_create_table() (handler.cc)  -- 存儲引擎接口
              → handler::ha_create()
                → ha_innodb::create() (ha_innodb.cc)
                  → create_table_def()        -- InnoDB 內部表定義構建
                    → row_create_table_for_mysql() (row0mysql.c)
                      → dict_table_create()   -- 數據字典註冊
                      → create_clustered_index_when_no_primary()  -- 隱式主鍵生成
                      → row_create_index_for_mysql()  -- 二級索引創建

關鍵鎖與同步點

  • LOCK_open:保護表緩存、防止並發 DDL 衝突。
  • MDL (Metadata Lock):5.5+ 引入,MDL_EXCLUSIVE 阻塞並發 DML/DDL,MDL_SHARED_READ 允許並發查詢。
  • creating_table++:統計計數器 Com_create_table。

二、 三種創建模式深度對比

階段 CREATE TABLE CREATE TABLE ... LIKE CREATE TABLE ... SELECT
解析入口mysql_create_table()mysql_create_like_table()mysql_execute_command() → handle_select()
源表處理無open_tables() 打開源表,持有 LOCK_open 直到 FRM 複製完成open_and_lock_tables() 加表鎖 (讀鎖) 直到整個 Statement 完成
FRM 創建mysql_create_frm() 根據 AST 構建my_copy() 直接複製源表 .frm 文件 (或 mysql_create_like_schema_frm() 處理 I_S 表)create_table_from_items() 根據 SELECT 列項構建 FRM,在 JOIN::prepare() 階段完成
存儲引擎創建ha_create_table() 正常流程同正常流程 (引擎層無感知 LIKE)同正常流程,但在 JOIN::exec() 階段邊查詢邊寫入 (select_insert::send_data() → write_record())
數據填充無無SELECT 結果集逐行寫入新表 (NLJ 內層)
鎖持有時間短 (僅 FRM 創建階段)中 (持有 LOCK_open 複製 FRM)**長** (持有源表讀鎖直到所有數據寫入完成)
事務性非事務 (隱式提交)非事務**隱式事務** (5.7+ 支援原子性:失敗自動回滾創建的表)

三、 關鍵內核函數職責解析

1. mysql_create_frm() — Server 層表定義持久化

  • 分配 TABLE 對象,填充 Field 數組、KEY 數組、PARTITION_INFO。
  • 調用 pack_record() 序列化為二進制格式寫入 .frm (5.7) 或數據字典表 (8.0 mysql.tables, mysql.columns, mysql.indexes...)。
  • 處理分區表、外鍵定義序列化。

2. ha_innodb::create() — InnoDB 表創建入口

  • 調用 create_table_def() 構建 dict_table_t 內存對象 (列、索引、外鍵、約束)。
  • 若無顯式主鍵:create_clustered_index_when_no_primary() 自動生成 6 字節 GEN_CLUST_INDEX (隱式 Row ID) 作為聚簇索引。**這是為什麼強烈建議顯式定義主鍵** — 隱式主鍵無業務含義、佔用空間、不可被引用、熱點爭用風險。
  • 調用 row_create_index_for_mysql() 創建二級索引 B+ 樹 (初始為空 Root Page)。
  • 註冊外鍵約束 row_table_add_foreign_constraints()。

3. CREATE TABLE ... SELECT 的特殊性 — 在 SELECT 準備階段創建表

此模式邏輯極其巧妙:

  1. JOIN::prepare() 初始化 select_result 為 select_create 類型 (繼承鏈:select_result ← select_result_interceptor ← select_insert ← select_create)。
  2. select_create::prepare() → create_table_from_items() → mysql_create_table_no_lock() **在優化器準備階段就創建好目標表結構**。
  3. JOIN::exec() → do_select() → sub_select() → evaluate_join_record() → end_send() → select_insert::send_data() → write_record() → ha_write_row() **邊查詢邊寫入**。

啟示:MySQL 將 CREATE TABLE ... SELECT 視為一種特殊的「查詢」,其輸出目標是新建表而非網絡流。這解釋了為什麼它需要對源表加鎖 (保證 SELECT 一致性)、且在 5.7 前非原子 (失敗留下空表)。


四、 MySQL 8.0 數據字典革命:.frm 文件消失了

項目 5.7 及之前 8.0+
表定義存儲.frm 文件 (每表一個,Server 層管理)InnoDB 系統表空間 mysql.ibd 內的數據字典表 (mysql.tables, mysql.columns, mysql.indexes, mysql.foreign_keys...)
DDL 原子性無 (中間狀態可見、崩潰留孤兒文件)原子 DDL (事務化,失敗自動回滾,無可見中間態)
崩潰恢復需修復 .frm/.ibd 不一致僅靠 Redo Log,字典表同樣受 WAL 保護
鎖機制LOCK_open + 文件系統鎖MDL (Metadata Lock) 統一管理,無文件操作
INFORMATION_SCHEMA臨時表填充 (慢、佔內存)直接查詢數據字典表 (極快、實時)

8.0 調用鏈變化:mysql_create_table() → dd::create_table() (Data Dictionary API) → dd::Table::create() → InnoDB 內部創建。Server 層不再操作 .frm 文件。


五、 典型故障診斷與源碼定位

現象 可能源碼定位點 排查方向
CREATE TABLE 卡住無響應mdl_lock_wait(), lock_wait_suspend_thread()SHOW ENGINE INNODB STATUS 看鎖等待、MDL 持有者、是否有長事務阻塞
ERROR 1005 (HY000): Can't create table (errno: 150)row_table_add_foreign_constraints()外鍵引用表/列不存在、類型不匹配、引用表非 InnoDB、缺少索引
CREATE TABLE ... SELECT 留下空表mysql_execute_command() 錯誤處理分支5.7 前無原子性,SELECT 失敗不回滾創建;8.0 原子 DDL 解決
隱式主鍵導致熱點爭用dict_table_create() → create_clustered_index_when_no_primary()顯式定義業務主鍵 (自增/UUID/雪花 ID),避免 GEN_CLUST_INDEX 尾部插入熱點

六、 總結

CREATE TABLE 是 MySQL Server 層 (SQL 解析、權限、MDL、數據字典) 與存儲引擎層 (InnoDB 表定義、聚簇/二級索引構建、外鍵註冊) **邊界最清晰、交互最複雜** 的 DDL 操作之一。理解三種模式的調用鏈差異、8.0 原子 DDL 的架構重構、隱式主鍵的性能代價,能讓你在內核研發、極致優化、疑難故障診斷中佔據源碼級視角優勢。**最佳實踐:永遠顯式定義主鍵、避免 CREATE TABLE ... SELECT 大表操作 (改用 xtrabackup/導入導出)、升級 8.0 享受原子 DDL 與高性能數據字典。**

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved