redis的数据类型
Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样:
五种基本数据类型
Redis 通用命令
通用指令是部分数据类型的,都可以使用的指令,常见的有:
-
KEYS:查看符合模板的所有key
-
DEL:删除一个指定的key
-
EXISTS:判断key是否存在
-
EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除
-
TTL:查看一个KEY的剩余有效期
通过help [command] 可以查看一个命令的具体用法,例如:
-
KEYS
127.0.0.1:6379> keys *
1) "name"
2) "age"
127.0.0.1:6379>
# 查询以a开头的key
127.0.0.1:6379> keys a*
1) "age"
127.0.0.1:6379>
贴心小提示:在生产环境下,不推荐使用keys 命令,因为这个命令在key过多的情况下,效率不高
-
DEL
127.0.0.1:6379> help del
DEL key [key ...]
summary: Delete a key
since: 1.0.0
group: generic
127.0.0.1:6379> del name #删除单个
(integer) 1 #成功删除1个
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 #批量添加数据
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
4) "age"
127.0.0.1:6379> del k1 k2 k3 k4
(integer) 3 #此处返回的是成功删除的key,由于redis中只有k1,k2,k3 所以只成功删除3个,最终返回
127.0.0.1:6379>
127.0.0.1:6379> keys * #再查询全部的key
1) "age" #只剩下一个了
127.0.0.1:6379>
贴心小提示:同学们在拷贝代码的时候,只需要拷贝对应的命令哦~
-
EXISTS
127.0.0.1:6379> help EXISTS
EXISTS key [key ...]
summary: Determine if a key exists
since: 1.0.0
group: generic
127.0.0.1:6379> exists age
(integer) 1
127.0.0.1:6379> exists name
(integer) 0
-
EXPIRE
贴心小提示:内存非常宝贵,对于一些数据,我们应当给他一些过期时间,当过期时间到了之后,他就会自动被删除~
127.0.0.1:6379> expire age 10
(integer) 1
127.0.0.1:6379> ttl age
(integer) 8
127.0.0.1:6379> ttl age
(integer) 6
127.0.0.1:6379> ttl age
(integer) -2
127.0.0.1:6379> ttl age
(integer) -2 #当这个key过期了,那么此时查询出来就是-2
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set age 10 #如果没有设置过期时间
OK
127.0.0.1:6379> ttl age
(integer) -1 # ttl的返回值就是-1
String字符串
String类型,也就是字符串类型,是Redis中最简单的存储类型。
其value是字符串,不过根据字符串的格式不同,又可以分为3类:
-
string:普通字符串
-
int:整数类型,可以做自增.自减操作
-
float:浮点类型,可以做自增.自减操作
String的常见命令有:
-
SET:添加或者修改已经存在的一个String类型的键值对
-
GET:根据key获取String类型的value
-
MSET:批量添加多个String类型的键值对
-
MGET:根据多个key获取多个String类型的value
-
INCR:让一个整型的key自增1
-
INCRBY:让一个整型的key自增并指定步长,例如:incrby num 2 让num值自增2
-
INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
-
SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行
-
SETEX:添加一个String类型的键值对,并且指定有效期
贴心小提示:以上命令除了INCRBYFLOAT 都是常用命令
-
SET 和GET: 如果key不存在则是新增,如果存在则是修改
127.0.0.1:6379> set name Rose //原来不存在
OK
127.0.0.1:6379> get name
"Rose"
127.0.0.1:6379> set name Jack //原来存在,就是修改
OK
127.0.0.1:6379> get name
"Jack"
-
MSET和MGET
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> MGET name age k1 k2 k3
1) "Jack" //之前存在的name
2) "10" //之前存在的age
3) "v1"
4) "v2"
5) "v3"
-
INCR和INCRBY和DECY
127.0.0.1:6379> get age
"10"
127.0.0.1:6379> incr age //增加1
(integer) 11
127.0.0.1:6379> get age //获得age
"11"
127.0.0.1:6379> incrby age 2 //一次增加2
(integer) 13 //返回目前的age的值
127.0.0.1:6379> incrby age 2
(integer) 15
127.0.0.1:6379> incrby age -1 //也可以增加负数,相当于减
(integer) 14
127.0.0.1:6379> incrby age -2 //一次减少2个
(integer) 12
127.0.0.1:6379> DECR age //相当于 incr 负数,减少正常用法
(integer) 11
127.0.0.1:6379> get age
"11"
-
SETNX
127.0.0.1:6379> help setnx
SETNX key value
summary: Set the value of a key, only if the key does not exist
since: 1.0.0
group: string
127.0.0.1:6379> set name Jack //设置名称
OK
127.0.0.1:6379> setnx name lisi //如果key不存在,则添加成功
(integer) 0
127.0.0.1:6379> get name //由于name已经存在,所以lisi的操作失败
"Jack"
127.0.0.1:6379> setnx name2 lisi //name2 不存在,所以操作成功
(integer) 1
127.0.0.1:6379> get name2
"lisi"
-
SETEX
127.0.0.1:6379> setex name 10 jack
OK
127.0.0.1:6379> ttl name
(integer) 8
127.0.0.1:6379> ttl name
(integer) 7
127.0.0.1:6379> ttl name
(integer) 5
Hash散列表
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构。
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便:
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD:
Hash类型的常见命令
-
HSET key field value:添加或者修改hash类型key的field的值
-
HGET key field:获取一个hash类型key的field的值
-
HMSET:批量添加多个hash类型key的field的值
-
HMGET:批量获取多个hash类型key的field的值
-
HGETALL:获取一个hash类型的key中的所有的field和value
-
HKEYS:获取一个hash类型的key中的所有的field
-
HINCRBY:让一个hash类型key的字段值自增并指定步长
-
HSETNX:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行
贴心小提示:哈希结构也是我们以后实际开发中常用的命令哟
-
HSET和HGET
127.0.0.1:6379> HSET heima:user:3 name Lucy//大key是 heima:user:3 小key是name,小value是Lucy
(integer) 1
127.0.0.1:6379> HSET heima:user:3 age 21// 如果操作不存在的数据,则是新增
(integer) 1
127.0.0.1:6379> HSET heima:user:3 age 17 //如果操作存在的数据,则是修改
(integer) 0
127.0.0.1:6379> HGET heima:user:3 name
"Lucy"
127.0.0.1:6379> HGET heima:user:3 age
"17"
-
HMSET和HMGET
127.0.0.1:6379> HMSET heima:user:4 name HanMeiMei
OK
127.0.0.1:6379> HMSET heima:user:4 name LiLei age 20 sex man
OK
127.0.0.1:6379> HMGET heima:user:4 name age sex
1) "LiLei"
2) "20"
3) "man"
-
HGETALL
127.0.0.1:6379> HGETALL heima:user:4
1) "name"
2) "LiLei"
3) "age"
4) "20"
5) "sex"
6) "man"
-
HKEYS和HVALS
127.0.0.1:6379> HKEYS heima:user:4
1) "name"
2) "age"
3) "sex"
127.0.0.1:6379> HVALS heima:user:4
1) "LiLei"
2) "20"
3) "man"
-
HINCRBY
127.0.0.1:6379> HINCRBY heima:user:4 age 2
(integer) 22
127.0.0.1:6379> HVALS heima:user:4
1) "LiLei"
2) "22"
3) "man"
127.0.0.1:6379> HINCRBY heima:user:4 age -2
(integer) 20
-
HSETNX
127.0.0.1:6379> HSETNX heima:user4 sex woman
(integer) 1
127.0.0.1:6379> HGETALL heima:user:3
1) "name"
2) "Lucy"
3) "age"
4) "17"
127.0.0.1:6379> HSETNX heima:user:3 sex woman
(integer) 1
127.0.0.1:6379> HGETALL heima:user:3
1) "name"
2) "Lucy"
3) "age"
4) "17"
5) "sex"
6) "woman"
List链表
Redis中的List类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似:
-
有序
-
元素可以重复
-
插入和删除快
-
查询速度一般
常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。
List的常见命令有:
-
LPUSH key element ... :向列表左侧插入一个或多个元素
-
LPOP key:移除并返回列表左侧的第一个元素,没有则返回nil
-
RPUSH key element ... :向列表右侧插入一个或多个元素
-
RPOP key:移除并返回列表右侧的第一个元素
-
LRANGE key star end:返回一段角标范围内的所有元素
-
BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil
-
LPUSH和RPUSH
127.0.0.1:6379> LPUSH users 1 2 3
(integer) 3
127.0.0.1:6379> RPUSH users 4 5 6
(integer) 6
-
LPOP和RPOP
127.0.0.1:6379> LPOP users
"3"
127.0.0.1:6379> RPOP users
"6"
-
LRANGE
127.0.0.1:6379> LRANGE users 1 2
1) "1"
2) "4"
Set集合
Redis的Set结构与Java中的HashSet类似,可以看做是一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征:
-
无序
-
元素不可重复
-
查找快
-
支持交集.并集.差集等功能
Set类型的常见命令
-
SADD key member ... :向set中添加一个或多个元素
-
SREM key member ... : 移除set中的指定元素
-
SCARD key: 返回set中元素的个数
-
SISMEMBER key member:判断一个元素是否存在于set中
-
SMEMBERS:获取set中的所有元素
-
SINTER key1 key2 ... :求key1与key2的交集
-
SDIFF key1 key2 ... :求key1与key2的差集
-
SUNION key1 key2 ..:求key1和key2的并集
例如两个集合:s1和s2:
求交集:SINTER s1 s2
求s1与s2的不同:SDIFF s1 s2
具体命令
127.0.0.1:6379> sadd s1 a b c
(integer) 3
127.0.0.1:6379> smembers s1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> srem s1 a
(integer) 1
127.0.0.1:6379> SISMEMBER s1 a
(integer) 0
127.0.0.1:6379> SISMEMBER s1 b
(integer) 1
127.0.0.1:6379> SCARD s1
(integer) 2
案例
-
将下列数据用Redis的Set集合来存储:
-
张三的好友有:李四.王五.赵六
-
李四的好友有:王五.麻子.二狗
-
利用Set的命令实现下列功能:
-
计算张三的好友有几人
-
计算张三和李四有哪些共同好友
-
查询哪些人是张三的好友却不是李四的好友
-
查询张三和李四的好友总共有哪些人
-
判断李四是否是张三的好友
-
判断张三是否是李四的好友
-
将李四从张三的好友列表中移除
127.0.0.1:6379> SADD zs lisi wangwu zhaoliu (integer) 3 127.0.0.1:6379> SADD ls wangwu mazi ergou (integer) 3 127.0.0.1:6379> SCARD zs (integer) 3 127.0.0.1:6379> SINTER zs ls 1) "wangwu" 127.0.0.1:6379> SDIFF zs ls 1) "zhaoliu" 2) "lisi" 127.0.0.1:6379> SUNION zs ls 1) "wangwu" 2) "zhaoliu" 3) "lisi" 4) "mazi" 5) "ergou" 127.0.0.1:6379> SISMEMBER zs lisi (integer) 1 127.0.0.1:6379> SISMEMBER ls zhangsan (integer) 0 127.0.0.1:6379> SREM zs lisi (integer) 1 127.0.0.1:6379> SMEMBERS zs 1) "zhaoliu" 2) "wangwu"
SortedSet(有序集合)
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表。
SortedSet具备下列特性:
-
可排序
-
元素不重复
-
查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
SortedSet的常见命令有:
-
ZADD key score member:添加一个或多个元素到sorted set ,如果已经存在则更新其score值
-
ZREM key member:删除sorted set中的一个指定元素
-
ZSCORE key member : 获取sorted set中的指定元素的score值
-
ZRANK key member:获取sorted set 中的指定元素的排名
-
ZCARD key:获取sorted set中的元素个数
-
ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
-
ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值
-
ZRANGE key min max:按照score排序后,获取指定排名范围内的元素
-
ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素
-
ZDIFF.ZINTER.ZUNION:求差集.交集.并集
注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:
-
升序获取sorted set 中的指定元素的排名:ZRANK key member
-
降序获取sorted set 中的指定元素的排名:ZREVRANK key memeber
五种基本数据类型的底层原理
String
String 类型的底层的数据结构实现主要是 SDS(简单动态字符串)。SDS 和我们认识的 C 字符串不太一样,之所以没有使用 C 语言的字符串表示,因为 SDS 相比于 C 的原生字符串:
- SDS 不仅可以保存文本数据,还可以保存二进制数据。因为 SDS 使用 len 属性的值而不是空字符来判断字符串是否结束,并且 SDS 的所有 API 都会以处理二进制的方式来处理 SDS 存放在 buf[] 数组里的数据。所以 SDS 不光能存放文本数据,而且能保存图片、音频、视频、压缩文件这样的二进制数据。
- SDS 获取字符串长度的时间复杂度是 O(1)。因为 C 语言的字符串并不记录自身长度,所以获取长度的复杂度为 O(n);而 SDS 结构里用 len 属性记录了字符串长度,所以复杂度为 O(1)。
- Redis 的 SDS API 是安全的,拼接字符串不会造成缓冲区溢出。因为 SDS 在拼接字符串之前会检查SDS 空间是否满足要求,如果空间不够会自动扩容,所以不会导致缓冲区溢出的问题。
List
List 类型的底层数据结构是由双向链表或压缩列表实现的:
- 如果列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),列表每个元素的值都小于 64 字节(默认值,可由 list-max-ziplist-value 配置),Redis 会使用压缩列表作为 List 类型的底层数据结构;
- 如果列表的元素不满足上面的条件,Redis 会使用双向链表作为 List 类型的底层数据结构;
但是在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了(结合了双向链表和压缩链表的优点),替代了双向链表和压缩列表。
压缩列表
- 不能保存过多的元素,否则查询效率就会降低;
- 新增或修改某个元素时,压缩列表占⽤的内存空间需要重新分配,甚⾄可能引发连锁更新的问题。
压缩列表结构设计
压缩列表是 Redis 为了节约内存而开发的,它是由连续内存块组成的顺序型数据结构,有点类似于数组。
- zlbytes,记录整个压缩列表占⽤对内存字节数;
- zltail,记录压缩列表「尾部」节点距离起始地址由多少字节,也就是列表尾的偏移量;
- zllen,记录压缩列表包含的节点数量;
- zlend,标记压缩列表的结束点,固定值 0xFF(⼗进制255)。

