Redis學(xué)習(xí)與應(yīng)用(一)位圖

什么是位圖

位圖(Bitmap)是通過(guò)一個(gè) bit 來(lái)表示某個(gè)元素對(duì)應(yīng)的值或者狀態(tài)。它并不是什么新的數(shù)據(jù)結(jié)構(gòu)。它的內(nèi)容其實(shí)就是普通的字符串。我們可以通過(guò) get/set 獲取位圖的內(nèi)容,也可以使用 getbit/setbit 操作 bit 值(0 或者 1)。

Bit即比特,是目前計(jì)算機(jī)中數(shù)據(jù)最小的單位。8個(gè)Bit一個(gè)Byte(字節(jié))。Bit的值,要么為 0 ,要么為 1。由于Bit是計(jì)算機(jī)中最小的單位,使用它進(jìn)行儲(chǔ)存將非常節(jié)省空間。特別適合一些數(shù)據(jù)量大的場(chǎng)景。例如,統(tǒng)計(jì)每日活躍用戶(hù)、統(tǒng)計(jì)每月打卡數(shù)等統(tǒng)計(jì)場(chǎng)景。

常用命令介紹

1)SETBIT

作用:對(duì)于某個(gè)KEY的某位設(shè)值
用法:SETBIT key offset value
返回值: 原來(lái)儲(chǔ)存的位

redis> SETBIT bit 10086 1
(integer) 0

2)GETBIT

作用:獲取某KEY某位的值
用法:GETBIT key offset
返回值:0 或 1。 當(dāng) offset 比字符串值的長(zhǎng)度大,或者 key 不存在時(shí),返回 0 。

redis> SETBIT bit 10086 1
(integer) 0

redis> GETBIT bit 10086
(integer) 1

3)BITOP

作用:對(duì)多個(gè)鍵進(jìn)行位操作。 OPoperation的簡(jiǎn)寫(xiě)。
用法:BITOP operation destkey key1 key2 [key ...]
參數(shù)說(shuō)明:
operation 表示位運(yùn)算符。一共有四種操作,見(jiàn)下表。
destkey 表示運(yùn)算結(jié)果保存的值
key1、key2、key3 表示進(jìn)行運(yùn)算的key

operation 描述
AND 邏輯并
OR 邏輯或
NOT 邏輯非
XOR 邏輯異或

返回值:保存到 destkey 的字符串的長(zhǎng)度,和輸入 key 中最長(zhǎng)的字符串長(zhǎng)度相等。

redis> SETBIT bits-1 0 1        # bits-1 = 1001
(integer) 0

redis> SETBIT bits-1 3 1
(integer) 0

redis> SETBIT bits-2 0 1        # bits-2 = 1011
(integer) 0

redis> SETBIT bits-2 1 1
(integer) 0

redis> SETBIT bits-2 3 1
(integer) 0

redis> BITOP AND and-result bits-1 bits-2
(integer) 1

redis> GETBIT and-result 0      # and-result = 1001
(integer) 1

redis> GETBIT and-result 1
(integer) 0

redis> GETBIT and-result 2
(integer) 0

redis> GETBIT and-result 3
(integer) 1

4)BITCOUNT

作用:計(jì)算給定字符串上,位為1的個(gè)數(shù)
用法:BITCOUNT key [start] [end] 注意:此處的[start] [end] 為 字節(jié)開(kāi)始和結(jié)束的位置,非偏移量的位置
返回值:被設(shè)置為 1 的位的數(shù)量。不存在的key,或空字符串,值為0

redis> SETBIT tian 0 1
(integer) 0
redis> BITCOUNT tian
(integer) 1
redis> SETBIT tian 2 1
(integer) 0
redis> BITCOUNT tian
(integer) 2

5)BITPOS

用法:獲取某個(gè)鍵第一位被設(shè)置為 0 或 1 位的位置
作用:BITPOS key bit [start] [end]
返回值:返回第一個(gè)被設(shè)為 0 或 1 的位置

redis> SET test_str 'youthcity'
OK
# 查看值為 1 的最開(kāi)始的位數(shù)
redis> BITPOS test_str 1
(integer) 1
# 查看值為 0 的最開(kāi)始位數(shù)
redis> BITPOS test_str 0
(integer) 0
redis> BITPOS test_1 1  # 若沒(méi)有找到指定位,則返回 -1
(integer) -1

6)魔術(shù)指令 BITFIELD

作用:一次對(duì)多個(gè)位范圍進(jìn)行操作。bitfield 有三個(gè)子指令,分別是 get/set/incrby。每個(gè)指令都可以對(duì)指定片段做操作。
用法:BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
返回值:返回一個(gè)數(shù)組作為回復(fù), 數(shù)組中的每個(gè)元素就是對(duì)應(yīng)操作的執(zhí)行結(jié)果。

# 從第1位開(kāi)始取4位,設(shè)值為5(有符號(hào)數(shù))
redis> BITFIELD key SET i4 0 5
1) (integer) 0

# 從第1位開(kāi)始取4位,結(jié)果為有符號(hào)數(shù)
redis> BITFIELD key GET i4 0
1) (integer) 5

# 從第1位取4位,結(jié)果為有符號(hào)數(shù)
# 從第5位取4位,設(shè)值為6,結(jié)果為無(wú)符號(hào)數(shù)
# 從第5位去4位,值增加1,結(jié)果為無(wú)符號(hào)數(shù)
redis> BITFIELD key GET i4 0 SET u4 4 6 INCRBY u4 4 1
1) (integer) 5
2) (integer) 0
3) (integer) 7

