偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Redis 實戰(zhàn)篇:巧用 Bitmap 實現(xiàn)億級海量數(shù)據(jù)統(tǒng)計

數(shù)據(jù)庫 其他數(shù)據(jù)庫 Redis
本文將由二值狀態(tài)統(tǒng)計類型作為實戰(zhàn)篇系列的開篇,文中將用到 String、Set、Zset、List、hash 以外的拓展數(shù)據(jù)類型 Bitmap 來實現(xiàn)。

[[401134]]

在移動應(yīng)用的業(yè)務(wù)場景中,我們需要保存這樣的信息:一個 key 關(guān)聯(lián)了一個數(shù)據(jù)集合。

常見的場景如下:

  • 給一個 userId ,判斷用戶登陸狀態(tài);
  • 顯示用戶某個月的簽到次數(shù)和首次簽到時間;
  • 兩億用戶最近 7 天的簽到情況,統(tǒng)計 7 天內(nèi)連續(xù)簽到的用戶總數(shù);

通常情況下,我們面臨的用戶數(shù)量以及訪問量都是巨大的,比如百萬、千萬級別的用戶數(shù)量,或者千萬級別、甚至億級別的訪問信息。

所以,我們必須要選擇能夠非常高效地統(tǒng)計大量數(shù)據(jù)(例如億級)的集合類型。

如何選擇合適的數(shù)據(jù)集合,我們首先要了解常用的統(tǒng)計模式,并運用合理的數(shù)據(jù)類型來解決實際問題。

四種統(tǒng)計類型:

  • 二值狀態(tài)統(tǒng)計;
  • 聚合統(tǒng)計;
  • 排序統(tǒng)計;
  • 基數(shù)統(tǒng)計。

本文將由二值狀態(tài)統(tǒng)計類型作為實戰(zhàn)篇系列的開篇,文中將用到 String、Set、Zset、List、hash 以外的拓展數(shù)據(jù)類型 Bitmap 來實現(xiàn)。

文章涉及到的指令可以通過在線 Redis 客戶端運行調(diào)試,地址:https://try.redis.io/,超方便的說。

寄語

多分享多付出,前期多給別人創(chuàng)造價值并且不計回報,從長遠來看,這些付出都會成倍的回報你。

特別是剛開始跟別人合作的時候,不要去計較短期的回報,沒有太大意義,更多的是鍛煉自己的視野、視角以及解決問題的能力。

二值狀態(tài)統(tǒng)計

碼哥,什么是二值狀態(tài)統(tǒng)計呀?

也就是集合中的元素的值只有 0 和 1 兩種,在簽到打卡和用戶是否登陸的場景中,只需記錄簽到(1)或 未簽到(0),已登錄(1)或未登陸(0)。

假如我們在判斷用戶是否登陸的場景中使用 Redis 的 String 類型實現(xiàn)(key -> userId,value -> 0 表示下線,1 - 登陸),假如存儲 100 萬個用戶的登陸狀態(tài),如果以字符串的形式存儲,就需要存儲 100 萬個字符串了,內(nèi)存開銷太大。

碼哥,為什么 String 類型內(nèi)存開銷大?

String 類型除了記錄實際數(shù)據(jù)以外,還需要額外的內(nèi)存記錄數(shù)據(jù)長度、空間使用等信息。

當保存的數(shù)據(jù)包含字符串,String 類型就使用簡單動態(tài)字符串(SDS)結(jié)構(gòu)體來保存,如下圖所示:

SDS

len:占 4 個字節(jié),表示 buf 的已用長度。

alloc:占 4 個字節(jié),表示 buf 實際分配的長度,通常 > len。

buf:字節(jié)數(shù)組,保存實際的數(shù)據(jù),Redis 自動在數(shù)組最后加上一個 “\0”,額外占用一個字節(jié)的開銷。

所以,在 SDS 中除了 buf 保存實際的數(shù)據(jù), len 與 alloc 就是額外的開銷。

另外,還有一個 RedisObject 結(jié)構(gòu)的開銷,因為 Redis 的數(shù)據(jù)類型有很多,而且,不同數(shù)據(jù)類型都有些相同的元數(shù)據(jù)要記錄(比如最后一次訪問的時間、被引用的次數(shù)等)。

所以,Redis 會用一個 RedisObject 結(jié)構(gòu)體來統(tǒng)一記錄這些元數(shù)據(jù),同時指向?qū)嶋H數(shù)據(jù)。

