如果你问程序员害怕什么,那我觉得接手「祖传代码」肯定可以排的上名号,你永远不知道它有哪些神奇的设计,你永远不知道还有哪些彩蛋,也许在下一个转角你就能得到惊喜,最近笔者就遇到了一件让人哭笑不得的事情。
事情是这样的,有一个发券的系统,产品经理准备在这个系统上加新功能,可以给券打上不同的标签,并且前端可以根据不同的标签来筛选我所获得的券,需求不算很复杂,开发,测试都很顺利,然后就上到了pre环境(和线上数据一致,但只有内部流量能访问),可以在测试环境却出了问题,测试同学反馈说接口无法返回数据,我一想这不应该啊,这都测多少遍了怎么还有问题,可以当我自己操作以后,果然接口返回了一个500错误。
出了问题就得排查,当打开日志的时候发现了一个很蹊跷的情况,一些很简单的SQL查询却执行了很长的时间,不信邪我们把日志里的SQL拿到数据库里执行但是发现非常快,几毫秒就返回了,那到底是什么情况呢?耗时超过2s
绝大部分mysql慢查询的事情其实归根结底都是索引的事情,我们也往索引这个方向查,但是发现表建了索引,explain的结果也表明查询用到了索引,那为什么用到了索引还是这么慢呢,而且还是一个必现的case。用到了索引
然后就和小伙伴一顿查,以为是gorm的问题等等,但都不是,最后在看建表语句的时候发现了一个问题,这个表uid的字段是用varchar类型的,而前端和web应用的uid都是int64类型,导致查询的时候对字段进行了隐式转换然后没有用到了索引,果不其然查询结果也验证了我们设想。全表扫描
事情到这基本就明了,这个老表建的时候没有考虑后续的情况,对索引字段使用了varchar类型,导致全表扫描耗时过长,此外sql日志对查询字段做了一些处理并不是最后执行的语句,这个地方也误导了我们很久,因为日志里的字段都被引号引起来了,然后手动执行的时候都命中了索引,有的时候「眼见都不一定为实」啊,那为什么测试环境都很正常呢,很简单,因为测试环境数据量少,就算全表扫描也是很快的。MySQL的查询对索引的使用是有条件的,其中一条就是,对索引的任何隐式转换和计算都会导致索引失效,经过了这个case,我想我会对这点记得更久了。