1.问题:在售后运营平台查找数据的时候,由于有人点了最后一页,导致了深分页,数据库cpu使用率飙高,影响售后业务。
sql: SELECT * FROM afs_service
WHERE afs_service_state = 32
AND approve_result = 10 AND create_time >= '2020-06-04 23:59:59'
AND apply_deal = '10' AND create_time <= '2020-06-11 23:59:59'
ORDER BY afs_service_id DESC LIMIT 55120,20
2.原因:
(1)一部分原因是由于分页太深导致的性能影响 LIMIT 55120,20
MySQL的limit工作原理就是先读取前面n条记录,然后抛弃前n条,读后面m条想要的,所以n越大,偏移量越大,性能就越差。
(2)另一部分原因是由于这张数据库表的索引加的太多,通过看执行计划,发现查询优化器选择了一个区分度较低的索引,用到的是idx_afs_status索引(afs_service_state字段的索引),区分度只有17,扫描出的rows行过于庞大。
而没有用到这个表中的另外一个索引,create_time和afs_service_state的联合索引。
(3)并且由于查询较慢,未加载出数据时,反复点击前一页,导致多个深分页查询,使数据库cpu升高,影响整个库的数据库访问。
3.解决方式: 避免深分页查询,去除掉最后一页,以及指定页码访问这种操作,降低查询带来的影响。
在查询页面每次记录下头尾的主键id,在查询条件中增加主键范围查找,避免深分页。
Select * from table where id in(
Select id from table where id > x and condition1 = y and condition2=z
Limit pageSize order by id sortType
)
一般正常查看列表不会有去看最后一页的这种需求,像天猫,淘宝,京东,搜索商品也是只能点击下一页,也没有跨页去查找的这种功能。通常都会有指定的条件去筛选出需要的结果。
4.es也存在着深分页的问题
(1)es默认使用的也是from,size的查询方式,和mysql类似。比如我们要找from=200,size=10。那么es需要在每个分片上,根据条件查找210条数据,然后进行排序处理,最后在所有的结果中取10条返回,这种查询浪费很多机器资源.并且es设置了分页的深度max_result_window,7.x之前默认是1w,7.x之后默认是25000。如果from+size超过了这个长度,分页查询就会抛异常。如果一定要用这种方式去分页查询,就要修改对应的设置。
(2)es提供了2种其他的分页查询的方式,一个是scroll,另一个是search_after。
其中scroll 相当于对查询维护了一份快照,初始化时将所有符合搜索条件的搜索结果缓存起来,遍历的时候从这份快照里取数据。因此不适合作为实时数据的查找,。在每次使用scroll的时候,需要制定一个时间窗口,每次搜索在时间窗口内完成即可。第一次请求后会返回一个scorll id,之后的请求要带着这个scrollid参数去查询。
(3)search_after是5.x版本之后提供的另一种分页查询方式。根据上一页来确定下一页的位置,这就跟我们上述mysql 的处理方式很像。并且如果索引数据有修改,也会实时查询出来,反映到对应的游标中。适合用于实时分页查找,但是不满足跳页查找,只能用于上一页下一页的这种查找。