Rediszsetの高度なゲームプレイ

序文

最近、ブロガーは、ユーザーの体重、ステータス、性別、オンラインとオフラインのステータスに従ってユーザーを並べ替える必要がある並べ替えリクエストを受け取りました。従来のデータベースによると、並べ替える場合はすべてのデータを取り出して並べ替える必要があるため、機能しません。この時点で大量のデータがある場合はどうなりますか?それらをすべて取り出してjavaで並べ替えることはできません。パフォーマンスは非常に低いです。それから何人かの友人は間違いなく言うでしょう、直接ソートするためにzsetを使用してください。したがって、zsetがスコアでソートされることは誰もが知っていますが、マルチウェイトソートをzsetにどのように組み込むのでしょうか。

コアテクノロジー

まず、ユーザーの体重、ステータス、性別、オンラインステータスの4つの側面に応じて、ニーズに応じて優先順位を付けましょう。まず、ステータスが最も優先度が高く、次に性別、次に部屋のオンラインとオフラインのステータス、最後にユーザーの体重が優先されることは言うまでもありません。
[ステータス]> [性別]> [部屋のステータス]> [ユーザーの体重]でオンラインとオフライン。
次に、2つのアプローチがあります。1:10分割法。2:バイナリ分割方式。

10分割

これは非常に理解しやすいです。それは私たちの条件を10進整数で不合理な数字に分割することであり、最後に優先度が高いほど数字が大きくなります。たとえば、私のユーザーのウェイトは最小ですが、ウェイトは10です。そうすれば、10を10位にすることができます。このように取り出すと、重さで分類されます。
たとえば、
ここに写真の説明を挿入
最初の状態はすでに良好です。並べ替えを行う前に、まず条件付き優先度を検討し、最後に自分で優先度を並べ替える必要があります。最初に最も低い優先度に従って並べ替えて、最終的に計算されたスコアが正しい順序になるようにします。 。
次に、2番目の条件はユーザーのオンラインステータスです。その場合、オンラインステータスは3つだけです。私の十分位数はすでに占有されているため、代わりに百分位数を使用します。100はオフライン、200はオンライン、300は部屋にあります。最終的に私たちのスコアは次のようになりました:
ここに写真の説明を挿入
など、他の条件値もこのように再生できます。したがって、条件付き優先度に従ってスコアを計算し、それらをzsetに格納する場合、それはすでに順序を並べ替えるのに役立つセットであり、それを使用するだけで済みます。1つ目は非常にシンプルで、理解しやすいものです。次に、2番目の高度なバージョンについて説明します。

バイナリスプリット

まず、zsetのジャンプテーブル構造を最初に理解しましょう。

typedef struct zskiplistNode {
    
    
    
        // 成员对象
        robj *obj;
    
        // 分值
        double score;
    
        // 后退指针
        struct zskiplistNode *backward;
    
        // 层
        struct zskiplistLevel {
    
    
            // 前进指针
            struct zskiplistNode *forward;
    
            // 跨度
            unsigned int span;
        } level[];
    
    } zskiplistNode;

彼のスコアは実際にはdouble型のデータであることがわかります。それで、最初にその二重を理解しましょう。
まず、彼のダブルタイプはIEEE754標準のダブルフローティングポイント番号です。これから私たちはその構造を知ることができます
ここに写真の説明を挿入

S:符号ビット1は負の数、0は正の数
Exp:数を指し、2番目の段落は11ビットを占め、コードシフト法は正と負の数を表します。ただし、すべて0とすべて1がゼロと無限大を表す特殊なケースを除き、指数部分は次のようになります。 −1022〜 + 1023 + 1023、指数のサイズは1〜2046です。
分数:重要な桁、52桁、固定小数点数、小数点がマンティッサの前に配置されます。
これを知っ後、さまざまな条件に応じてさまざまなものを使用できます。データにアクセスするためのビット数。次に、ブロガーの条件が少ないため、intタイプを使用してデータを保存します。
まず、条件ごとに必要なポジション数を把握する必要があります

  1. ユーザーウェイト:ユーザーウェイトは10個しかないため。したがって、10をバイナリに変換すると、4桁を占める1010になるため、次の図に示すように、ユーザーの重みをintタイプ値の下4桁に格納します。現時点で
    ここに写真の説明を挿入
    は、バイナリ表現を使用していますが、 redisでは、彼は10である10進値を表示します。次に、先ほど説明した10進除算に戻ります。実際、2進除算は、10進を2進に変更することを除いて、10進に似ていますが、一般的な考え方は10進です。
  2. オンラインステータス:オンラインステータスには、1。室内、2。オンライン、3。オフラインの3つの条件があります。そうすれば、ユーザーのオンラインステータスを計算するのに必要なのは2人だけであることがわかり、0をオフライン、1をオンライン、2を室内と見なすことができます。バイナリに変換されるのはオフライン:00、オンライン:01、部屋:10です。以前はまだユーザーの重みがあるため、オンラインステータスを4桁目と5桁目に保存する必要があります。このとき、ビットは次のようになります。次のように、オンラインステータスを部屋のステータスとして使用します。

