データの分割と検索インデックスの更新
書込みの分散と読取り時の検索範囲、原データと検索用索引の鮮度をそれぞれ説明します。
説明
データを複数の保存先へ分けても、要求が同じキーへ集中すると一部の保存先だけが忙しくなります。また、原データと検索用の索引を別に持つ構成では、更新が検索結果へ現れるまでの反映時点も確認が必要です。
利用者aの注文16件、bとcが2件ずつの計20件を四つのSQLiteへ分けます。利用者だけで分割する方法と、注文番号も加える方法を比較します。次に実SQLiteのFTS5索引へ検索用コピーを作り、原データ更新前後の検索結果を確認します。
書込みの分散と読取り時の検索範囲、原データと検索用索引の鮮度をそれぞれ説明します。
1利用者をキーに分割する
この教材の分割式は、利用者名のUTF-8バイト値の合計を4で割った余りです。実製品のハッシュ関数ではなく、同じキーが同じ場所へ集まる条件を確認する式です。
from partition import PartitionStore
plain = PartitionStore()利用者名で四つのSQLiteへ分割する
print("partition counts", plain.counts()各保存先の件数を数える)件数は[0,16,2,2]です。aの16件が一つに集まり、総件数20でも負荷が均等になるとは限りません。
2利用者の一覧は一箇所から読める
利用者aの注文IDを取得します。
plain.for_user("a")利用者aの保存先を絞って注文を読む検索先はslot 1だけで、ID1〜16を取得できます。書込みが集中する一方、利用者単位の一覧は一箇所で取得できる構成です。
3注文番号も分割に加える
利用者名のバイト値合計へ注文番号を足し、4で割った余りで分ける案を作ります。同じ注文データを使います。
salted = PartitionStore(salted=True)注文番号も含めた分割で同じデータを配置する
print("partition counts", salted.counts()分割後の偏りを確認する)件数は[5,4,5,6]になり、aの16件も四つへ分散します。入力は同じ20件で、分割先を決める式だけが変わりました。
4一覧を読む範囲が増える
分散後の利用者aの注文を取得します。
salted.for_user("a")複数の保存先からaの注文を集める同じID1〜16ですが、検索先は[0,1,2,3]へ増えます。書込みを分散する利点と、一覧取得で複数の結果を集める負担を合わせて検討します。
5検索用のFTS5索引を作る
注文の本文はblue penです。検索用コピーをFTS5へ入れ、blueで検索します。
salted.build_index()本文を検索用のFTS5へコピーする
print(salted.search("blue")語から注文IDを検索する)ID1〜20が返ります。原データの保存先と、語からIDを探す検索用索引は別のSQLite接続にあります。
6原データを更新して読む
注文1の本文をred penへ変更し、原データから取得します。
salted.change(1, "red pen")注文1の本文を更新する
print(salted.source(1)現在の原データを取得する)原データは(1, a, red pen)です。この更新は原データの各SQLiteへ確定していますが、検索用コピーはまだ更新していません。
7検索結果にはまだ旧内容が残る
同じ時点でredとblueを検索します。
print("red", salted.search("red")新しい語が検索に現れるか確認する)
print("blue", salted.search("blue")古い語で注文1が返るか確認する)redは空、blueにはまだID1が含まれます。原データを直接読む結果と検索結果が違うのは、検索用コピーに更新を反映していないためです。
8該当する検索用コピーを更新する
注文1を原データから読み直し、FTS5の該当行を置き換えます。
salted.refresh(1)注文1の検索用コピーを現在の本文へ更新する
print("red", salted.search("red")新しい語で注文1を確認する)
print("blue", salted.search("blue")古い語から注文1が消えたことを確認する)redは[1]となり、blueは2〜20です。検索用の反映処理が完了すると、原データと同じ内容に対応する結果になりました。
まとめ
分割キーへの集中は、一部の保存先への集中につながります。
分割先を増やすと、一覧読取りで集める範囲が増える場合があります。
原データの確定と検索用コピーの反映を別々に確認します。
操作履歴と解説を振り返れます。
Python 3 / SQLite
from partition import PartitionStore
plain = PartitionStore()
print("partition counts", plain.counts())partition counts [0, 16, 2, 2]
plain.for_user("a")slots=[1] ids=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
salted = PartitionStore(salted=True)
print("partition counts", salted.counts())partition counts [5, 4, 5, 6]
salted.for_user("a")slots=[0, 1, 2, 3] ids=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
salted.build_index()
print(salted.search("blue"))[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
salted.change(1, "red pen")
print(salted.source(1))(1, 'a', 'red pen')
print("red", salted.search("red"))
print("blue", salted.search("blue"))red [] blue [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
salted.refresh(1)
print("red", salted.search("red"))
print("blue", salted.search("blue"))red [1] blue [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
演習完了