我干了十五年数据库,见过太多人把MySQL当Excel用。数据量一上来,查询慢得像蜗牛爬,然后就开始砸钱买硬件、加缓存,其实根本问题出在SQL本身。今天就聊聊五个实实在的优化方案,不整虚的,全是实战经验。

先说说索引这个老生常谈的话题。很多人建索引就是给主键加一个,其他字段全靠运气。你想想,一张表几百万条数据,没有索引的查询就像在图书馆里找一本书,管理员把书全部倒在地上让你翻。但索引也不是越多越好,我见过有人一张表建了三十多个索引,写入慢得要死,查询也没快多少。关键在于理解联合索引的最左前缀原则。比如你经常用查询,那就建一个(a,b)的联合索引,别建两个单独的索引。还有,索引列不要参与运算,这种写法,索引直接废掉。记住一个原则:能用索引覆盖就用索引覆盖,避免回表查询,这是提升查询速度最直接的方法。
再说说查询语句本身的问题。很多人写SQL就像写散文,想怎么写就怎么写。这种写法,在数据量小的时候没问题,一旦数据量上百万,把不需要的字段都查出来,数据传输就要花大量时间。正确的做法是只查需要的字段,比如。还有,子查询能不用就不用,很多子查询都可以改写成join,性能能提升好几倍。比如说要查每个分类下最新的十条记录,用子查询写出来很复杂,用join加临时表反而简单。另外,配合的时候要小心,如果的字段没有索引,MySQL要先把所有数据排序再取前几条,数据量大的时候直接爆炸。
第三个方案是关于表结构和字段类型的选择。我见过有人用varchar(255)存ip地址,用text存状态字段,这都是灾难。字段类型越小,数据库检索越快,这是铁律。能用tinyint就别用int,能用char就别用varchar。比如性别字段,存0和1比存"男""女"快得多。还有,字段尽量不要设置为null,因为null值在索引中是个特殊存在,查询效率会降低。如果某个字段确实可能没有值,设置成默认值0或者空字符串都比null好。另外,大字段要单独拎出来,比如文章内容这种text类型,如果经常和主表一起查,可以把内容单独放在一张表里,用主键关联,这样主表的查询速度会快很多。
第四个方案是关于分页查询的优化。传统的在offset很大的时候会非常慢,因为MySQL要扫描前面所有的数据。比如你要查第100万页的数据,MySQL先扫描前100万条,然后再取10条,前面的990万条都白扫了。解决方案是用子查询或者join来跳过前面的数据。比如,这样只扫描索引,不用扫描数据行。或者用上一页一条记录的id来做分页,比如,这种方式在数据量很大的时候表现特别好。记住,分页查询的核心是避免扫描无用的数据。
第五个方案是关于慢查询的监控和分析。很多人遇到性能问题就瞎猜,觉得是某个查询慢,改来改去发现根本不是那回事。正确做法是开启慢查询日志,设置,记录所有执行时间超过1秒的查询。然后配合分析执行计划,看看有没有全表扫描、有没有文件排序、有没有使用临时表。输出中的字段特别重要,如果看到那就是全表扫描,必须加索引。字段出现或者也要警惕,说明查询需要额外的排序或者临时表,通常意味着需要优化索引或者改写SQL。我曾经帮一个客户优化,发现一个查询执行了30秒,分析后发现就是因为join的时候没有索引,加了一个索引后直接降到0.3秒。
这些优化方案看起来都不难,但真正能在实际工作中落实的人不多。原因很简单,很多人觉得数据库优化是DBA的事,开发只要把功能实现就行。但你想一下,一个查询慢10倍,用户就在那里等10倍的时间,业务损失谁来承担?数据库优化不是锦上添花,是基本功。每次写SQL之前,多想一下这条语句会怎么执行,索引能不能用上,数据量大起来会不会出问题。养成这个习惯,你的MySQL性能至少提升10倍。别等到线上报警了再想办法,那时候代价就大了。


