1. 数据类型

  • redis
  • redis

一、String

String是redis中最常用的数据类型,一个key对应一个字符串。

1.1 使用场景

  • 缓存:将数据序列化为字符串,存储在redis中
  • 记录token
  • 计数器
  • 分布式锁

1.2 常用命令

命令描述
SET key value设置key的值
GET key获取key的值
GETRANGE key start end 获取指定key的字符串的子串,start和end指定子串起始和结束位置
GETSET key value将key对应的值设为value,并返回key的旧值
MGET key1 [key2..]获取一个或多个key的值
SETEX key seconds value设置key的值,并设置key的过期时间为seconds秒
SETNX key value只有在key不存在时,设置key的值
STRLEN key获取key的值的字符串长度
MSET key value [key value ...]同时设置一个或多个key的值
MSETNX key value [key value ...]同时设置一个或多个key的值,当且仅当所有给定 key 都不存在
PSETEX key milliseconds value跟setex一样,只不过过期时间的单位是毫秒
INCR key将key中储存的数字值加一
INCRBY key increment将key 所储存的值加上给定的增量值(increment)
DECR key将key中储存的数字值减一
DECRBY key decrementkey 所储存的值减去给定的减量值(decrement)
APPEND key value如果 key 已经存在并且是一个字符串, 将value追加到key的值的末尾

1.3 底层实现

字符串对象的编码可以是int、raw或者embstr

  • int: 如果一个字符串对象保存的是整数值,redis底层会用long去存储
  • raw: 如果存的是字符串,并且这个字符串值的长度大于32字节,那么使用SDS保存这个字符串
  • embstr: 小于等于32字节时,也是SDS

redis的字符串表示为sds(simple dynamic string),而不是C语言的字符串(以\0结尾的char*)。

1.3.1 sds组成

  • len: 字符串长度
  • buf[]: 字符数组,用来保存实际数据
  • alloc: 分配给字符数组的空间长度。修改字符串时,可以通过 alloc - len判断剩余空间是否足够,不足够时sds会扩容
  • flags: 用来表示不同类型的SDS。sds有5种类型: sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64。区别就在于,它们len和alloc的数据类型不同。

1.3.2 为什么不用c语言的的字符串

  • 获取字符串长度的时间复杂度是O(n)。 而sds有一个专门存储字符串长度的属性len,时间复杂度O(1)
  • 二进制不安全。c语言用\0标识字符串结尾,而sds不依赖\0(以为属性len,知道了字符串长度), 因此不会因为数据包含\0而导致数据出问题
  • 缓冲区溢出。拼接字符串时,C语言使用strcat拼接,当没有分配足够长度的内存空间,就会造成缓冲区溢出

二、List

Redis中的List是简单的字符串List,List里面存储的都是String类型。

2.1 使用场景

  • 模拟栈
lpush key value #入栈
lpop key #出栈
  • 模拟队列
lpush key value #入队列
rpop key #出队列

2.2 常用命令

命令描述
RPUSH key value将value添加到列表右端(尾部)
LPUSH key value将value添加到列表左端(头部)
RPOP key从列表的右端(尾部)的值弹出并返回弹出的值
LPOP key从列表的左端(头部)的值弹出并返回弹出的值
LRANGE key start stop获取列表中指定范围的值
LINDEX key index获取列表中指定位置的值
LTRIM key start stop只保留列表中指定区间内的元素
BLPOP key timeout从列表的左端(头部)的值弹出并返回弹出的值,如果列表中没有元素,就等待,直到超时或有元素可以弹出为止

2.3 底层实现

2.3.1 双向链表

typedef struct list {
    //链表头节点
    listNode *head;
    //链表尾节点
    listNode *tail;
    //节点值复制函数
    void *(*dup)(void *ptr);
    //节点值释放函数
    void (*free)(void *ptr);
    //节点值比较函数
    int (*match)(void *ptr, void *key);
    //链表节点数量
    unsigned long len;
} list;

优点:

  • 因为是双向链表,所以获取某个节点的前置节点或后置节点的时间复杂度只需O(1)
  • 因为存储了头结点、尾结点指针,所以获取时的时间复杂度只需O(1)
  • 因为存储了len,所以获取链表长度的时间复杂度只需O(1)

