分库深度翻页问题&Elasticsearch的解决方式

主要内容

o一 业界难题-跨库分页需求

o二 解决方案

oelasticsearch采用的解决方案&源码解析

o四 由分页问题引发对es性能的思考

一 业界难题-跨库分页需求

1.1分页查询的业务需求&常用的解决方式

互联网分页拉取获取数据的需求:

1)微信消息过多时,拉取第N页消息

2)京东下单过多时,拉取第N页订单

3)浏览58同城,查看第N帖子

4)乐高后台,查看第N页的视频信息

 

分页拉取的特点:

   (1)有一个业务主键id, 例如msg_idorder_idtiezi_id

   (2)分页排序是按照非业务主键id来排序的,业务中经常按照时间time来排序order by

 

常用的解决方案:

     可以通过在排序字段time上建立索引利用SQL提供的offset/limit功能就能满足分页查询需求

     select * from t_msg order by time offset 200 limit 100

     select * from t_order order by time offset 200 limit 100

     select * from t_tiezi order by time offset 200 limit 100

此处假设一页数据为100条,均拉取第3页数据。

1.2分库分页需求

分库需求

     高并发大流量的互联网架构中,随着数据量的增加,数据库需要进行水平切分,将数据分布到不同的数据库实例上,来达到数据扩容的目的。

Eg:使用分库依据patition key进行分库示例为id取模

问题

   select * from t_user order by time offset 200 limit 100

   变成两个库后,分库依据是uid,排序依据是time。如果跨越多个水平切分数据库,且分库依据与排序依据为不同属性,并需要进行分页,实现:

   select * from T order by time offset X limit Y的跨库分页SQL

数据库层失去了排序字段的全局视野,数据分布在两个库,如何解决?

二 全局视野法解决跨库分页

服务层通过uid取模将数据分布到两个库上去之后,每个数据库都失去了全局视野,数据按照time局部排序之后,不管哪个分库的第3页数据,都不一定是全局排序的第3页数据

2.1全局排序场景分类

 (1极端情况,两个库的排序字段的数据完全一样。方案:每个库offeset一半,再取半页,就是最终需要的结果:

 (2极端情况:数据分布极不均匀,结果数据全部来自于一个库(eg.db1前三页的time排序字段均小于db0的前三页中的time字段)。

方案:只取结果库db1中的第3页。

3)一般情况,每个库数据各包含一部分。方案:由于不清楚到底是哪种情况,所以必须每个库都返回3页数每个库返回X+Y条数据,X为偏移量,Y为每个分页的大小所得到的6页数据在服务层进行内存排序,得到数据全局视野,再取第3页数据,便能够得到想要的全局分页数该方案为elasticsearch分页排序采用的方案

2.1.2 分库(X+Y)*N聚合详细步骤

一般情况步骤

1)将order by time offset X limit Y,改写成order by time offset 0 limit X+Y

2)服务层将改写后的SQL语句发往各个分库:即例子中的各取3页数据

3)假设共分为N个库,服务层将得到N*(X+Y)条数据:即例子中的6页数据

4)服务层对得到的N*(X+Y)条数据进行内存排序,内存排序后再取偏移量X后的Y条记录,就是全局视野所需的一页数

   eg:索引有5个分片,现在要请求size大小为100,获取第200页的数据。则偏移量X=100*200 ,Y=100,N=5,需要在内存中进行排序SIZE=5*(100*200+100)=100500

方案优点:通过服务层修改SQL语句,扩大数据召回量,能够得到全局视野,业务无损,精准返回所需数据。

方案缺点

1)每个分库需要返回更多的数据,增大了网络传输量(耗网络);

2)除了数据库按照time进行排序,服务层还需要进行二次排序,增大了服务层的计算量(耗CPU);

