一个600多万条记录的大表,两条几乎相同的查询语句,仅排序有区别:
sql> SELECT *
FROM t_admin_log
WHERE imei = '3f073c2150aa4bfd'
ORDER BY id
LIMIT 100
[2019-10-09 15:48:51] 100 rows retrieved starting from 1 in 3s 104ms (execution: 3s 62ms, fetching: 42ms)
sql> SELECT *
FROM t_admin_log
WHERE imei = '3f073c2150aa4bfd'
ORDER BY id DESC
LIMIT 100
[2019-10-09 15:49:00] 100 rows retrieved starting from 1 in 370ms (execution: 95ms, fetching: 275ms)
数据位于表的靠后位置。
可以看到,倒序比正序的速度快很多,原因应该是需要扫描的数据量比较少吧。
另一个例子:
sql> SELECT *
FROM t_admin_log
WHERE imei = '3f073c2150aa4bfd'
ORDER BY id DESC
LIMIT 10
[2019-10-09 15:54:23] 10 rows retrieved starting from 1 in 143ms (execution: 119ms, fetching: 24ms)
sql> SELECT *
FROM t_admin_log
WHERE imei = '3f073c2150a6a4bfd'
ORDER BY id DESC
LIMIT 10
[2019-10-09 15:54:36] 0 rows retrieved in 3s 963ms (execution: 3s 946ms, fetching: 17ms)
两个查询语句只有imei有区别,前一个是存在的,后一个是不存在的,查询结果也相差好多。推测是imei存在时,由于limit 10,只要找到10条就返回了;如果不存在,就会从后向前一直找,直到表的开头才发现原来没有这项数据,所以花费的时间就很长。
所以,查询性能并不只和数据量有关,还和数据的分布有关。假如某个日志表有一亿条数据,但是都是id自增的,时间为插入的时间。那么查询最近的数据是非常快的。而如果不幸表里没有符合查询条件的数据,那么查起来就比较慢了。
那么,我觉得有一个思路,对单个大表,假如是有序的,那么每隔一定距离就添加一个标记,比如日志表按天为标记,记录下对应的日志id和日期,存入单独的表。后续查询时,假如我们知道起始日期,那么就可以根据日期查找标记表中的id,在以id为限定范围进行查询,这样速度就很快了。比如一个大型应用的日志记录,某用户注册日期是2019年10月1日,标记表中2019年10月1日对应的id是50000000,那么对所以该用户的查询都可以加上限定条件:where id>50000000,这样就避免了limit的最后一页对后续的5千万条数据进行扫描。
当然综合来看,如果能分表,查询性能肯定更好些,但是结构上也会变复杂了。 |