Redis 有哪些数据类型
Redis 有 String、List、Hash、Set、ZSet、Bitmap、Hyperloglog 等,下面来详细说下这几种类型的底层实现。
1. String
String 底层实现分为两种:
- 整数:长度小于 21 且能够转化为整数的字符串
- SDS (Simple Dynamic String 简单动态字符串):len 长度 + free 剩余未使用空间 + buf 字符数组
SDS 的好处:
- 可以根据 len 快速获取字符串长度
- 当修改字符串长度时可以减少内存分配次数
使用场景:缓存,限流,计数器,分布式锁,分布式 Session
基本命令:
SET key value GET key
MSET key value [key value] MGET [key1 key2]
INCR key DECR key
SETEX key second value SETNX key value
2. List
List 在 Reids 中主要用于实现队列或栈,在不同版本有不同的实现方式:
- 旧版本:
- 数据量小时:ZipList 压缩链表
- 数据量大时:LinkedList 双向链表
- 新版本:
- 数据量小时:ZipList 压缩链表
- 数据量大时:QuickList 快速链表
ZipList:压缩链表,由一系列特殊编码的连续内存块组成的顺序存储结构,其不固定长度数组,每个元素大小通过元素头的 len 来确定。
QuickList:也是一个链表结构,只不过链表节点维护一个 ZipList,从而压缩内存空间。
使用场景:微博关注人时间轴列表,简单队列
基本命令
LPOP # key 移除第一个元素 RPOP # 移除最后一个元素
LPUSH RPUSH
LSET key index value # 通过索引设置列表元素的值
3. Hash
Hash 是一个类似 Map 的映射结构,其底层有两种实现:
- 数据量小时:ZipList
- 数据量大时:HashTable
使用场景:存储用户信息,用户主页访问量,文章点赞数据
基本命令
HGET key field HGETALL key HMGET key field1 field2
HSET key field value HMSET key field1 value1 field2 value2
HSETNX key field value
4. Set
Set 是一个用于去重的数据结构,底层实现:HashTable 的 Key 部分
使用场景:点赞,踩,标签,好友关系
基本命令
SADD key member1 member2
SDIFF key1 key2 # 返回第一个集合与其他集合的差异
SINTER key1 key2 # 返回交集
SREM key member1
SPOP key # 移除集合的一个随机元素
5. ZSet
ZSet 是一个排序,去重的数据结构框架,其底层有两种实现:
- 数据量小时:ZipList
- 数据量大时:SkipList
采用 ZipList 的原因:由于数据量少,O(n) 复杂度的查询和 O(logN) 的查询时间相差不大,同时,ZipList 能很好的压缩内存,节约空间。
SkipList:跳跃表,其根据数据量会分为多层结构,当查询时从上层往下找,复杂度为 O(logN),当插入时,会先找到插入的位置,同时生成一个随机数,来表示此节点要生成多少层。SkipList 相较于二叉搜索树的好处是其不用严格的按照左右节点,通过随机数的大小来判断此节点的层数。
使用场景:排行榜,分页数据
基本命令:
ZADD key score1 member1 score2 member2
ZCOUNT key min max # 范围内的成员数
6. BitMap
BitMap 通过每一个来记录一些数据的状态,达到压缩内存的效果。
使用场景:用户签到,火车座位在不同站点时的状态
基本命令:
setbit name 1 1
getbit name 1 # 1
setbit name 3 1
bitcount bittest 0 0 # 表示第一个字节(8位)0 的个数
7. HyperLogLog
HyperLogLog 用于基数统计,每个 HyperLogLog 只需要花费 12K 内存,就可以计算接近 2^16 个不同元素的基数,且错误率在 0.82%,这和 set (元素越多内存越大)形成对比。但是 HyperLogLog 只会根据输入元素来计算基数,不会存储元素本身,所以 HyperLogLog 不能输出各个元素。但其也能作为交集,返回元素个数。
基数:在数组中出现过的值,例如:{1, 3, 5, 7, 5, 7, 8} 的基数集为 {1, 3, 5, 7, 8}
底层原理:
- 假设一个人抛硬币,每轮可以抛任意次,但抛到正面就要结束掉。若它抛到为
001
,因为001
出现的概率为 1/8,于是我们可以断定抛了 8 次。 - 同理,我们可以根据数组中的数据的二进制位 1 出现的位置,来大概估计这组数据中基数的个数,同时设置多个槽,再根据加权平均值得出最终的结果。
基本命令:
PFADD key element1 element2
PFCOUNT key1 key2 # 查找基数的个数,若多个 key,交集