對于二值狀態(tài)場景,我們就可以利用 Bitmap 來實現(xiàn)。比如登陸狀態(tài)我們用一個 bit 位表示,一億個用戶也只占用 一億 個 bit 位內(nèi)存 ≈ (100000000 / 8/ 1024/1024)12 MB。

  1. 大概的空間占用計算公式是:($offset/8/1024/1024) MB 

什么是 Bitmap 呢?

Bitmap 的底層數(shù)據(jù)結(jié)構(gòu)用的是 String 類型的 SDS 數(shù)據(jù)結(jié)構(gòu)來保存位數(shù)組,Redis 把每個字節(jié)數(shù)組的 8 個 bit 位利用起來,每個 bit 位 表示一個元素的二值狀態(tài)(不是 0 就是 1)。

可以將 Bitmap 看成是一個 bit 為單位的數(shù)組,數(shù)組的每個單元只能存儲 0 或者 1,數(shù)組的下標在 Bitmap 中叫做 offset 偏移量。

為了直觀展示,我們可以理解成 buf 數(shù)組的每個字節(jié)用一行表示,每一行有 8 個 bit 位,8 個格子分別表示這個字節(jié)中的 8 個 bit 位,如下圖所示:

Bitmap

8 個 bit 組成一個 Byte,所以 Bitmap 會極大地節(jié)省存儲空間。 這就是 Bitmap 的優(yōu)勢。

判斷用戶登陸態(tài)

怎么用 Bitmap 來判斷海量用戶中某個用戶是否在線呢?

Bitmap 提供了 GETBIT、SETBIT 操作,通過一個偏移值 offset 對 bit 數(shù)組的 offset 位置的 bit 位進行讀寫操作,需要注意的是 offset 從 0 開始。

只需要一個 key = login_status 表示存儲用戶登陸狀態(tài)集合數(shù)據(jù), 將用戶 ID 作為 offset,在線就設(shè)置為 1,下線設(shè)置 0。通過 GETBIT判斷對應(yīng)的用戶是否在線。50000 萬 用戶只需要 6 MB 的空間。

SETBIT 命令

  1. SETBIT <key> <offset> <value> 

設(shè)置或者清空 key 的 value 在 offset 處的 bit 值(只能是 0 或者 1)。

GETBIT 命令

  1. GETBIT <key> <offset> 

獲取 key 的 value 在 offset 處的 bit 位的值,當 key 不存在時,返回 0。

假如我們要判斷 ID = 10086 的用戶的登陸情況:

第一步,執(zhí)行以下指令,表示用戶已登錄。

  1. SETBIT login_status 10086 1 

第二步,檢查該用戶是否登陸,返回值 1 表示已登錄。

  1. GETBIT login_status 10086 

第三步,登出,將 offset 對應(yīng)的 value 設(shè)置成 0。

  1. SETBIT login_status 10086 0 

用戶每個月的簽到情況在簽到統(tǒng)計中,每個用戶每天的簽到用 1 個 bit 位表示,一年的簽到只需要 365 個 bit 位。一個月最多只有 31 天,只需要 31 個 bit 位即可。

比如統(tǒng)計編號 89757 的用戶在 2021 年 5 月份的打卡情況要如何進行?

key 可以設(shè)計成 uid:sign:{userId}:{yyyyMM},月份的每一天的值 - 1 可以作為 offset(因為 offset 從 0 開始,所以 offset = 日期 - 1)。

第一步,執(zhí)行下面指令表示記錄用戶在 2021 年 5 月 16 號打卡。

  1. SETBIT uid:sign:89757:202105 15 1 

第二步,判斷編號 89757 用戶在 2021 年 5 月 16 號是否打卡。

  1. GETBIT uid:sign:89757:202105 15 

第三步,統(tǒng)計該用戶在 5 月份的打卡次數(shù),使用 BITCOUNT 指令。該指令用于統(tǒng)計給定的 bit 數(shù)組中,值 = 1 的 bit 位的數(shù)量。

  1. BITCOUNT uid:sign:89757:202105 

這樣我們就可以實現(xiàn)用戶每個月的打卡情況了,是不是很贊。

如何統(tǒng)計這個月首次打卡時間呢?

Redis 提供了 BITPOS key bitValue [start] [end]指令,返回數(shù)據(jù)表示 Bitmap 中第一個值為 bitValue 的 offset 位置。

在默認情況下, 命令將檢測整個位圖, 用戶可以通過可選的 start 參數(shù)和 end參數(shù)指定要檢測的范圍。

所以我們可以通過執(zhí)行以下指令來獲取 userID = 89757 在 2021 年 5 月份首次打卡日期:

  1. BITPOS uid:sign:89757:202105 1 

