Skip to content

MySQL 表设计优化实战指南 ​

前言 ​

上一篇文章我们讲了「表设计范式」,核心是怎么把表设计得干净、规范。但光规范还不够——当数据量从几万涨到几百万、几千万,业务从单机变分布式时,一张"设计正确"的表照样会变慢。这时就需要表设计优化:在规范的基础上,通过数据字典、索引、拆分等手段,让表跑得快、好维护、扛得住并发。

本文围绕四个核心模块展开:

  • 数据字典维护:把表的"说明书"管理起来,让团队协作有章可循。
  • 索引维护:让查询从"全表扫"变成"精准定位"。
  • 垂直拆分:把一张"又宽又重"的表按字段拆开。
  • 水平拆分:把一张"行数爆炸"的表按行拆到多个库/表。

最后用一个电商订单表和用户信息表的完整案例,把优化前后的性能指标量化给你看。


一、优化总览:从评估到验证的四步闭环 ​

表设计优化不是"拍脑袋改字段",而是一个标准闭环。建议按下面四步走,每步都有明确的产出:

阶段核心动作产出物
评估找出慢查询、大表、热点字段,量化当前瓶颈慢查询日志、表大小统计、性能基线
规划确定优化手段(加索引/拆字段/分表),评估影响面优化方案、风险清单
实施数据字典、索引、拆分的具体落地建表 SQL、数据迁移脚本
验证对比优化前后指标,回滚预案性能对比报告

黄金原则:先有指标,再谈优化。 没有性能基线的优化都是耍流氓——你根本不知道优化到底有没有效果。所以本文每个案例都会给出优化前后可量化的指标。


二、数据字典维护 ​

2.1 数据字典的核心作用与维护边界 ​

数据字典就是数据库的"说明书":它记录每张表、每个字段的类型、含义、约束、注释,以及表与表之间的关联关系。

它的作用有三点:

  1. 统一认知:新人看到表,能快速明白每个字段是什么意思,不用追着老员工问。
  2. 约束规范:字段类型、长度、是否为空、默认值都有统一标准,避免"A 员工建的表字段叫 name,B 员工叫 username"。
  3. 变更留痕:字段的新增、修改、删除有记录,出了问题能追溯。

维护边界要分清:数据字典管的是表结构(Schema)和元数据,不是业务数据本身。它回答的问题是"这张表有什么字段、字段什么类型、外键指向哪",而不是"这张表里存了哪些具体数据"。

在 MySQL 里,可以用三种方式承载数据字典:

  • 官方自带:information_schema(只读元数据)、performance_schema(性能元数据)、mysql 库里的系统表。8.0 之前数据字典是零散的 .frm 文件,8.0 之后统一放进了 InnoDB 的 mysql.ibd。
  • 注释规范:在建表 SQL 里写 COMMENT,这是最轻量、最贴近业务的数据字典。
  • 第三方工具/文档:用专门的表结构文档工具(如 screw、tbls)自动生成 HTML/Word 版数据字典。

2.2 全流程标准化维护步骤 ​

数据字典的维护要形成固定流程,建议如下:

  1. 建表即建档:任何新建表,必须带 COMMENT 表注释和字段注释,不允许"裸字段"。
  2. 变更走评审:字段的新增/修改/删除,先提变更单,说明原因、影响面、是否需要数据迁移。
  3. 同步更新文档:改完表结构,立即同步数据字典文档(或重新生成工具导出),不能"表改了、文档没改"。
  4. 定期盘点:每月/每季度盘点一次,找出无注释字段、孤儿表、僵尸字段。
  5. 纳入版本管理:把建表 SQL(DDL)纳入 Git 管理,用 Flyway / Liquibase 等工具做数据库版本迁移。

2.3 字段类型、约束、注释、关联关系的规范要求 ​

这是数据字典落地最核心的部分。给出一套可以直接照抄的规范:

字段类型规范:

数据类别推荐类型说明
主键BIGINT UNSIGNED AUTO_INCREMENT不用业务字段当主键
短字符串(用户名等)VARCHAR(50)长度按需,不无脑 255
长文本TEXT / MEDIUMTEXT大段文字单独考虑拆表
金额DECIMAL(10,2)绝不用 FLOAT/DOUBLE
状态/类型TINYINT配合注释说明枚举含义
布尔TINYINT(1)0/1
时间DATETIME需要自动更新用 ON UPDATE
唯一标识CHAR(32)如 MD5、订单号等定长串

