MySQL高性能优化合集

一、数据库命名规范

1、通用规则

(1)字符与格式

  • 仅使用小写字母、数字、下划线(_) ,禁止使用大写(MySQL 在 Linux 下大小写敏感,统一小写避免歧义)、特殊字符(@、#、$ 等)、空格;
  • 命名必须以小写字母开头,禁止以数字 / 下划线开头(避免与 MySQL 内置函数 / 关键字冲突);
  • 长度控制:库 / 表名≤32 字符,字段名≤64 字符(适配 MySQL 标识符长度限制),避免过长(如user_order_2025_history而非user_order_in_2025_year_payment_history)。

(2)语义化原则

  • 命名必须 "见名知意",使用业务术语(而非拼音 / 英文缩写,除非是行业通用缩写);
  • 禁止拼音 + 英文混用(如yonghu_info❌,user_info✅),禁止无意义缩写(如u_i❌,user_info✅),通用缩写需统一(如id= 标识、num= 数量、cnt= 计数、addr= 地址)。

(3)关键字规避

  • 禁止使用 MySQL 保留字(如userorderdatetable等),若必须使用需用反引号(`)包裹(不推荐);
  • 示例:order表改为order_infouser表改为user_base

(4)下划线分隔

  • 多单词命名用下划线分隔(蛇形命名法),禁止驼峰(如userInfo❌,user_info✅)。

2、具体规范

(1)数据库(Schema)命名

  • 格式:业务域_模块名[_环境标识]
  • 说明:
    • 业务域:核心业务模块(如mall= 电商、erp= 企业资源、log= 日志);
    • 模块名:细分功能(如order= 订单、user= 用户、pay= 支付);
    • 环境标识(可选):测试 / 预发环境添加(如_test_pre),生产环境不添加;
  • 示例:
    • 生产:mall_order(电商订单库)、erp_inventory(ERP 库存库);
    • 测试:mall_order_testerp_inventory_pre

(2)数据表命名

1)普通业务表
  • 格式:业务模块_表含义[_扩展标识]
  • 扩展标识:区分同类型表(如_history= 历史表、_backup= 备份表、_temp= 临时表);
  • 示例:
    • user_base(用户基础表)、order_main(订单主表)、order_item(订单项表);
    • user_login_history(用户登录历史表)、order_temp(订单临时表)。
2)分区表
  • 格式:在普通表名后加分区维度标识(如_time= 按时间分区、_hash= 按哈希分区);
  • 示例:order_main_time(订单主表 - 按时间分区)、user_order_hash(用户订单表 - 按用户 ID 哈希分区)。
3)中间表 / 临时表
  • 临时表(会话级):tmp_业务模块_用途(如tmp_order_stat= 订单统计临时表);
  • ETL 中间表:mid_业务模块_用途(如mid_user_behavior= 用户行为中间表);
  • 注意:临时表使用后必须及时删除,避免占用资源。

(3)字段命名

1)通用字段
  • 主键:统一用id(单主键),复合主键需语义化(如user_order_id= 用户订单复合主键);

  • 通用关键字段(全表统一):

    字段用途 命名 类型建议
    创建时间 create_time DATETIME/DATETIME(3)
    更新时间 update_time DATETIME/DATETIME(3)
    创建人 create_by VARCHAR/INT(关联用户 ID)
    更新人 update_by VARCHAR/INT(关联用户 ID)
    状态 status TINYINT(枚举值,需注释说明)
    逻辑删除标识 is_deleted TINYINT(0 = 未删,1 = 已删)
  • 示例:order_id(订单 ID)、user_mobile(用户手机号)、pay_amount(支付金额)。

2)关联字段
  • 格式:关联表名_关联字段名(避免歧义);
  • 示例:用户表关联订单表,订单表中用户字段为user_id(而非uid),商品表中分类字段为category_id(而非cid)。
3)字段注释
  • 所有字段必须加注释(尤其是状态字段),示例: sql

    复制代码
    ALTER TABLE order_main MODIFY COLUMN status TINYINT NOT NULL COMMENT '订单状态:1-待支付,2-已支付,3-已取消,4-已完成';

(4)索引命名

  • 格式:索引类型_表名_字段名(字段名多则取前 3 个,用下划线分隔);
  • 索引类型标识:
    • 主键索引:pk_(Primary Key);
    • 唯一索引:uk_(Unique Key);
    • 普通索引:idx_(Index);
    • 全文索引:ft_idx_(Fulltext Index);
  • 示例:
    • 主键:pk_order_main_id(订单主表 ID 主键);
    • 唯一索引:uk_user_mobile(用户表手机号唯一索引);
    • 普通索引:idx_order_main_create_time(订单主表创建时间索引);
    • 联合索引:idx_order_main_user_id_create_time(订单主表 - 用户 ID + 创建时间)。

(5)视图 / 存储过程 / 函数命名

1)视图(View)
  • 格式:v_业务模块_视图用途
  • 示例:v_order_user(订单 - 用户关联视图)、v_user_pay_stat(用户支付统计视图)。
2)存储过程(Procedure)
  • 格式:proc_业务模块_功能
  • 示例:proc_order_calculate(订单金额计算存储过程)、proc_user_sync(用户数据同步存储过程)。
3)函数(Function)
  • 格式:func_功能描述
  • 示例:func_format_mobile(手机号格式化函数)、func_calculate_age(年龄计算函数)。

(6)触发器 / 事件命名

1)触发器(Trigger)
  • 格式:trg_表名_触发时机_触发动作
  • 触发时机:before/after
  • 触发动作:insert/update/delete
  • 示例:trg_order_main_after_insert(订单主表插入后触发器)。
2)事件(Event)
  • 格式:evt_业务模块_事件用途
  • 示例:evt_order_clean_expire(清理过期订单事件)、evt_user_stat_cron(用户统计定时事件)。

二、数据库基本设计规范

1、设计前:需求与模型规范

(1)需求分析与边界定义

  • 明确业务核心实体:梳理核心业务对象(如用户、订单、商品)及实体间关系(一对一、一对多、多对多),避免冗余实体;
  • 定义数据生命周期:明确数据的留存策略(如订单数据保留 3 年、日志数据保留 6 个月),为后续分区 / 归档做准备;
  • 确认性能指标:明确 QPS、读写比例、数据量预估(如单表峰值数据量、年增长数据量),提前规划分库分表 / 索引策略。

QPS = 系统每秒能够处理的请求(Query / Request)数量

(2)ER 模型设计规范

  • 实体命名:与后续库表命名保持一致(见「数据库命名规范」),避免抽象命名(如 "信息表",应使用"用户基础表");
  • 关系表达:
    • 一对一:优先合并表(如用户基础表 + 用户认证表,仅拆分大字段 / 低频字段);
    • 一对多:通过外键(或业务层)关联(如订单主表→订单项表,订单项表加order_id);
    • 多对多:新增中间关联表(如用户 - 角色,新增user_role表,含user_id+role_id);
  • 字段粒度:原子化设计(不可再拆分),如地址拆分为provincecitydistrictdetail_addr,避免存储 "XX 省 XX 市 XX 区 XX 路" 整体字符串。

2、库表结构设计核心规范

(1)数据库选型与引擎

  • 优先选择 InnoDB:支持事务、行锁、崩溃恢复、外键(按需使用),MyISAM 仅用于只读 / 日志类场景;
  • 字符集与排序规则:
    • 库 / 表统一用utf8mb4(兼容 emoji、特殊字符,避免乱码);
    • 排序规则选utf8mb4_general_ci(通用不区分大小写,性能优),需精准区分大小写用utf8mb4_bin
  • 避免多数据库混用字符集:如部分库utf8、部分utf8mb4,导致关联查询乱码。

(2)表结构设计黄金法则

1)主键设计

  • 优先用自增主键(BIGINT UNSIGNED AUTO_INCREMENT):InnoDB 聚簇索引,避免 UUID(离散写入导致索引碎片);
  • 复合主键场景:仅用于多对多关联表(如user_role表主键(user_id, role_id)),禁止单表滥用复合主键;
  • 主键值禁止修改:主键是数据唯一标识,修改会导致关联关系失效。
2)分表策略(提前规划)
  • 单表阈值:MySQL 单表数据量建议≤1000 万行,超过则规划拆分;
  • 拆分方式:
    • 垂直拆分:按字段热度拆分(高频字段→主表,即常用的存放在一个表中,低频 / 大字段→子表);
    • 水平拆分:按时间(如订单表按年 / 月拆分)、哈希(如用户表按用户 ID 取模)、范围(如按订单 ID 区间)拆分;
  • 分区表替代:数据量 500 万 - 1000 万可先用分区表(如按时间分区),延迟分表复杂度。

(3)约束设计:保障数据一致性

1)主键约束
  • 单表必须有主键:无主键的 InnoDB 表会隐式生成主键,导致性能下降;
  • 主键字段禁止为空:id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
2)唯一约束
  • 业务唯一字段必须加唯一索引 / 约束:如手机号、订单编号,避免重复数据;
  • 示例:UNIQUE KEY uk_user_mobile (mobile)
3)外键约束(谨慎使用)
  • 核心业务表:可加外键约束(如订单表user_id关联用户表id),保障数据引用完整性;
  • 高并发表:禁用外键(外键会增加写入开销),业务层控制关联关系;
  • 禁用级联操作:ON DELETE CASCADE/ON UPDATE CASCADE易导致批量数据删除 / 更新,引发性能问题。

3、SQL 与性能设计规范

(1)读写设计

  • 读优化:
    • 避免SELECT *,仅查询需要的字段;
    • 分页查询必须加LIMIT,且用主键 / 索引字段分页(如WHERE id > 1000 LIMIT 100,而非LIMIT 10000, 100);
  • 写优化:
    • 批量操作替代循环单条(如批量 INSERT/UPDATE);
    • 大事务拆分:单事务操作行数≤1000,避免锁等待 / 回滚开销。

(2)避免性能陷阱

  • 禁止大表全表扫描:如SELECT * FROM order_main WHERE status=1(无索引);
  • 禁用SELECT COUNT(*)统计大表行数:用缓存 / 中间表 / 近似计数(如EXPLAIN预估行数);
  • 避免临时表 / 文件排序:ORDER BY/GROUP BY字段必须命中索引。

4、安全与可维护性规范

(1)数据安全

  • 敏感数据加密:手机号、身份证、银行卡等字段用脱敏 / 加密存储(如手机号存138****8000,核心字段用 AES 加密);
  • 逻辑删除替代物理删除:通过is_deleted字段标记删除,避免数据丢失,便于恢复;
  • 权限最小化:数据库账号按业务分配权限(如只读账号、仅增删改账号),禁止使用 root 账号连接业务。

(2)可维护性

  • 注释强制:库、表、字段、索引必须加注释,说明业务含义;示例:

    复制代码
    CREATE TABLE user_base (
      id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID',
      mobile VARCHAR(11) NOT NULL DEFAULT '' COMMENT '用户手机号',
      PRIMARY KEY pk_user_base_id (id),
      UNIQUE KEY uk_user_base_mobile (mobile)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户基础表';
  • 版本控制:数据库结构变更(DDL)纳入版本管理,记录变更内容、执行人、时间;

  • 备份策略:核心库每日全量备份 + 增量备份,测试库按需备份,备份后验证可恢复性。

5、设计评审与落地规范

(1)评审要点

  • 表结构:字段类型是否合理、通用字段是否齐全、主键 / 索引是否合规;
  • 性能:是否存在全表扫描风险、索引是否冗余、大字段是否拆分;
  • 一致性:约束是否完善、关联关系是否清晰、数据生命周期是否明确;
  • 扩展性:是否预留字段(如ext1/ext2,仅用于临时扩展,避免频繁 DDL)、是否适配未来分库分表。

(2)灰度落地

  • 新表上线:先在测试环境验证性能(如模拟数据量、压测 QPS),再灰度到预发环境;
  • 禁止在线上做数据库压力测试;
  • 禁止从开发环境、测试环境直接连接生产环境数据库;
  • DDL 变更:大表 DDL(如加索引、改字段)选择低峰期执行,MySQL 8.0 + 用ALGORITHM=INPLACE减少锁表时间。

6、反模式(禁止做法)

  1. 用 VARCHAR 存储数字(如手机号、订单 ID),导致索引失效 / 排序异常;
  2. 单表字段过多(>50 个),未做垂直拆分;
  3. 滥用 ENUM 类型(ENUM 修改需 DDL,不如 TINYINT 灵活);
  4. 大事务操作(如一次性插入 10 万行数据),导致锁等待 / 回滚;
  5. 无注释的表 / 字段,新人维护成本高;
  6. 用 UUID 作为主键(InnoDB 聚簇索引离散写入,性能下降)。

三、字段设计规范

1、通用基础规则(所有字段必遵循)

(1)命名规范(与库表命名统一)

  • 仅用小写字母、数字、下划线(_) ,禁止大写、特殊字符、空格,以字母开头;
  • 多单词用下划线分隔(蛇形命名),禁止驼峰(如userName❌,user_name✅);
  • 见名知意:使用业务术语,禁止拼音 / 无意义缩写(如yh_mobile❌,user_mobile✅;u_id❌,user_id✅);
  • 通用缩写统一:仅使用行业通用缩写(如id= 标识、num= 数量、cnt= 计数、addr= 地址、amt= 金额)。

(2)数据类型选择原则

  • 最小化 :选择 "够用且最小" 的类型,减少存储和 IO 开销(如年龄用TINYINT而非INT);
  • 精准化 :避免精度丢失 / 类型转换(如金额用DECIMAL而非FLOAT,时间用DATETIME而非VARCHAR);
  • 兼容性 :优先选择 MySQL 全版本支持的类型,避免小众类型(如JSON仅 MySQL 5.7 + 支持,需兼容低版本则用VARCHAR)。

(3)约束与默认值

  • 禁止 NULL 值 :所有字段默认设为NOT NULL,NULL 值会增加索引开销、导致查询条件复杂(如WHERE mobile IS NOT NULL);
    • 空值替代方案:字符串用空串('')、数字用0、布尔用0/1
  • 默认值合理 :根据业务场景设置默认值,避免无意义默认(如status TINYINT NOT NULL DEFAULT 1,而非默认NULL);
  • 避免过度约束:仅对核心业务字段加唯一 / 外键约束,非核心字段由业务层控制。

(4)注释强制要求

  • 所有字段必须加注释,明确业务含义、枚举值说明、取值范围;

  • 示例:

    复制代码
    status TINYINT NOT NULL DEFAULT 1 COMMENT '订单状态:1-待支付,2-已支付,3-已取消,4-已完成',
    user_mobile VARCHAR(11) NOT NULL DEFAULT '' COMMENT '用户手机号,仅支持11位中国大陆手机号'

2、核心字段类型选型规范(MySQL 适配)

(1)数值型字段

类型 占用空间 适用场景 注意事项
TINYINT 1 字节 枚举值(状态、类型)、年龄 取值范围 - 128~127(无符号 0~255),优先用无符号(UNSIGNED)
SMALLINT 2 字节 数量较少的数值(如商品分类 ID、地区编码) 取值范围 - 32768~32767(无符号 0~65535)
INT 4 字节 常规 ID(如用户 ID、订单 ID)、数量 取值范围 - 2^31~2^31-1(无符号 0~2^32-1),单表数据量≤4000 万优先用
BIGINT 8 字节 大表 ID、分布式 ID、大数统计 取值范围 - 2^63~2^63-1(无符号 0~2^64-1),单表数据量 > 4000 万必用
DECIMAL(M,D) 按精度定 金额、费率、百分比 M = 总位数,D = 小数位数(如金额用DECIMAL(10,2),费率用DECIMAL(5,4));禁止用 FLOAT/DOUBLE(精度丢失)
数值型设计禁忌
  • 禁止用VARCHAR存储数值(如手机号、订单 ID、身份证号):导致索引失效、排序异常、查询条件复杂;
  • 禁止用INT存储手机号:手机号超过 INT 取值范围(最大 2147483647),需用CHAR(11)
  • 无符号数值加UNSIGNED:如id BIGINT UNSIGNED,扩展取值范围。

(2)字符串型字段

类型 占用空间 适用场景 注意事项
CHAR(N) 固定 N 字节 长度固定的字符串(手机号、身份证号、订单编号) N≤255,如手机号CHAR(11)、订单编号CHAR(32);查询性能优于 VARCHAR
VARCHAR(N) 可变(1~N+1 字节) 长度可变的字符串(姓名、地址、备注) N 按实际最大长度设置(如姓名VARCHAR(32)、地址VARCHAR(255));避免设为VARCHAR(255)(浪费空间)
TEXT 最大 65535 字节 长文本(如商品描述、用户简介) 拆分子表存储(避免主表查询性能下降);禁止用 TEXT 存储可拆分的结构化数据
LONGTEXT 最大 4GB 超大文本(如富文本内容、日志) 仅用于非核心字段,且必须拆分子表
字符串型设计禁忌
  • 禁止用TEXT存储短字符串(如备注≤200 字用VARCHAR(200));
  • 禁止用VARCHAR存储时间 / 日期(如VARCHAR(20)2025-01-01);
  • 字符串字段加默认值:如user_name VARCHAR(32) NOT NULL DEFAULT ''

(3)时间 / 日期型字段

类型 占用空间 适用场景 注意事项
DATETIME 8 字节 业务时间(创建时间、更新时间) 无时区依赖,精准到秒;MySQL 5.6 + 支持DATETIME(3)(毫秒级),优先使用
TIMESTAMP 4 字节 需时区适配的时间(如跨境业务) 取值范围 1970~2038,受时区影响;自动更新场景可用(如ON UPDATE CURRENT_TIMESTAMP
DATE 3 字节 仅需日期的场景(如生日、下单日期) 避免用 DATETIME 存储仅需日期的数据(节省空间)
时间型设计规范
  • 优先用DATETIME(3):兼顾精度(毫秒)和存储开销,满足绝大多数业务场景;

  • 禁止用VARCHAR存储时间:无法直接排序 / 范围查询(如WHERE create_time > '2025-01-01');

  • 通用时间字段配置:

    复制代码
    create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
    update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间'

(4)特殊类型字段

类型 适用场景 设计规范
BOOLEAN 布尔值(是否删除、是否启用) MySQL 中 BOOLEAN 是 TINYINT (1) 的别名,优先用TINYINT(0 = 否,1 = 是),如is_deleted TINYINT NOT NULL DEFAULT 0
ENUM 固定枚举值 禁用(修改枚举值需 DDL,灵活性差),改用TINYINT+ 注释说明;仅用于永不修改的枚举(如性别:1 - 男,2 - 女)
JSON 非结构化数据(如扩展字段) MySQL 5.7 + 支持,仅用于临时扩展字段(如ext_info JSON NOT NULL DEFAULT '{}');核心数据禁止用 JSON 存储(无法高效索引)
BLOB 二进制数据(如图片、文件) 禁用(数据库存储二进制效率低),改为存储文件 URL,文件存对象存储(如 OSS)

3、通用关键字段设计规范(全表统一)

所有业务表必须包含以下通用字段,保障数据可追溯、可管理:

字段名 类型 默认值 注释 设计说明
id BIGINT UNSIGNED - 主键 ID 自增(AUTO_INCREMENT),InnoDB 聚簇索引最优;禁止用 UUID 作为主键
create_time DATETIME(3) CURRENT_TIMESTAMP(3) 创建时间 记录数据插入时间,不可手动修改
update_time DATETIME(3) CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) 更新时间 数据修改时自动更新,无需业务层维护
create_by BIGINT UNSIGNED 0 创建人 ID 关联用户表 ID,无创建人时默认 0
update_by BIGINT UNSIGNED 0 更新人 ID 关联用户表 ID,无更新人时默认 0
is_deleted TINYINT 0 逻辑删除标识 0 = 未删除,1 = 已删除;禁止物理删除核心数据

4、关联字段设计规范

  • 格式:关联表名_关联字段名(避免歧义);
  • 示例:
    • 订单表关联用户表:user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID,关联user_base.id'
    • 订单项表关联订单表:order_id BIGINT UNSIGNED NOT NULL COMMENT '订单ID,关联order_main.id'
  • 约束:核心关联字段加索引(如订单表user_id加普通索引),保障关联查询性能;
  • 禁止:关联字段类型与被关联字段不一致(如用户表id是 BIGINT,订单表user_id是 INT)。

5、特殊场景字段设计规范

(1)敏感数据字段

  • 脱敏存储:手机号(138****8000)、身份证号(110***********1234)、银行卡号(622848*******1234);
  • 加密存储:核心敏感数据(如支付密码、身份证明文)用 AES 加密存储,密钥独立管理;
  • 禁止:明文存储密码(需哈希 + 盐值存储,如 MD5/SHA256 + 随机盐)。

AES 是一种对称分组加密算法,用同一把密钥完成加密和解密,安全性高、性能好,是现代系统的事实标准.

(2)大字段(TEXT/LONGTEXT)

  • 拆分原则:大字段必须拆到子表,主表仅保留关联 ID;
  • 示例:
    • 主表user_base(用户基础信息:id、mobile、name);
    • 子表user_ext(用户扩展信息:user_id、intro、avatar_url);
  • 访问原则:仅在需要时查询子表,避免主表查询携带大字段。

(3)扩展字段

  • 临时扩展:用ext1/ext2/ext3(如ext1 VARCHAR(255) NOT NULL DEFAULT '' COMMENT '临时扩展字段:存储用户标签'),仅用于短期临时需求;
  • 长期扩展:优先新增字段 / 子表,禁止滥用扩展字段导致数据结构混乱;
  • JSON 扩展:仅用于非核心、非查询的扩展数据(如ext_info JSON NOT NULL DEFAULT '{}' COMMENT '用户扩展信息,格式:{"hobby":"读书","address":"北京"}')。

(4)枚举类字段

  • 类型:优先用TINYINT,禁止用ENUM(修改枚举值需 DDL);

  • 注释:必须明确枚举值含义,示例:

    复制代码
    pay_type TINYINT NOT NULL DEFAULT 0 COMMENT '支付方式:0-未支付,1-微信支付,2-支付宝,3-银行卡支付'
  • 扩展:新增枚举值时,仅需更新注释,无需修改字段类型(优于 ENUM)。

6、反模式(禁止做法)

  1. 字段类型过大(如用INT存储年龄、VARCHAR(255)存储姓名);
  2. VARCHAR存储数值 / 时间(如VARCHAR(11)存储手机号、VARCHAR(20)存储日期);
  3. 核心字段允许 NULL 值(如user_mobile VARCHAR(11) NULL);
  4. 滥用 TEXT 类型(如用 TEXT 存储短备注);
  5. 枚举类字段用VARCHAR(如status VARCHAR(10)存储 "待支付 / 已支付");
  6. 关联字段类型不匹配(如主表 ID 是 BIGINT,关联字段是 INT);
  7. 无注释的字段(如col1 INT NOT NULL,无法理解业务含义);
  8. 物理删除核心数据(未设置is_deleted逻辑删除字段)。

四、索引设计规范

1、通用基础规则(所有索引必遵循)

(1)命名规范(见名知意,统一格式)

  • 格式:索引类型前缀_表名_字段名(多字段取前 3 个,下划线分隔);

  • 索引类型前缀(统一):

    索引类型 前缀 示例
    主键索引 pk_ pk_order_main_id
    唯一索引 uk_ uk_user_base_mobile
    普通索引 idx_ idx_order_main_user_id
    全文索引 ft_idx_ ft_idx_goods_desc
    空间索引 sp_idx_ sp_idx_location_geo
  • 规则:

    • 仅用小写字母、数字、下划线,以前缀开头,禁止大写 / 特殊字符;
    • 表名 / 字段名用缩写时需统一(如order_main缩写为om,需团队共识);
    • 联合索引字段名按顺序拼接(如idx_order_main_user_id_create_time)。

(2)索引类型选择原则

优先选择适配查询场景的索引类型,避免 "一刀切" 使用 B + 树索引:

索引类型 适用场景 禁用场景 注意事项
B + 树索引(默认) 等值查询(=)、范围查询(>/-/<)、排序(ORDER BY)、分组(GROUP BY) 全文模糊匹配、空间坐标查询 遵循 "最左前缀",InnoDB 聚簇索引基于此实现
唯一索引 业务唯一字段(手机号、订单编号、用户账号) 非唯一字段(如状态、创建时间) 避免过度使用(写入时需校验唯一性,增加开销)
全文索引(FULLTEXT) 长文本模糊匹配(商品描述、文章内容) 短字符串、等值 / 范围查询 仅 InnoDB(5.6+)/MyISAM 支持,仅适用于 CHAR/VARCHAR/TEXT
空间索引(SPATIAL) 地理坐标查询(经纬度、地理位置) 非空间数据查询 仅 InnoDB(5.7+)/MyISAM 支持,需用空间类型字段(GEOMETRY/POINT)
哈希索引 仅等值查询(Memory 引擎场景) 范围查询、排序 MySQL 仅 Memory 引擎原生支持,InnoDB 自适应哈希索引为隐式优化(无需手动创建)

(3)索引数量控制

  • 单表索引总数≤5 个:索引越多,INSERT/UPDATE/DELETE 的写入开销越大;
  • 联合索引替代多个单列索引:如(user_id, create_time)可覆盖user_iduser_id+create_time查询,避免重复创建idx_user_id

2、核心设计原则(B + 树索引为主)

(1)最左前缀匹配原则(核心中的核心)

  • 联合索引(a,b,c)仅匹配以下查询场景(按优先级):
    1. WHERE a=1(仅左前缀 a);
    2. WHERE a=1 AND b=2(左前缀 a+b);
    3. WHERE a=1 AND b=2 AND c=3(全字段);
  • 失效场景:WHERE b=2WHERE b=2 AND c=3WHERE a=1 AND c=3(跳过 b);
  • 设计技巧:联合索引字段按 "查询频率高→低、区分度高→低" 排序(如(user_id, create_time),user_id 查询更频繁、区分度更高)。

(2)高区分度优先原则

  • 区分度公式:区分度 = 不同值数量 / 总行数(值越接近 1,区分度越高);
  • 示例:
    • 手机号(区分度≈1)> 用户 ID(区分度≈0.8)> 订单状态(区分度≈0.01);
    • 联合索引中,区分度高的字段放左侧(如(mobile, status)而非(status, mobile));
  • 禁忌:为低区分度字段创建单列索引(如status(仅 1/2/3 三个值),优化器会选择全表扫描而非索引)。

(3)覆盖索引优先原则

  • 定义:查询的所有字段(SELECT/WHERE/ORDER BY)均包含在索引中,无需回表(Extra 显示Using index);
  • 设计示例:
    • 业务查询:SELECT id, name FROM user WHERE age=20
    • 索引设计:idx_age_name (age, name)(id 是主键,InnoDB 聚簇索引自动包含,无需显式加入);
  • 优势:避免回表操作,将随机 IO 转为顺序 IO,查询效率提升 5~10 倍。

(4)索引列禁用操作原则

索引列做以下操作会导致索引失效,需优先规避:

禁用操作 示例(❌) 优化方案(✅)
函数 / 运算 WHERE DATE(create_time)='2025-01-01' WHERE create_time BETWEEN '2025-01-01 00:00:00' AND '2025-01-01 23:59:59'
隐式类型转换 WHERE mobile=13800138000(mobile 为字符串) WHERE mobile='13800138000'
负向查询 WHERE status != 1/WHERE id NOT IN (1,2,3) 改写为正向查询(如WHERE status IN (2,3)),或业务层过滤
模糊匹配(% 开头) WHERE name LIKE '%张三' 改用全文索引(FULLTEXT),或业务前置处理(如关键词分词)
OR 连接无索引字段 WHERE a=1 OR b=2(仅 a 有索引) 拆分查询(SELECT * FROM tbl WHERE a=1 UNION ALL SELECT * FROM tbl WHERE b=2),或给 b 加索引

(5)短索引原则(字符串字段)

  • 字符串字段(如手机号、邮箱、URL)创建前缀索引 ,减少索引存储空间:
    • 格式:INDEX idx_mobile (mobile(11))(手机号固定 11 位);
    • 计算最优前缀长度:通过SELECT COUNT(DISTINCT LEFT(col, n))/COUNT(*) FROM tbl,取区分度≥95% 的最小 n;
  • 示例:邮箱字段email,计算得LEFT(email, 10)区分度 98%,则创建idx_email (email(10))

3、分场景索引设计规范

(1)主键索引设计

  • 必选规则:所有 InnoDB 表必须显式创建主键索引,禁止依赖隐式主键;
  • 类型选择:
    • 优先自增主键:id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY(InnoDB 聚簇索引,避免页分裂);
    • 复合主键:仅用于多对多关联表(如user_role表,主键(user_id, role_id));
  • 禁忌:禁止用 UUID/MD5 值作为主键(离散写入导致索引碎片、IO 飙升)。

(2)唯一索引设计

  • 适用场景:业务唯一标识字段(手机号、订单编号、用户账号);
  • 设计技巧:
    • 允许空值的唯一字段:若业务允许NULL,需注意UNIQUE KEY允许多个NULL(可通过uk_mobile (mobile) + mobile NOT NULL规避);
    • 组合唯一:多字段组合唯一(如uk_user_date (user_id, create_date),限制用户单日仅 1 条记录);
  • 禁忌:禁止为非唯一字段创建唯一索引(如uk_status (status),会导致写入失败)。

(3)联合索引设计

  • 字段顺序优先级:查询频率高 > 区分度高 > 等值查询字段 > 范围查询字段
  • 示例:
    • 业务查询:SELECT * FROM order_main WHERE user_id=123 AND create_time > '2025-01-01' ORDER BY amount
    • 索引设计:idx_user_create_amount (user_id, create_time, amount)(user_id 等值查询→create_time 范围查询→amount 排序);
  • 禁忌:范围查询字段后加等值字段(如(create_time, user_id),user_id 无法命中索引)。

(4)排序 / 分组索引设计

  • 规则:ORDER BY/GROUP BY字段必须包含在索引中,且顺序与查询一致;
  • 示例:
    • 业务查询:SELECT user_id, COUNT(*) FROM order_main WHERE status=1 GROUP BY user_id ORDER BY COUNT(*) DESC
    • 索引设计:idx_status_user (status, user_id)(覆盖 WHERE+GROUP BY,排序可由优化器优化);
  • 禁忌:排序字段与索引顺序相反(如索引(a,b),查询ORDER BY b,a DESC,导致Using filesort)。

(5)大表 / 分区表索引设计

  • 单表数据量 > 1000 万:优先创建分区表 + 分区内索引,避免全表索引扫描;

  • 分区表索引:

    • 本地索引(LOCAL):索引与分区一一对应,查询仅扫描目标分区(推荐);
    • 全局索引(GLOBAL):仅用于跨分区查询的主键 / 唯一索引;
  • 示例:

    复制代码
    CREATE TABLE order_main (
      id BIGINT UNSIGNED AUTO_INCREMENT,
      user_id BIGINT UNSIGNED,
      create_time DATETIME(3),
      PRIMARY KEY pk_order_main_id (id, create_time) -- 分区键必须包含在主键中
    ) ENGINE=InnoDB
    PARTITION BY RANGE (TO_DAYS(create_time)) (
      PARTITION p202501 VALUES LESS THAN (TO_DAYS('2025-02-01')),
      PARTITION p202502 VALUES LESS THAN (TO_DAYS('2025-03-01'))
    );
    CREATE INDEX idx_user_id ON order_main (user_id) LOCAL; -- 本地索引

4、索引维护规范

(1)索引有效性校验

  • 定期检查未使用索引:通过sys.schema_unused_indexes(MySQL 5.7+)查询,删除冗余 / 无用索引;
  • 检查索引碎片:
    • 查看碎片率:SELECT TABLE_NAME, DATA_FREE/(DATA_LENGTH+INDEX_LENGTH) AS frag_rate FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='db_name'
    • 碎片率 > 30% 时重建索引:ALTER TABLE tbl_name ENGINE=InnoDB(InnoDB 无锁重建),或OPTIMIZE TABLE tbl_name(有锁,低峰期执行);
  • 验证执行计划:用EXPLAIN ANALYZE(MySQL 8.0+)检查索引是否命中,重点关注type(避免 ALL)、key(非 NULL)、Extra(无 Using temporary/Using filesort)。

(2)索引变更规范

  • 大表索引操作:
    • MySQL 8.0+:用ALTER TABLE tbl ADD INDEX idx_col (col) ALGORITHM=INPLACE, LOCK=NONE(无锁在线 DDL);
    • MySQL 5.7:选择业务低峰期执行,避免锁表导致读写阻塞;
  • 变更流程:测试环境验证→预发环境灰度→生产环境执行,变更后监控 QPS / 响应时间 / 锁等待;
  • 禁忌:禁止在大事务期间创建 / 删除索引(加剧锁竞争)。

(3)索引冗余识别与清理

以下场景判定为冗余索引,需及时删除:

  1. 已有联合索引(a,b),又创建单列索引(a)
  2. 索引(a,b)(a,b,c),且(a,b)无单独查询场景;
  3. 索引字段与主键重复(如主键id,又创建idx_id (id));
  4. 索引创建后长期未使用(超过 30 天无命中)。

5、索引SET规范

索引中,尽量避免使用外键约束。不建议使用外键约束,但是一定要在表与表之间的关联上建立索引。因为外键可用于保证数据的参照完整性,但建议在业务端实现就可以了。且外键会影响父表和子表的写操作从而降低性能。

6、反模式(禁止做法)

  1. 为所有字段创建索引(单表索引 > 5 个,写入性能下降 50% 以上);
  2. 联合索引违反最左前缀原则(如查询b=2,索引(a,b));
  3. 索引列使用函数 / 运算(如WHERE id+1=100);
  4. 小表(<10 万行)创建大量索引(优化器优先全表扫描,索引无意义);
  5. 用唯一索引约束非唯一字段(如uk_status (status));
  6. 主键用 UUID / 字符串(离散写入导致索引碎片);
  7. 忽略覆盖索引,频繁回表查询(如SELECT *导致Using index condition);
  8. 在线上核心表随意删除索引(导致慢查询爆发)。

五、SQL开发的规范

1、运算逻辑:数据库做 "存储",业务层做 "计算"

(1)核心规范

  1. 禁止数据库内执行复杂运算 :所有复杂计算(如多维度统计、数据格式化、复杂公式运算、字符串拼接 / 拆分、日期差值高阶计算等)迁移至业务应用层完成;
    • 反例:SELECT id, (price * num + IF(discount>0, discount, 0)) * 1.13 AS final_amount FROM order_item;(数据库计算最终金额);
    • 正例:仅查询基础字段id, price, num, discount,由业务代码计算最终金额;
  2. 简化数据库端轻量运算 :仅保留必要的基础运算(如简单加减、NULL 值判断IFNULL()),且优先通过索引 / 字段设计规避(如新增total_price字段存储price*num结果,避免实时计算)。

(2)性能影响

数据库的核心优势是数据存储和快速检索,而非复杂逻辑运算;复杂运算会占用数据库 CPU / 内存资源,导致并发处理能力下降,迁移至业务层可分散计算压力,提升整体系统吞吐量。

2、索引利用:最大化已有索引价值

核心规范

  1. 所有查询必须命中索引 :编写 SQL 前先确认表上已有索引,WHERE/ORDER BY/GROUP BY 字段必须匹配索引(遵循最左前缀原则);
    • 反例:表有索引idx_user_create_time (user_id, create_time),却执行SELECT * FROM order WHERE create_time > '2025-01-01'(违反最左前缀,索引失效);
    • 正例:补充user_id条件或调整索引,执行SELECT id FROM order WHERE user_id=123 AND create_time > '2025-01-01'(命中索引);
  2. 禁止索引列函数 / 计算 :WHERE 从句中严禁对列做函数转换、运算,避免索引失效;
    • 反例:WHERE DATE(create_time)='2025-01-01'WHERE id+1=100WHERE SUBSTR(mobile,1,3)='138'
    • 正例:WHERE create_time BETWEEN '2025-01-01 00:00:00' AND '2025-01-01 23:59:59'WHERE id=99WHERE mobile LIKE '138%'
  3. 避免隐式类型转换 :确保查询值与字段类型一致,防止索引失效;
    • 反例:字符串字段mobile用数值匹配WHERE mobile=13800138000
    • 正例:WHERE mobile='13800138000'

3、查询规范:精准、高效、可控

核心规范

  1. 禁用 SELECT *,强制指定字段列表: 仅查询业务所需字段,减少网络传输、回表 IO 开销,同时避免因表结构变更导致的 SQL 异常;
    • 反例:SELECT * FROM user_base WHERE id=123;
    • 正例:SELECT id, user_name, mobile FROM user_base WHERE id=123;
  2. 禁用 ORDER BY RAND () 随机排序 :该操作会全表扫描并生成随机值,性能极差;
    • 反例:SELECT id FROM goods ORDER BY RAND() LIMIT 10;
    • 正例:业务层先获取主键范围,随机生成 ID 后查询(如SELECT id FROM goods WHERE id IN (101,205,308));
  3. OR 判断替换为 IN :同一列的多值 OR 判断,用 IN 替代(优化器更易命中索引);
    • 反例:WHERE status=1 OR status=2 OR status=3
    • 正例:WHERE status IN (1,2,3)
  4. 拆分复杂大 SQL 为多个小 SQL :单个复杂 SQL(多表关联、多层聚合、大结果集)会占用数据库连接和 CPU 资源,拆分后降低单次执行开销;
    • 反例:一次性关联 8 张表查询 + 聚合 + 排序;
    • 正例:先查询核心表数据,再按需查询关联表,业务层拼接结果;
  5. 限制 JOIN 关联表数量 :单次 JOIN 表数≤5 张,过多表关联会导致优化器选择低效执行计划,且锁竞争风险升高;
    • 说明:超过 5 张表的关联需求,优先拆分查询或通过中间表预处理数据。

4、写入规范:安全、明确、低风险

核心规范

  1. 禁用不含字段列表的 INSERT 语句 :必须显式指定插入字段,避免表结构变更(如新增字段)导致的插入失败或数据错位;
    • 反例:INSERT INTO user_base VALUES (NULL, '13800138000', '张三');
    • 正例:INSERT INTO user_base (mobile, user_name) VALUES ('13800138000', '张三');
  2. 优先使用预编译语句 :业务代码中通过 PreparedStatement/ORM 参数绑定执行 SQL,禁止字符串拼接;
    • 优势:防止 SQL 注入、复用执行计划、避免重复解析 SQL,提升执行效率;
    • 示例(MyBatis :SELECT id FROM user WHERE mobile = #{mobile}(参数绑定),而非${mobile}(字符串拼接)。

5、语法优化:规避低效写法

核心规范

  1. 子查询替换为 JOIN 操作 :子查询易生成临时表,JOIN 更易利用索引,执行效率更高;
    • 反例:SELECT * FROM user WHERE id IN (SELECT user_id FROM order WHERE status=1);
    • 正例:SELECT u.* FROM user u JOIN order o ON u.id=o.user_id WHERE o.status=1;
  2. UNION ALL 替代 UNION(无重复场景) :UNION 会触发去重排序(额外开销),确认无重复值时必须用 UNION ALL;
    • 反例:SELECT id FROM order_2024 UNION SELECT id FROM order_2025;(无重复却用 UNION);
    • 正例:SELECT id FROM order_2024 UNION ALL SELECT id FROM order_2025;

6、交互规范:减少数据库访问频次

核心规范

  1. 降低同数据库交互次数
    • 批量操作替代循环单条:如批量 INSERT(VALUES (1,2),(3,4))、批量 UPDATE(按 ID 区间分批);
    • 缓存热点数据:将高频查询的静态 / 准静态数据(如商品基础信息、字典表)缓存至 Redis,避免重复查询数据库;
    • 合并多次查询:业务层整合查询需求,单次 SQL 获取多维度数据(而非多次单字段查询);
  2. 不同数据库使用独立连接:程序访问不同数据库(如订单库、用户库)时,使用独立的数据库连接池,避免连接混用导致的事务异常、性能干扰。

六、操作行为规范

1、超大规模批量写操作规范

针对单批次操作超 100 万行的批量写场景,核心原则是分批执行、降低单次操作风险

(1)强制分批操作

  • 拆分阈值:将超 100 万行的批量写操作拆分为若干批次,单批次操作行数建议控制在 1000~5000 行(可根据数据库负载调整),避免单次操作占用大量锁资源、引发事务超时或阻塞;

  • 分批方式:基于主键 / 唯一索引的范围拆分(如按 ID 区间WHERE id BETWEEN 1 AND 1000),避免全表扫描加锁;

  • 示例(UPDATE 分批):

    -- 分批更新100万行订单状态,每次1000行
    UPDATE order_main SET status=3 WHERE id BETWEEN 1 AND 1000 AND is_deleted=0;
    UPDATE order_main SET status=3 WHERE id BETWEEN 1001 AND 2000 AND is_deleted=0;
    -- 依次执行直至完成,每批次执行后可短暂休眠(如100ms),降低数据库压力

(2)操作时机与监控

  • 执行时机:批量写操作需在业务低峰期(如凌晨)执行,避免影响核心业务;
  • 实时监控:执行过程中监控数据库 CPU、IO、锁等待指标,出现异常立即暂停操作;

(3)INSERT 批量优化

  • 超 100 万行 INSERT 同样分批,单批次 INSERT VALUES 数量≤5000 行,避免生成超大事务日志,同时禁用INSERT ... SELECT直接批量插入(改为 "SELECT 导出→分批 INSERT")。

2、大表结构变更规范

针对数据量大的表(通常指千万级以上或占用空间超 10GB),核心原则是无锁 / 低锁变更、避免业务中断

  • 强制使用 pt-online-schema-change 工具
    • 禁用原生 ALTER TABLE 直接修改大表结构(会锁表,导致读写阻塞),必须使用 Percona Toolkit 的pt-online-schema-change工具;
    • 工具优势:通过创建临时表、增量同步数据、交换表名的方式实现无锁表结构变更,全程不阻塞业务读写;
    • 操作要点:
      • 变更前备份表结构和数据,确认工具参数(如--alter指定修改内容、--no-drop-old-table保留旧表);
      • 低峰期执行,执行过程中监控同步延迟和数据库负载,避免增量同步占用过多资源;
  • 变更前验证
    • 大表结构变更(加索引、改字段类型、新增字段等)需先在测试环境验证,确认变更耗时、资源占用及对业务的影响;
    • 优先选择 "非侵入式" 变更(如新增字段设默认值、加索引用 INPLACE 算法),避免重写全表数据。

3、数据库账号权限管控规范

针对程序连接数据库的账号,核心原则是最小权限、禁止高危权限

(1)禁用 SUPER 权限

程序使用的数据库账号严禁赋予SUPER权限(该权限包含修改全局参数、终止连接、管理复制等高危操作),防止程序异常 / 漏洞导致数据库配置被篡改;

(2)遵循最小权限原则

按业务场景分配精准权限,禁止赋予全库 / 全表的万能权限:

业务场景 允许权限 禁止权限
只读查询 SELECT INSERT/UPDATE/DELETE/ALTER
读写业务 SELECT/INSERT/UPDATE/DELETE ALTER/DROP/CREATE/GRANT
数据导入(运维) INSERT/SELECT SUPER/ALL PRIVILEGES

示例(创建只读账号):

复制代码
CREATE USER 'app_read'@'%' IDENTIFIED BY 'xxx';
GRANT SELECT ON mall_order.* TO 'app_read'@'%'; -- 仅授予指定库的只读权限
FLUSH PRIVILEGES;

(3)账号隔离规范

  • 不同环境(测试 / 预发 / 生产)、不同业务模块(订单 / 用户 / 支付)使用独立数据库账号,避免账号混用导致权限泄露或故障扩散;
  • 禁止程序使用 root / 管理员账号连接数据库,所有程序账号需设置强密码并定期更换。
相关推荐
weixin_404679312 小时前
vscode 配置cpp调试环境
数据库·ide·vscode·编辑器
TG:@yunlaoda360 云老大2 小时前
华为云国际站代理商GaussDB主要有什么作用呢?
服务器·数据库·华为云·gaussdb
ohoy2 小时前
业务数据聚合分析 xxl-job定时任务
数据库·oracle
技术破壁人2 小时前
MySQL 8+ 高级特性与高级用法详解(Spring Boot + Java 实战)
spring boot·mysql
数据皮皮侠AI2 小时前
数字经济政策工具变量数据(2008-2023)
大数据·数据库·人工智能·笔记·1024程序员节
赫尔·普莱蒂科萨·帕塔2 小时前
从 “脑梗“ 到 “功夫高手“
数据库·人工智能·agi
IvanCodes3 小时前
openGauss 存储核心机制:从表空间到数据块
数据库·sql·oracle·opengauss
Francek Chen3 小时前
【IoTDB】时序数据库选型指南:国产自研技术如何应对数据洪流
大数据·数据库·时序数据库·iotdb
白露与泡影3 小时前
春招 Java 面试大纲:Java+ 并发 +spring+ 数据库 +Redis+JVM+Netty 等
java·数据库·面试