前言:建表即架構,一行 DDL 決定十年維護成本
CREATE TABLE 不只是定義欄位,更是資料型別、約束、索引、分區、字符集、引擎、壓縮、統計資訊的綜合決策。本文從實戰角度,構建「建表決策清單」,覆蓋 12 個關鍵決策點,附標準範本與反模式對照。
建表決策清單 12 項
| # | 決策點 | 核心原則 | 推薦配置 |
| 1 | 引擎 | 預設 InnoDB,特殊場景才考慮其他 | ENGINE=InnoDB |
| 2 | 字符集 | 全鏈路 utf8mb4,支援 Emoji/生僻字 | DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci |
| 3 | 主鍵 | 業務無關、單調遞增、盡量小 | BIGINT UNSIGNED AUTO_INCREMENT / UUID v7 / 雪花算法 |
| 4 | 欄位型別 | 剛好夠用、拒絕 TEXT/BLOB 入主表 | 參考「最小型別對照表」 |
| 5 | NOT NULL | 預設 NOT NULL,業務可空才允許 NULL | NOT NULL DEFAULT '' / 0 / '1970-01-01' |
| 6 | 預設值 | 業務預設值下推到資料庫層 | DEFAULT CURRENT_TIMESTAMP / DEFAULT 0 |
| 7 | 索引策略 | 查詢驅動建索引、複合索引最左前綴、覆蓋索引 | EXPLAIN 驗證每個慢查詢 |
| 8 | 外鍵 | 核心主檔硬約束、高併發明細軟約束 | 參考「外鍵實戰」文章 |
| 9 | 分區 | 億級資料、時間序列、歸檔刪除 | RANGE BY YEAR/MONTH / LIST BY REGION |
| 10 | 壓縮 | 歷史/唯讀/大字段表啟用 | ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8 |
| 11 | 註釋 | 每表每欄必寫,Data Dictionary 基石 | COMMENT='業務含義/單位/來源/範例' |
| 12 | 命名規範 | snake_case、單數名詞、無保留字、前綴隔離 | t_user, t_order_item, idx_user_phone |
最小型別對照表(省空間=省 I/O=快)
| 業務場景 | 推薦型別 | 範圍/長度 | 位元組 |
| 布林/開關 | TINYINT UNSIGNED | 0/1 | 1 |
| 狀態碼/枚舉(<256) | TINYINT UNSIGNED | 0-255 | 1 |
| 年齡/小整數 | SMALLINT UNSIGNED | 0-65535 | 2 |
| ID/自增主鍵(<40億) | INT UNSIGNED | 0-42億 | 4 |
| 分散式 ID/大量資料 | BIGINT UNSIGNED | 0-1800京 | 8 |
| 金額/精確小數 | DECIMAL(12,2) | ±999億.99 | 6 |
| 時間戳(秒級) | TIMESTAMP | 1970-2038 | 4 |
| 日期時間(年-秒) | DATETIME | 1000-9999 | 8 |
| 短字串(<255) | VARCHAR(N) | N+1/2 位元組 | 變長 |
| 長文本/JSON | TEXT/JSON | 64KB/4GB | 溢位頁 |
標準建表範本(電商訂單表)
CREATE TABLE `t_order` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '訂單ID,雪花算法生成',
`order_no` CHAR(32) NOT NULL COMMENT '業務訂單號,唯一',
`user_id` BIGINT UNSIGNED NOT NULL COMMENT '下單用戶ID',
`shop_id` INT UNSIGNED NOT NULL COMMENT '店鋪ID',
`status` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '狀態:0待付款 1已付款 2已發貨 3已完成 4已退款 5已取消',
`total_amount` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '訂單總金額(元)',
`pay_amount` DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '實付金額(元)',
`pay_time` DATETIME NULL COMMENT '支付時間',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
`deleted_at` DATETIME NULL COMMENT '軟刪除時間,為空表示未刪除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_user_status` (`user_id`, `status`),
KEY `idx_shop_created` (`shop_id`, `created_at`),
KEY `idx_deleted_at` (`deleted_at`)
) ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_general_ci
COMMENT='訂單主表,支援軟刪除、多維查詢、分區擴展'
ROW_FORMAT=DYNAMIC; -- 8.0 預設,支援長欄位溢位
反模式對照:別這樣寫
| 反模式 | 危害 | 正確範例 |
| 用 INT 存手機號 | 截斷前導 0、無法存 +86、數學運算無意義 | CHAR(11) / VARCHAR(20) |
| 用 FLOAT 存金額 | 精度丟失、對帳永遠不平 | DECIMAL(12,2) |
| 主鍵用 UUID v4 (CHAR(36)) | 隨機寫入導致 B+ 樹分裂、主鍵 36 字節、二級索引膨脹 | BIGINT 雪花 / UUID v7 (BINARY(16)) |
| 大字段放主表 | 緩衝池污染、掃描慢、備份大 | 拆分到獨立表 1:1 關聯 |
| 無 NOT NULL、無 DEFAULT | NULL 語義模糊、應用層判斷不一、索引失效 | NOT NULL DEFAULT 业务默认值 |
| 索引建在低選擇性欄位 | 性別/狀態/布林值索引幾乎不生效 | 複合索引包含高選擇性欄位 |
驗證清單:建表後必跑
-- 1. 結構確認
SHOW CREATE TABLE t_order\G
-- 2. 索引確認
SHOW INDEX FROM t_order;
-- 3. 插入測試
INSERT INTO t_order (order_no, user_id, shop_id, status, total_amount, pay_amount)
VALUES ('ORD202401010001', 1001, 2001, 1, 299.00, 299.00);
-- 4. 查詢計畫驗證
EXPLAIN SELECT * FROM t_order WHERE user_id=1001 AND status=1;
-- type=ref, key=idx_user_status, rows=1 → 完美
-- 5. 空間佔用
SELECT TABLE_NAME, DATA_LENGTH, INDEX_LENGTH, DATA_FREE
FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='t_order';
結語
建表是一次性投資、長期收益的高杠桿動作。用「決策清單」強迫思考、「最小型別」省資源、「標準範本」落地規範、「反模式」避免踩坑、「驗證清單」確保品質。將建表標準化為團隊共識文檔、Code Review 必檢項、CI/CD 自動檢查規則,才能在規模增長時保持架構健康。