与其说是bug,倒不如说是自己的失误。
背景:php代码重构为go语言beego项目,重构完后,对部分接口进行压测,发现某个接口qps一直上不去,响应时间非常高。经过排查发现是数据库查询花了很多时间。
表是一张两千万张数据的表。有建立索引
sql如下:
SELECT
user_id,count(1) as count
FROM
star_fans
WHERE flag=1 and user_id in (
2165,286,2471,3052892,13582,1351
)
GROUP BY user_id
通过explain sql 分析sql 发现Select_type 为index
根据这个命令的相关字段详解发现:
表示MySQL在表中找到所需行的方式,又称“访问类型”。
常用的类型有: ALL, index, range, ref, eq_ref, const, system, NULL(从左到右,性能从差到好)
Select_type 说明查询中使用到的索引类型,如果没有用有用到索引则为all
ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行
index: Full Index Scan,index与ALL区别为index类型只遍历索引树
range:只检索给定范围的行,使用一个索引来选择行
ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system
NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
其中row居然是完整的2000w条数据,意味着等于全表扫描了。
所以非常疑惑,况且对索引的理解不是很深,看了表结构,发现索引建立的也没问题。况且php中也是使用同样的sql,它的接口却访问的很快。
后面几经排查,发现是由于sql中数据类型不对导致的,string类型没有加引号,php为弱类型语言,可能orm框架中有对其进行优化。
果不其然,sql改为:SELECT
user_id,count(1) as count
FROM
star_fans
WHERE flag=1 and user_id in (
'2165','286','2471','3052892','13582','1351'
)
GROUP BY user_id
再通过explain sql发现 select_type变成了range ,同时rows变成几万条而不是两千万条
总结:在写业务代码时或者重构时,要注意不同语言的差异,以免踩坑,虽然不加引号也能查出来,但是效率却是千差万别,足以说明压测的必要性,以及写代码时的严谨性,可以运行是一回事,写出优秀的代码更是不容易。