MySQL 完全指南 / 第 22 章:最佳实践
第 22 章:最佳实践
把经验沉淀为规范,把踩坑变成预防。本章汇总 MySQL 开发与运维的最佳实践。
22.1 连接池管理
22.1.1 为什么需要连接池
| 无连接池 | 有连接池 |
|---|
| 每次请求创建/销毁连接 | 复用已有连接 |
| 连接开销大(TCP握手+认证) | 几乎零开销 |
| 容易耗尽 max_connections | 控制最大连接数 |
| 连接泄漏风险高 | 自动回收空闲连接 |
22.1.2 各语言连接池配置
# Java - HikariCP(推荐)
spring:
datasource:
hikari:
maximum-pool-size: 20 # 最大连接数
minimum-idle: 5 # 最小空闲连接
idle-timeout: 600000 # 空闲超时(ms)
max-lifetime: 1800000 # 连接最大存活时间(ms)
connection-timeout: 30000 # 获取连接超时(ms)
leak-detection-threshold: 60000 # 连接泄漏检测阈值(ms)
# Python - SQLAlchemy
from sqlalchemy import create_engine
engine = create_engine(
'mysql+pymysql://user:pass@localhost/myapp',
pool_size=10, # 连接池大小
max_overflow=20, # 超出pool_size后最多再创建的连接数
pool_timeout=30, # 获取连接超时(秒)
pool_recycle=3600, # 连接回收时间(秒)
pool_pre_ping=True # 使用前检测连接是否有效
)
// Go - database/sql
db, err := sql.Open("mysql", "user:pass@tcp(localhost:3306)/myapp")
db.SetMaxOpenConns(25) // 最大打开连接数
db.SetMaxIdleConns(10) // 最大空闲连接数
db.SetConnMaxLifetime(5 * time.Minute) // 连接最大存活时间
db.SetConnMaxIdleTime(3 * time.Minute) // 空闲连接最大存活时间
22.1.3 连接池大小计算
连接池大小 ≈ CPU 核心数 × 2 + 磁盘数
示例:
- 4 核 CPU → 连接池 ≈ 10-15
- 8 核 CPU → 连接池 ≈ 20-25
- 多个应用连接同一 MySQL → 总连接数 ≤ max_connections × 0.8
⚠️ 连接数不是越大越好:过多连接导致上下文切换增加、内存消耗增大。
22.2 ORM 选择与使用
22.2.1 常见 ORM 对比
| 语言 | ORM | 特点 |
|---|
| Java | MyBatis | SQL 灵活,写 XML 映射 |
| Java | JPA/Hibernate | 对象映射,SQL 自动生成 |
| Python | SQLAlchemy | 功能强大,两种模式 |
| Python | Django ORM | 简单易用,与 Django 深度集成 |
| Go | GORM | 全功能 ORM |
| Go | sqlx | 轻量级,原生 SQL 增强 |
| Node.js | Prisma | 类型安全,Schema 驱动 |
| Node.js | Sequelize | 成熟稳定 |
22.2.2 ORM 使用原则
1. 简单 CRUD → 用 ORM(提高开发效率)
2. 复杂查询 → 用原生 SQL(ORM 生成的 SQL 可能低效)
3. 批量操作 → 用原生 SQL 或 ORM 的批量方法
4. 必须检查 ORM 生成的 SQL(EXPLAIN)
5. 开启 SQL 日志,定期审查
22.3 SQL 编写规范
22.3.1 查询规范
-- ❌ 禁止:SELECT *
SELECT * FROM users;
-- ✅ 明确指定列
SELECT id, username, email, created_at FROM users;
-- ❌ 禁止:隐式类型转换
SELECT * FROM users WHERE phone = 13800000000; -- phone 是 VARCHAR
-- ✅ 类型一致
SELECT * FROM users WHERE phone = '13800000000';
-- ❌ 禁止:索引列上使用函数
SELECT * FROM users WHERE YEAR(created_at) = 2026;
-- ✅ 范围查询
SELECT * FROM users WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01';
-- ❌ 禁止:大 OFFSET 分页
SELECT * FROM orders LIMIT 10 OFFSET 1000000;
-- ✅ 游标分页
SELECT * FROM orders WHERE id > 1000000 LIMIT 10;
-- ❌ 禁止:OR 导致全表扫描(无索引时)
SELECT * FROM users WHERE username = 'a' OR email = 'b';
-- ✅ 使用 UNION 或确保有索引
SELECT * FROM users WHERE username = 'a'
UNION
SELECT * FROM users WHERE email = 'b';
22.3.2 写入规范
-- ❌ 禁止:逐行插入
INSERT INTO t VALUES (1);
INSERT INTO t VALUES (2);
INSERT INTO t VALUES (3);
-- ✅ 批量插入
INSERT INTO t VALUES (1), (2), (3);
-- ❌ 禁止:大事务
START TRANSACTION;
-- 100万行的 INSERT/UPDATE/DELETE
COMMIT;
-- ✅ 分批提交
-- 每次 1000-5000 行,分多次提交
-- ❌ 禁止:不带 WHERE 的 UPDATE/DELETE
DELETE FROM users;
UPDATE users SET status = 0;
-- ✅ 必须带 WHERE 和 LIMIT
DELETE FROM users WHERE status = 0 AND created_at < '2023-01-01' LIMIT 1000;
22.3.3 表设计规范
-- 1. 每张表必须有主键(BIGINT UNSIGNED AUTO_INCREMENT)
-- 2. 每张表必须有 created_at 和 updated_at
-- 3. 字符集统一 utf8mb4
-- 4. 存储引擎统一 InnoDB
-- 5. 字段尽量 NOT NULL + DEFAULT 值
-- 6. 金额用 DECIMAL 或 BIGINT 存分
-- 7. 状态/类型用 TINYINT UNSIGNED
-- 8. 索引命名:idx_表名_列名
-- 9. 唯一索引命名:uk_表名_列名
22.4 生产环境 Checklist
22.4.1 上线前检查
| # | 检查项 | 状态 |
|---|
| 1 | slow_query_log = ON | □ |
| 2 | long_query_time ≤ 1 | □ |
| 3 | innodb_buffer_pool_size 设置合理 | □ |
| 4 | character-set-server = utf8mb4 | □ |
| 5 | binlog_format = ROW | □ |
| 6 | sync_binlog = 1 | □ |
| 7 | innodb_flush_log_at_trx_commit = 1 | □ |
| 8 | 从库 read_only = ON | □ |
| 9 | 备份策略就绪 | □ |
| 10 | 监控告警就绪 | □ |
| 11 | 所有 SQL 已 EXPLAIN | □ |
| 12 | 连接池配置合理 | □ |
| 13 | 用户权限最小化 | □ |
| 14 | 时区设置正确 | □ |
| 15 | max_allowed_packet 够大 | □ |
22.4.2 my.cnf 生产模板(16G 内存)
[mysqld]
# 基础
server-id = 1
port = 3306
default-time-zone = '+08:00'
character-set-server = utf8mb4
collation-server = utf8mb4_0900_ai_ci
# InnoDB
innodb_buffer_pool_size = 10G
innodb_buffer_pool_instances = 8
innodb_log_file_size = 2G
innodb_flush_log_at_trx_commit = 1
innodb_file_per_table = ON
innodb_open_files = 65535
# 连接
max_connections = 500
wait_timeout = 600
interactive_timeout = 600
# 日志
log-error = /var/log/mysql/error.log
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = ON
# Binlog
log-bin = mysql-bin
binlog-format = ROW
sync_binlog = 1
binlog_expire_logs_seconds = 604800 # 7天
max_binlog_size = 256M
# 安全
local_infile = OFF
skip-name-resolve
[client]
default-character-set = utf8mb4
22.5 常见反模式
| 反模式 | 问题 | 正确做法 |
|---|
| 存储大文件 | 数据库膨胀 | 存 OSS/S3,数据库存路径 |
| 过度范式化 | 太多 JOIN | 适度反范式 |
| 无索引 | 全表扫描 | 为 WHERE/JOIN 列建索引 |
| 过多索引 | 写入变慢 | 只建必要的索引 |
| 使用 UUID 做主键 | 无序、占空间 | BIGINT AUTO_INCREMENT |
| 存储过程写复杂业务 | 难调试、难版本控制 | 业务逻辑放应用层 |
| 不用事务 | 数据不一致 | 关键操作必须事务 |
| 大事务 | 锁持有时间长 | 分批提交 |
业务场景
场景 1:新项目初始化 Checklist
1. 创建数据库(utf8mb4)
2. 设计表结构(遵循规范)
3. 创建必要索引
4. 配置 my.cnf(参考生产模板)
5. 设置用户权限(最小化)
6. 开启慢查询日志
7. 配置备份策略
8. 配置监控告警
9. 编写 SQL 规范文档
10. 上线前 EXPLAIN 所有 SQL
扩展阅读