强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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特点
JavaMyBatisSQL 灵活,写 XML 映射
JavaJPA/Hibernate对象映射,SQL 自动生成
PythonSQLAlchemy功能强大,两种模式
PythonDjango ORM简单易用,与 Django 深度集成
GoGORM全功能 ORM
Gosqlx轻量级,原生 SQL 增强
Node.jsPrisma类型安全,Schema 驱动
Node.jsSequelize成熟稳定

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 上线前检查

#检查项状态
1slow_query_log = ON
2long_query_time ≤ 1
3innodb_buffer_pool_size 设置合理
4character-set-server = utf8mb4
5binlog_format = ROW
6sync_binlog = 1
7innodb_flush_log_at_trx_commit = 1
8从库 read_only = ON
9备份策略就绪
10监控告警就绪
11所有 SQL 已 EXPLAIN
12连接池配置合理
13用户权限最小化
14时区设置正确
15max_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

扩展阅读