千万级数据深度分页如何优化

 需求描述

报表系统:涉及订单数据全量查询,千万级数据深度分页性能优化

其他应用场景:

1、查询文章列表,一直滑动翻页,不用跳转到指定页数(移动端瀑布流效果)。

2、从数据库查询百万客户数据写入到redis。

3、访问某小程序的积分商城查看商品,一直滑动翻页,不用跳转到指定页数。

4、ETL大数据量,数据拆分。

难点分析

设计客户表结构,插入1000万数据。

CREATE TABLE `ry-cloud`.`person`  (
  `id` bigint NOT NULL  AUTO_INCREMENT,
  `name` varchar(50) NULL,
  `sex` varchar(10) NULL,
  `age` int(11) NULL,
  `email` varchar(100) NULL,
  `tel` varchar(50) NULL,
  `address` varchar(100) NULL,
  `create_time` datetime NULL,
  `update_time` datetime NULL,
  PRIMARY KEY (`id`)
);
  • 批量插入一千万数据
  • 随机datetime
  • -- 随机日期
    select DATE_FORMAT('1970-1-1' + INTERVAL FLOOR(RAND() * 10000) DAY, '%Y-%m-%d')
    
    -- 随机时间
    SELECT TIME_FORMAT(SEC_TO_TIME(FLOOR(RAND()* 86400)), '%H:%i:%s')
    
    -- 随机datetime
    select CONCAT(DATE_FORMAT('1970-1-1' + INTERVAL FLOOR(RAND() * 10000) DAY, '%Y-%m-%d'),' ',TIME_FORMAT(SEC_TO_TIME(FLOOR(RAND()* 86400)), '%H:%i:%s'))

    批量插入一千万数据

  • delimiter $
    
    create procedure batchInsert(in args int)
    begin 
    declare i int default 1;
    start transaction;
    while i<=args do
    INSERT INTO person( `name`, `sex`, `age`, `email`, `tel`, `address`, `create_time`, `update_time`) VALUES (concat('刘德华',i), '男', 66, 'ldh@163.com', '12345678999', '北京海淀区1号', CONCAT(DATE_FORMAT('1970-1-1' + INTERVAL FLOOR(RAND() * 10000) DAY, '%Y-%m-%d'),' ',TIME_FORMAT(SEC_TO_TIME(FLOOR(RAND()* 86400)), '%H:%i:%s')), NOW());
    set i = i+1;
    end while;
    commit;
    end
    $
    
    call batchInsert(10000000);
    $
    
    执行结果:
    call batchInsert(10000000);
    > OK
    > 时间: 977.277s
    
    -- 查询插入数据量
    select count(*) from person

    深度分页测试 性能灾难 325S

  • -- 深度分页测试,性能灾难   
    select * from person p order by p.create_time desc limit 10000000,100; -- 325S

    问题分析

  • limit m,n查询过程是先回表查询m+n条记录,然后丢掉前m条,取后面n条结果返回

    解决方案

    避免大量回表

  • 子查询
  • 表连接inner join
  • 使用标签记录
  • 拓展方案

    1、需求端解决,比如淘宝,京东,搜索时最多100页,直接避免深度分页

    2、翻页体验层面,只保留上一页,下一页,有效使用游标解决任意深度性能稳定高效

    3、通过子查询,表连接有效减少回表次数

    4、水平分库分表

    5、采用ES、Hive+Impala、ClickHouse等OLAP方案需要引入其他技术栈

  • 优化实施

  • 1、正常测试
select * from person p limit 100;  -- 0.020S

实际业务场景中一般需要排序,以创建时间逆序为例

select * from person p order by p.create_time desc limit 100; --7.45s

增加创建时间逆序的索引后,再执行一次耗时36ms

alter table person add index idx_create_time (create_time desc)
  • 2、深度分页,性能急剧下降

    select * from person p order by p.create_time desc limit (pageNo-1)*pageSize,pageSize
  • -- 10001页,在第一百万行,取100条

    select * from person p order by p.create_time desc limit (10001-1)*100,100; -- 31S

    -- 在第一千万行,取100条

    select * from person p order by p.create_time desc limit 10000000,100; -- 325S

    limit m,n查询过程是先回表查询m+n条记录,然后丢掉前m条,取后面n条结果返回

  • 3、优化一【子查】

-- 利用子查询过滤掉大量数据,有效避免大量数据的回表
select  p.* from person pwhere p.create_time<=(select t.create_time t from person t order by t.create_time desc limit 1000000,1)
order by p.create_time desc limit 100;   -- 534ms
  • 4、优化二【表连接inner join】

    -- 利用主键查询,缩小需要回表的数据量
    select p1.*
    from person p1 , (select p2.id from person p2 order by p2.create_time desc limit 1000000,100) p2
    where p1.id = p2.id order by create_time desc

    5、优化三【使用标签记录优化】

-- 每次查询保存上次最小create_time,下次只查询比上页create_time小的一页数据查询第1000万行耗时38ms,提升4000多倍
select * from person where create_time<'1994-08-13 21:54:37' order by create_time desc limit 100  -- 38ms

性能最高,单表查询,索引命中,回表数据少

缺点:只能连续分页,特定场景下才可以使用,需要调用端代码改造,代码耦合度较大

双新调研

ClickHouse 是联机分析 (OLAP) 的列式数据库,适用于商业智能领域,在垂直和水平方向上都可以很好地缩放,在 1 亿数据集体量的情况下,ClickHouse 的平均响应速度是MySQL 的 429 倍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值