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_xxxxxx_flagis_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='用户基本信息表';
相关推荐
华仔啊2 小时前
如何避免MySQL死锁?资深DBA的9条黄金法则
后端·mysql
@老蝴2 小时前
MySQL数据库 - 约束和联合查询
android·数据库·mysql
程序猿20232 小时前
MySQL索引使用--最左前缀法则
数据库·mysql
老华带你飞2 小时前
列车售票|基于springboot 列车售票系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习·spring
IvorySQL3 小时前
PostgreSQL 中的“脏页(Dirty Pages)”是什么?
数据库·postgresql·开源
咖啡の猫3 小时前
Python字典的查询操作
数据库·python·c#
这儿有一堆花3 小时前
2025 年免费指纹浏览器清单
数据库
czhc11400756635 小时前
c# 1213
开发语言·数据库·c#
voltina5 小时前
【SQL】【事务】
数据库·sql