[Solr]De-dupe,Atomic Updates,In-Place Updates and Optimistic Concurrency

3.9K Views

August 05, 22

スライド概要

Solrの部分更新機能や条件付き更新について

profile-image

LIFULL HOME'Sを運営する株式会社LIFULLのアカウントです。 LIFULLが主催するエンジニア向けイベント「Ltech」等で公開されたスライド等をこちらで共有しております。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

[Solr] De-dupe,Atomic Updates,In-Place Updates and Optimistic Concurrency 株式会社LIFULL 社内技術勉強会 秀野 亮

2.

• • • • 目次 De-Duplication Atomic Updates In-Place Updates Optimistic Concurrency

3.

Solr8.8.2で検証

4.

ででゅーぷとは? 一般的な用語 データの重複を排除する仕組みのこと 記憶容量の削減目的に行われたりする 逆に重複することを「でゅぷった」などと言う

5.

Solrのdedupe 基本動作はドキュメントのハッシュ値で重複を検知し排除する MD5Signature • • 128-bitのハッシュ値 完全一致で排除 Lookup3Signature • • • 64-bitのハッシュ値 MD5より速く、サイズも小さい 完全一致で排除 TextProfileSignature • • • Apache Nutch(クローラー)より拝借 近しいドキュメントを排除 長い文章を扱う場合に適している

6.

De-Duplication

7.

<updateRequestProcessorChain default="true"> <processor class="solr.processor.SignatureUpdateProcessorFactory"> <bool name="enabled">true</bool> ←ハッシュ値を保存するフィールド。デフォルト値は”signatureField”。 <str name="signatureField">id</str> ←ハッシュ値の計算元フィールド。デフォルト値は全フィールド。 <str name="fields">name,features,cat</str> <str name="signatureClass">solr.processor.Lookup3Signature</str> <str name="overwriteDupes">false</str> </processor> ←重複時上書きするか。SolrCloudでtrueにするとうまく動作しない。 <processor class="solr.LogUpdateProcessorFactory" /> <processor class="solr.DistributedUpdateProcessorFactory"/> <processor class="solr.RunUpdateProcessorFactory" /> </updateRequestProcessorChain>

8.

SolrCloudでの注意点 SolrCloudでoverwriteDups=trueで動作させるための条件 ● uniqueKey = signatureFieldにする ○ 別のフィールドをsignatureFieldにしてもレプリカでコケる ○ 多分レプリケーションはuniqueKeyで更新してる ● SignatureUpdateProcessorFactoryはDistributedUpdateProcessorFactoryの前に実行 ○ uniqueKeyでシャードを決めるから先に生成しておかないと

9.

