一、数据类型选择规范
1.IP 地址存储
推荐方式:使用 INT UNSIGNED + INET_ATON() / INET_NTOA()
sql
CREATE TABLE access_log (
id BIGINT AUTO_INCREMENT,
ip_int INT UNSIGNED NOT NULL COMMENT '用户IP(整数存储)',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 插入
INSERT INTO access_log(ip_int) VALUES (INET_ATON('192.168.1.100'));
-- 查询
SELECT INET_NTOA(ip_int) AS ip FROM access_log;
优势
节省空间(4 字节 vs VARCHAR(15) 的 15+ 字节)
支持高效范围查询(如 WHERE ip_int BETWEEN ... 判断 IP 段)
注意
必须用 UNSIGNED,否则高位为 1 的 IP(如 192.168.x.x)会转为负数
IPv6 不支持!若需支持 IPv6,请用 VARBINARY(16) + INET6_ATON()(MySQL 5.6.3+)
补充建议
若业务未来可能支持 IPv6,不要用 INT 存 IP,改用 CHAR(39) 或 BINARY(16) 统一处理。
2. 时间类型选择
| 类型 | 存储大小 | 范围 | 时区 | 推荐场景 |
|---|---|---|---|---|
DATETIME |
8 字节 | '1000-01-01 00:00:00' ~ '9999-12-31 23:59:59' |
无 | 需要超大时间范围(如历史档案) |
TIMESTAMP |
4 字节 | '1970-01-01 00:00:01' ~ '2038-01-19 03:14:07' |
自动转为 UTC 存储,查询转回 session 时区 | 绝大多数业务场景(日志、订单、用户行为) |
INT UNSIGNED |
4 字节 | 0 ~ 4294967295(≈2106年) | 无 | 高并发写入、需极致性能(如埋点) |
推荐
优先用 TIMESTAMP(自动时区转换、节省空间)
超高频写入场景(如每秒万级) → 用 INT + UNIX_TIMESTAMP():
sql
INSERT INTO event_log(event_time) VALUES(UNIX_TIMESTAMP());
SELECT FROM_UNIXTIME(event_time) FROM event_log;
注意:TIMESTAMP 在 MySQL 8.0 中默认不自动更新,需显式指定:
sql
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
二、字符集与排序规则(重点/普遍)
-
统一使用
utf8mb4(不是utf8!)utf8在 MySQL 中是阉割版(最多 3 字节),无法存 emoji(如 👍、❤️)utf8mb4支持完整 4 字节 UTF-8
-
排序规则推荐
utf8mb4_general_ci(快)或utf8mb4_unicode_ci(准) -
必须保证:库、表、列三级字符集一致,避免隐式转换导致索引失效
sql
-- 建库
CREATE DATABASE myapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 建表(显式指定)
CREATE TABLE user (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
三、索引设计规范
1. 索引类型
- InnoDB / MyISAM:仅支持 B+Tree(即 BTREE)
- MEMORY:可选 HASH(等值快)或 BTREE(范围/排序快)
2. 联合索引最左前缀原则
sql
-- 查询:WHERE a=1 AND b=2 ORDER BY c
-- 最佳索引:KEY idx_abc (a, b, c)
3. 覆盖索引优化
sql
-- 只查索引字段,避免回表
SELECT user_id, create_time FROM user WHERE username = '张三';
-- 索引:KEY idx_username_cover (username, user_id, create_time)
4. 禁止滥用索引
- 单表索引数 ≤ 5 个(写多读少场景更少)
- 高频更新字段慎加索引(如
update_time)
四、事务与批量操作
1. 事务大小控制
- 单事务操作行数 ≤ 1000 行
- 批量任务每批后
SLEEP(1~5)秒,避免主从延迟、binlog 积压
2. 自增主键并发控制
INSERT并发 ≤ 200 线程(避免自增锁竞争)- 高并发插入考虑:
- 批量
INSERT INTO ... VALUES (...), (...), ... - 使用
innodb_autoinc_lock_mode=2(MySQL 5.7+)
- 批量
五、排序、分组、去重优化
| 操作 | 建议 |
|---|---|
ORDER BY |
尽量程序端排序;若 DB 排序,确保走索引且结果集 < 1000 行 |
GROUP BY |
同上,避免 Using temporary; Using filesort |
DISTINCT |
优先用 GROUP BY 替代,或确保字段有索引 |
关键原则 :
WHERE 过滤后结果集 > 1 万行时,禁止在 DB 层做排序/分组!
六、线上高危 SQL 禁令
| 禁止项 | 原因 | 替代方案 |
|---|---|---|
UPDATE/DELETE ... LIMIT N |
主从执行顺序不一致 → 数据错乱 | 加 ORDER BY PK,或程序分页更新 |
| 关联子查询(尤其 UPDATE/DELETE 中) | 性能 O(N²),极易拖垮 DB | 改为 JOIN 或程序端处理 |
| 存储过程 / 函数 / 触发器 / 视图 | 难以监控、扩展性差、逻辑分散 | 全部移至应用层 |
INSERT ... ON DUPLICATE KEY UPDATE |
高并发下主从不一致(auto_inc 锁问题) | 改用 SELECT + INSERT/UPDATE 两步(加分布式锁) |
多表联表更新 UPDATE t1, t2 SET ... |
语法模糊、易锁表、难优化 | 拆分为单表更新 + 程序关联 |
特别强调 :
所有业务逻辑必须在应用层实现,数据库只负责"可靠存储"和"高效检索"。
七、补充:其他最佳实践
1. 字段命名规范
- 小写 + 下划线:
user_id,create_time - 布尔字段用
is_xxx或xxx_flag:is_deleted,review_status - 避免关键字:不用
order,group,desc等
2. NULL vs NOT NULL
- 尽量设为
NOT NULL,避免索引失效、存储开销增加 - 无值用默认值(如
'',0,'N/A')
3. 大字段分离
TEXT/BLOB字段单独建扩展表,避免影响主表查询性能
4. 软删除代替物理删除
sql
is_deleted TINYINT DEFAULT 0 COMMENT '0-未删,1-已删'
八、规范建表示例
sql
CREATE TABLE user (
`id` BIGINT(16) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` BIGINT(16) NOT NULL COMMENT '业务唯一ID',
`username` VARCHAR(45) NOT NULL COMMENT '真实姓名',
`email` VARCHAR(100) NOT NULL COMMENT '邮箱',
`nickname` VARCHAR(45) NOT NULL COMMENT '昵称',
`avatar_url` VARCHAR(255) NOT NULL COMMENT '头像URL',
`birthday` DATE NOT NULL COMMENT '生日',
`sex` TINYINT NOT NULL DEFAULT 0 COMMENT '性别:0未知,1男,2女',
`intro` VARCHAR(150) DEFAULT '' COMMENT '个人简介',
`resume_url` VARCHAR(255) NOT NULL COMMENT '简历地址',
`register_ip` INT UNSIGNED NOT NULL COMMENT '注册IP(整数)',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`review_status` TINYINT NOT NULL DEFAULT 1 COMMENT '审核状态:1通过,2审核中,3未通过,4未提交',
`is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-正常,1-删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_id` (`user_id`),
KEY `idx_create_time_status` (`create_time`, `review_status`),
KEY `idx_nickname` (`nickname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户基本信息表';