ホームページ  >  記事  >  データベース  >  Redis の奇妙なデータ型とクラスターの知識は何ですか?

Redis の奇妙なデータ型とクラスターの知識は何ですか?

王林
王林転載
2023-06-02 22:01:56616ブラウズ

    さまざまなデータ型

    文字列型はシンプルで便利で、スペースの事前割り当て、つまりより多くのスペースをサポートします。毎回文字列が確保されるので、次回文字列が長くなった場合に追加のスペースを申請する必要はありません。もちろん、十分なスペースが残っていることが前提です。

    List 型は単純なメッセージ キューを実装できますが、ACK モードをサポートしていないため、メッセージ損失が発生する可能性があることに注意してください。

    ハッシュ テーブルはリレーショナル データベースに似ていますが、ハッシュ テーブルがますます大きくなる場合は、hgetall などのステートメントの使用を避けるように注意してください。大量のデータをリクエストすると Redis がブロックされるためです。 、そのため、後続の兄弟たちは待たなければなりません。

    set セット タイプは統計を行うのに役立ちます。たとえば、特定の日のアクティブ ユーザーをカウントしたい場合は、ユーザー ID をセットに直接投入できます。セットはいくつかの便利な操作をサポートしています。セット間のデータを取得できる sdiff など、セット間の和集合を取得できる差分セット、Sunion など、多くの機能がありますが、素晴らしい機能には代償が伴うため注意が必要です。これらの操作にはある程度の CPU と IO が必要ですリソースが多くブロックされる可能性があるため、大規模なセットを使用する場合はランダム操作を使用する場合は注意が必要です。

    zset は最も明るい星と言えます。ソートできます。ソートできるため、多くの星があります。アプリケーション シナリオ (気に入った上位 xx ユーザー、遅延キューなど)。

    ビットマップ ビットマップの利点は、特に、特定の日にサインインしたユーザーの数や、特定のユーザーがサインインしたかどうかを数えるなど、統計を実行する場合にスペースを節約できることです。ビットマップ、set の使用を考えるかもしれません。

    SADD day 1234//签到就添加到集合
    SISMEMBER day 1234//判断1234是否签到
    SCARD day   //有多少个签到的

    set は機能的には満足できますが、ビットマップに比べてより多くの記憶領域を消費します。セットの最下層は主に整数コレクションまたはハッシュテーブルで構成されます。整数コレクションはデータ量が制限されている場合にのみ使用できます。非常に小さいです。通常は 512 要素未満のみ使用でき、要素はすべて整数である必要があります。セットの場合、整数セットのデータはよりコンパクトで、メモリ内で連続しています。クエリは二分検索のみ可能です。時間計算量は O(logN) ですが、ハッシュテーブルは異なります。ここでのハッシュテーブルは、値がないことを除いて、redis の 5 つの主要なデータ型のハッシュと同じです。値は null を指します。はセットであるため競合しませんが、リハッシュに関連する問題を考慮する必要があります。わかりました。少し遠いです。ユーザーのサインインの問題について話すとき、ユーザーが多い場合、セットにはハッシュテーブルが必ず使用されます。ハッシュテーブルの場合、実際には、各要素は dictEntry 構造体です。

    typedef struct dictEntry {
        // 键
        void *key;
        // 值
        union {
            void *val;
            uint64_t u64;
            int64_t s64;
        } v;
        // 指向下个哈希表节点,形成链表
        struct dictEntry *next;
    } dictEntry;

    From この構造では何が見えますか?まず、union (値なし) と next (衝突なし) という値は空ですが、構造自体にはスペースとキーが必要で、この占有スペースは実数であり、ビットマップを使用する場合は 1 ビットで十分です。数値を表し、スペースを節約します。ビットマップを設定してカウントする方法を見てみましょう。

    SETBIT day 1234 1//签到
    GETBIT day 1234//判断1234是否签到
    BITCOUNT day//有多少个签到的

    bf これは、redis4.0 以降でサポートされるブルーム フィルター RedisBloom ですが、対応するモジュールを別途ロードする必要があります。もちろん、上記のビットマップに基づいて独自のブルーム フィルターを実装することもできますが、redis 以降すでにサポートされています。RedisBloom を使用すると、開発時間を短縮できます。ブルーム フィルターの機能についてはここでは詳しく説明しません。RedisBloom の関連した使用法を見てみましょう。

    # 可以通过docker的方式快速拉取镜像来玩耍
    docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest
    docker exec -it redis-redisbloom bash
    redis-cli
    # 相关操作
    bf.reserve sign 0.001 10000
    bf.add sign 99 //99这个用户加入
    bf.add exists 99//判断99这个用户是否存在

    ブルームフィルタには誤判定があるため、すべてのbfでカスタム誤判定率をサポートしています。0.001は誤判定率を表し、10000はブルームフィルタが格納できる要素数を表します。実際に格納する場合要素数がこれを超えた場合値を大きくすると、偽陽性率が増加します。

    HyperLogLog は統計に使用できます。その利点は、必要なストレージ スペースが非常に少ないことです。2^64 要素をカウントするのに必要なメモリは 12KB だけです。では、主に何をカウントするのでしょうか?実際には、主に UV などのカーディナリティ統計に関するものです。機能的に言​​えば、UV はセットまたはハッシュを使用して保存できますが、ストレージを消費し、大きなキーになりやすいという欠点があります。スペースを節約したい場合は、ビットマップ12KB 空間ビットマップは 12*1024*8=98304 要素しかカウントできませんが、HyperLogLog は 2^64 要素をカウントできます。ただし、このような強力なテクノロジには実際にはエラーがあります。HyperLogLog は確率と標準誤差に基づいてカウントします。計算率は 0.81% 大量のデータがカウントされ、精度要件がそれほど高くないシナリオでも、HyperLogLog はスペースの節約に優れています。

    PFADD uv 1 2 3 //1 2 3是活跃用户
    PFCOUNT uv //统计

    GEO 是可以应用在地理位置的业务上,比如微信附近的人或者附近的车辆等等,先来看一下如果没有GEO 这种数据结构,你如何知道你附近的人?首先得上报自己的地理位置信息吧,比如经度 116.397128,纬度 39.916527,此时可以用 string、hash 数据类型存储,但是如果要查找你附近的人,string 和 hash 这种就无能为例了,你不可能每次都要遍历全部的数据来判断,这样太耗时了,当然你也不可能通过 zset 这种数据结构来把经纬度信息当成权重,但是如果我们能把经纬度信息通过某种方式转换成一个数字,然后当成权重好像也可以,这时我们只需通过zrangebyscore key v1 v2也可以找到附近的人。真的需要这么麻烦吗?于是 GEO 出现了,GEO 转换经纬度为数字的方法是“二分区间,区间编码”,这是什么意思呢?以经度为例,它的范围是[-180,180],如果要采用3位编码值,那么就是需要二分3次,二分后落在左边的用0表示,右边的用1表示,以经度是121.48941 来说,第一次是在[0,180]这个区间,因此记1,第二次是在[90,180],因此再记1,第三次是在[90,135],因此记0。纬度也是同样的逻辑,假设此时对应的纬度编码后是010,最后把经纬度合并在一起,需要注意的是经度的每个值在偶数位,纬度的每个值在奇数位。

    1 1 0   //经度
     0 1 0  //纬度
    ------------
    101100 //经纬度对应的数值

    原理是这样,我们再来看看 redis 如何使用 GEO:

    GEOADD location 112.123456 41.112345 99 //上报用户99的地理位置信息
    GEORADIUS location  112.123456 41.112345 1 km ASC COUNT 10 //获取附近1KM的人

    搞懂集群

    生产环境用单实例 redis 的应该比较少,单实例的风险在于:

    1. 单点故障即服务故障,没有backup

    2. 单实例压力大,又要提供读,又要提供写

    于是我们首先想到的就是经典的主从模式,而且往往是一主多从,这是因为大部分应用都是读多写少的情况,我们的主负责更新,从负责提供读,就算我们的主宕机了,我们也可以选择一个从来充当主,这样整个应用依然可以提供服务。

    复制过程的细节

    当一个 redis 实例首次成为某个主的从的时候,这时主得把数据发给它,也就是 rdb 文件,这个过程 master 是要 fork 一个子进程来处理的,这个子进程会执行 bgsave 把当前的数据重新保存一下,然后准备发给新来的从,bgsave 的本质是读取当前内存中的数据然后保存到 rdb 文件中,这个过程涉及大量的 IO,如果直接在主进程中来处理的话,大概率会阻塞正常的请求,因此使用个子进程是个明智的选择。

    那 fork 的子进程在 bgsave 过程中如果有新的变更请求会怎么办?

    严格来说子进程出来的一瞬间,要保存的数据应该就是当时那个点的快照数据,所以是直接把当时的内存再复制一份吗?不复制的话,如果这期间又有变更改怎么办?其实这要说到写实复制(COW)机制,首先从表象上来看内存是一整块空间,其实这不太好维护,因此操作系统会把内存分成一小块一小块的,也就是内存分页管理,一页的大小一般是4K、8K或者16K等等,redis 的数据都是分布在这些页面上的,出于效率问题,fork 出来的子进程是和主进程是共享同一块的内存的,并不会复制内存,如果这期间主进程有数据变更,那么为了区分,这时最快捷的做法就是把对应的数据页重新复制一下,然后主的变更就在这个新的数据页上修改,并不会修改来的数据页,这样就保证了子进程处理的还是当时的快照。

    以上说的变更是从快照的角度来考虑的,如果从数据的一致性来说,当快照的 rdb 被从库应用之后,这期间的变更该如何同步给从库?答案是缓冲区,这个缓冲区叫做 replication buffer,主库在收到需要同步的命令之后,会把期间的变更都先保存在这个缓冲区中,这样在把 rdb 发给从库之后,紧接着会再把 replication buffer 的数据也发给从库,最终主从就保持了一致。

    replication buffer不是万能的补给剂

    我们来看看 replication buffer 持续写入的时间有多长。

    1. 我们知道主从同步的时候,主库会执行 fork 来让子进程完成相应地工作,因此子进程从开始执行 bgsave 到执行完毕这期间,变更是要写入 replication buffer 的。

    2. rdb 生成好之后,需要把它发送给从库,这个网络传输是不是也需要耗点时间,这期间也是要写入 replication buffer 的。

    3. 从库在收到 rdb 之后需要把 rdb 应用到内存里,这期间从库是阻塞的,无法提供服务,因此这期间也是要写入 replication buffer 的。

    レプリケーション バッファ バッファであるため、そのサイズには制限があります。上記 3 つの手順のいずれかに時間がかかると、レプリケーション バッファが急速に増大します (通常の書き込みがある場合)。 , レプリケーション バッファーが制限を超えると、マスター ライブラリとスレーブ ライブラリ間の接続が切断されます。切断後、スレーブ ライブラリが再度接続されると、レプリケーションが再開され、同じ長いレプリケーション ステップが実行されます。そのため、このレプリケーション バッファのサイズは依然として非常に重要であり、通常は書き込み速度、1 秒あたりの書き込み量、ネットワーク伝送速度などの要素に基づいて総合的に判断する必要があります。

    スレーブ データベースのネットワークが良好ではなく、マスター データベースが切断されている場合はどうすればよいですか?

    通常、マスターとスレーブ間の接続が確立されている限り、マスター ライブラリに対する後続の変更をスレーブ ライブラリに直接送信して、スレーブ ライブラリから直接再生できますが、ネットワーク環境が 100% スムーズであることは保証できません。そのため、スレーブ データベースとマスター データベース間の切断の問題も考慮する必要があります。

    redis2.8 以前では、スレーブ データベースがたとえ短時間であっても切断された場合、後でスレーブ データベースが再び接続されたときに、メイン データベースが直接かつ無神経に完全同期を実行していたはずです。バージョン 2.8 以降では、増分レプリケーションがサポートされています。増分レプリケーションの原則は、変更レコードを保存するためのバッファーが必要であるということです。このバッファーは、ここでは repl_backlog_buffer と呼ばれます。このバッファーは、論理的にはリング バッファーです。いっぱいになると書き込み、最初から上書きされるのでサイズ制限もあります。スレーブ ライブラリが再接続すると、スレーブ ライブラリはメイン ライブラリに「xx の場所にコピーしました。」と通知します。メイン ライブラリはスレーブ ライブラリからメッセージを受信した後、xx の場所にデータが残っているかどうかの確認を開始します。 repl_backlog_buffer にある場合は、xx 以降のデータをスレーブ ライブラリに送信します。存在しない場合は、何もすることができず、再度完全同期を実行することしかできません。

    マネージャーが必要です

    マスター/スレーブ モードでは、メイン ライブラリがハングアップした場合、スレーブ ライブラリをメイン ライブラリにアップグレードできますが、このプロセスは手動であり、人力に依存しています。 . 損失を最小限に抑えることはできません。自動管理と選出メカニズムのセットが依然として必要です。これが Sentinel です。Sentinel 自体もサービスですが、データの読み取りと書き込みは処理しません。すべての Redis の管理のみを担当します。セントリーは定期的に各 Redis と通信します (ping 操作)。各 Redis インスタンスは、指定された時間内に応答する限り、その位置を表現できます。もちろん、Sentinel 自体がダウンしているか、ネットワークが利用できない可能性があるため、一般的に Sentinel は Sentinel クラスターも構築します。クラスターの数は 3 または 5 など、奇数にするのが最適です。奇数の目的は次のとおりです。主に選挙のためです(少数派は多数派に従う)。

    センチネルが ping を開始した後、時間内にポンを受信しない場合、redis インスタンスはオフラインとしてマークされます。この時点では、まだ実際にはオフラインではありません。この時点で、他のセンチネルも現在のこのセンチネルは本当にオフラインですか? ほとんどのセンチネルは、この Redis がオフラインであると判断すると、それをクラスターから追い出します。オフラインのスレーブ データベースであれば問題ありません。直接追い出すだけです。メインの場合は、データベースは選挙をトリガーする必要があります。選挙はブラインド選挙ではなく、常に新しいメイン データベースとして機能するのに最適なものを選択する必要があります。マスター ライブラリとして機能するのに最も適したこのライブラリは、通常、次の優先順位に従って決定されます:

    1. 重み 各スレーブ ライブラリは実際に重みを設定できます。重みが大きいほど、コピーの進行状況が優先されます。

    2. . 各スレーブ ライブラリからのコピーの進行状況は異なる場合があります。現在のライブラリとメイン ライブラリのデータギャップが最も小さいものは、指定された優先度

    3. サービスの ID。実際、各 Redis インスタンスには独自の ID があります。上記の条件が同じ場合、最小の ID を持つライブラリが選択されます。メイン ライブラリとして機能します

    より強力な水平スケーラビリティ

    マスター/スレーブ モードは、単一障害点の問題を解決します。テクノロジーによりアプリケーションのサポートが強化され、センチネルモードによりクラスターを自動的に監視し、自動マスター選択を実現し、障害のあるノードを自動的に排除できます。

    通常、読み取りの負荷が増大する限り、スレーブ ライブラリを追加して軽減することができますが、メイン ライブラリの負荷が非常に高い場合はどうなるでしょうか?次にシャーディング テクノロジについて説明します。メイン ライブラリをいくつかの部分に分割し、それらを異なるマシンにデプロイするだけです。このシャーディングは、redis におけるスロットの概念です。シャーディングすると、redis はデフォルトで 0 ~ 16383 に分割され、合計 16384 スロットになります。その後、これらのスロットが各シャード ノードに均等に分散されて負荷分散が実現されました。各キーをどのスロットに割り当てる必要がありますか? 重要なことは、最初に CRC16 を使用して 16 ビット数値を取得し、次にこの数値を 16384 を法として使用することです:

    crc16(key)%16384

    客户端将缓存插槽信息,以便在每个键到达时只需计算即可确定该将其发送到哪个实例进行处理。但是客户端缓存的槽信息并不是一成不变的,比如在增加实例的时候,这时候会导致重新分片,那么原来客户端缓存的信息就会不准确,一般这时候会发生两个常见的错误,严格来说也不是错误,更像一种信息,一个叫做MOVED,一个叫做ASK。moved的意思就说,原来是实例A负责的数据,现在被迁移到了实例B,MOVED 代表的是迁移完成的,但是 ASK 代表的是正在迁移过程中,比如原来是实例A负责的部分数据,现在被迁移到了实例B,剩下的还在等待迁移中,当数据迁移完毕之后 ASK 就会变成 MOVED,然后客户端收到 MOVED 信息之后就会再次更新下本地缓存,这样下次就不会出现这两个错误了。

    以上がRedis の奇妙なデータ型とクラスターの知識は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。