動作(overwriteDups=true) 更新後 更新前 { 更新データ "id":"1", "id":"2", { "dup_s":"hoge", "dup_s":"hoge", "id":"2", "f1_s":"a", "signature_s":"c660de517f294888", "dup_s":"hoge", "signature_s":"c660de517f294888”, "_version_":1735788773003755520 } { "f1_s":"a" "_version_":1735789202863292416 } }

10.

動作(overwriteDups=false) 更新後 更新前 { 更新データ "id":"1", "id":"1", { "dup_s":"hoge", "dup_s":"hoge", "id":"2", "signature_s":"c660de517f294888", "signature_s":"c660de517f294888", "dup_s":"hoge", "_version_":1735788773003755520 "_version_":1735788773003755520 } { "f1_s":"a" }, } { "id":"2", "dup_s":"hoge", "f1_s":"a", "signature_s":"c660de517f294888”, "_version_":1735789202863292416 }

11.

世の中そんなうまい話はない 全てのフィールドが stored=trueかdocValues=trueじゃないとダメ ● ● copyFieldでコピーしたフィールドを除く 内部的には既存ドキュメントからコピってるだけだから copyField先のフィールドが stored=trueだと上書きされる ● インデクシング時に何らかの処理をしてコピーしても、Atomic Updatesがそのあと上書きしてしまう 自動的に追加される派生フィールドは stored=trueにしてはいけない ● ● ● BBoxField、LatLogType、CurrencyFieldType、など Lucene的にそっと追加されるタイプのフィールド? これらのフィールドをstored=trueにすると更新時にエラーになる

12.

まとめ overwriteDups=trueにすると期待する動作 ● ● ● 重複するドキュメントに上書きされる signatureFieldはindex=trueにする SolrCloudで使うにはuniqueKeyにシグネチャーを設定する必要がある overwriteDups=falseにすると誰も得しない動作 ● 重複させていくスタイル なんか使いにくい

13.

参考 De-Duplication | Apache Solr Reference Guide 8.11

14.

Atomic Updates

15.

あとみっくあっぷでーととは? デフォルトの挙動では、登録されたドキュメントを更新するには全フィールドを再送信しない とダメ 更新したいフィールドが1つでも、他の全てのフィールドが必要 辛い Atomic Updatesを使うと更新したいフィールドだけ送信すれば更新できる

16.

更新対象のフィールドごとに可能な操作 set 値を登録する。nullや空のリストを指定すると値が削除される。 add Multi-valuedフィールドに値やリストを追加。 add-distinct Multi-valuedフィールドに値やリストを追加。登録済みの値は追加されない。 remove Multi-valuedフィールドから値やリストを削除。 removeregex Multi-valuedフィールドからJavaの正規表現にマッチしたものを削除。 inc 数値フィールドを指定値分インクリメント。負の値指定でデクリメント。

17.

更新リクエストの例 { “id”: “1”, “f1_s”: {“set”: “a”}, ←f1_sフィールドの値を ”a”に設定 “f2_i”: {“inc”:”5”}, ←f2_iフィールドの値を +5 “f3_ss”: {“add”: [”b”, “c”]} } ←f3_ssフィールドのリストに ”b”と”c”を追加

18.

まとめ 部分更新 ● ● ドキュメント全体を用意しなくても更新データだけで更新できる 数値データを差分だけで更新できる(インクリメント/デクリメント) 条件が厳しい ● 全フィールドで値を保持する必要がある ○ ● インデックスサイズが大きくなる コピーフィールドや派生フィールドに注意する必要がある なんか使いにくい

19.

参考 Updating Parts of Documents | Apache Solr Reference Guide 8.11 Solr Atomic Updates / Partial Updates

20.

In-Place Updates

21.

いんぷれーすあっぷでーととは? Atomic Updatesのサブセットのような機能 さらに強い制約があるが、値を保持していなくても部分更新を実現 条件が厳しすぎるので条件を満たせなければfail fastする仕組みあり (Atomic Updatesになってエラーになることを避ける目的っぽい) 内部的にインデックスを更新しない フィールド数やサイズの影響を受けないので効率がいい 構文はAtomic Updatesと同じなので使う側は意識しないかも

22.

制約 数値型DocValuesのフィールドのみ ● index=false, stored=false, multiValue=false, docValues=true ● ● docValues=trueのみ指定する感じ DocValuesの値を取得するのは低速 _version_フィールドも同様の制約 copyField先のフィールドも同様の制約

23.

構文 数値型限定なので、この2つだけしかない set 値を設定する inc インクリメント

24.

update.partial.requireInPlace クエリー時のパラメーターでupdate.partial.requireInPlace=trueを付けると、 In-Place Updatesできない場合はエラーになる Atomic Updatesを避けてIn-Place Updatesできない場合はさっさとエラーに したい時に使う

25.

まとめ なんか使いにくい

26.

参考 Updating Parts of Documents | Apache Solr Reference Guide 8.11

27.

Optimistic Concurrency

28.

おぷてぃみすてぃっくこんかれんしーとは? Optimistic Locking(楽観的ロック)、Conditional Update(条件付き更新)と呼ばれるも のと同様の代物 1) 2) 3) 4) 最新のデータを取得 そのデータを手元で変更 更新先のデータが、誰かに更新されてないか確認 データ更新 or リトライ a) b) 変わってなければ更新 誰かが更新してたら(1)からやり直し i) SolrはデフォルトでHTTP 409 Conflictエラーを返す

29.

おぷてぃみすてぃっくこんかれんしーとは? 一般的な用語でSolr以外にも色々なDBで実装されている CPUのCAS(コンペア・アンド・スワップ)命令、memcachedのcas API、DynamoDBの Conditional Update、など Optimistic ConcurrencyとAtomic Updatesを併用したりもできる 条件にマッチした時だけAtomic Updatesする、など

30.

仕組み 更新時に_version_フィールドを含めるのがトリガー ● ● ● ● リクエストパラメーター指定でもいい(単体更新の場合のみ?) _version_フィールドがなかったら普通に更新する _version_フィールドはデフォルトで使用されてるはず ドキュメント登録時にSolr内部で自動採番されるフィールド 更新リクエスト内の_version_フィールドの値で更新可否を判断する(OK=update/NG=reject) ● _version_ > 1 更新対象の_version_フィールドの値と一致したらOK ● _version_ == 1 更新対象がいればOK(_version_の比較はしない)、未登録だとNG →更新のみ可能 ● _version_ == 0 OK(未指定の時と同じ挙動) ● _version_ < 0 更新対象がいなければOK(_version_の比較はしない)、登録済みだとNG →新規登録のみ可能