约束规范:

  • 主键:每张表必须有主键,优先自增 BIGINT。
  • 唯一约束:手机号、用户名、订单号等业务唯一字段必须加 UNIQUE KEY。
  • 非空约束:核心业务字段加 NOT NULL,避免出现"半吊子数据"。
  • 默认值:状态、数量等字段给合理 DEFAULT,减少插入时漏写。
  • 外键:可选(见下文),但关联列必须建索引。

注释规范:

  • 表注释:说明这张表的业务用途,如 COMMENT '订单主表'。
  • 字段注释:说明字段含义,枚举字段要把每个值含义写清楚,如 status TINYINT COMMENT '订单状态:0待支付 1已支付 2已发货 3已完成 4已取消'。
  • 索引注释:复杂索引说明用途。

关联关系规范:

  • 关联字段命名统一:外键统一叫 xxx_id,如 user_id、order_id。
  • 关联字段类型必须和被引用表主键类型完全一致(否则无法建外键、JOIN 时还会类型转换)。
  • 关联关系要在数据字典里明确标注"一对多 / 多对多 / 一对一"。

下面是一段规范化的建表示例,可以作为团队的"标准模板":

sql
CREATE TABLE `order` (
    id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    order_no     VARCHAR(32)     NOT NULL                COMMENT '订单号(业务唯一)',
    user_id      BIGINT UNSIGNED NOT NULL                COMMENT '下单用户ID(关联user.id)',
    user_name    VARCHAR(50)     NOT NULL                COMMENT '下单时用户名快照(反范式冗余)',
    total_amount DECIMAL(10,2)   NOT NULL DEFAULT 0.00   COMMENT '订单总金额(元)',
    status       TINYINT         NOT NULL DEFAULT 0      COMMENT '订单状态:0待支付 1已支付 2已发货 3已完成 4已取消',
    created_at   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (id),
    UNIQUE KEY uk_order_no (order_no),
    KEY idx_user_id (user_id),
    KEY idx_status_created (status, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='订单主表';

2.4 日常维护检查清单 ​

给团队做一个可勾选的检查清单,每周/每月过一遍:

  • [ ] 是否有新建表缺少表注释或字段注释?
  • [ ] 是否有字段该加 UNIQUE 却漏了,导致出现重复数据?
  • [ ] 是否有字段类型和关联主键不一致?
  • [ ] 是否有孤儿表(长期无人访问、无归属)未清理?
  • [ ] 是否有僵尸字段(代码已不用,但表里还留着)?
  • [ ] 枚举字段的注释是否和代码里的枚举定义一致?
  • [ ] 数据字典文档是否和线上表结构同步更新?
  • [ ] 是否有未纳入版本管理的 DDL 变更?

三、索引维护 ​

3.1 索引设计的底层逻辑 ​

索引的本质,是用额外的存储空间和写入维护成本,换取查询速度。它的底层是 B+ 树:查询时从根节点一路走到叶子,定位极快;代价是每次 INSERT/UPDATE/DELETE 都要同步维护索引结构。

所以索引设计的核心矛盾永远是:查询快了,写入慢了,还占了空间。设计时永远围绕"这个字段到底查不查、怎么查"来决策。

3.2 常见索引类型与适用场景 ​

索引类型说明适用场景
主键索引表的主键,唯一且非空每表必建,InnoDB 数据就是按主键聚簇存储
唯一索引值不重复,可为空手机号、用户名、订单号等业务唯一字段
普通索引最基础的非唯一索引高频查询、JOIN、排序字段
复合索引多个列组成的索引多条件组合查询,注意最左前缀
覆盖索引查询字段都在索引里,无需回表高频且只查少量字段的查询
前缀索引只索引长字符串的前 N 个字符长文本、URL 等,减少索引体积
全文索引针对文本的关键词检索文章、评论的模糊搜索

最左前缀原则是复合索引最重要的坑:复合索引 (a, b, c) 相当于同时建立了 a、(a,b)、(a,b,c) 三个索引,但无法单独支持 b、c、(b,c) 的查询。所以复合索引的字段顺序要按"最常被单独查询/区分度高的字段放最左"来设计。

3.3 索引创建、删除、优化的判断标准 ​

该建索引的判断标准(满足越多越该建):

  • 字段出现在 WHERE、JOIN、ORDER BY、GROUP BY 里。
  • 字段区分度高(不同值多,如用户 ID;区分度低的如性别"男/女"就不值得单建)。
  • 查询频率高、数据量大(小表全表扫也很快,不必建)。

该删索引的判断标准:

  • 完全没被使用(可通过 performance_schema 或慢查询日志排查)。
  • 冗余索引:被别的索引"前缀"覆盖了。比如有 (a,b) 索引,又单独建 a 索引,a 就是冗余的。
  • 区分度极低的索引:如给"性别"字段建索引,几乎没用还占空间。

判断区分度的方法:

sql
-- 计算字段区分度,越接近 1 越好
SELECT COUNT(DISTINCT user_id) / COUNT(*) AS selectivity FROM `order`;
-- 如果结果接近 1(如 0.9),值得建索引;接近 0(如 0.001),不值得

3.4 冗余索引、无效索引的识别与清理 ​

识别冗余索引:MySQL 没有直接命令,但可以用 SHOW INDEX 看索引列,人工判断。典型冗余:

sql
-- 冗余:idx_a 被 idx_a_b 覆盖
KEY idx_a (a),
KEY idx_a_b (a, b)   -- 有它,idx_a 就是多余的

清理方法:直接 ALTER TABLE t DROP INDEX idx_a;,然后观察慢查询无变化即可确认安全。

识别无效索引:从 performance_schema 看索引从未被使用:

sql
-- 开启索引使用统计(MySQL 5.6+)
SELECT * FROM sys.schema_unused_indexes;
-- 输出:哪些索引从未被使用,可考虑删除

sys.schema_unused_indexes 是 MySQL 自带的视图,直接列出从未用过的索引。注意要在业务高峰期跑一段时间后再看,避免"刚建还没用"被误判。

3.5 索引维护的自动化监控方案 ​

手动排查索引太累,建议做自动化监控:

  1. 慢查询日志 + pt-query-digest:定期分析慢查询,找出缺索引的 SQL。
  2. sys.schema_unused_indexes 定时巡检:写个脚本每天跑一次,把从未使用的索引推送到群里。
  3. sys.schema_redundant_indexes:MySQL 自带的冗余索引视图,自动识别冗余索引。
  4. EXPLAIN 审查机制:上线新 SQL 前强制 EXPLAIN,检查是否走索引、是否全表扫描。
  5. 告警阈值:当某张表大小超过阈值(如 100 万行)或某查询耗时超过阈值(如 1s),自动告警提醒优化。

四、垂直拆分 ​

4.1 核心原理与适用场景 ​

垂直拆分:把一张"又宽又重"的表,**按字段(列)**拆成多张表。

它的核心原理是:让热点数据更紧凑、让冷数据不拖累主表。一张表字段越多,单行数据越大,InnoDB 一页(默认 16KB)能装的行数就越少,查询时要读的页就越多,越慢。

适用场景:

  • 表字段特别多(几十上百个),其中大部分字段很少被查询。
  • 存在大字段(TEXT、BLOB),主表查询时被迫把大字段一起读出来。
  • 部分字段访问频率极高,部分字段几乎没人碰(冷热分明)。

4.2 拆分判断维度(冷热数据、访问频率、数据长度) ​

拆字段时,从三个维度判断:

  1. 冷热数据:把"高频访问的字段"放主表,"低频访问的字段"放扩展表。比如用户表里 id/username/phone 天天查,而 address/bio/avatar_url 偶尔才看。
  2. 访问频率:经常一起查的字段放一起,分开访问的字段分开。
  3. 数据长度:TEXT、BLOB 等大字段单独放一张表,主表保持"小而紧凑"。

4.3 实施步骤与数据同步方案 ​

以「用户信息表」为例,假设原来是一张大宽表:

sql
-- 原表:字段多,且包含大字段
CREATE TABLE user (
    id          BIGINT PRIMARY KEY AUTO_INCREMENT,
    username    VARCHAR(50),
    phone       VARCHAR(20),
    password    VARCHAR(100),
    avatar_url  VARCHAR(500),      -- 头像URL
    bio         TEXT,              -- 个人简介(大字段)
    address     VARCHAR(200),      -- 地址
    created_at  DATETIME
);

拆分后的两张表:

sql
-- 主表:只保留高频、小字段
CREATE TABLE user (
    id          BIGINT PRIMARY KEY AUTO_INCREMENT,
    username    VARCHAR(50),
    phone       VARCHAR(20),
    password    VARCHAR(100),
    created_at  DATETIME
);

-- 扩展表:低频、大字段
CREATE TABLE user_profile (
    user_id     BIGINT PRIMARY KEY,    -- 与 user.id 一一对应
    avatar_url  VARCHAR(500),
    bio         TEXT,
    address     VARCHAR(200)
);

实施步骤:

  1. 建好新表结构。
  2. 双写/回填:先把 user 的历史数据按字段分别回填到 user 和 user_profile。
  3. 数据同步:迁移期间让代码同时读写新表(或通过触发器/订阅 binlog 同步)。
  4. 切换读:读逻辑改为"先查主表,需要时再查扩展表"。
  5. 下线旧字段:验证无误后,删除旧表的大字段,完成切换。

数据同步方案(迁移过程中保持一致性):

  • 一次性回填 + 双写:迁移期间新旧表都写,切完后只写新表。
  • binlog 订阅:用 Canal 等工具订阅 binlog,实时同步到新表。
  • 触发器:在旧表上建 AFTER INSERT/UPDATE 触发器同步到新表(适合小规模)。

4.4 拆分后的关联查询优化策略 ​

拆表后查询"完整用户信息"需要 JOIN,优化策略:

  1. 主表优先:大多数场景只需要主表字段,避免 JOIN,性能反而更好。
  2. 按需加载:只有真正需要扩展字段时才 JOIN user_profile,且用 user_id 等值关联(走主键)。
  3. 一对一主键关联:user_profile.user_id 直接作为主键,JOIN 时走主键索引,极快。
sql
-- 只在需要扩展信息时才 JOIN
SELECT u.id, u.username, p.bio, p.address
FROM user u
LEFT JOIN user_profile p ON u.id = p.user_id
WHERE u.id = 100;

4.5 拆分的边界与不适合拆分的场景 ​

垂直拆分不是万能的,以下情况不要拆:

  • 字段本来就不多、访问都均匀:拆了徒增 JOIN,得不偿失。
  • 字段强关联、总是一起读写:比如订单金额和订单号,拆开反而增加复杂度。
  • 事务频繁涉及多字段:拆开后一个事务要跨表,增加锁和一致性问题。
  • 数据量很小:垂直拆分主要解决"行太大",如果数据量很小,收益可忽略。

拆分边界:垂直拆分一般只拆 1~2 次(主表 + 扩展表),不要拆成七八张碎片表,否则 JOIN 复杂度失控。


五、水平拆分(分库分表) ​

5.1 核心逻辑 ​

水平拆分:把一张"行数爆炸"的表,**按行(记录)**拆分到多个表或多个库。每个分片结构完全相同,只是数据不同。

它解决的是垂直拆分解决不了的问题:单表数据量太大。当单表超过千万级,即使索引再好,B+ 树层级变高、单表操作也会成为瓶颈,此时只能"把一张大表切成多张小表"。

先分清两个概念:

  • 分表:同一个库里,把一张表拆成多张表(如 order_0、order_1)。
  • 分库:拆到不同数据库实例(如 db_0、db_1),能进一步提升并发承载能力。

5.2 常用分片策略与适用场景 ​

分片策略原理优点缺点适用场景
哈希分片对分片键取模/一致性哈希数据分布均匀范围查询需跨片用户、订单等无范围查询需求
范围分片按分片键范围划分(如时间、ID 段)范围查询友好可能数据倾斜按时间归档的日志、订单
列表分片按枚举值划分(如地区)业务清晰分布可能不均按地区、业务线划分

哈希分片示例(取模):

sql
-- 按 user_id % 4 分到 4 张表
CREATE TABLE order_0 (...) ;  -- user_id % 4 = 0
CREATE TABLE order_1 (...) ;  -- user_id % 4 = 1
CREATE TABLE order_2 (...) ;  -- user_id % 4 = 2
CREATE TABLE order_3 (...) ;  -- user_id % 4 = 3

范围分片示例(按时间):

sql
-- 按订单创建时间分表
CREATE TABLE order_202401 (...) ;  -- 2024年1月
CREATE TABLE order_202402 (...) ;  -- 2024年2月

5.3 分布式 ID 生成方案 ​

分表后,原来单表自增主键 AUTO_INCREMENT 就不能用了(多个分片会生成重复 ID)。常见方案:

方案原理优点缺点
雪花算法(Snowflake)时间戳+机器号+序列号组成 64 位 ID趋势递增、不依赖 DB、高性能依赖机器时钟
数据库号段每次从 DB 取一段 ID 区间简单、可控依赖 DB,有单点
Redis 自增INCR 命令生成 ID简单快速依赖 Redis,需持久化
美团 Leaf / 滴滴 Tinyid号段+双 buffer 优化高性能、高可用需引入组件

推荐:大多数场景用雪花算法最省心,趋势递增(对索引友好)、全局唯一、不依赖外部服务。

5.4 跨分片查询优化 ​

分片后最大的坑是跨片查询。优化策略:

  1. 查询必须带分片键:设计分片时,保证绝大多数查询都带分片键(如用户查询自己的订单,一定带 user_id),这样能精准路由到单个分片。
  2. 避免跨片聚合:COUNT、SUM 等聚合如果跨片,要么在各片分别算再合并(应用层归并),要么用专门的分析库。
  3. 冗余反查表:如果业务要"按订单号查订单",但分片键是 user_id,就需要一张 订单号→user_id 的映射表(或 ES 索引)来先找到 user_id,再定位分片。
  4. 全局表(广播表):像"地区、字典"这类小表,每个库都放一份,避免跨库 JOIN。

5.5 事务处理方案 ​

分库分表后,跨库事务无法用本地事务保证。方案:

  1. 尽量避免跨片事务:通过合理的分片键设计,让一笔业务的数据落在同一个分片,走本地事务。
  2. 最终一致性(推荐):用本地消息表 + 可靠消息(如 RocketMQ 事务消息)保证最终一致,不追求强一致。
  3. 分布式事务框架:如 Seata(AT/TCC/SAGA 模式),但引入成本和性能开销较大,能不用尽量不用。

核心结论:分库分表后,优先用"单分片本地事务 + 消息最终一致",而不是一上来就上分布式事务框架。

5.6 分片规则的扩展与调整 ​

分片一旦上线,扩展是个难题(尤其哈希取模,加片会导致所有数据重新路由)。应对方法:

  1. 预分片:一开始就按 2 的幂次分片(如 128 片),前期把多个分片放同一个库,后期再平滑迁移到独立库。
  2. 一致性哈希:加节点时只影响相邻节点,减少数据迁移。
  3. 倍数扩容:4 片扩到 8 片,旧数据只需按规则拆分迁移一半。
  4. 双写 + 灰度迁移:扩容期间双写新旧片,数据逐步搬迁,验证无误后切读。

六、其他优化方向(扩展) ​

除了四大核心模块,还有几个值得一起做的优化:

6.1 字段类型优化 ​

  • 能用 TINYINT 不用 INT,能用 INT 不用 BIGINT,减少存储和索引体积。
  • VARCHAR 长度按需设置,过长的定义会让内存排序更耗资源。
  • 定长字符串用 CHAR,不定长用 VARCHAR。
  • 枚举优先用 TINYINT + 注释,而不是 ENUM 类型(ENUM 修改枚举值代价大)。

6.2 大字段拆分 ​

TEXT、BLOB 字段即使不经常读,也会拖慢主表。两种处理:

  • 拆到扩展表(同垂直拆分)。
  • 用 SUBSTRING 前缀查询 + 文件/对象存储:大文本、图片二进制直接放 OSS,表里只存 URL。

6.3 归档与冷热分离 ​

历史数据(如 3 年前的订单)访问率极低,却占着主表。做法:

  • 定期把冷数据归档到历史表(order_archive)。
  • 或结合范围分片,把冷数据分到独立的冷片,用更低成本的存储。

6.4 SQL 与参数优化 ​

  • 避免 SELECT *,只查需要的字段(配合覆盖索引)。
  • 避免在索引列上做函数运算(WHERE DATE(created_at) = ... 会让索引失效)。
  • 用 EXPLAIN 审查执行计划,关注 type(最好 ref/const,避免 ALL 全表扫)。
  • 避免大事务、长事务,减少锁持有时间。

七、实战案例:电商订单表与用户信息表 ​

下面把前面讲的内容,串到一个完整的电商案例里。

7.1 案例一:用户信息表——垂直拆分 ​

优化前:一张大宽表,字段 30+,包含头像 URL、个人简介等大字段,单行约 2KB。

sql
CREATE TABLE user (
    id          BIGINT PRIMARY KEY AUTO_INCREMENT,
    username    VARCHAR(50),
    phone       VARCHAR(20),
    password    VARCHAR(100),
    avatar_url  VARCHAR(500),
    bio         TEXT,
    address     VARCHAR(200),
    -- ... 还有 20 多个低频字段
    created_at  DATETIME
);

痛点:用户列表页只查 id/username/phone,但因为行很大,InnoDB 一页装不了几行,全表扫时 IO 巨大。

优化后:拆成主表 + 扩展表(见 4.3)。

性能对比(量化):

指标优化前优化后提升
单行大小~2KB主表 ~300B缩小约 85%
用户列表查询(10 万行)380ms45ms快约 8 倍
每页可缓存行数~8 行~50 行缓冲池命中率大幅提升
存储占用3.2GB主表 0.6GB + 扩展表 2.4GB主表瘦身,热点查询不再读大字段

7.2 案例二:订单表——水平拆分 ​

优化前:单表 order 增长到 5000 万行,单表约 40GB,按 user_id 查询已经要 2~3 秒。

分片设计:以 user_id 为分片键,哈希取模分 16 片。

sql
-- 分片键:user_id % 16
-- 建 16 张结构相同的表 order_0 ~ order_15
CREATE TABLE order_0 (
    id           BIGINT NOT NULL COMMENT '雪花算法生成',
    order_no     VARCHAR(32) NOT NULL,
    user_id      BIGINT NOT NULL,        -- 分片键
    total_amount DECIMAL(10,2),
    status       TINYINT,
    created_at   DATETIME,
    PRIMARY KEY (id),
    UNIQUE KEY uk_order_no (order_no),
    KEY idx_user_id (user_id)
) COMMENT '订单表_分片0';
-- order_1 ~ order_15 结构同上

主键改造:AUTO_INCREMENT 改为雪花算法生成全局唯一 ID。

查询路由:SELECT * FROM order_{user_id % 16} WHERE user_id = ?,精准命中单分片。

跨片查询处理:用户列表页按 user_id 查,天然带分片键,无需跨片;后台按 order_no 查单条时,先查一张 order_no → user_id 的映射(Redis/ES),再定位分片。

性能对比(量化):

指标优化前优化后提升
单表行数5000 万单分片约 300 万缩小 16 倍
单表大小40GB单分片约 2.5GB缩小 16 倍
按 user_id 查询2.8s45ms快约 60 倍
并发承载(TPS)~800~12000提升约 15 倍
单次写入锁竞争高(单表热点)低(分散到 16 片)显著缓解

7.3 从评估到验证的完整落地步骤 ​

把上面两个案例串成一条标准流程:

1. 评估

sql
-- 找大表
SELECT table_name, table_rows, ROUND(data_length/1024/1024/1024, 2) AS data_gb
FROM information_schema.tables
WHERE table_schema = 'shop' ORDER BY data_length DESC;

-- 找慢查询:开启慢查询日志后分析
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;

2. 规划:确定 user 表做垂直拆分、order 表做水平拆分,明确分片键 user_id、分片数 16。

3. 实施:建新表 → 回填数据 → 双写 → 切读 → 下线旧表(见 4.3、5.6)。

4. 验证:用压测工具(sysbench / jmeter)跑基准测试,对比上表的性能指标;同时用 EXPLAIN 确认关键 SQL 都走索引、命中单分片。

5. 回滚预案:保留旧表一段时间,一旦新结构出问题,切回旧表读即可。


八、总结:一套可落地的优化方法论 ​

把全文浓缩成一套可直接执行的优化指南:

四大模块的定位:

模块解决什么问题一句话原则
数据字典维护表结构混乱、难维护建表即建档,变更留痕
索引维护查询慢高频查询字段才建索引,冗余索引及时清
垂直拆分表太宽、行太大冷热分离,大字段独立
水平拆分表太大、行太多带分片键路由,避免跨片

核心原则汇总:

  1. 先有指标,再优化:优化前必须记录性能基线,否则无法验证效果。
  2. 数据字典是地基:规范的类型、约束、注释,是一切优化的前提。
  3. 索引宁缺毋滥:区分度低、不常用、被覆盖的索引都是负担。
  4. 垂直拆分解"宽",水平拆分解"大":别用错方向。
  5. 拆分要带分片键设计:水平拆分前先想清楚"绝大多数查询怎么路由"。
  6. 能用单分片事务,就不用分布式事务:通过分片键设计把业务圈在一个分片内。
  7. 一切优化都可量化:查询耗时、存储占用、并发 TPS,用数据说话。

记住:优化不是炫技,而是用最小的改动,解决最痛的瓶颈。评估清楚再动手,指标验证再收工,这才是成熟的表设计优化。