- prevlen,记录了「前⼀个节点」的⻓度;
- encoding,记录了当前节点实际数据的类型以及长度;
- data,记录了当前节点的实际数据;
- 如果前⼀个节点的长度小于 254 字节,那么 prevlen 属性需要⽤ 1 字节的空间来保存这个⻓度值;
- 如果前⼀个节点的⻓度⼤于等于 254 字节,那么 prevlen 属性需要⽤ 5 字节的空间来保存这个⻓度值;
- 如果当前节点的数据是整数,则 encoding 会使⽤ 1 字节的空间进⾏编码。
- 如果当前节点的数据是字符串,根据字符串的⻓度⼤⼩,encoding 会使⽤ 1 字节/2字节/5字节的空间进⾏编码。
连锁更新
压缩列表除了查找复杂度⾼的问题,还有⼀个问题。


压缩列表的缺陷
空间扩展操作也就是重新分配内存,因此连锁更新⼀旦发⽣,就会导致压缩列表占用的内存空间要多次重新分配,这就会直接影响到压缩列表的访问性能。
quicklist

Hash
Hash 类型的底层数据结构是由压缩列表或哈希表实现的:
- 如果哈希类型元素个数小于 512 个(默认值,可由 hash-max-ziplist-entries 配置),所有值小于 64 字节(默认值,可由 hash-max-ziplist-value 配置)的话,Redis 会使用压缩列表作为 Hash 类型的底层数据结构;
- 如果哈希类型元素不满足上面条件,Redis 会使用哈希表作为 Hash 类型的底层数据结构。
在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。
listpack
listpack结构设计
我们先看看 listpack 结构:

- encoding,定义该元素的编码类型,会对不同长度的整数和字符串进行编码;
- data,实际存放的数据;
- len,encoding+data的总⻓度;
哈希表
哈希表结构设计
Redis 的哈希表结构如下:
哈希冲突
什么是哈希冲突呢?

链式哈希
Redis 采⽤了「链式哈希」的⽅法来解决哈希冲突。
链式哈希是怎么实现的?

rehash

- 给「哈希表 2」 分配空间,⼀般会比「哈希表 1」 大 2 倍;
- 将「哈希表 1 」的数据迁移到「哈希表 2」 中;
- 迁移完成后,「哈希表 1 」的空间会被释放,并把「哈希表 2」 设置为「哈希表 1」,然后在「哈希表 2」 新创建⼀个空⽩的哈希表,为下次 rehash 做准备。
渐进式哈希rehash
- 给「哈希表 2」 分配空间;
- 在 rehash 进行期间,每次哈希表元素进行新增、删除、查找或者更新操作时,Redis 除了会执行对应的操作之外,还会顺序将「哈希表 1 」中索引位置上的所有 key-value 迁移到「哈希表 2」 上;
- 随着处理客户端发起的哈希表操作请求数量越多,最终在某个时间点,会把「哈希表 1 」的所有key-value 迁移到「哈希表 2」,从⽽完成 rehash 操作。
rehash触发条件
- 当负载因子大于等于 1 ,并且 Redis 没有在执行bgsave 命令或者 bgrewiteaof 命令,也就是没有执行 RDB 快照或没有进行 AOF 重写的时候,就会进行 rehash 操作。
- 当负载因子大于等于 5 时,此时说明哈希冲突非常严重了,不管有没有有在执行 RDB 快照或 AOF重写,都会强制进⾏ rehash 操作。
Set
Set 类型的底层数据结构是由哈希表或整数集合实现的:
- 如果集合中的元素都是整数且元素个数小于 512 (默认值,set-maxintset-entries配置)个,Redis 会使用整数集合作为 Set 类型的底层数据结构;
- 如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构。
整数集合
整数集合结构设计

