首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL InnoDB 建表指定初始表空間大小:源碼級 Patch 實現 DATAFILE_INITIAL_SIZE 參數

     編輯:MySQL 資料庫相關知識

背景問題

InnoDB 寫密集場景下,B+ 樹頁面分裂導致表空間文件 (`.ibd`) 頻繁擴展。每次擴展需獲取 **Mutex** 保護文件元數據,造成**性能抖動 (Jitter)**,嚴重影響寫入吞吐。

解決思路

若預知表數據最終大小,**預先分配 (Pre-extend) 表空間文件**,避免運行時擴展爭用 Mutex。InnoDB 內部由 `FIL_IBD_FILE_INITIAL_SIZE` 常量控制初始大小 (默認較小),通過 `fil_create_new_single_table_tablespace()` 初始化。

Patch 方案:引入 `DATAFILE_INITIAL_SIZE` 表選項

修改 MySQL 源碼 (基於 5.5/5.6 版本),在 `CREATE TABLE` 語法中支持:

CREATE TABLE test (
  id INT PRIMARY KEY,
  data VARCHAR(100)
) ENGINE=InnoDB DATAFILE_INITIAL_SIZE=100000;  -- 頁數 (16KB/頁),約 1.5GB

核心代碼變更點 (5 個文件)

1. `storage/innobase/dict/dict0crea.c` - 表空間創建傳遞初始大小

// 原代碼
error = fil_create_new_single_table_tablespace(
    space, path_or_name, is_path,
    flags == DICT_TF_COMPACT ? 0 : flags,
    FIL_IBD_FILE_INITIAL_SIZE);  // 固定常量

// 修改後:優先使用表定義的 datafile_initial_size,小於默認值則用默認值
error = fil_create_new_single_table_tablespace(
    space, path_or_name, is_path,
    flags == DICT_TF_COMPACT ? 0 : flags,
    table->datafile_initial_size < FIL_IBD_FILE_INITIAL_SIZE ? 
        FIL_IBD_FILE_INITIAL_SIZE : table->datafile_initial_size);

2. `storage/innobase/handler/ha_innodb.cc` - 從表定義讀取參數

// create_table_def 階段保存參數
form->datafile_initial_size = create_info->datafile_initial_size;

// 建表時傳遞給 dict_table
table->datafile_initial_size = form->datafile_initial_size;

3. `storage/innobase/include/dict0mem.h` - 字典結構體新增字段

struct dict_table_struct {
    // ... 現有字段
    uint datafile_initial_size;  // 新增:表空間初始大小 (頁數)
};

4. `sql/table.h` - SQL 層表定義新增字段

class TABLE {
    // ...
    uint datafile_initial_size;  // CREATE_INFO 傳遞用
};

5. `sql/sql_yacc.yy` - 詞法/語法解析新增關鍵字

%token DATAFILE_INITIAL_SIZE_SYM

// create_option 規則中添加
| DATAFILE_INITIAL_SIZE_SYM opt_equal ulonglong_num
{
    if ($3 > UINT_MAX32) 
        Lex->create_info.datafile_initial_size = UINT_MAX32;
    else
        Lex->create_info.datafile_initial_size = $3;
    Lex->create_info.used_fields |= HA_CREATE_USED_DATAFILE_INITIAL_SIZE;
}

// 關鍵字註冊
{ "DATAFILE_INITIAL_SIZE", SYM(DATAFILE_INITIAL_SIZE_SYM) },

6. `sql/handler.h` - HA_CREATE_INFO 傳遞標誌

#define HA_CREATE_USED_DATAFILE_INITIAL_SIZE (1L << 22)
// HA_CREATE_INFO 結構體新增
uint datafile_initial_size;

7. `sql/lex.h` - 詞法關鍵字映射

{ "DATAFILE_INITIAL_SIZE", SYM(DATAFILE_INITIAL_SIZE_SYM) },

效果驗證

-- 建表指定 10 萬頁 (約 1.6GB)
CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB DATAFILE_INITIAL_SIZE=100000;

-- 查看文件大小 (Linux)
ls -lh /var/lib/mysql/db/t1.ibd
# -rw-r----- 1 mysql mysql 1.6G ... t1.ibd  <-- 瞬時創建 1.6GB 文件,無運行時擴展

官方演進與現狀

  • Patch 提交至 MySQL Bug #67792。
  • **MySQL 5.7+ / 8.0 已原生支持類似功能**:`CREATE TABLE ... TABLESPACE ...` / `innodb_data_file_path` 配置 / `AUTOEXTEND_SIZE` (8.0.23+)。
  • 現代版本推薦使用 **`AUTOEXTEND_SIZE`** 定義擴展步長,而非初始大小:
-- MySQL 8.0.23+
CREATE TABLE t1 (id INT PRIMARY KEY) 
TABLESPACE ts_1 
AUTOEXTEND_SIZE=64M;  -- 每次擴展 64MB,減少擴展頻率

適用場景與避坑

場景建議
歷史版本 (5.5/5.6) 高寫入大表應用此 Patch 或預先 `INSERT` 大量佔位數據撐大文件
MySQL 5.7+使用 `innodb_data_file_path` 規劃系統表空間 / 文件表空間預分配
MySQL 8.0.23+**首選 `AUTOEXTEND_SIZE`** 控制擴展粒度,配合 `innodb_flush_log_at_trx_commit=2` 降低抖動
雲托管 RDS通常不支持文件級控制,依賴實例規格 IOPS

總結

本文展示了從源碼層面解決 InnoDB 表空間擴展抖動的經典 Patch:**引入 `DATAFILE_INITIAL_SIZE` 表選項,修改詞法解析、SQL 層傳遞、InnoDB 字典結構、表空間創建入口**。雖現代版本已有更完善原生方案 (`AUTOEXTEND_SIZE`),但此 Patch 體現的**「預分配避免運行時鎖競爭」**核心思想,仍是數據庫內核優化的通用原則。

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