これは興味深い問題であり、Redisがここで役立つと思います。
Redisは、最適化された「intset」形式を使用して整数のセットを格納できます。詳細については、 http://redis.io/topics/memory-optimizationを参照してください。
ここでの正しいデータ構造は、ターゲットタグセットのコレクションに加えて、タグをターゲットタグセットにマップするための逆インデックスであると思います。
2つのターゲットタグセットを保存するには:
0 -> [ 1 2 3 4 5 6 7 8 ]
1 -> [ 6 7 8 9 10 ]
私は使うだろう:
# Targeted tag sets
sadd tgt:0 1 2 3 4 5 6 7 8
sadd tgt:1 2 6 7 8 9 10
# Reverse index
sadd tag:0 0
sadd tag:1 0
sadd tag:2 0 1
sadd tag:3 0
sadd tag:4 0
sadd tag:5 0
sadd tag:6 0 1
sadd tag:7 0 1
sadd tag:8 0 1
sadd tag:9 1
sadd tag:10 1
この逆インデックスは、ターゲットタグセットがシステムに追加/システムから削除されたときに維持するのが非常に簡単です。
グローバルメモリ消費量は、複数のターゲットタグセットに共通するタグの数によって異なります。疑似データをRedisに保存し、メモリ消費をシミュレートするのは非常に簡単です。単純なnode.jsスクリプトを使用して実行しました。
100万のターゲットタグセット(タグは8桁の数字、セットあたり40タグ)の場合、ターゲットタグセットによって共有されるタグが非常に少ない場合(リバースインデックスに3200万以上のエントリ)、メモリ消費量は4GBに近くなります。タグが大量に共有される場合は約500MB(リバースインデックスのエントリは100Kのみ)。
このデータ構造を使用すると、特定の顧客のすべてのタグを含むターゲットタグセットを見つけることが非常に効率的です。
1- Get customer tag set (suppose it is 1 2 3 4)
2- SINTER tag:1 tag:2 tag:3 tag:4
=> result is a list of targeted tag sets having all the tags of the customer
Redisはカーディナリティごとにセットを注文するのに十分スマートであり、カーディナリティが最も低いセットから開始するため、交差操作は効率的です。
これで、逆の操作を実装する必要があることを理解しました(つまり、顧客タグセットにすべてのタグがあるターゲットタグセットを見つける)。逆インデックスはまだ役立ちます。
ここに醜い擬似コードの例があります:
1- Get customer tag set (suppose it is 1 2 3 4)
2- SUNIONSTORE tmp tag:1 tag:2 tag:3 tag:4
=> result is a list of targeted tag sets having at least one tag in common with the customer
3- For t in tmp (iterating on the selected targeted tag sets)
n = SCARD tgt:t (cardinality of the targeted tag sets)
intersect = SINTER customer tgt:t
if n == len(intersect), this targeted tag set matches
したがって、100万のターゲットタグセットに対して顧客タグセットをテストする必要はありません。逆インデックスを使用して、検索の範囲を許容可能なレベルに制限できます。