3)最致命的,这个算法随着页码的增大,性能会急剧下降,这是因为SQL改写后每个分库要返回X+Y行数据:返回第3页,offset中的X=200;假如要返回第100页,offset中的X=9900,即每个分库要返回100页数据,数据量和排序量都将大增,性能平方级下降es只允许最多对1w条数据分页排序的原因,0.9版本不做限制,导致频繁OOM,性能急剧下降

2.2.2 业务折衷法解决分页排序允许精度缺失

数据库分库-数据均衡原理

使用patition key进行分库,在数据量较大,数据分布足够随机的情况下,各分库所有非patition key属性,在各个分库上的数据分布,统计概率情况是一致的。

例如,在uid随机的情况下,使用uid取模分两库,db0和db1:

(1)性别属性,如果db0库上的男性用户占比70%,则db1上男性用户占比也应为70%

(2)年龄属性,如果db0库上18-28岁少女用户比例占比15%,则db1上少女用户比例也应为15%

(3)时间属性,如果db0库上每天10:00之前登录的用户占比为20%,则db1上应该是相同的统计规律

       利用数据均衡原理原理,要查询全局100页数据,offset 9900 limit 100改写为offset 4950 limit 50,每个分库偏移4950(一半),获取50条数据(半页),得到的数据集的并集,基本能够认为,是全局数据的offset 9900 limit 100的数据,当然,这一页数据的精度,并不是精准的。

      使用允许精度缺失的技术方案,技术复杂度大大降低了,既不需要返回更多的数据,也不需要进行服务内存排序。典型的应用如论坛帖子1024 你懂得) 58同城早期招聘网站。

2.3 二次查询法

为了方便举例,假设一页只有5条数据,查询第200页的SQL语句为select * from T order by time offset 1000 limit 5;

步骤一:查询改写

将select * from T order by time offset 1000 limit 5

改写为select * from T order by time offset 500 limit 5

并投递给所有的分库,注意,这个offset的500,来自于全局offset的总偏移量1000,除以水平切分数据库个数2。

