SQL使用及注意事项

一、数据类型选择规范

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='用户基本信息表';
相关推荐
j7~8 分钟前
【仿 RabbitMQ 项目】发布订阅消息队列 (篇八):交换机数据管理模块,分层类设计、SQLite 回调原理与代码实现
服务器·网络·数据库·消息队列·交换机·交换机数据管理·sqlite回调原理
PHP实战开发录10 分钟前
PHP上传文件为什么会被覆盖
数据库·mysql·php
SelectDB技术团队23 分钟前
VARIANT 列接入网关 JSON 日志:建表、导入、子列查询与物化的完整步骤
大数据·数据库·人工智能·elasticsearch·搜索引擎·json·apache doris
涛思数据(TDengine)28 分钟前
TDengine IDMP 2.0.2 发布:元素关系可视化、数据建模与联邦查询三大升级
大数据·数据库·人工智能·时序数据库·tdengine·工业互联网·工业ai
小米里的大麦29 分钟前
17 MySQL 视图
数据库·mysql
ss27334 分钟前
PG实战笔记 | 07 PostgreSQL常见编译项及其作用
数据库·笔记·postgresql
川石课堂软件测试35 分钟前
自动化测试|持续集成Git使用详解
git·python·jmeter·mysql·ci/cd·容器·kubernetes
丨白色风车丨1 小时前
LangChain 1.0 核心用法速查:创建模型、创建 Agent、调用
数据库·langchain
临床数据科学和人工智能兴趣组1 小时前
正则表达式的基础语法的常用字符类
数据库·r语言·r语言-4.2.1·临床研究·临床试验
鱼很腾apoc1 小时前
【MySQL】 第2期 查询与函数系统详解:从基础 CRUD 到复合多表查询
linux·数据库·学习·mysql·dml