ここに写真の説明を挿入
これをdecimal:42に変換します。そうすると、redisに保存されるスコアも42になります。

  1. 性別:性別は非常に中間的な優先順位であるため、整数ビットの中央に格納できます。現時点では、次のように記述できます。
    ここに写真の説明を挿入

したがって、データを再度取得するときに、この時点ですべての男性データを取り出したい場合は、スコア範囲を
0〜1 << 16に設定できます。この範囲のデータは男性データであり、 1 << 16より高いものは女性のデータです。
では、ビット操作のシンボルとはどういう意味か、後で詳しく説明します。でも左シフト記号は理解できると思います〜

  1. ステータス:私の意見では、ステータスは通常オン、オフなどであるため、ステータスの優先度より高いものはありません。ここには2つの状態しかないので、異なる状態を表すために0と1を使用するだけで済みます。たとえば、1を使用してオフ状態を示し、0を使用してオン状態を示す場合、次のように記述できます。

ここに写真の説明を挿入

次に、データをフェッチするときに、スコア間隔を0〜1 << 30の範囲のデータに設定するだけで、ステータスがオンになっているすべてのユーザーをフェッチできます。それで、これを知った後、どのようにスコアを計算するのですか?

バイナリ分割計算スコア

まず、グローバル静的定数を定義しましょう。

/**
     * 用户在线状态bit长度
     */
    public static final int onlineStatusBits = 2;

    /**
     * 用户权重bit长度
     */
    public static final int WEIGHT_BITS= 4;

    /**
     * 用于保存用户性别 1女 0男
     */
    public static final int SCORE_WOMAN = 1 << 16;

    /**
     * 用户状态
     */
    public static final int SCORE_STATUS = 1 << 30;

次に、スコアを計算するとき、現在の状態が部屋の状態で、ユーザーの体重が5であるとすると、次のように書くことができます。

int score = (3 << WEIGHT_BITS) | 5;

weightBitsを3つ左に移動する目的は、下位ビット用のスペースを確保し、ユーザーの重みに場所を持たせることです。次に、ビット単位のORによってスコアを取得できます。|この記号の意味は次のとおりです。2つの数値をバイナリで比較すると、2つの数値の桁は0になる前に0になり、一方が1の場合は1
になります。上記のコードを例として取り上げます
。we3<< :WeightBitsをバイナリに変換される
0011 0000
5バイナリ:
0000 0101
、我々はビット単位によって計算するかである結果:
0011 0101
我々は、最終結果の上位4ビットがオンライン状態の値であり、4番目のビットであることが判明します私たちの体重。
データを取得したい場合はどうなりますか?

スコアから対応する値を取得する方法

実際、これも非常に簡単です。00110101の結果を使用して距離を計算します。まず、桁数を取り出す方法についてお話しましょう。

(1 << bit) - 1 & score

それだけです。まず、&ビットワイズの意味とこの記号について説明しましょう。次に、このように、2つのバイナリ値が比較されます。両方が1の場合、私は1になり、それ以外の場合は0になります。
これにより、ユーザーの重みを計算する必要があります。ユーザーの重みは非常に簡単で、次のように記述するだけです。

int weight = score & ((1 << WEIGHT_BITS) - 1);

1最初に、4つのバイナリ表現が次のようになった後に終了しました
。00010000
この時点では、確かにそのような操作を実行できず、間違いなく間違っています。必要なのは-1だけです。この時点で、再び
0000
になります。スコアの計算
0011 0101を
ビット単位のAND結果を通じて
0000 0101
我々はまた、ユーザの体重値を取り出します。

他の高い値に行きたい場合はどうなりますか?今でも例を使用しています。書く必要があります

int onlineStatus = (score >> WEIGHT_BITS) & ((1 << ONLINE_STATUS_BITS) - 1);

スコアを重みに右に移動します。たとえば、0011 0101 >> 4桁

00000011になります。
このとき、ビット単位のAND演算
0000 0011を使用すると、
結果は重み値のスコアになり、他のアルゴリズムは同じです。

さて、今日の記事は以上です。気に入ったら、コメント、いいね、転送、お気に入りを忘れないでください〜

おすすめ

転載: blog.csdn.net/qq_39685066/article/details/109379747