一覧CL-07-3 · 管理されたデータサービス

データの分割と検索インデックスの更新

書込みの分散と読取り時の検索範囲、原データと検索用索引の鮮度をそれぞれ説明します。

説明

データを複数の保存先へ分けても、要求が同じキーへ集中すると一部の保存先だけが忙しくなります。また、原データと検索用の索引を別に持つ構成では、更新が検索結果へ現れるまでの反映時点も確認が必要です。

利用者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

1
from partition import PartitionStore plain = PartitionStore() print("partition counts", plain.counts())
partition counts [0, 16, 2, 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]
3
salted = PartitionStore(salted=True) print("partition counts", salted.counts())
partition counts [5, 4, 5, 6]
4
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]
5
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]
6
salted.change(1, "red pen") print(salted.source(1))
(1, 'a', 'red pen')
7
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]
8
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]

演習完了