• 技术文章 >后端开发 >php教程

    如何兼容 MySQL + ES + MongoDB 实现上亿数据的深度分页?

    GuanhuiGuanhui2020-07-27 17:28:49转载841

    面试题 & 真实经历

    面试题:在数据量很大的情况下,怎么实现深度分页?

    大家在面试时,或者准备面试中可能会遇到上述的问题,大多的回答基本上是分库分表建索引,这是一种很标准的正确回答,但现实总是很骨感,所以面试官一般会追问你一句,现在工期不足,人员不足,该怎么实现深度分页?

    这个时候没有实际经验的同学基本麻爪,So,请听我娓娓道来。

    惨痛的教训

    首先必须明确一点:深度分页可以做,但是<font color="red">深度随机跳页绝对需要禁止。</font>

    上一张图:

    Snipaste_2020-07-27_17-11-45.png

    你们猜,我点一下第142360页,服务会不会爆炸?

    像MySQL,MongoDB数据库还好,本身就是专业的数据库,处理的不好,最多就是慢,但如果涉及到ES,性质就不一样了,我们不得不利用 SearchAfter Api,去循环获取数据,这就牵扯到内存占用的问题,如果当时代码写的不优雅,直接就可能导致内存溢出。

    为什么不能允许随机深度跳页

    从技术的角度浅显的聊一聊为什么不能允许随机深度跳页,或者说为什么不建议深度分页

    MySQL

    分页的基本原理:

    SELECT * FROM test ORDER BY id DESC LIMIT 10000, 20;

    LIMIT 10000 , 20的意思扫描满足条件的10020行,扔掉前面的10000行,返回最后的20行。如果是LIMIT 1000000 , 100,需要扫描1000100 行,在一个高并发的应用里,每次查询需要扫描超过100W行,不炸才怪。

    MongoDB

    分页的基本原理:

    db.t_data.find().limit(5).skip(5);

    同样的,随着页码的增大,skip 跳过的条目也会随之变大,而这个操作是通过 cursor 的迭代器来实现的,对于cpu的消耗会非常明显,当页码非常大时且频繁时,必然爆炸。

    ElasticSearch

    从业务的角度来说,ElasticSearch不是典型的数据库,它是一个搜索引擎,如果在筛选条件下没有搜索出想要的数据,继续深度分页也不会找到想要的数据,退一步讲,假如我们把ES作为数据库来使用进行查询,在进行分页的时候一定会遇到max_result_window 的限制,看到没,官方都告诉你最大偏移量限制是一万。

    查询流程:

    由此可以看出为什么要限制偏移量,另外,如果使用 Search After 这种滚动式API进行深度跳页查询,也是一样需要每次滚动几千条,可能一共需要滚动上百万,千万条数据,就为了最后的20条数据,效率可想而知。

    再次和产品对线

    俗话说的好,技术解决不了的问题,就由业务来解决!

    在实习的时候信了产品的邪,必须实现深度分页 + 跳页,如今必须拨乱反正,业务上必须有如下更改:

    尽可能的增加默认的筛选条件,如:时间周期,目的是为了减少数据量的展示

    修改跳页的展现方式,改为滚动显示,或小范围跳页

    滚动显示参考图:

    Snipaste_2020-07-27_17-13-49.png

    小规模跳页参考图:

    Snipaste_2020-07-27_17-13-59.png

    通用解决方案

    短时间内快速解决的方案主要是以下几点:

    MySQL

    原分页SQL:

    # 第一页
    SELECT * FROM `year_score` where `year` = 2017 ORDER BY id limit 0, 20;
    # 第N页
    SELECT * FROM `year_score` where `year` = 2017 ORDER BY id limit (N - 1) * 20, 20;

    通过上下文关系,改写为:

    # XXXX 代表已知的数据
    SELECT * FROM `year_score` where `year` = 2017 and id > XXXX ORDER BY id limit 20;

    在 没内鬼,来点干货!SQL优化和诊断 一文中提到过,LIMIT会在满足条件下停止查询,因此该方案的扫描总量会急剧减少,效率提升Max!

    ES

    方案和MySQL相同,此时我们就可以随用所欲的使用 FROM-TO Api,而且不用考虑最大限制的问题。

    MongoDB

    方案基本类似,基本代码如下:

    Snipaste_2020-07-27_17-15-20.png

    相关性能测试:

    Snipaste_2020-07-27_17-15-29.png

    如果非要深度随机跳页

    如果你没有杠过产品经理,又该怎么办呢,没关系,还有一丝丝的机会。

    在 SQL优化 一文中还提到过MySQL深度分页的处理技巧,代码如下:

    # 反例(耗时129.570s)
    select * from task_result LIMIT 20000000, 10;
    # 正例(耗时5.114s)
    SELECT a.* FROM task_result a, (select id from task_result LIMIT 20000000, 10) b where a.id = b.id;
    # 说明
    # task_result表为生产环境的一个表,总数据量为3400万,id为主键,偏移量达到2000万

    该方案的核心逻辑即基于聚簇索引,在不通过回表的情况下,快速拿到指定偏移量数据的主键ID,然后利用聚簇索引进行回表查询,此时总量仅为10条,效率很高。

    因此我们在处理MySQL,ES,MongoDB时,也可以采用一样的办法:

    瑕疵:当偏移量非常大时,耗时较长,如文中的 5s

    推荐教程:《MySQL教程

    文章来源:https://juejin.im/post/5f0de4d06fb9a07e8a19a641

    以上就是如何兼容 MySQL + ES + MongoDB 实现上亿数据的深度分页?的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:juejin,如有侵犯,请联系admin@php.cn删除
    专题推荐:mysql
    上一篇:详解PHP论坛实现积分系统的思路代码 下一篇:详解PHP中的OPcache 扩展
    线上培训班

    相关文章推荐

    • php mysql如何查询数据• mysql中innodb和myisam的区别是什么?• php5 mysql安装配置方法• php系统不支持mysql数据库怎么办• php中如何设置mysql查询读取数据的超时时间

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网