缺点:

  • 链表的节点之间在内存中不一定连续,没有数组访问起来快速
  • 链表的节点存储开销比较大,比较占内存

2.3.2 压缩列表(ZipList)

压缩列表是一种内存紧凑型的数据结构。其占用一块连续的内存空间,而且会针对不同长度的数据,进行相应编码,能够有效地节省内存开销。

优点:

  • 连续的内存空间。可以利用 CPU 缓存,访问快速
  • 针对性的编码。能够节省内存开销

缺点:

  • 不能保存过多的元素,否则查询效率就会降低
  • 新增或修改某个元素时,压缩列表占用的内存空间需要重新分配,甚至可能引发连锁更新的问题,影响数据访问

压缩列表只适用于元素数量较少的情况

2.3.3 快表 quicklist

快表是一个数组链表,为什么这么说?因为它的链表节点是一个压缩列表

三、Set

Redis中的Set是无序字符串集合,集合中的成员是唯一的。

3.1 使用场景

  • 点赞:使用set存储点赞用户的uid,保证一个用户只能点一个赞
sadd key uid  #点赞(/收藏) 
srem key uid  #取消点赞(/收藏)
smembers key     # 获取所有点赞(/收藏)用户  
scard key  # 获取点赞用户数量
sismember key uid #判断是否点赞(/收藏)
  • 好友关系: 使用set计算交集、差集等
sadd uid1 1 2 3 4 5 
sadd uid2 4 5 6 7 8 --某个user的好友id放入集合
sinter uid uid2 --获取共同好友
sdiff uid uid2 --给user2推荐user1的好友
sismember uid1 5
sismember uid2 5 --验证某个用户是否同时被user1和user2关注

3.2 常用命令

命令描述
SADD key value1 [value2]向集合添加一个或多个成员
SREM key value1 [value2]移除集合中一个或多个成员
SCARD key获取集合成员数
SMEMBERS key获取集合中的所有值
SMEMBERS key member判断member是否是在集合中
SINTER key1 [key2]返回给定集合的交集
SDIFF key1 [key2]返回第一个集合与其他集合之间的差集

3.3 底层实现

  • intset 整数集合
  • hashtable

intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合里面,hashtable跟java中的hashmap实现原理一样,不再赘述

四、Hash

Redis中的Hash是就类似于java中的map对象

4.1应用场景

  • 缓存:多数场景下是将数据序列化成json字符串,存储成string类型。当有频繁读取或修改数据中的某个字段时,可以考虑使用hash存储数据,避免反复序列化、反序列化导致的性能开销。以下是一个购物车场景:
# 添加商品
hset cart:{用户id} {商品id} 1  
# 增加数量
hincrby cart:{用户id} {商品id} 1 
# 获取商品类型总数
hlen cart:{用户id} 
# 删除商品
hdel cart:{用户id} {商品id} 
# 获取购物车所有商品
hgetall cart:{用户id} 
  • 分布式锁: Redisson在实现分布式锁时,用的不是string,而是hash

4.2 常用命令

命令描述
HSET key field valueset值
HDEL key field删除值
HLEN key获取field数量
HINCRBY key field给指定的field值加一
HKEYS key获取所有field
HVALS key获取所有field对应的值
HGETALL key获取所有field对应的值

4.3 底层实现

压缩列表上面讲过,hashtable跟java中的hashmap实现原理一样,不再赘述。

五、ZSet

Set是无序的,ZSet是有序的,实现有序的方式是引入score字段,通过score进行升序排列。

5.1 应用场景

5.2 常用命令

命令描述
ZADD key score1 member1向集合添加一个或多个成员
ZCARD key获取集合成员数
ZCOUNT key min max计算score在min~max之间的成员个数
ZINCRBY key increment member给定成员的score增加member
ZRANGE key start stop [WITHSCORES]返回index在start~stop之间的成员
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT]返回score在min~max之间的成员
zrank key member获取member在集合中的正向排名
zrevrank key member获取member在集合中的逆向排名

5.3 底层实现

六、HyperLogLogs

HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的

6.1 应用场景

  • 计数器

6.2 常用命令

命令描述
PFADD key element [element ...]添加元素到HyperLogLog 中
PFCOUNT key [key ...]返回HyperLogLog计算的元素个数
PFMERGE newKey key1 [key2 ...]合并多个HyperLogLog
Loading...