上周帮一个做电商的朋友调数据库,他服务器配置不低,16 核 32 GB 内存,SSD 硬盘,结果每次大促活动页面加载要三四秒。查了半天,问题出在数据库上——一个简单的商品查询语句居然要扫描上百万行数据。这事让我想起很多创业公司,技术团队拼命堆硬件,却忽略了数据库本身还能优化的空间其实很大。

先说索引,这玩意就像书的目录。没有索引的数据库查询,等于在图书馆里一本本翻书。很多开发人员建表时图省事,主键不设,或者把所有字段都加上索引。前者导致全表扫描,后者让写入变慢,因为每次插入都要更新一堆索引。正确的做法是,分析业务中的高频查询条件,比如用户查询订单时常用“用户ID + 订单状态”,就给这两个字段建复合索引。还有一点,索引字段的区分度要高,像性别这种只有两个值的字段,建索引基本没用,因为扫描一半数据和全表扫描区别不大。
查询语句的写法也大有讲究。见过太多人用 ,这种写法在数据量大的时候特别坑。子查询先执行,返回的结果集可能很大,外层查询再逐条匹配,性能直接崩掉。更好的做法是用 或 ,让优化器能够生成更高效的执行计划。还有一种常见错误:在 条件中对字段做函数运算,例如 ,这会让索引失效,因为数据库必须先对每行计算函数再比较。改成 ,索引就能生效。
分库分表这事,很多人一听就觉得高大上,其实真到需要的时候,问题往往已经很大。有个做 SaaS 的朋友,用户表三千万条数据,查询慢得要命,结果发现单表已经超过千万级别。MySQL 在几百万条数据时性能还行,超过千万就需要考虑拆分。分库分表的核心思路是水平切分,按用户 ID 哈希或按时间范围把数据分散到不同库或表里。但这会带来跨库查询、分页排序、事务一致性等复杂性。所以别一上来就分,先看看能否通过索引优化、缓存解决。
说到缓存,这是性价比最高的优化手段之一。Redis、Memcached 这些内存数据库能把热点数据从磁盘拉到内存,查询速度从毫秒级提升到微秒级。但缓存也有坑,最常见的是缓存穿透和缓存雪崩。缓存穿透指查询一个不存在的数据,每次都要查数据库,并发高时会把数据库打挂。解决办法是布隆过滤器或缓存空值。缓存雪崩指大量缓存同时过期,导致请求全部落到数据库。可以为不同的键设置不同的过期时间,加入随机值,避免集体失效。还有一点容易忽略:缓存的数据要与数据库保持最终一致性,别用户改了密码,缓存里还是旧密码。
数据库参数调优,这事很多人懒得做,觉得默认配置就行。实际上 MySQL 的默认配置是为小型应用准备的,生产环境必须调。比如 默认只有 128 MB,它决定 InnoDB 用多少内存来缓存数据和索引。如果服务器有 32 GB 内存,建议设到 20 GB 左右。再看 ,默认 151,高并发时很容易把连接数打满,导致新请求排队。但也不是越大越好,连接数太多会消耗系统资源,建议根据业务峰值压测后设定一个合理值,例如 500。 在 MySQL 8.0 已经废弃,因为查询缓存在高并发下反而会成为瓶颈。
监控和慢查询日志是找性能瓶颈的利器。很多公司数据库出问题才去查,平时根本不看。打开慢查询日志,设置阈值,例如 1 秒,所有超过 1 秒的查询都会被记录。我见过最夸张的案例,一个查询跑了 30 秒,开发人员竟然全然不知。定期分析慢查询日志,能看到哪些 SQL 需要优化,哪些索引没有被使用。配合 Performance Schema 或 pt-query-digest 等工具,能够精准定位问题点。还有一点容易被忽视:磁盘 I/O 使用率。如果持续超过 80%,说明磁盘已经成瓶颈,需要升级硬件或考虑读写分离。
读写分离是应对高并发读场景的经典方案。主库负责写,从库负责读,数据通过主从复制同步。很多电商、社交应用的读请求远多于写请求,比例可能达到 10:1 甚至更高。如果所有读都走主库,主库压力会非常大。读写分离后,主库专注写操作,从库可以水平扩展,增加从库数量提升读能力。但要注意主从延迟问题,用户刚下单就查询订单状态,如果数据还没同步到从库,用户可能看到旧数据。解决办法是关键查询强制走主库,或使用中间件感知延迟并做相应处理。
说个容易被忽略的点,数据归档。很多业务系统的数据只增不减,订单表三年累计上亿条,查询自然慢。但业务上,用户一般只查最近三个月的订单,一年前的数据几乎没人看。此时可以把历史数据从主表迁移到归档表或历史库,主表只保留最近三个月的数据,查询性能立刻提升。归档后,索引维护成本降低,备份恢复时间也缩短。我有个客户做了数据归档后,数据库大小从 200 GB 降到 30 GB,查询速度从 3 秒降到 0.1 秒,备份时间从 2 小时缩短到 15 分钟。
数据库优化说到底是技术和业务的博弈。索引建太多写入慢,分库分表搞太复杂开发成本高,缓存用不好数据不一致。最好的优化是理解业务逻辑,知道哪些数据是热数据,哪些查询是高频查询,然后有针对性地调整。别想着一步到位,先解决最痛的点,比如把慢查询干掉、把索引建对,再考虑更复杂的架构。毕竟,数据库优化的最终目的不是炫技,而是让用户体验更好,让老板省钱。


