同事反馈了一个问题,MySQL 5.7的环境中,这条SQL非常慢,test表就一万多数据,而且字段tid有索引,

select * from test where tid = 'xxxxx1';

P. S. 屏蔽业务属性,表、字段等均为模拟,旨在说明。

看了下他的执行计划,type=ref,key是tid的单键值索引,ref是const,都是很正常的。

test表数据量1万,实际执行一次如上的SQL,需要10秒才可以返回229条数据,执行计划应该是最优的了。

看下数据分布,'xxxxx1'是记录最多的,229条,'xxxxxx2'排名第二,160条,

select tid, count(*) from test group by tid order by 2 desc;

但是按照'xxxxx2'条件进行检索,返回160条数据,就很快,

select * from test where tid = 'xxxxx2';

有可能是数据量的问题么?

"229/1万"和"160/1万",从过滤性上,应该差距不大。

再看下test的结构,几十个字段,发现了个text类型的字段(detail)。

对比下这两个检索字段值,返回的detail内容,发现'xxxxx1'的detail内容很多,每行detail字段长度是617256个字符,如果*229行,换算一下,所有的detail大约135MB的空间容量,而'xxxxx2'的detail字段长度,只是33个字符,如果*160行,detail大约只占0.005MB,由于单键值tid索引只是包含tid一个字段,因此每次执行,都需要进行回表,才可以得到detail等其他的字段,返回135MB和0.005MB的消耗,显而易见了,

select detail, length(detail) from test where tid = 'xxxxx1';
select detail, length(detail) from test where tid = 'xxxxx2';

为了侧面证明,可以检索除detail外的其他字段(均是int、varchar等类型,无大字段),同样的'xxxxx1'条件,很快返回,

select tid, id, description from test where tid = 'xxxxx1';

因此,有理由相信,之所以'xxxxx1'返回慢,还是因为他要返回的数据中detail大字段的内容导致返回的数据量较大,回表消耗太高。

一条SQL的执行计划正确,未必代表他的执行速度一定很快,SQL执行的方方面面,都可能存在优化的需求,还是得综合考量。

针对这个问题,目标就很明确了,为了降低消耗,可从业务层面,考虑降低返回的记录数,进而减少返回的数据容量。