Redis深度历险 核心原理与应用实践-笔记

 

1.2 5种基础数据结构

string(字符串)

字符串string是Redis最简单的数据结构,其内部表示就是一个字符数组。Redis所有的数据结构都是以唯一的key字符串作为名称,然后通过这唯一的key来获取相应的value数据。不同类型的数据结构差异就在于value的结果不一样。

Redis的字符串 是动态字符串,是可以修改的字符串。才用预分配冗余空间的方式来减少内存的频繁分配。当字符串长度小于1MB时,扩容都是加倍现有的空间,如果超过1MB,每次扩容只会多扩1MB,注意字符串最大长度为512MB。

 

list(列表)

Redis的列表 相当于Java语言里的LinkList, 链表结构。Redis的列表结构常用来做异步队列。 将需要延后处理的任务结构体序列化成字符串,塞进redis 列表中,另一个线程从这个列表中轮训数据进行处理。

【右边进左边出:对列】

【右边进右边出:栈】

【慢操作】 lindex相当于java链表的get(int index)方法,需要对链表进行遍历,性能会随着index增大而变差

【快速列表】redis底层存储的是一个 “快速链表”的结构。首先在列表元素较少的情况下,会使用一块连续的内存存储,这个结构是ziplist(压缩列表)。它将所有元素彼此紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成quicklist。 所以redis将链表与ziplist结合起来形成quicklist,也就是将多个ziplist使用双向指针串起来使用。既能满足快速插入删除性能,也不会出现太大的空间冗余。

 

hash(字典)

Redis 中的字典相当于Java中的hashMap.都是数组加链表。

当hash移除最后一个元素之后,该数据结构会被自动删除,内存被回收

 

set(集合)

Redis的集合相当于Java中的hashset,它内部的键值对是无序的、唯一的。

当set移除最后一个元素之后,该数据结构会被自动删除,内存被回收.

 
zset(有序列表)
它类似hashset与hashMap的结合体。一方面,它是一个set,保证内部value的唯一性。另一方面它可以给每一个value赋予一个score,代表这个value的排序权重。内部由一个“跳跃列表”的数据结构。 

当zset移除最后一个元素之后,该数据结构会被自动删除,内存被回收.

 

 

1.2.3 容器型数据结构的通用规则 

list、set、zset、hash 这四种数据结构是容器型数据结构,它们共享下面两条通用规则。

a.如果容器不存在,那就创建一个。

b.如果容器中没有元素了,那么立即删除容器。

 

1.2.4 过期时间

Redis所有数据结构都可以设置过期时间,时间到了,Redis会自动删除相应的对象,需要注意的是,过期是以对象为单位的,比如一个hash结构的过期是整个hash对象的过期,而不是其中某个子key过期。

如果一个字符串设置了过期时间,然后调用set方法修改了它,它的过期时间会消失。

 

1.3 分布式锁

setNx

超时问题

可重入性(考虑下thraedlocal  增加计数器

 

1.4 延时队列

redis的消息队列不是专业的消息队列,没有ack保证,如果对消息的可靠性有极高要求,它就不适用。

1.4.1 异步消息队列

使用rpush、lpush操作入队,lpop、rpop 操作出队。
 1.4.2 队列空了怎么办?
客户端会陷入pop死循环。 此时考虑sleep
1.4.3 阻塞读
blpop、brpop,阻塞读在队列没有数据的时候会立即进入休眠状态,一旦数据到来,立刻醒来。
1.4.4 空闲连接自动断开
果线程一直阻塞,redis客户端连接会成为闲置连接,闲置过久,服务器一般会主动断开连接,减少闲置资源占用。这时候blpop、brpop会抛异常。需要重试。
1.4.5 锁冲突处理
直接抛出异常、sleep一会然后重试、将请求转入延时队列。
1.4.6 延时队列的实现
通过zset实现,消息序列化为zset的value, 消息的到期处理时间为score。
 

1.5 位图

位图的最小单位是bit. 位图就是普通的字符串,也就是byte数组。

1.6 HyperLogLog

1.7 布隆过滤器

每个布隆过滤器对应到redis的数据结构中就是一个大型的位数组和几个不一样的无偏hash函数。所谓无偏就是能够把元素的hash算的比较均匀,让元素被hash映射到位数组中的位置比较随机。

向布隆过滤器添加数据的时,会使用多个hash函数对key进行hash,算的一个整数索引值,然后对位数组长度取模运算得到一个位置。每个hash函数会得到一个不同的位置,再把位数组的这几个位置都置为1.

向布隆过滤器询问key是否存在时,会看hash函数对应的索引出的值是否为1.如果有位置为0,则一定不存在,如果都为1,则说明这个key有可能存在。

 

1.8 简单限流 

zset,简单实现某个行为在指定时间内最多执行N次。 这个限流需求存在一个滑动时间窗口(定宽)。可以使用zset的score来圈出这个时间窗口。 用score记录时间戳。

思路:每一个行为到来,都维护一次时间窗口,将时间窗口外的记录全部清除掉。zset集合中只有score值比较重要,value值没实际意义,保证唯一性即可。

这种方案如果量很大的情况,会消耗大量内存。

 

1.9 漏斗限流 

原理:在每次灌水前都会调用算法计算两次灌水之间可以漏掉多少空间,空间大小取决于 时间跟流水的速率。

如何保证取数据+计算+存数据的原子性,可以使用redis 中的 Redis-Cell,提供了原子的限流指令。

 

1.9 GeoHash

Redis在3.2版本后增加了地址位置Geo模块。

 

 

1.11 scan

简单粗暴的查询命令 keys,查询所有满足特定正则字符串规则的key

有两个缺点:

  • 没有offset、limit参数,一次性返回所有数据,不好查看
  • keys算法是遍历算法,复杂度是O(n)。数据量大的情况会导致Redis服务卡顿。

 

作为替换可以使用scan命令,拥有以下特点:

  • 通过游标分布进行,不阻塞线程
  • 提供limit参数
  • 返回结果可能有重复,需要客户端去重
  • 单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为0

1.11.8 大key扫描

在集群环境下,如果某个key太大,会导致数据迁移卡顿。

如果一个key太大,那么当它需要扩容的时候,会一次性申请更大的一块内存,可以导致卡顿。大key被删除,内存会被一次性回收,卡顿现象也会再次产生。

当Redis的内存大起大落,极有可能是大key导致的。

 

2.1 线程IO模型

redis 是一个单线程程序,通过 多路复用 解决并发客户端连接。

使用事件轮训,感知事件到来或者指定的时间(timeout)到达后,立即返回。拿到事件后,线程可以顺序处理事件,结束后继续轮轮询。

2.1.5 定时任务

如果线程一直阻塞在select系统调用上,定时任务将无法得到处理。

redis将定时任务记录在“最小堆”的数据结构中,在每个循环周期中,redis会对最小堆里面已经到事件点的任务进行处理.处理完成后,计算下一次将要执行任务的时间差,作为再次循环的timeuot.

2.2 通信协议

RESP 直观的文本协议

 

2.3 持久化

Redis 持久化机制有两种:

1.快照,一次全量备份。快照是内存数据的二进制序列化形式。

2.AOF日志,连续的增量备份,记录的是内存数据修改的指令记录文本,AOF日志在运行中会变的臃肿,需要定期进行AOF重新,给AOF日志进行瘦身。

 

快照原理:fork进程+ COW.子进程做持久化的时候,不会修改现有的内存数据结构。父进程会修改现有的内存数据结构。

posted @ 2023-10-24 10:01  逍的遥  阅读(45)  评论(0编辑  收藏  举报