需要注意的是,我們需要將返回的 value + 1 ,因為 offset 從 0 開始。

連續(xù)簽到用戶總數(shù)

在記錄了一個億的用戶連續(xù) 7 天的打卡數(shù)據(jù),如何統(tǒng)計出這連續(xù) 7 天連續(xù)打卡用戶總數(shù)呢?

我們把每天的日期作為 Bitmap 的 key,userId 作為 offset,若是打卡則將 offset 位置的 bit 設(shè)置成 1。

key 對應(yīng)的集合的每個 bit 位的數(shù)據(jù)則是一個用戶在該日期的打卡記錄。

一共有 7 個這樣的 Bitmap,如果我們能對這 7 個 Bitmap 的對應(yīng)的 bit 位做 『與』運算。

同樣的 UserID offset 都是一樣的,當一個 userID 在 7 個 Bitmap 對應(yīng)對應(yīng)的 offset 位置的 bit = 1 就說明該用戶 7 天連續(xù)打卡。

結(jié)果保存到一個新 Bitmap 中,我們再通過 BITCOUNT 統(tǒng)計 bit = 1 的個數(shù)便得到了連續(xù)打卡 7 天的用戶總數(shù)了。

Redis 提供了 BITOP operation destkey key [key ...]這個指令用于對一個或者多個 鍵 = key 的 Bitmap 進行位元操作。

opration 可以是 and、OR、NOT、XOR。當 BITOP 處理不同長度的字符串時,較短的那個字符串所缺少的部分會被看作 0 ??盏?key 也被看作是包含 0的字符串序列。

便于理解,如下圖所示:

BITOP

3 個 Bitmap,對應(yīng)的 bit 位做「與」操作,結(jié)果保存到新的 Bitmap 中。

操作指令表示將 三個 bitmap 進行 AND 操作,并將結(jié)果保存到 destmap 中。接著對 destmap 執(zhí)行 BITCOUNT 統(tǒng)計。

  1. // 與操作 
  2. BITOP AND destmap bitmap:01 bitmap:02 bitmap:03 
  3. // 統(tǒng)計 bit 位 =  1 的個數(shù) 
  4. BITCOUNT destmap 

簡單計算下 一個一億個位的 Bitmap占用的內(nèi)存開銷,大約占 12 MB 的內(nèi)存(10^8/8/1024/1024),7 天的 Bitmap 的內(nèi)存開銷約為 84 MB。同時我們最好給 Bitmap 設(shè)置過期時間,讓 Redis 刪除過期的打卡數(shù)據(jù),節(jié)省內(nèi)存。

小結(jié)

思路才是最重要,當我們遇到的統(tǒng)計場景只需要統(tǒng)計數(shù)據(jù)的二值狀態(tài),比如用戶是否存在、 ip 是否是黑名單、以及簽到打卡統(tǒng)計等場景就可以考慮使用 Bitmap。

只需要一個 bit 位就能表示 0 和 1。在統(tǒng)計海量數(shù)據(jù)的時候?qū)⒋蟠鬁p少內(nèi)存占用。

 

責任編輯:姜華 來源: 碼哥字節(jié)
相關(guān)推薦

2021-06-08 08:51:50

Redis 數(shù)據(jù)類型數(shù)據(jù)統(tǒng)計

2021-07-06 08:41:54

RedisGeo 類型數(shù)據(jù)類型

2021-07-05 08:41:49

RedisGEO系統(tǒng)

2023-12-08 07:55:37

MySQL數(shù)據(jù)統(tǒng)計InnoDB

2024-11-15 10:39:11

2021-08-08 22:08:41

Redis開發(fā)網(wǎng)頁

2024-08-22 14:16:08

2019-05-21 14:33:01

2009-06-15 16:05:30

設(shè)計AnnotatioJava

2021-07-02 10:10:55

SecurityJWT系統(tǒng)

2024-11-27 09:32:58

2017-09-17 09:29:26

APP大數(shù)據(jù)架構(gòu)

2015-02-12 16:05:51

微信SDK

2015-02-12 16:17:09

微信SDK

2015-02-12 15:45:05

微信SDK

2025-01-14 16:14:10

2017-11-08 13:31:34

分層架構(gòu)代碼DDD

2020-09-01 17:19:36

數(shù)據(jù)監(jiān)控建模

2015-02-12 16:53:22

微信SDK

2024-06-19 21:12:02

點贊
收藏

51CTO技術(shù)棧公眾號