Redis学习与应用(一)位图

什么是位图

位图(Bitmap)是通过一个 bit 来表示某个元素对应的值或者状态。它并不是什么新的数据结构。它的内容其实就是普通的字符串。我们可以通过 get/set 获取位图的内容,也可以使用 getbit/setbit 操作 bit 值(0 或者 1)。

Bit即比特,是目前计算机中数据最小的单位。8个Bit一个Byte(字节)。Bit的值,要么为 0 ,要么为 1。由于Bit是计算机中最小的单位,使用它进行储存将非常节省空间。特别适合一些数据量大的场景。例如,统计每日活跃用户、统计每月打卡数等统计场景。

常用命令介绍

1)SETBIT

作用:对于某个KEY的某位设值
用法:SETBIT key offset value
返回值: 原来储存的位

redis> SETBIT bit 10086 1
(integer) 0

2)GETBIT

作用:获取某KEY某位的值
用法:GETBIT key offset
返回值:0 或 1。 当 offset 比字符串值的长度大,或者 key 不存在时,返回 0 。

redis> SETBIT bit 10086 1
(integer) 0

redis> GETBIT bit 10086
(integer) 1

3)BITOP

作用:对多个键进行位操作。 OPoperation的简写。
用法:BITOP operation destkey key1 key2 [key ...]
参数说明:
operation 表示位运算符。一共有四种操作,见下表。
destkey 表示运算结果保存的值
key1、key2、key3 表示进行运算的key

operation 描述
AND 逻辑并
OR 逻辑或
NOT 逻辑非
XOR 逻辑异或

返回值:保存到 destkey 的字符串的长度,和输入 key 中最长的字符串长度相等。

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

作用:计算给定字符串上,位为1的个数
用法:BITCOUNT key [start] [end] 注意:此处的[start] [end] 为 字节开始和结束的位置,非偏移量的位置
返回值:被设置为 1 的位的数量。不存在的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

用法:获取某个键第一位被设置为 0 或 1 位的位置
作用:BITPOS key bit [start] [end]
返回值:返回第一个被设为 0 或 1 的位置

redis> SET test_str 'youthcity'
OK
# 查看值为 1 的最开始的位数
redis> BITPOS test_str 1
(integer) 1
# 查看值为 0 的最开始位数
redis> BITPOS test_str 0
(integer) 0
redis> BITPOS test_1 1  # 若没有找到指定位,则返回 -1
(integer) -1

6)魔术指令 BITFIELD

作用:一次对多个位范围进行操作。bitfield 有三个子指令,分别是 get/set/incrby。每个指令都可以对指定片段做操作。
用法:BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
返回值:返回一个数组作为回复, 数组中的每个元素就是对应操作的执行结果。

# 从第1位开始取4位,设值为5(有符号数)
redis> BITFIELD key SET i4 0 5
1) (integer) 0

# 从第1位开始取4位,结果为有符号数
redis> BITFIELD key GET i4 0
1) (integer) 5

# 从第1位取4位,结果为有符号数
# 从第5位取4位,设值为6,结果为无符号数
# 从第5位去4位,值增加1,结果为无符号数
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,回绕)。一个i8的整数,值为127,递增1会导致值变为-128;
  • SAT(saturation arithmetic,饱和计算)。一个i8的整数,值为120,递增10结果变为127(i8 类型所能储存的最大整数值);
  • FAIL。 发生溢出时,操作失败。并返回空值表示计算未被执行。
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)

应用场景

1) 统计用户上线次数

实现原理:
每当用户在某一天上线的时候,我们就使用 SETBIT,以用户名作为 key ,将那天所代表的网站的上线日作为 offset 参数,并将这个 offset 上的位设置为 1

例如:

  1. 某应用上线第100天,若用户A在该天上线一次。
SETBIT A 100 1 
  1. 某应用上线第101天,用户A上线。
SETBIT A 101 1 
  1. 统计用户 A 总共上线次数。
BITCOUNT A

2)用户签到

与统计用户上线次数原理类似。

原理:以用户ID为KEY,以当前时间距离开始时间的时间差为偏移量,若用户签到一次,则将位置为 1。最后 bitcountKEY,获取用户一共签到的次数。

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);

// 统计活跃天数
redis.bitCount('user_id_2018');

3)统计活跃用户

需求:统计某天或连续几天,活跃用户数
方案:若某用户上线,则以日期为KEY,以用户user_id为偏移量(若ID不为整数,则将ID hash化为唯一ID),设置位为 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号与20180821日进行和运算,得出两天都上线的结果。并存入KEY—— dest_201808_20_21
redis.bitOp('AND', 'dest_201808_20_21', 'active_20180820', 'active_20180821');
redis.bitCount('dest_201808_20_21');

4)用户在线状态

需求:提供接口检查用户是否在线。
方案:使用bitmap存储用户在线状态。使用一个KEY,若用户在线,则以用户ID位偏移量,将位设为 1;若不在线,则设置为 0

参考资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容