Redis+Bitmap を使用して 10 億レベルの大規模なデータ統計を達成する方法を段階的に説明します。

青灯夜游
リリース: 2021-10-09 10:09:48
転載
3824 人が閲覧しました

この記事はRedisの実践編であり、Redis Bitmapを利用して10億レベルの膨大なデータ統計を実現する方法を紹介しています。

Redis+Bitmap を使用して 10 億レベルの大規模なデータ統計を達成する方法を段階的に説明します。

#モバイル アプリケーションのビジネス シナリオでは、キーがデータ コレクションに関連付けられているような情報を保存する必要があります。 [関連する推奨事項: Redis ビデオ チュートリアル ]

一般的なシナリオは次のとおりです:

  • ユーザーのログイン ステータスを判断するために userId を指定します;
  • display ある月のユーザーのチェックイン数と最初のチェックイン時刻;
  • 過去 7 日間の 2 億ユーザーのチェックイン状況 (総ユーザー数をカウント) 7 日以内に継続的にチェックインした人;

通常、私たちが直面するユーザーと訪問の数は、数百万、数千万のユーザー、あるいは数千万、さらには数十億のアクセスなど、膨大な数になります。情報。

したがって、大量のデータ (数十億など) を非常に効率的にカウントできるコレクション タイプを選択する必要があります。

適切なデータセットを選択するには、まず一般的に使用される統計モデルを理解し、合理的なデータ分析を使用して実際的な問題を解決する必要があります。

4 つの統計タイプ:

  • バイナリ ステータス統計、

  • 集計統計、

  • 統計の並べ替え;

  • カーディナリティ統計。

この記事では、実践シリーズの開始として バイナリ状態統計タイプ を使用します。String、Set、Zset、List、hash## は次のようになります。 #Bitmap 以外の拡張データ型も実装可能。

この記事に含まれる手順は、オンライン Redis クライアント (アドレス

try.redis.io/) を通じて実行およびデバッグできます。これは非常に便利です。

メッセージ

より多くの情報を共有し、より多くのお金を支払います。利益を考慮せず、初期段階で他の人のためにより多くの価値を生み出します。長期的には、これらの努力は指数関数的に報われます。

特に、初めて他の人と協力し始めるときは、短期的な利益を心配する必要はありません。それはあまり意味がありません。むしろ、自分自身のビジョン、視点、問題解決能力を発揮することが重要です。

バイナリ ステータス統計

マー兄弟、バイナリ ステータス統計とは何ですか?

つまり、コレクション内の要素の値は 0 と 1 のみです。チェックインとチェックイン、およびユーザーがログインしているかどうかのシナリオでは、必要なのは記録するには

チェックイン (1) または サインインしていません (0) , ログイン済み (1) または ログインしていません (0)

ユーザーがログインしているかどうかを判断するシナリオで Redis の String 型実装を使用するとします (

key -> userId、value -> 0 はオフライン、1 - ログインを意味します ), if 100万人のログイン状態を保存するには、文字列形式で保存すると100万個の文字列を保存する必要があり、メモリを大量に消費します。

Ma 兄さん、String 型のメモリ オーバーヘッドが高いのはなぜですか?

String 型では、実際のデータを記録するだけでなく、データ長、スペース使用量、その他の情報を記録するために追加のメモリも必要です。

保存されたデータに文字列が含まれている場合、次の図に示すように、String 型は Simple Dynamic String (SDS) 構造を使用して保存されます。

len

: 4 バイトを占め、使用される buf の長さを示します。 Redis+Bitmap を使用して 10 億レベルの大規模なデータ統計を達成する方法を段階的に説明します。

    alloc
  • : 4 バイト。実際に割り当てられた buf の長さを示します。通常は > len です。
  • buf
  • : バイト配列、実際のデータを保存します。Redis は配列の末尾に「\0」を自動的に追加し、追加バイトのオーバーヘッドを占有します。
  • つまり、実際のデータを SDS に保存する buf に加えて、len と alloc が追加のオーバーヘッドになります。
  • さらに、Redis には多くのデータ型があり、異なるデータ型には記録する同じメタデータ (最終アクセス時刻など) があるため、RedisObject 構造
  • のオーバーヘッドがあります。 、引用数など)。

したがって、Redis は RedisObject 構造を使用してこれらのメタデータを均一に記録し、実際のデータをポイントします。

#バイナリ状態のシナリオでは、ビットマップを使用してそれを実現できます。たとえば、ログイン ステータスを表すのに 1 ビットを使用しますが、1 億人のユーザーが占有するメモリは 1 億ビット、つまり (100000000 / 8/ 1024/1024) 12 MB だけです。

大概的空间占用计算公式是:($offset/8/1024/1024) MB
ログイン後にコピー

ビットマップとは何ですか? Redis+Bitmap を使用して 10 億レベルの大規模なデータ統計を達成する方法を段階的に説明します。

Bitmap 的底层数据结构用的是 String 类型的 SDS 数据结构来保存位数组,Redis 把每个字节数组的 8 个 bit 位利用起来,每个 bit 位 表示一个元素的二值状态(不是 0 就是 1)。

可以将 Bitmap 看成是一个 bit 为单位的数组,数组的每个单元只能存储 0 或者 1,数组的下标在 Bitmap 中叫做 offset 偏移量。

为了直观展示,我们可以理解成 buf 数组的每个字节用一行表示,每一行有 8 个 bit 位,8 个格子分别表示这个字节中的 8 个 bit 位,如下图所示:

Redis+Bitmap を使用して 10 億レベルの大規模なデータ統計を達成する方法を段階的に説明します。

8 个 bit 组成一个 Byte,所以 Bitmap 会极大地节省存储空间。 这就是 Bitmap 的优势。

