数据深分页问题

乐云一
  • 笔记
  • note
About 877 wordsAbout 3 min

MySQL深分页

深分页是一个老生常谈,出现在数据长久累积,查询分页时的慢查询问题;

本篇为记录当数据量大时,对于分页业务的几种解决方式。

背景

大数据列表中,在没有引入搜索引擎前,存在10000+页码的数据。

当用户选择大页面数据时,由于MySQL分页的特性,即搜索前10000页偏移量,丢弃前10000页不相关数据。导致MySQL从索引树上搜寻、回表、再搜寻,需要N量级的开销;即需要丢弃的数据越多,搜寻下标耗时越久。

解决方案

根据资料和相关了解简单有以下三种说法

分页游标

根据拿到上一页最后一条数据的主键ID,然后在SQL语句中将该主键作为条件,只查询大于该ID的数据,随后在进行条件分页查询

SELECT FROM test WHERE create_time>='2023-07-18' limit 10;

//下一页
SELECT from test WHERE create_time>='2023-07-18' AND id > 10 LIMIT 10;


优点:在可以拿到上一页最后一条数据的场景中完全可用,比如APP的下一页下一页;或者根据业务条件查询拿到上一条数据。

缺点:只适用与自增ID,同时局限性很大:必须拿到上一条数据,在APP上一页一页操作是不可能翻到10000页数据的,而WEB上该方法又很难适用

子查询SQL

当ID同样为自增主键时

可通过先将条件中分页数据的临界值查询出来,在进行ID的过滤

SELECT FROM test WHERE id > (SELECT FROM test WHERE create_time >='2023-7-18' LIMIT 1) LIMIT 10000,10

当ID为不规则主键时

可通过子查询分页,只查询ID,虽然没有根本性解决问题,但是可以解决索引回表的问题。

SELECT  FROM test WHERE id IN ( SELECT id FROM ( SELECT id FROM test WHERE create_time>='2023-07-18' LIMIT 100000,10 ) as t);

Inner Join关联查询

和子查询相似,通过检索主键值解决索引回表的问题。

SELECT FROM test INNER JOIN ( SELECT id FROM test WHERE create_time>='2023-07-18'     LIMIT 100000,10) AS t ON test.id=t.id;

题外话

深分页问题再原则上来说是没有一个根本性的解决方案的,即使由于数据体量问题采用了ES/Lucene等搜索引擎;

由于数据搜索的局限性,都存在 过滤搜寻>找到范围值>丢弃前n页数据问题

虽然搜索引擎使用的是倒表结构,可以有效的根据条件找到目录值,不过依然无法精准的去定位页码数量。

不过我们可以通过业务级的手段去优化查询速度。

业务级优化

可以简单实现的是,根据数据库表中数据的特性,新加一个分组字段。

比如年份/业务标识/组织ID等等...

在查询上进行强制条件分组,这样用户所见的页码最大数就是我们可以进行控制的数量。

第二就是可以根据数据量进行分表,将一个页面展示的数据根据分表去划分新旧区域。

最后就是 建议产品谨慎考虑深分页问题,最好砍掉 :)

Last update:
Contributors: leyunone
Comments
  • Latest
  • Oldest
  • Hottest
Powered by Waline v2.14.7