31.

パラメーター failOnVersionConflicts デフォルトはtrue バッチ更新時は通常コンフリクトする全てのドキュメントの更新に失敗する false を指定するとコンフリクトの発生しなかったドキュメントの更新処理は成功する versions ドキュメント更新時にtrueを設定すると、レスポンスに更新後の_version_フィールドの値が含まれるよう になる これによってバージョンを知るために一度取得する処理を端折れる

32.

例 リクエストパラメーターに指定 ● /solr/collection/update?_version_=999999 ドキュメントのフィールドに指定 ● /solr/collection/update [ { “id”: “aaa”, “_version_”: 999999, … }, { “id”: “bbb”, “_version_”: 888888, … } ] 未登録のドキュメントだけ登録する ● /solr/techproducts/update?_version_=-1&versions=true&failOnVersionConflicts=false [ { “id”: “aaa”, … }, { “id”: “bbb”, … } ] ↓ { "adds": [ "bbb", 1632740949182382080 ] }

33.

DocBasedVersionConstraintsProcessorFactory _version_フィールド以外の独自フィールドで同じ処理ができる バージョンの値は自分で管理する必要がある 例えば、タイムスタンプをバージョンに使えば、古いドキュメントで上書きしてしまうこと を防げる

34.

DocBasedVersionConstraintsProcessorFactory solrconfig.xmlに DocBasedVersionConstraintsProcessorFactory を UpdateRequestProcessorChain に設定する <processor class="solr.DocBasedVersionConstraintsProcessorFactory"> <str name="versionField">my_version_l</str> </processor>

35.

DocBasedVersionConstraintsProcessorFactory ignoreOldUpdates デフォルトは false 更新NGだった時Conflict(409)を返さずOK(200)を返す 更新してないのに deleteVersionParam 削除後に古いバージョンで登録されると古いドキュメントが復活してしまうので、それを防ぐ機能? Delete By IDリクエスト時に必須とするパラメーター名を指定する パラメーター指定時、内部的にDelete By IDリクエストをAdd Documentリクエストに変換し論理削除状態にする その後、再登録するには論理削除されたドキュメントより新しいバージョンである必要がある すごくよい機能に見えるが、論理削除ドキュメントが検索にヒットしないよう頑張るのは自分たち 論理削除ドキュメントを掃除するのも自分たち supportMissingVersionOnOldDocs デフォルトは false この機能を有効化する前に登録されていたドキュメントが versionFieldを持っていなくても更新できる tombstoneConfig deleteVersionParamで論理削除されたドキュメントに含むフィールドを指定できるっぽい ドキュメントに記述がなかったのでよく分からない

36.
[beta]
設定例
<!-- this chain uses the processor using the "deleteVersionParam" option so that deleteById requests are translated into updates to preserve the (logically) deleted document in the index with a record of its deleted version. It also
demonstrates how to mix in TimestampUpdateProcessorFactory and DefaultValueUpdateProcessorFactory to ensure these logically deleted documents are kept out of searches, but can be cleaned up periodically after some amount of
time has elapsed. -->
<updateRequestProcessorChain name="external-version-constraint" default="true">
<!-- give all docs a true value to denote that they are alive -->
<processor class="solr.DefaultValueUpdateProcessorFactory">
<str name="fieldName">live_b</str>
<bool name="value">true</bool>
</processor>
<!-- process the external version constraint, ignoring any updates that don't satisfy the constraint -->
<processor class="solr.DocBasedVersionConstraintsProcessorFactory">
<bool name="ignoreOldUpdates">true</bool>
<str name="versionField">my_version_l</str>
<str name="deleteVersionParam">del_version</str>
</processor>
<!-- any doc that makes it this here w/o a live value is a logically deleted doc generated by the previous processor in place of deleteById -->
<processor class="solr.DefaultValueUpdateProcessorFactory">
<str name="fieldName">live_b</str>
<bool name="value">false</bool>
</processor>
<!-- give every doc, including the logically deleted ones, a timestamp -->
<processor class="solr.TimestampUpdateProcessorFactory">
<str name="fieldName">timestamp_tdt</str>
</processor>
<processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>

37.

参考 Optimistic Concurrency | Apache Solr Reference Guide 8.11 Document Centric Versioning Constraints | Apache Solr Reference Guide 8.11 https://github.com/apache/solr/blob/main/solr/core/src/test-files/solr/collection 1/conf/solrconfig-externalversionconstraint.xml