BITFIELD還提供了三種溢出策略:

  • WRAP(wrap around,回繞)。一個(gè)i8的整數(shù),值為127,遞增1會(huì)導(dǎo)致值變?yōu)?128;
  • SAT(saturation arithmetic,飽和計(jì)算)。一個(gè)i8的整數(shù),值為120,遞增10結(jié)果變?yōu)?27(i8 類(lèi)型所能儲(chǔ)存的最大整數(shù)值);
  • FAIL。 發(fā)生溢出時(shí),操作失敗。并返回空值表示計(jì)算未被執(zhí)行。
redis> BITFIELD tian_key SET i8 0 127 OVERFLOW WRAP INCRBY i8 0 1
1) (integer) 0
2) (integer) -128
redis> BITFIELD tian_key_2 SET i8 0 120 OVERFLOW SAT INCRBY i8 0 10
1) (integer) 0
2) (integer) 127
redis> BITFIELD tian_key_3 SET i8 0 127 OVERFLOW FAIL INCRBY i8 0 1
1) (integer) 0
2) (nil)

應(yīng)用場(chǎng)景

1) 統(tǒng)計(jì)用戶(hù)上線次數(shù)

實(shí)現(xiàn)原理:
每當(dāng)用戶(hù)在某一天上線的時(shí)候,我們就使用 SETBIT,以用戶(hù)名作為 key ,將那天所代表的網(wǎng)站的上線日作為 offset 參數(shù),并將這個(gè) offset 上的位設(shè)置為 1

例如:

  1. 某應(yīng)用上線第100天,若用戶(hù)A在該天上線一次。
SETBIT A 100 1 
  1. 某應(yīng)用上線第101天,用戶(hù)A上線。
SETBIT A 101 1 
  1. 統(tǒng)計(jì)用戶(hù) A 總共上線次數(shù)。
BITCOUNT A

2)用戶(hù)簽到

與統(tǒng)計(jì)用戶(hù)上線次數(shù)原理類(lèi)似。

原理:以用戶(hù)ID為KEY,以當(dāng)前時(shí)間距離開(kāi)始時(shí)間的時(shí)間差為偏移量,若用戶(hù)簽到一次,則將位置為 1。最后 bitcountKEY,獲取用戶(hù)一共簽到的次數(shù)。

const start_date = '20180801';
const end_date = '20180830';

const offset = moment(start_date).unix() - moment(end_date).unix();
redis.setBit('user_id_2018', offset, 1);

// 統(tǒng)計(jì)活躍天數(shù)
redis.bitCount('user_id_2018');

3)統(tǒng)計(jì)活躍用戶(hù)

需求:統(tǒng)計(jì)某天或連續(xù)幾天,活躍用戶(hù)數(shù)
方案:若某用戶(hù)上線,則以日期為KEY,以用戶(hù)user_id為偏移量(若ID不為整數(shù),則將ID hash化為唯一ID),設(shè)置位為 1

redis.setBit('')
const status = 1;
const user_id = 100;
redis.setBit('active_20180820', user_id, status);
redis.setBit('active_20180821', user_id, status);
// 將20180820號(hào)與20180821日進(jìn)行和運(yùn)算,得出兩天都上線的結(jié)果。并存入KEY—— dest_201808_20_21
redis.bitOp('AND', 'dest_201808_20_21', 'active_20180820', 'active_20180821');
redis.bitCount('dest_201808_20_21');

4)用戶(hù)在線狀態(tài)

需求:提供接口檢查用戶(hù)是否在線。
方案:使用bitmap存儲(chǔ)用戶(hù)在線狀態(tài)。使用一個(gè)KEY,若用戶(hù)在線,則以用戶(hù)ID位偏移量,將位設(shè)為 1;若不在線,則設(shè)置為 0。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 基本的Redis key的操作都已經(jīng)熟悉了之后,便可以開(kāi)始針對(duì)Redis提供的各種可操作的數(shù)據(jù)結(jié)構(gòu)進(jìn)行學(xué)習(xí)和了解。...
    Yorking閱讀 907評(píng)論 0 0
  • 一億個(gè)用戶(hù),有的用戶(hù)頻繁登錄,也有不經(jīng)常登錄的。如何記錄用戶(hù)的登錄信息?如何查詢(xún)活躍用戶(hù)?[如一周內(nèi) 登錄三次的]...
    小胖學(xué)編程閱讀 9,022評(píng)論 0 12
  • Redis中BitMap技術(shù)簡(jiǎn)介及應(yīng)用 BitMap簡(jiǎn)介 BitMap是一串連續(xù)的二進(jìn)制數(shù)字(0和1),類(lèi)似于位數(shù)...
    747大雄閱讀 1,328評(píng)論 0 1
  • 1.什么叫做Redis的bitmap 即:操作String數(shù)據(jù)結(jié)構(gòu)的key所存儲(chǔ)的字符串指定偏移量上的位,返回原位...
    香沙小熊閱讀 5,999評(píng)論 0 3
  • 新發(fā)現(xiàn)的《墨子經(jīng)義釋詁》錯(cuò)誤 本文用于記錄《墨子經(jīng)義釋詁》所含錯(cuò)誤。發(fā)現(xiàn)一個(gè)記錄一個(gè)。手上有《墨子經(jīng)義釋詁》的朋友...
    墨者顧如閱讀 982評(píng)論 0 2

友情鏈接更多精彩內(nèi)容