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

参考资料

  • 使用redis实现点赞功能的几种思路
  • Redis中bitmap的妙用
  • When would you use a long, string ID instead of a simple integer
  • 按位操作符
  • Bitmap的秘密
  • redis使用bitmap实现网站活跃用户的统计·
  • 模式:使用 bitmap 实现用户上线次数统计
  • Wikipedia - 有符号数处理
  • 一看就懂系列之 详解redis的bitmap在亿级项目中的应用

你可能感兴趣的:(Redis学习与应用(一)位图)