ElasticSearch分页方案

1:from + size 浅分页

"浅"分页是最简单的分页方案。es会根据查询条件在每一个DataNode分片中取出from+size条文档,然后在MasterNode中聚合、排序,再截取size-from的文档返回给调用方。当页数越靠后,也就是from+size越大,es需要读取的数据也就是越大,聚合和排序的时候处理的数据量也越大,此时会加大服务器CPU和内存的消耗。


GET test_dev/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "age": 28
          }
        }
      ]
    }
  },
  "size": 10,
  "from": 20,
  "sort": [
    {
      "timestamp": {
        "order": "desc"
      },
      "_id": {
        "order": "desc"
      }
    }
  ]
}

其中,from定义了目标数据的偏移值,size定义当前返回的数目。默认from为0,size为10,即所有的查询默认仅仅返回前10条数据。

在这里有必要了解一下from/size的原理:

因为es是基于分片的,假设有5个分片,from=100,size=10。则会根据排序规则从5个分片中各取回100条数据数据,然后汇总成500条数据后选择最后面的10条数据。

做过测试,越往后的分页,执行的效率越低。总体上会随着from的增加,消耗时间也会增加。而且数据量越大,就越明显!

2:scroll 深分页

from+size查询在10000-50000条数据(1000到5000页)以内的时候还是可以的,但是如果数据过多的话,就会出现深分页问题。

为了解决上面的问题,elasticsearch提出了一个scroll滚动的方式。

scroll 类似于sql中的cursor,使用scroll,每次只能获取一页的内容,然后会返回一个scroll_id。根据返回的这个scroll_id可以不断地获取下一页的内容,所以scroll并不适用于有跳页的情景。


GET test_dev/_search?scroll=5m
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "age": 28
          }
        }
      ]
    }
  },
  "size": 10,
  "from": 0,
  "sort": [
    {
      "timestamp": {
        "order": "desc"
      },
      "_id": {
        "order": "desc"
      }
    }
  ]
}

scroll=5m表示设置scroll_id保留5分钟可用。

使用scroll必须要将from设置为0。

size决定后面每次调用_search搜索返回的数量

然后我们可以通过数据返回的_scroll_id读取下一页内容,每次请求将会读取下10条数据,直到数据读取完毕或者scroll_id保留时间截止:


GET _search/scroll

{

  "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAJZ9Fnk1d......",

  "scroll": "5m"

}

注意:请求的接口不再使用索引名了,而是 _search/scroll,其中GET和POST方法都可以使用。

scroll删除

根据官方文档的说法,scroll的搜索上下文会在scroll的保留时间截止后自动清除,但是我们知道scroll是非常消耗资源的,所以一个建议就是当不需要了scroll数据的时候,尽可能快的把scroll_id显式删除掉。

清除指定的scroll_id:

DELETE _search/scroll/DnF1ZXJ5VGhlbkZldGNo.....

清除所有的scroll:

DELETE _search/scroll/_all

3:search_after 深分页

scroll 的方式,官方的建议不用于实时的请求(一般用于数据导出),因为每一个 scroll_id 不仅会占用大量的资源,而且会生成历史快照,对于数据的变更不会反映到快照上。

search_after 分页的方式是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。但是需要注意,因为每一页的数据依赖于上一页最后一条数据,所以无法跳页请求。

为了找到每一页最后一条数据,每个文档必须有一个全局唯一值,官方推荐使用 _uid 作为全局唯一值,其实使用业务层的 id 也可以。


GET test_dev/_search

{

  "query": {

    "bool": {

      "filter": [

        {

          "term": {

            "age": 28

          }

        }

      ]

    }

  },

  "size": 20,

  "from": 0,

  "sort": [

    {

      "timestamp": {

        "order": "desc"

      },

      "_id": {

        "order": "desc"

      }

    }

  ]

}

使用search_after必须要设置from=0。

这里我使用timestamp和_id作为唯一值排序。

我们在返回的最后一条数据里拿到sort属性的值传入到search_after。

使用sort返回的值搜索下一页:


GET test_dev/_search

{

  "query": {

    "bool": {

      "filter": [

        {

          "term": {

            "age": 28

          }

        }

      ]

    }

  },

  "size": 10,

  "from": 0,

  "search_after": [

    1541495312521,

    "d0xH6GYBBtbwbQSP0j1A"

  ],

  "sort": [

    {

      "timestamp": {

        "order": "desc"

      },

      "_id": {

        "order": "desc"

      }

    }

  ]

}

4:修改默认分页限制值10000

可以使用下面的方式来改变ES默认深度分页的index.max_result_window 最大窗口值

curl -XPUT http://127.0.0.1:9200/my_index/_settings -d '{ "index" : { "max_result_window" : 500000}}'

其中my_index为要修改的index名,500000为要调整的新的窗口数。将该窗口调整后,便可以解决无法获取到10000条后数据的问题。

注意事项

通过上述的方式解决了我们的问题,但也引入了另一个需要我们注意的问题,窗口值调大了后,虽然请求到分页的数据条数更多了,但它是用牺牲更多的服务器的内存、CPU资源来换取的。要考虑业务场景中过大的分页请求,是否会造成集群服务的OutOfMemory问题。

5:获取总数据量

修改最大限制值之后确实可以使from+size查询到更后面页的数据,但是每次查询得到的总数量最大任然是10000,要想获取大于1万的查询数据量,可以分两步查询,第一步使用scroll查询获取总数据量;第二部使用from+size查询每页的数据,并设置分页。这样即解决了from+size无法查询10000之后的数据,也解决了scroll无法跳页的问题。

使用scroll可能遇到的问题:

Caused by: org.elasticsearch.ElasticsearchException: Trying to create too many scroll contexts. Must be less than or equal to: [500]. This limit can be set by changing the [search.max_open_scroll_context] setting.

这个报错是从es的日志文件中查出来的,大致意思是:尝试创建更多的scroll对象失败了,scroll对象总数量应该控制在500以内。可修改search.max_open_scroll_context的值来改变500这个阈值。

原因:通过scroll 深分页可知道,es服务端会在内存中生成一个scroll_id对象,并会为该值指定过期时间,翻页的时候使用scroll_id来获取下一页的数据。默认情况下,一个实例下面仅可以创建最多500个scroll上下文对象,也就是500个scroll_id。报此错误的原因就是创建scroll上下文对象失败,因为当前已经存在500个这样的对象了。

解决办法:

1:通过观察可以发现,即使不做任何的处理,过一会就又可以发起scroll请求了,这是因为时间超过了scroll生命周期时间,scroll对象自己死掉了一些。

2:按照提示说的,修改search.max_open_scroll_context的值

put http://{{es-host}}/_cluster/settings

{

"persistent": {

    "search.max_open_scroll_context": 5000

},

"transient": {

    "search.max_open_scroll_context": 5000

}

}

[图片上传失败...(image-4dc354-1583253824871)]

image.png

3:在使用完scroll_id之后立即调用删除接口,删除该scroll对象

删除单个scroll

DELETE http://{{es-host}}/_search/scroll

{

"scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAdsMqFmVkZTBJalJWUmp5UmI3V0FYc2lQbVEAAAAAAHbDKRZlZGUwSWpSVlJqeVJiN1dBWHNpUG1RAAAAAABpX2sWclBEekhiRVpSRktHWXFudnVaQ3dIQQAAAAAAaV9qFnJQRHpIYkVaUkZLR1lxbnZ1WkN3SEEAAAAAAGlfaRZyUER6SGJFWlJGS0dZcW52dVpDd0hB"

}

删除所有scroll

delete http://{{es-host}}/_search/scroll/_all

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341