- 如果 encoding 属性值为 INTSET_E0.N0C_INT16,那么 contents 就是⼀个 int16_t 类型的数组,数组中每⼀个元素的类型都是 int16_t;
- 如果 encoding 属性值为 INTSET_ENC_INT32,那么 contents 就是⼀个 int32_t 类型的数组,数组中每⼀个元素的类型都是 int32_t;
- 如果 encoding 属性值为 INTSET_ENC_INT64,那么 contents 就是⼀个 int64_t 类型的数组,数组中每⼀个元素的类型都是 int64_t;



整数集合⽀持降级操作吗?
Zset
Zset 类型的底层数据结构是由压缩列表或跳表实现的:
- 如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用压缩列表作为Zset 类型的底层数据结构;
- 如果有序集合的元素不满足上面的条件,Redis 会使用跳表作为 Zset 类型的底层数据结构;
在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。
跳表
跳表结构设计

- L0 层级共有 5 个节点,分别是节点1、2、3、4、5;
- L1 层级共有 3 个节点,分别是节点 2、3、5;
- L2 层级只有 1 个节点,也就是节点 3 。

- 先从头节点的最⾼层开始,L2 指向了「元素:abc,权重:3」节点,这个节点的权重⽐要查找节点的小,所以要访问该层上的下⼀个节点;
- 但是该层上的下⼀个节点是空节点,于是就会跳到「元素:abc,权重:3」节点的下⼀层去找,也就是 leve[1];
- 「元素:abc,权重:3」节点的 leve[1] 的下⼀个指针指向了「元素:abcde,权重:4」的节点,然后将其和要查找的节点⽐较。虽然「元素:abcde,权重:4」的节点的权重和要查找的权重相同,但是当前节点的 SDS 类型数据「⼤于」要查找的数据,所以会继续跳到「元素:abc,权重:3」节点的下⼀层去找,也就是 leve[0];
- 「元素:abc,权重:3」节点的 leve[0] 的下⼀个指针指向了「元素:abcd,权重:4」的节点,该节点正是要查找的节点,查询结束。
三种特殊的数据类型
Geo 地理位置
命令介绍
(1)geoadd命令:将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。
格式:geoadd key 经度 纬度 地理位置
例如:geoadd china:city 116.40 39.90 beijing
有效的经度从-180度到180度。
有效的纬度从-85.05112878度到85.05112878度。
当坐标位置超出上述指定范围时,该命令将会返回下述错误。
(error) ERR invalid longitude,latitude pair 39.900000,116.400000
同时,这个命令还可以添加多个元素,例如:
geoadd china:city 116.40 39.90 beijing 121.47 32.23 shanghai
规则:两级(南极北极)是无法直接添加的,一般会下载城市数据,直接通过Java程序一次性导入
(2)geopos命令:从key里返回所有给定位置元素的位置(经度和纬度)。
格式:geopos key member
例如:geopos china:city beijing
返回值:GEOPOS 命令返回一个数组, 数组中的每个项都由两个元素组成: 第一个元素为给定位置元素的经度, 而第二个元素则为给定位置元素的纬度。
当给定的元素不存在时,对应的数组项为空值。
(3)geodist命令:返回两个给定位置之间的直线距离,如果两个位置之间的其中一个不存在,那么命令就返回空值。
格式:geodist key member1 member2 [unit]
指定单位的参数 unit 必须是以下单位的其中一个:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。
(4)georadius命令:以给定的经纬度为中心,找出某一半径内的元素。
以给定的经纬度为中心,返回键包含的位置元素当中,与中心的距离不超过给定最大距离的所有位置元素。
格式:georadius key longitude latitude radius m|km|ft|mi
例如: georadius china:city 110 23 1000 km
key为键,longitude、latitude表示当前位置的经纬度,radius表示搜索半径,m|km|ft|mi表示单位。
上图的代码解释:
以110 23为经纬度查询方圆1000km内的城市。前提是所有的数据录入了china:city中。
Hyperloglog 基数统计
- 什么是基数?
一个可重复集合内不重复元素的个数就是基数。
- 简介
Redis 2.8.9版本就更新了Hyperloglog数据结构。
Redis Hyperloglog基数统计的算法。
- 优点
占用的内存是固定的,2^64不同元素的技术,只需要费12KB内存。如果要从内存角度来比较的话,Hyperloglog是首选。
举例:网页的UV(一个人访问一个网站多次,但是还是算作一个人)
传统的方式:使用set保护用户的ID,然后就可以统计set中的元素数量作为标准判断,这个方式如果保存大量的用户id,就会比较麻烦。
我们的目的是为了计数,而不是存储用户id。
(1)pfadd key member1 member2 ....:添加一个或者多个元素
(2)pfcount key:统计key集合中基数的个数
(3)pfmerge newKey key1 key2 ...:合并key1和key2中的元素,并且剔除其中重复的元素,产生新的集合newKey。
key1中的元素有:a b c d e f g
key2中的元素有:h i j a e q n m c
重复的元素有:a e c
产生的newKey中的元素有:a b c d e f g h i j q n m
应用场景:适合做页面统计。
如果允许容错,那么一定要使用Hyperloglog;
如果不允许容错,就使用set或者自己的数据类型即可。
(容错就是当由于种种原因在系统中出现了数据、文件损坏或丢失时,系统能够自动将这些损坏或丢失的文件和数据恢复到发生事故以前的状态,使系统能够连续正常运行的一种技术,很简单的意思)
Bitmap位图
- 概括
Bitmap是位图,数据结构,都是操作二进制位来进行记录,就只有0和1两个状态。
365天 = 365bit 1字节 = 8bit 大约45个字节左右
基本命令
(1)setbit key offset value:value只能是0或者1,如下例子所示:
使用bitmap来记录周一到周日的打卡。
上图中的offeset表示周几,value的0表示未打卡,1表示打开成功。
周一0:1,周二1:0,周三2:1,周四3:1,…
(2)getbit key offset:获取key中的offset值,查看某一天是否打卡
以上表示的是查看周四是否打卡,周日是否打卡。
(3)bitcount key:统计操作,统计打开的天数,相当于统计sign中value为1的元素个数
应用场景
统计用户信息、活跃、不活跃、登录、未登录、打卡等等… 两个状态的都可以使用Bitmap。
在生活或者开发中都有十分多的应用场景。