Solr Advent Calendar 2016 の6日目です。
経緯
ある日、私が担当していない違う現場のSolrの更新速度がいきなり落ちた(今まで1分ほどだったのが、20分近くかかるようになった)ので見て欲しいと言われました。調べたところ削除リクエストの大幅な増加に起因するものでした。
今回と同じケースで問題になることはあまり無いと思いますが、せっかくなので記事にしたいと思います。
Solrの削除リクエスト
Solrのインデックスから削除するドキュメントを指定するフォーマットには大きく2種類があります。
1つがユニークキーを指定する方法です。
{"delete":{"id":"500000"}}
これは下記の用に配列のように指定することもできます。
{"delete": ["500000", "500001"]}
もう1つがクエリで削除するドキュメント指定する方法です。
何らかの条件に合致するドキュメントをまとめて削除するのに適しています。
先に今回の結果を言うと、これが上手く使えていないために急激な削除パフォーマンスの悪化を招いていました。
{"delete":{"query":"id:500000"}}
今回は大量のドキュメントを削除する場合にどう指定すると高速に削除できるのかに注目して調査をしたいと思います。
調査に使ったインデックス
前回と同じです
Solrのバージョン
5.5.2
調査したパターン
インデックスが100万件(optimize済み)の状態から、1度の更新リクエストで5万件のデータを削除します。
初期のインデックスは同じデータで作られていて、削除対象のドキュメントも全て同じものを削除します。
指定する削除フォーマットは下記の4パターンになります。
- 削除対象のドキュメントをユニークキーで指定
- {"delete":{"id":"500000"},"delete":{"id":"500001"}, ... "delete":{"id":"550000"}}
- ユニークキーを配列で指定
- {"delete": ["50000", "50001", ... "55000"]}
- 完全一致検索クエリで指定
- {"delete":{"query":"id:50000"},"delete":{"query":"id:50001"}, ... "delete":{"query":"id:55000"}}
- 範囲検索クエリで指定
- {"delete":{"query":"id_int:[500000 TO 550000]"}
- id_intはユニークキーを範囲検索で削除するために、idをコピーフィールドしたもの
結果と考察
処理時間(秒) | |
---|---|
パターン1 | 0.489 |
パターン2 | 0.422 |
パターン3 | 52.803 |
パターン4 | 0.445 |
パターン2 > パターン4 > パターン1 > パターン3 の順番となりました。
概ね直感通りの結果になったと言えます。
まずパターン3ですが、これが冒頭で述べた更新速度低下の原因でした(そこは新しいインデックスの更新と削除をバッチでまとめてやっていた)。
見て分かる通り、何度も完全一致で削除対象のドキュメントを検索しているので、その分のオーバーヘッドがかなり大きく、時間がかかっていると思われます。
一方で、パターン4は1度の検索で大量のドキュメントを削除するため効率が良かったのだと思われます。
パターン1,2では2の方がわずかに高速でしたが、そこまで大きな差はありませんでした。
パフォーマンスがいきなり落ちる問題について
過去に更新のパフォーマンスがいきなり落ちたので見て欲しいという経験が何度かあったのですが、大体が更新対象のデータが突然増えたというのが原因でした。
今回のケースも、もともと非効率なパターン3でインデックスの削除を実施していたのですが、その件数が一気に増えたことが原因でした。
いきなりパフォーマンスが悪なった場合はまずは量を疑ってみると原因に近づけるかもしれません。
まとめ
クエリでの削除は柔軟な条件が指定できるため、条件指定で大量のドキュメントをするのに適しています。
一方でユニークキーを直接指定できる場合でそれで削除した方が高速です。
ただし、クエリでの削除はフォーマットが簡潔だったり、必ずしも削除するユニークキーが簡単に分かる状況にあるとは限らないため、ケースバイケースで適切な方法を選べればと思います。