设计数据库表结构和编写 SQL 语句时,性能往往是决定系统稳定性的关键。这里不罗列枯燥的理论,只针对 MySQL 这一 Web 应用中最常用的数据库,分享一些经过实战验证的优化技巧。
1. 谨慎使用查询缓存
大多数 MySQL 服务器默认开启查询缓存,这是提升性能的有效手段之一。当相同查询多次执行时,结果会被缓存,后续请求直接读取缓存即可。
但要注意,程序员容易忽略某些函数会导致缓存失效。例如 CURDATE()、NOW() 或 RAND(),因为它们的返回值是不确定的。如果必须用这些函数,建议先用变量接收值再传入 SQL,或者意识到这部分无法被缓存。
2. 善用 EXPLAIN 分析查询
在 SELECT 前加上 EXPLAIN 关键字,可以查看 MySQL 如何处理你的 SQL 语句。它能告诉你索引是否生效、数据扫描行数以及排序方式等关键信息。
比如一个多表关联查询,如果忘记加索引,扫描行数可能高达数千;加上索引后,可能只需几行。重点关注 rows 列,它是发现性能瓶颈的直观指标。
3. 限制返回行数
如果你确定只需要一条数据,务必加上 LIMIT 1。这样 MySQL 引擎找到第一条记录后就会停止搜索,而不是继续遍历剩余数据。同时,尽量只查询需要的字段,避免 SELECT *,减少网络传输和内存消耗。
4. 为搜索字段建立索引
索引不仅限于主键。对于经常用于搜索的字段(如 last_name),建立索引能显著提升速度。但要注意,模糊查询中 % 在前缀的情况(如 LIKE '%apple%')通常无法利用普通索引,此时可考虑全文索引或自定义索引策略。
5. JOIN 查询的规范
多表连接时,确保 Join 字段已建立索引,且类型完全一致。例如 DECIMAL 不能直接和 INT 连接,字符串类型还需字符集匹配。否则 MySQL 无法使用索引,导致全表扫描。
6. 避免 ORDER BY RAND()
随机打乱数据是新手常犯的性能杀手。ORDER BY RAND() 会让 MySQL 对每一行计算随机数并排序,即使加了 LIMIT 1 也无法避免。更好的做法是先统计总数,在应用层生成随机偏移量,再用 LIMIT offset, 1 获取。
7. 主键设计的最佳实践
每张表都应设置一个自增主键,推荐使用无符号整型(UNSIGNED INT)。虽然 VARCHAR 也能做主键,但性能较差。主键在集群、分区等场景下至关重要。注意,关联表的主键可由多个外键组成,这属于特殊情况。
8. ENUM 替代 VARCHAR
对于取值固定且有限的字段(如性别、状态),使用 ENUM 类型更紧凑高效。它底层存储为 TINYINT,但对外表现为字符串。配合 PROCEDURE ANALYSE() 工具,MySQL 能根据实际数据给出字段类型优化建议。
9. NOT NULL 的使用习惯
除非业务逻辑明确需要空值,否则字段应设为 NOT NULL。NULL 值占用额外空间,且在比较运算中会增加逻辑复杂度。Oracle 甚至将 NULL 视为空字符串,但在 MySQL 中需特别注意区分。
10. Prepared Statements 的重要性
预编译语句不仅能防止 SQL 注入,还能在重复查询时提升性能。MySQL 只需解析一次 SQL 模板,后续仅绑定参数。PHP 中推荐使用 mysqli 扩展或 PDO。注意,旧版本中 Prepared Statements 不支持查询缓存,但新版已修复。
11. 无缓冲查询的应用
默认情况下,PHP 脚本会等待所有结果返回才继续执行。使用 mysql_unbuffered_query()(或 mysqli 对应方法)可以边查边处理,节省内存,尤其适合大数据量结果集。但需注意,部分函数(如 mysql_num_rows())在无缓冲模式下不可用。
12. IP 地址的存储方式
不要将 IP 存为 VARCHAR(15),建议使用 UNSIGNED INT。IP 转整形只需 4 字节,定长字段查询更快。PHP 中可用 ip2long() 和 转换,SQL 中可用 和 。


