在最近的一次MySQL测试过程中,我的同事幺加明遇到了一些令人困惑的现象,这些现象超出了我们最初的预期。一直以来,我们在建立索引时,首要考虑的原则是在区分度大的字段上建立索引。然而,在实际测试中,我们发现区分度大的索引并不总是最佳选择,而是需要结合具体的使用场景进行深入分析。在幺加明的授权下,我将这个对比案例分享给大家,并再次向幺加明表示感谢。
首先,我们简要回顾一下MySQL的数据结构,具体可参考《ES 与 MySQL 在较大数据量下查询性能对比》这篇文章。
起初,我们只在content_id
字段上创建了索引(idx_content_id
),因为content_id
的区分度最大,理论上在此索引上进行查询应该是速度最快的。然而,实际测试结果却让我们大跌眼镜:查询公开数据和部分不可见数据时,速度确实很快;但当查询部分可见数据时(例如content_id < 987872 AND user_id = 100
),速度却非常慢,达到了612毫秒左右。如下:
经过深入分析,我们发现造成这一结果的主要原因是:虽然查询条件content_id < 987872
能够快速定位到索引所在的叶子节点,但要想找到满足user_id = 100
条件的数据,却需要进行大量的回表操作。这是因为我们的数据特性是:1.5亿条数据对应5000个用户,而user_id = 100
只相当于取满足匹配条件数据中的五千分之一。这也解释了为什么查询user_id != 100
的条件时能够快速返回结果,因为不等于100相当于取数据总量的五千分之四千九百九十九,所以可以快速匹配到数据。
基于以上分析,我们决定增加一个联合索引(idx_content_id_user_id
),希望这样能够快速定位到user_id = 100
的数据。
测试结果果然不负所望,查询时间提升到了244毫秒,比之前的612毫秒提升了2倍多。通过EXPLAIN
语句查看执行计划,我们发现最主要的区别是扫描的行数(rows
)大幅减少。
尽管uid
的区分度不高,导致扫描的行数仍然较多,但由于是联合索引,至少减少了回表操作,所以查询速度得到了显著提升。然而,244毫秒的查询速度仍然不是很快。结合我们的数据特性,我们进一步思考:是否可以建立user_id
和content_id
的联合索引(idx_user_id_content_id
),以减少回表次数?
测试结果令人振奋:使用idx_user_id_content_id
索引后,查询时间只需要6毫秒!这个结果非常令人满意。
综上所述,索引的建立并不是一件简单的事情,而是需要根据实际数据场景进行测试和分析,才能得到最优解。这个案例也再次提醒我们:在数据库优化方面,没有一成不变的规则,只有不断尝试和实践才能找到最适合自己的解决方案。