这是阅读Redis深度历险读后的总结
String(字符串)
- 内部是一个动态(可修改)字符数组
- 内部使用预分配来减少内存频繁分配->类似Java的ArrayList
- 常见用来缓存用户数据,将数据JSON化后存入redis
- 如果字符串代表的是一个数字(范围:-263—263-1),则可以对其自增减操作。
List(列表)
- 内部是链表结构,而不是数组->插入和删除很快,但是索引定位很慢
- 在列表元素较少时,使用连续的内存存储元素->ziplist
- 当元素变多后,由多个ziplist组成quicklist
- 通过索引来定位元素时,索引可以为负数,表示倒数第几个
Hash(字典)
- 内部结构和Java的HashMap等同,都是数组+链表结构
- 存储的值只能是字符串
- 并且扩容的rehash是渐进rehash
- 保留新旧的hash,在每次查询以及定时任务中,渐进的将旧元素移动到新hash中
- 在移除旧hash的所有元素后,会被删除
- 新增存储的值时如果值有空格,需要被""括起来
- 因为redis的命令会根据空格来分析关键字
- 也可以使用单个值计数和string类型一样
Set(集合)
- 和java的HashSet相当,内部键值无序,且唯一
- 当最后一个元素被移除,就会被自动删除,内存回收
Zset(有序列表)
- 一方面是set,保证唯一性,一方面每个元素都有一个score来保证排序
- 可以用来实现排名,比如value为排名的信息,score作为排序字段
- 内部由跳表实现
- 一种层级机制的链表结构
- 当最后一个元素移除后,自身会被删除
位图BitMap
基本用法
- Redis的位数组是自动扩展的,会自动将位数组进行零补充
- 对于位图的存取有两个概念:零存零取,整存零取
- 零存代表是零散的存储,比如一个位一个位的存储
- 零取代表零散的取,比如一个位一个位的获取位的值
- 整存代表整个的存,redis的位图存储可以直接存储一个字符串,会自动将其转为二进制
统计用法
- bitcount [xx] start end ,统计位图xx从start到end的1的个数
bitcount w 0 0 #0到0范围中1的个数
bitcount w 1 3 #1到3的范围中1的个数
- bittops [xx] 0或1 start end,统计xx在start和end中出现的第一个0或1
bitops w 0或1 #整个位图中0/1第一次出现的位置
bitpos w 0或1 1 1 # 从第二个字符出现1或0的位置
适用场景
- 布隆过滤器
- 统计过滤,比如点赞id记录
HyperLogLog
- 类似于set的数据集合,但是误差在0.81%左右,并不是太离谱
- 相比于Set来说使用一定的误差来换取占据的空间更小,但并不代表可以随意使用和浪费,需要在一定的业务场景中使用:不适用在统计单个用户的数据信息。
使用方法
- pfadd 增加计数
- pfadd [kye] [value] 类似于set集合,只需要将value加进去,就可以统计成功
- pfcount 获取计数
- pfcount [key] 获取key代表的HyperLogLog的计数值
适用场景
- 对于一个网站的UV以及PV的统计,对于一个需要去重,但是对精确性要求不是非常严格的场景
Redis限流
限流的作用
- 控制流量
- 控制用户行为,避免垃圾请求
Redis-Cell-Redis自己的限流模块~
- Redis 4.0提供的模块,也是使用的漏斗算法
- 提供了原子的限流指令。
- 只有一个命令
cl.throttle
- 但是返回值和参数都比较复杂
- 参数
- cl.throttle [key] [漏斗容量] [速率分子] [速率分母] [每次操作需要容量]
cl.throttle pangpi:reply 15 30 60 3
- 胖批的回复行为 当漏斗到15次满了后开始漏水,60秒最多漏30次,也就是两秒漏一次,每次漏3个容量
- 返回值
![[Pasted image 20231101144420.png]]
GEO定位
GeoHash算法
- 属于业界比较通用的地理位置距离排序算法
- Redis中也是用了这个算法。
- 将二维的经纬度数据映射到一维的整数,虽然相对位置的值会变化,但是每个位置的附近节点的距离排序不会发生变化
- 使用Redis的Geo查询时,可以将其看成内部结构为zset,通过score排序就可以得到坐标附近的元素
- 具体算法:请看85页
注意事项
- 使用geo作为应用计算车的数据,餐馆的数据,最好不要使用集群一起存储geo,而是根据不同的区域划分来使用,比如先按国家拆分,按照省市拆分,可以显著降低单个geo的大小
Scan模糊查询
- keys就是键的模糊查询,但是:
- 没有offset、limit参数,查多少就吐出多少
- 是遍历算法,复杂度是O(n),会导致服务卡顿
Scan概念
- 字典结构
- redis的key都存在一个很大的字典中,类似java中的hashmap
- scan指令返回的游标就是key在数组中的位置索引->槽
- 所以会出现空,肯就是某些槽中是空的
- 遍历顺序
- 使用高位进位加法遍历
- 可以在扩缩容时避免槽位的遍历重复和遗漏
- 字典扩容
- 就是扩容hash槽,重新取模运算
Stream消息队列
- Redis5.0的Stream新数据结构极大借鉴了Kafka的设计
- 一个Stream对应一个key
- 每个Stream可以挂多个消费组(Consumer Group)
- 消费组不会自动创建,需要使用
xgroup create
进行创建- 消费组需要指定Stream的某个消费ID开始消费,用来初始化
last_delivered_id
- 消费组需要指定Stream的某个消费ID开始消费,用来初始化
- 每个消费组有一个游标
last_delivered_id
在Stream上移动来表示当前消费组消费到哪里了 - 每个消费组都有Stream内唯一名称
- 消费组下有多个消费者,共用一个
last_delivered_id
- 消费者内部有一个状态变量(数组)
pending_ids
->Pending Entries List
- 记录了当前被客户端读取但是还没ack的消息
- 消费者内部有一个状态变量(数组)
- 消费组不会自动创建,需要使用
- 消息ID形式是timestampInMillis-sequence
- 例如1527846880572-5,表示消息是在1527846880572毫秒时间戳时产生,并且是这个时间戳的第5条消息
- 消息可以由客户端定义,但是格式必须满足
整数-整数