今天有一个客户说,他拉取流水记录总是超时。
我打开数据库一看,流水记录表有900万数据。为了方便描述问题,我们假设这张表的名字叫 t_flow。
其实拉取记录的语句很简单。
SELECT * FROM `t_flow` WHERE `time` > timestamp AND `userid`=my_userid OR `uuid`=my_userid ORDER BY `time` DESC LIMIT 0,10;
在900万条数据的情况下,执行这条语句花了7秒多。这张表的各个列的索引都已建立。
我一眼看到了`userid`=my_userid OR `uuid`=my_userid这个地方。 这是项目里面为了方便,同事设计的,不管my_userid是UUID还是真的userid都可以用同一个函数。好在我们的userid是6位,而uuid是10多位。所以我做了一个优化。 先判断my_userid是uuid还是userid,这样少了一个OR。 时间缩短到了5秒多。
伪代码如下
if(my_userid.length == 6){
SELECT * FROM `t_flow` WHERE `time` > timestamp AND `userid`=my_userid ORDER BY `time` DESC LIMIT 0,10;
}
else{
SELECT * FROM `t_flow` WHERE `time` > timestamp AND `uuid`=my_userid ORDER BY `time` DESC LIMIT 0,10;
}
但是5秒多也不行啊。才这么点数据。于是我去掉了LIMIT,发现,去掉LIMIT后,执行就只需要2秒多了。
百思不得其解。我于在网上搜索了许久的关于SQL语句中添加了LIMIT反而更慢的问题,仍未找到答案。
由于有900多万条,而我用的是SELECT *,我怀疑是这里有问题,导致WHERE语句在查找的时候,消耗了太多内存。
于是我做了一个新版本。
SELECT * FROM `t_flow` WHERE `uuid` IN( SELECT `uuid` FROM `t_flow` WHERE `time` > timestamp AND `userid`=my_userid OR `uuid`=my_userid) ORDER BY `time` DESC LIMIT 0,10;
先查找出uuid,再直接获取uuid的内容。这样有一个好处就是,在进行 time > timestamp筛选的时候,只拿出了uuid,减少了内存开销。
因为我们的MYSQL服务器的内存有限,如果大量的内存开销就会触发内存页面置换,从而导致花更多的时间。
最后本条语句执行只花了400ms,由于只是一个数据统计功能,所以免强算达标。