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

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

面试题 & 真实经历

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

这个时候没有实际经验的同学基本麻爪,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 的限制,看到没,官方都告诉你最大偏移量限制是一万。

查询流程:

  • 如查询第501页,每页10条,客户端发送请求到某节点

  • 此节点将数据广播到各个分片,各分片各自查询前 5010 条数据

  • 查询结果返回至该节点,然后对数据进行整合,取出前 5010 条数据

  • 返回给客户端

由此可以看出为什么要限制偏移量,另外,如果使用 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时,也可以采用一样的办法:

  • 限制获取的字段,只通过筛选条件,深度分页获取主键ID

  • 通过主键ID定向查询需要的数据

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

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

关于如何兼容 MySQL + ES + MongoDB 实现上亿数据的深度分页?的文章就分享到这,如果对你有帮助欢迎继续关注我们哦

本文来自投稿,不代表重蔚自留地立场,如若转载,请注明出处https://www.cwhello.com/40405.html

如有侵犯您的合法权益请发邮件951076433@qq.com联系删除

(0)
php学习php学习订阅用户
上一篇 2022年6月12日 22:56
下一篇 2022年6月12日 22:57

相关推荐

  • 经验分享mysql怎么修改表字符集编码。

    您可以使用以下命令来修改MySQL表的字符集编码:,,“,ALTER TABLE 表名 CONVERT TO CHARACTER SET 字符集;,`,,如果您想将表“mytable”的字符集编码更改为utf8mb4,则可以使用以下命令:,,`,ALTER TABL…

    2024年7月9日
    01
  • mysql.zip怎么安装。

    MySQL是一种广泛使用的关系型数据库管理系统,它提供了一种高效、可靠的数据存储和检索方式,对于许多用户来说,安装MySQL可能会遇到一些困难,特别是在没有图形界面的服务器上,为了解决这个问题,MySQL官方提供了…

    2024年6月20日
    03
  • 聊聊mysql保留小数点后两位。

    MySQL保留小数 在MySQL中,我们可以使用不同的数据类型来存储小数,以下是一些常用的数据类型以及它们的取值范围: 1. FLOAT:单精度浮点数,取值范围为-3.4E+38到3.4E+38之间。 2. DOUBLE:双精度浮点数,取值范围为…

    2024年6月19日
    00
  • 我来教你MySQL中的不等于条件详解。

    在MySQL中,不等于条件用于筛选出不符合指定条件的记录,它通常与SELECT、UPDATE和DELETE语句一起使用,以便对数据库中的数据进行操作,本节将详细介绍MySQL中的不等于条件,包括其语法、用法和示例。 (图片来源网…

    2024年6月20日
    03
  • 使用PHP和MySQL进行数据备份的最佳实践。

    随着企业信息化水平的不断提高,数据备份已经成为了每个企业必须重视的问题。在备份数据的过程中,使用PHP和MySQL进行数据备份已经成为了备受推崇的最佳实践。PHP是一种开源服务器端脚本语言,其拥有简单易学、灵活…

    2023年5月28日
    03
  • PHP操作MySQL的流程

    1.链接数据库 2.选择数据库并设置编码 3.准备SQL语句 4.发生SQL语句到MySQL服务器 5.接收返回的结果集资源 6.解析结果集资源 7.关闭链接资源 流程图:

    2018年3月23日
    0295
  • 经验分享mysql 设置非空。

    在MySQL中添加非空约束,可以通过两种方式实现:在创建表时添加约束和在已有表中添加约束,下面将详细介绍这两种方法。 (图片来源网络,侵删) 在创建表时添加非空约束 在创建表时,可以直接在列定义中添加NOT NUL…

    2024年6月19日
    01
  • 聊聊MySQL个人信息表代码分享。

    在数据库设计中,个人信息表是最常见的一种数据表,它通常包含用户的基本信息,如姓名、性别、年龄、联系方式等,在MySQL中,我们可以通过SQL语句来创建和操作这种数据表,下面,我将详细介绍如何在MySQL中创建和使…

    2024年6月20日
    03

联系我们

QQ:951076433

在线咨询:点击这里给我发消息邮件:951076433@qq.com工作时间:周一至周五,9:30-18:30,节假日休息