如果是3个分库,则可以改写为select * from T order by time offset 333 limit 5(offset=1000/3

假设这三个分库返回的数据(time, uid)如下(每页的返回结果都是按照time排序)

步骤二:找到所返回3页全部数据的最小值,以及各个库的最大值。

第一个库,5条数据的time最小值是1487501123

第二个库,5条数据的time最小值是1487501133

第三个库,5条数据的time最小值是1487501143

在全局中,三个库的最小值为1487501123

步骤三:查询二次改写

第一次改写的SQL语句是select * from T order by time offset 333 limit 5

第二次要改写成一个between语句,between的起点是time_min,between的终点是原来每个分库各自返回数据的最大值

第一个分库,第一次返回数据的最大值是1487501523

所以查询改写为select * from T order by time where time between time_min and 1487501523

第二个分库,第一次返回数据的最大值是1487501323

所以查询改写为select * from T order by time where time between time_min and 1487501323

第三个分库,第一次返回数据的最大值是1487501553

所以查询改写为select * from T order by time where time between time_min and 1487501553

相对第一次查询,第二次查询条件放宽了,故第二次查询会返回比第一次查询结果集更多的数据,假设这三个分库返回的数据(time, uid)如下:

可以看到:

由于time_min来自原来的分库一,所以分库一的返回结果集和第一次查询相同(所以其实这次访问是可以省略的);

分库二的结果集,比第一次多返回了1条数据,头部的1条记录(time最小的记录)是新的(上图中粉色记录);

分库三的结果集,比第一次多返回了2条数据,头部的2条记录(time最小的2条记录)是新的(上图中粉色记录);

步骤四:在每个结果集中虚拟一个time_min记录,找到time_min在全局的offset

在第一个库中,time_min在第一个库的offset是333

在第二个库中,(1487501133, uid_aa)的offset是333(根据第一次查询条件得出的),故虚拟time_min在第二个库的offset是331

在第三个库中,(1487501143, uid_aaa)的offset是333(根据第一次查询条件得出的),故虚拟time_min在第三个库的offset是330

综上,time_min在全局的offset是333+331+330=994

步骤五:既然得到了time_min在全局的offset,就相当于有了全局视野,根据第二次的结果集,就能够得到全局offset 1000 limit 5记录

    在步骤二的子结果集中,取子结果集为:limit+1000-time_min_offset的数据,然后对三个子数据集进行汇总,排序。

2.2二次查询法存在的问题

步骤12的作用在于找到最小值time_min,偏移量在X/N,对es来说该步骤在建立在列式存储的结构中【time列式存储,已经排好序速度很快,可以迅速返回。步骤3二次查询的范围是 order by time between time_min and time_i_max。 假设time_min在分库1,其他分库返回的结果量为:分库比time_min大的结果数量+size,

极端情况下,其他分库的结果都比time_min大,那么其他库返回的结果最大数据量为X/N+sizeN为常数,则x越大,返回的结果集越大,需要在步骤5中汇总排序的量就越大。极端情况下需要汇总排序的量为

      假如偏移量x为10亿分片个数为3,分页大小为5,深度翻页需要排序的量为(3-1)*(10亿/3+5)+5=6.7亿。

      因此即使采用这种方式,在数据分布极其不均匀的情况下,进行深度翻页,一次进行汇总排序总量也是非常大的。推测这也是es不采用这种方式来进行深度翻页的原因。

elasticsearch采用的解决方案&源码解析

      ES search_after采用的方式为业务折衷法。每次查询的时候,为常量的查询延迟和开销。但是对大规模数据导出来说,短时间内需要重复一个查询成百上千次也是一个非常巨大的消耗。以下图为示例,如果批量导出数据时,每次都使用业务折衷法,则会使得各个分片再次执行一遍请求。

3.1 essearch_after&scroll实现基本原理

ES scroll查询是对业务折中法做了进一步的优化。将汇总的结果缓存一段时间。如scroll=1mrespose比传统的查询返回多了一个scroll_id相当用于唯一标识】,下次查询时候,用scroll_id即可找到上次缓存的结果。

注:如果业务处理时间超过缓存时间,则缓存会失效,导致导出数据不全。缓存时间太长会大量消耗内存。目前搜索系统设置默认时长为5min。阅读业务曾出现业务处理数据太慢,导致导出数据量不全,解决方式为同步导出,异步处理业务数据

ES scroll查询原生API使用举例(size代表一次交互所返回的数据总量):

Elasticsearch系统架构图

Elasticsearch系统交互图

Elasticsearch 中处理REST相关的客户端请求的类都放在包:org.elasticsearch.action.rest下。该包下所有的类均继承自抽象类BaseRestHandler使用了设计模式:模板方法,业务逻辑均类似。需要对Restrequest请求作出不同的响应。

ScrollAction向控制器注册包含滚动查询关键字【scroll】的路径,当httpServer获取客户端发来的请求时,调用restController.dispatchRequest(),restctroller通过路径判断将请求转发给scrollAction.

四 由分页问题引发对es性能的思考

       “任何脱离业务的架构设计都是耍流氓”。对于深度翻页场景来说,除了网络爬虫,很少有人去深度逐页去查询,查看每条数据,即便是百度、google搜索引擎,也对返回页数做了限制。ES对分页排序,使用全局视野法,业务无损,精准返回全所需要的数据,但是限制分页数据的最大size大小(默认1万条),避免页码太大导致占用大量网络和CPU资源,性能急剧下降。在我看来,ES在资源占用和性能之间找到一个平衡点。

        同时ES对业务做了折中,支持search_after查询方式,禁止跳页查询,降低了对内存的大量消耗以及CPU资源的占用,为业务需求提供了另外的解决方式。

      对于批量导出数据的场景,ES使用了scroll查询,其是通过对search_after加入缓存,做了进一步优化,实现对多分库的数据快速、高效导出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值