百万表遇到的分页问题

176 阅读3分钟

新的中台系统已经打造。进行了百万级设备的接入压测,目前这种trpc-go/trpc-java框架下看起来还是非常的有效率,我所在的数据资产管理服务与api资源中心进行交互时,大约1000条数据访问只花了500us-2ms看起来提升的非常之大。 但是也有问题,今天关注的问题是 ‘设备列表响应时间有点长’,表现为设备列表页面,点击页签2023,响应耗时为8s左右。 这是个分页查询接口,数据库对应的是mysql,表名为t_device_bak,引擎是innodb,索引如下:

PRIMARY KEY (id) USING BTREE,

UNIQUE KEY t_device_pk (w_id) USING BTREE,

UNIQUE KEY uk_pidsn (product_id,sn) USING BTREE,

KEY status_sum_index (product_id,status,row_status),

KEY cr_index (create_time)

查询sql如下: SELECT xxxxxxx WHERE row_status=0 ORDER BY create_time desc, id LIMIT 10 OFFSET 1010130; 第一眼看的出来,索引根本没命中,explain走起:

image.png 截图显示了MySQL中一个特定SELECT查询的EXPLAIN命令输出。EXPLAIN命令用于获取查询执行计划,这提供了MySQL执行查询的方式的信息。

从输出中可以推断:

表: t_device_bak

类型: ALL,表示进行了全表扫描。

可能的键: NULL,意味着没有索引可以使用。

键: NULL,表示实际上没有使用索引。

行: 970031,表明扫描了这么多行。

额外: Using where; Using filesort,表示使用了WHERE子句来过滤结果,并使用了文件排序来排序结果,因为没有可用的索引来避免排序操作。

查询之所以花费很长时间,是因为它进行了全表扫描来检索数据。当你对没有索引的列进行ORDER BY操作时,MySQL必须对所有行进行排序 - 在这个案例中大约是970,031行 - 过滤它们通过WHERE子句。OFFSET也非常大(1010130),这增加了执行时间,因为MySQL必须先计数这么多行,然后才开始返回结果。

没走索引

加上索引看看 KEY cr_id_index (row_status,create_time,id)

加上索引也没用,索引失效了

image.png 执行时间仍然高达6.8s几乎没提升。

--》另一个思路,深分页 先说答案 sql语句如下:

SELECT ***** FROM QQQQ.t_device_bak a

INNER JOIN (SELECT id FROM QQQQ.t_device_bak

WHERE row_status=0 ORDER BY create_time desc, id LIMIT 1010130, 10)

AS b ON a.id = b.id;

image.png

image.png 性能差距非常明显

这条语句之所以比简单的SELECT语句性能更高,是因为它使用了一种叫作“子查询”的技术,结合了INNER JOINLIMIT子句,以减少查询的数据量和提高查询效率。

在这个例子中,子查询 (SELECT xxxxxxx WHERE row_status=0 ORDER BY create_time DESC, id LIMIT 1010130, 10) 首先根据row_status过滤数据,然后按照create_timeid排序,并使用LIMIT子句仅获取第1010130条到第1010140条记录的id。这个子查询可以大大减少需要处理的数据量。

然后,主查询通过INNER JOIN子句将子查询的结果与原始表QQQQ.t_device_bak结合在一起,基于匹配的id值来获取其他相关列的数据。这样,主查询只需要处理子查询返回的那些id对应的数据,而不是整个表的数据,从而提高了查询性能。

总之,这个查询之所以性能更高,是因为它利用了子查询、INNER JOINLIMIT子句来减少处理的数据量,从而提高了查询效率。