Redis知识点

1. 应用场景

缓存:根据键值过期时间设置

请求频率限制:比如短信验证码120秒内只能发送一次,则将标志性的key-value键值对设置过期时间为120秒,用户请求的时候判断一下【SET key value EX 120 NX

排行榜:利用zset数据类型

计数器:利用 INCR KEY 命令,key不存在初始化为0,存在则自增1

用户爱好:利用set集合,其特有的命令方法能够计算用户共同爱好

消息队列:利用list数据类型的lpush和brpop

分布式锁:利用 SET key value [EX seconds] [PX milliseconds] [NX|XX] 

2.速度快的原因

  • 纯内存访问
  • 数据结构简单
  • 底层I/O用多路复用模型实现,利用epoll去监听多个流的IO事件:空闲的时候,线程是阻塞的;如果有一个或者多个事件到达,线程被唤醒,开始轮询并处理那些发生了事件的流。
  • 单线程避免了线程切换和竞争,也不用考虑加锁的问题

3. 数据类型

  • 字符串(String):内部编码【8个字节长整型:int】【小于等于39个字节的字符串:embstr】【大于39个字节的字符串:raw】
  • 哈希(Hash):内部编码【元素个数小于512个并且每个值都小于64字节:ziplist-压缩列表】【否则是hashtable-哈希表】
  • 列表(List):内部编码【元素个数小于512个并且每个值都小于64字节:ziplist-压缩列表】【否则是linkedlist-链表】
  • 集合(Set):内部编码【元素都是整数并且个数小于512个:intset-整数集合】【否则是hashtable-哈希表】
  • 有序集合(ZSet):内部编码【元素个数小于128个并且每个值都小于64字节:ziplist-压缩列表】【否则是skiplist-跳跃表】

4. 持久化-RDB

触发:save或者bgsave命令。不同的是save会阻塞服务器,直到持久化完成;bgsave会fock子进程,子进程负责完成持久化的任务,这种只在fock的时候会阻塞很短一部分时间。

RDB持久化流程:

  • 执行bgsave命令,如果已存在相关的子进程,则直接返回。
  • 父进程执行fock操作创建子进程。fock完成后不再阻塞父进程。
  • 子进程创建RDB文件,根据父进程的内存生成临时快照文件,完成后对原有文件进行原子替换。
  • 最后给父进程发送完成信号,父进程更新统计信息。

Redis默认采用LZF算法对生成的RDB文件做压缩处理,压缩后的文件远远小于内存大小。

5. 持久化-AOF

工作流程:

  • 命令写入:所有的写入命令都会追加到AOF缓冲区(处于性能的考量,不追加到硬盘)
  • 文件同步:缓冲区根据对应的策略向硬盘做同步操作(策略:always,everysec,no,默认everysec)
  • 文件重写:AOF文件越来越大,所以定期对AOF文件进行重写,达到压缩的目的(无效命令比如del、set,文件只保留最终数据的写入命令;多条命令合并成一条)
  • 重启加载:Redis服务器重启后,会根据AOF文件进行数据恢复

AOF重写流程:

首先主进程会fock一个子进程。Redis会维护一个AOF重写缓冲区,该缓冲区会在子进程创建新的AOF文件期间,记录服务器执行的所有写命令。当子进程完成创建新AOF文件之后,服务器会将重写缓冲区中的所有内容追加到新的AOF文件的末尾。最后,服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件重写操作。

如果同时配置了RDB和AOF,则Redis会优先加载AOF

6. 数据分区

分布式环境下要解决把数据映射到多个节点的问题。

A:节点取余分区

【对key取hash】%【节点数量】,来决定数据打到哪一个节点上。

B:一致性哈希分区

为系统中每个节点分配一个token,范围为2的32次幂,这些token构成一个hash环。读写数据的时候,根据key取hash,然后顺时针找到第一个大于等于该hash值的token所对应的节点。

一致性哈希存在的问题:

  • 加减节点会导致一部分数据无法命中,需要手动处理或者忽略,所以一致性哈希常用语缓存场景。
  • 节点比较少的情况,节点发生变化将影响很大一部分数据命中,所以不适合节点少的情况。

C:虚拟槽分区

Redis Cluser采用虚拟槽分区,所有的键根据哈希函数映射到0~16383整数槽内,计算公式:slot=CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据

优点:解耦数据和节点之间的关系,简化了节点扩容和收缩难度。

7. 集群功能限制

不支持批量操作比如:mget,mset

不支持多数据库空间,即只有一个db0

8. 缓存穿透

原因:请求不存在的数据,而导致每次请求都要跳过缓存直接去存储层拿数据,失去了缓存保护后端存储的意义。

解决:

  • 方法一:存储层不命中,依然缓存空对象,之后再访问这个数据就会从缓存中获取。可是:如果是恶意攻击,意味着这部分无意义的数据会占用内存,可以通过设置过期时间来缓解。
  • 方法二:利用布隆过滤器把存在的key缓存起来,作为访问的第一层,之后才是缓存和数据库,如果布隆过滤器认为请求的key不存在,则不会访问数据库,进而保护了存储层。
  • 比较:存储空对象适用于:数据频繁变化实时性高的场景。布隆过滤器适用于:数据相对固定实时性低的场景。

9. 缓存击穿

原因:比如有个热点key承载了大量请求,在key失效的一瞬间,大量请求跳过缓存直接去请求了数据库

解决:

  • 方法一:一般是因为做促销活动导致key成为这种热点,对于这类key要么设置过期时间长一些以至于熬过高峰期,要么直接设置成永不过期。
  • 方法二:加互斥锁,即如果缓存不存在,则加锁去读取数据库并更新缓存,保证失效的时候只有一个线程能去访问数据库。

10. 缓存雪崩

原因:

  • 在同一时间点,承载大量请求的缓存集中失效,大量请求会到达存储层,进而造成存储层压力过大而宕机。
  • 缓存层由于异常不能提供服务后,大量请求会到达存储层,进而造成存储层压力过大而宕机。

解决:

  • 保证缓存服务高可用
  • 将key的过期时间设置随机,这样避免同一时间大量失效

11. 淘汰策略(6种)

这6种策略的前提都是:当内存不足以容纳新写入数据时

noeviction:新写入操作会报错(默认策略)

范围:在主键空间中

allkeys-lru:移除最近最少使用的key

allkeys-random:随机移除某个key

范围:在设置了过期时间的键空间中

volatile-lru:移除最近最少使用的key

volatile-random:随机移除某个key

volatile-ttl:有更早过期时间的key优先移除

原文地址:https://www.cnblogs.com/LUA123/p/12877791.html