3

フィールドを持つ単純な mysql テーブル (ユーザー) があるとします。

id
rating
salary

指定された範囲(50-100)で最高の評価と給与を持つ10人のユーザーを取得したい、つまりmysqlでは

SELECT id from user WHERE salary>50 and salary<100 ORDER by rating limit 0, 10

これは、100K ユーザー テーブルで 20 ミリ秒実行されます。

Redis にも同じものがあると仮定します: Zlist 評価 (rating=>user_id) Zlist 給与 (salary=>user_id)

私が redis で見たすべてのソリューションには、100k 給与 Zlist のコピー、不要なエントリの削除、および 100k 評価リストとのマージが含まれます。

zinterstore 1 search salary
zremrange search -inf 50
zremrange search 100 +inf
zinterstore 2 search rating weights 0 1
zrange search 0 10

これは絶対に遅いです (なぜ 10 万個の要素をコピーしてほとんどを削除するのでしょうか?)。

これを redis で少なくとも同等に効率的に実装する方法はありますか?

4

2 に答える 2

3

あなたが説明するユースケースは、NoSQL ソリューションでエレガントにモデル化することはできません。これは Redis の制限ではありません。

もう少し説明しましょう。あるフィールドで範囲クエリを実行し、別のフィールドで並べ替えています。これは、NoSQL ソリューションが得意とすることではありません。たとえば、Google App Engine はそのようなクエリを禁止しています。GAE Query Restrictionsを見て、「Properties in Inequality Filters Must Be Sorted before Other Sort Orders」セクションを読んでください。

不等式フィルターに一致するすべての結果を取得するために、クエリはインデックス テーブルをスキャンして最初に一致する行を探し、一致しない行が見つかるまで連続するすべての結果を返します。連続する行が完全な結果セットを表すには、他の並べ替え順序の前に行を不等式フィルターで並べ替える必要があります。

そうは言っても、クエリを効率的に実行することはできますが、ソリューションは洗練されたものにはなりません。

  1. 給与範囲の作成 - 0 ~ 5000、5000 ~ 10000、10000 ~ 15000 など
  2. のようなセットを作成しますusers_with_salary:10000-15000。このセットには、指定された範囲の給与を持つユーザー ID が含まれます。
  3. 同様に、`users_with_rating:1-2" のようなセットを作成します。このセットには、指定された範囲の評価を持つユーザー ID が含まれます。
  4. ここで、次の擬似コードを実行します

String userids[];
for(rating = 10; rating > 0; rating--) {
  for(salary = min_salary; salary < max_salary; salary += 5000) {
      String salary_key = "users_with_salary:" + salary + "-" + (salary+5000);
      String rating_key = "users_with_rating:" + rating + "-" + (rating+1);

      userids.append(redis.sinter(salary_key, rating_key));

      if(userids.length > 10) {
         break;
      }
   }
}

redis 2.6 と lua スクリプトを使用すると、これを lua サーバーで実行することもできます。

結論として、データに対して複雑なクエリを実行する場合は、リレーショナル データベースでモデル化することをお勧めします。

于 2012-04-18T16:12:37.087 に答える
2

スクリプトを使用すると、「ZRANGEBYSCORE Salary 50 100」を使用して、給与が 50 から 100 の間のユーザーを取得し、結果を tmp セットに保存できます。ユーザーの評価をキー "user:[id]" のハッシュに保存すると仮定すると、"SORT tmp BY user:*->rating LIMIT 0 10" を実行できます。

残念ながら、現在 zset のエントリに関連付けられたスコアを SORT BY することはできないため、この方法を使用するには、評価値のみ、または別のハッシュに追加で保存する必要があります。

もちろん、「ZINTERSTORE tmp2 2 rating tmp WEIGHTS 1 0」を使用してから「ZRANGE tmp2 0 10」を使用することもできますが、SORT を使用するよりも効率が大幅に低下します。一方、LIMIT を指定した SORT は、実際に返された 10 個の結果のみを効果的にソートする部分的なクイックソート アルゴリズムを使用します。範囲内の他のユーザーをすばやく返すことができるように tmp2 を保持したい場合がありますが、その場合、評価によってランク付けされた給与が 50 から 100 の間のユーザーの一時的な zset を保存することは理にかなっています。

私が説明する SORT メソッドは、アルゴリズム的には、SQL データベースが実現できるものと同じくらい優れていると思います。インデックスを使用して 1 つのフィールドの範囲でフィルター処理すると、別のフィールドのインデックスを使用して、その小さな結果セットの並べ替えの効率を向上させる方法はわかりません。SQL データベースは、返された結果のみをソートするために、部分的なクイックソートまたは同等のものを使用するだけだと思います。

于 2012-05-10T06:22:19.953 に答える