判断用户登陆态

怎么用 Bitmap 来判断海量用户中某个用户是否在线呢?

Bitmap 提供了 GETBIT、SETBIT 操作,通过一个偏移值 offset 对 bit 数组的 offset 位置的 bit 位进行读写操作,需要注意的是 offset 从 0 开始。

只需要一个 key = login_status 表示存储用户登陆状态集合数据, 将用户 ID 作为 offset,在线就设置为 1,下线设置 0。通过 GETBIT判断对应的用户是否在线。 50000 万 用户只需要 6 MB 的空间。

SETBIT 命令

SETBIT <key> <offset> <value>
ログイン後にコピー

设置或者清空 key 的 value 在 offset 处的 bit 值(只能是 0 或者 1)。

GETBIT 命令

GETBIT <key> <offset>
ログイン後にコピー

获取 key 的 value 在 offset 处的 bit 位的值,当 key 不存在时,返回 0。

假如我们要判断 ID = 10086 的用户的登陆情况:

第一步,执行以下指令,表示用户已登录。

SETBIT login_status 10086 1
ログイン後にコピー

第二步,检查该用户是否登陆,返回值 1 表示已登录。

GETBIT login_status 10086
ログイン後にコピー

第三步,登出,将 offset 对应的 value 设置成 0。

SETBIT login_status 10086 0
ログイン後にコピー

用户每个月的签到情况

在签到统计中,每个用户每天的签到用 1 个 bit 位表示,一年的签到只需要 365 个 bit 位。一个月最多只有 31 天,只需要 31 个 bit 位即可。

比如统计编号 89757 的用户在 2021 年 5 月份的打卡情况要如何进行?

key 可以设计成 uid:sign:{userId}:{yyyyMM},月份的每一天的值 - 1 可以作为 offset(因为 offset 从 0 开始,所以 offset = 日期 - 1)。

第一步,执行下面指令表示记录用户在 2021 年 5 月 16 号打卡。

SETBIT uid:sign:89757:202105 15 1
ログイン後にコピー

第二步,判断编号 89757 用户在 2021 年 5 月 16 号是否打卡。

GETBIT uid:sign:89757:202105 15
ログイン後にコピー

第三步,统计该用户在 5 月份的打卡次数,使用 BITCOUNT 指令。该指令用于统计给定的 bit 数组中,值 = 1 的 bit 位的数量。

BITCOUNT uid:sign:89757:202105
ログイン後にコピー

这样我们就可以实现用户每个月的打卡情况了,是不是很赞。

如何统计这个月首次打卡时间呢?

Redis 提供了 BITPOS key bitValue [start] [end]指令,返回数据表示 Bitmap 中第一个值为 bitValue 的 offset 位置。

在默认情况下, 命令将检测整个位图, 用户可以通过可选的 start 参数和 end 参数指定要检测的范围。

所以我们可以通过执行以下指令来获取 userID = 89757 在 2021 年 5 月份首次打卡日期:

BITPOS uid:sign:89757:202105 1
ログイン後にコピー

需要注意的是,我们需要将返回的 value + 1 ,因为 offset 从 0 开始。

连续签到用户总数

在记录了一个亿的用户连续 7 天的打卡数据,如何统计出这连续 7 天连续打卡用户总数呢?

我们把每天的日期作为 Bitmap 的 key,userId 作为 offset,若是打卡则将 offset 位置的 bit 设置成 1。

key 对应的集合的每个 bit 位的数据则是一个用户在该日期的打卡记录。

一共有 7 个这样的 Bitmap,如果我们能对这 7 个 Bitmap 的对应的 bit 位做 『与』运算。

同样的 UserID offset 都是一样的,当一个 userID 在 7 个 Bitmap 对应对应的 offset 位置的 bit = 1 就说明该用户 7 天连续打卡。

结果保存到一个新 Bitmap 中,我们再通过 BITCOUNT 统计 bit = 1 的个数便得到了连续打卡 7 天的用户总数了。

Redis 提供了 BITOP operation destkey key [key ...]这个指令用于对一个或者多个 键 = key 的 Bitmap 进行位元操作。

opration 可以是 andORNOTXOR。当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0 。空的 key 也被看作是包含 0 的字符串序列。

便于理解,如下图所示:

Redis+Bitmap を使用して 10 億レベルの大規模なデータ統計を達成する方法を段階的に説明します。

3 个 Bitmap,对应的 bit 位做「与」操作,结果保存到新的 Bitmap 中。

操作指令表示将 三个 bitmap 进行 AND 操作,并将结果保存到 destmap 中。接着对 destmap 执行 BITCOUNT 统计。

// 与操作
BITOP AND destmap bitmap:01 bitmap:02 bitmap:03
// 统计 bit 位 =  1 的个数
BITCOUNT destmap
ログイン後にコピー

简单计算下 一个一亿个位的 Bitmap占用的内存开销,大约占 12 MB 的内存(10^8/8/1024/1024),7 天的 Bitmap 的内存开销约为 84 MB。同时我们最好给 Bitmap 设置过期时间,让 Redis 删除过期的打卡数据,节省内存。

小结

思路才是最重要,当我们遇到的统计场景只需要统计数据的二值状态,比如用户是否存在、 ip 是否是黑名单、以及签到打卡统计等场景就可以考虑使用 Bitmap。

只需要一个 bit 位就能表示 0 和 1。在统计海量数据的时候将大大减少内存占用。

原文地址:https://juejin.cn/post/6999908907791417351

作者:码哥字节

更多编程相关知识,请访问:编程视频!!

以上がRedis+Bitmap を使用して 10 億レベルの大規模なデータ統計を達成する方法を段階的に説明します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート