ElasticSearch-ESの深いページング問題を解決する(カーソルscroll)

12527 ワード

ES深さのページングを避けるために、ページング(from&size)を使用して10000以降のデータをクエリーすることはできません.したがって、10000以降のデータをクエリーする場合は、ESが提供するscroll(カーソル)を使用してクエリーします.
取りページ数が大きいと仮定すると(奥行きページ)、20ページ目を要求すると、Elasticsearchはすべてのスライス上の1ページ目から20ページ目までのすべてのドキュメントを取り出し、並べ替えを行い、最終的にfrom後のsizeエントリの結果を最終的な戻り値としてとして取り出す.
16個のスライスがあると仮定すると、coordinate nodeがshards*(from+size)バーレコードにまとめる必要があります.すなわち、16*(20+10)レコードが必要になった後、グローバルソートを行う必要があります.
したがって、インデックスが非常に大きい(千万または億)場合、from+sizeを使用して深いページを作成することはできません.ページが深いほどOOMが容易になり、OOMでなくてもCPUとメモリリソースが消費されます.
従って、ESは、保護手段としてindex.max_result_window:10000を用いる、すなわち、デフォルトfrom+sizeは10000を超えてはならない.このパラメータは動的に変更してもよいし、プロファイルで構成してもよいが、そうしないほうがよい.ESカーソルを用いてデータを取得すべきである.

scrollカーソルの原理
scrollはリレーショナル・データベースのcursorとして理解できます.そのため、scrollはリアルタイム検索に適していません.群発などのバックグラウンド・バッチ・タスクに適しています.
scrollは具体的に初期化と遍歴の2つのステップに分けられます
初期化時に検索条件に合致するすべての検索結果をキャッシュし、スナップショットとして想像できる.
遍歴中、このスナップショットからデータを取得
すなわち、初期化後にインデックスの挿入、削除、更新データに対して遍歴結果に影響を与えることはない.

カーソルがパフォーマンスを向上させる理由は、深いページを作成すると、検索のたびに並べ替え直さなければならないため、非常に無駄です.scrollを使用すると、使用するデータを一度に並べ替えてバッチで取り出すので、from+sizeを使用するよりもです.

具体例
初期化
リクエスト
注意URLのsearchの後にscroll=1mを付けてrequest bodyに書くことはできません.1mはこのカーソルが1分間を開いたままであることを示しています.
sizeサイズを指定できます.つまり、何回かのデータが返信されるたびに、データがないまで返信されると200が成功します.hitsのhitsは空のlist になります.
初期化時には、_scroll_idに加えて、前の100ペン(size=100と仮定)のデータも返信する.
request bodyは一般検索と同様であるため、初期化の過程でscroll設定カーソルオン時間を加えた以外は、一般検索と変わらない(クエリー条件を設定するには、前sizeペンのデータも返信する)

POST 127.0.0.1:9200/my_index/_search?scroll=1m
{
    "query":{
        "range":{
            "createTime": {
                "gte": 1522229999999
            }
        }
    },
    "size": 1000
}

結果

{
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAfv5-FjNOamF0Mk1aUUhpUnU5ZWNMaHJocWcAAAAAAH7-gBYzTmphdDJNWlFIaVJ1OWVjTGhyaHFnAAAAAAB-_n8WM05qYXQyTVpRSGlSdTllY0xocmhxZwAAAAAAdsJxFmVkZTBJalJWUmp5UmI3V0FYc2lQbVEAAAAAAHbCcBZlZGUwSWpSVlJqeVJiN1dBWHNpUG1R",
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 84,
        "max_score": 1,
        "hits": [
            {
                "_index": "video1522821719",
                "_type": "doc",
                "_id": "84056",
                "_score": 1,
                "_source": {
                    "title": "    ",
                    "createTime": 1522239744000
                }
            }
            ....99 data
        ]
    }
}
を返します.

データの遍歴
リクエスト
初期化から戻る_scroll_idを使用して要求が行われ、各要求は初期化中の未完了データを返し続け、_scroll_idが返される.この_scroll_idは変更される可能性があるため、要求ごとに前回の要求から戻る_scroll_id を持つべきである._scroll_idを返しますが、リクエストに入れたのはscroll_idで、スペルが異なるです.
また、scrollリクエストを送信たびに、このscrollのオープン時間を再リフレッシュし、うっかりタイムアウトしてデータ取得が不完全になることを防止する.

結果を返す
データがないと空のhitsが回送され、この判断でデータ

POST 127.0.0.1:9200/_search/scroll?scroll=1m
{
    "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAdsMqFmVkZTBJalJWUmp5UmI3V0FYc2lQbVEAAAAAAHbDKRZlZGUwSWpSVlJqeVJiN1dBWHNpUG1RAAAAAABpX2sWclBEekhiRVpSRktHWXFudnVaQ3dIQQAAAAAAaV9qFnJQRHpIYkVaUkZLR1lxbnZ1WkN3SEEAAAAAAGlfaRZyUER6SGJFWlJGS0dZcW52dVpDd0hB"
}
が遍歴完了したか否かを判断することができる.



scrollクエリーの最適化
一般的なシナリオでは、scrollは通常、ソートが必要な大きなデータを取得するために使用されますが、データ間のソート性は私たちにとって関係なく、すべてのデータが取り出せばよい場合があります.この場合、scrollを最適化することができます.
初期化_docを使用してsortを除去した結果、この実行の効率は最も速いが、データはソートされず、すべてのデータを取得したいシーン
{
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAdsMqFmVkZTBJalJWUmp5UmI3V0FYc2lQbVEAAAAAAHbDKRZlZGUwSWpSVlJqeVJiN1dBWHNpUG1RAAAAAABpX2sWclBEekhiRVpSRktHWXFudnVaQ3dIQQAAAAAAaV9qFnJQRHpIYkVaUkZLR1lxbnZ1WkN3SEEAAAAAAGlfaRZyUER6SGJFWlJGS0dZcW52dVpDd0hB",
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 84,
        "max_score": null,
        "hits": []
    }
}
に適している.


scrollのクリア
scrollをオンに設定すると、1つのscrollの生存時間が設定されますが、使い終わった後に手で閉じることができれば、リソースを早期に解放することができ、ESの負担

POST 127.0.0.1:9200/my_index/_search?scroll=1m
{
    "query": {
        "match_all" : {}
    },
    "sort": [
        "_doc"
        ]
    }
}
を低減することができます.