这是一份Redis学习总结,请查收

最近又复习了一下redis中比较重要的几个知识点,知识点多且碎,在这里做一个简单的总结,便于以后复习。

主流应用架构

我们都知道多数情况下redis是作为缓存应用来使用的,下面则显示出当前主流的应用架构(客户端、缓存、存储层).


这是一份Redis学习总结,请查收_第1张图片
主流应用架构.png

对比缓存中间件 Memcache和Redis的区别

⭐️Memcache: 在代码层次上比较类似于Hash
  • 支持简单的数据类型
  • 不支持数据持久化存储
  • 不支持主从
  • 不支持分片
⭐️Redis
  • 数据类型丰富
  • 支持数据磁盘持久化存储(RDB、AOF)
  • 支持主从
  • 支持分片

我们知道,Redis是内存级数据库,它的QPS(Query Per Second)可以达到100000+,那它为啥那么快呢?

原因如下:

  1. 完全基于内存,绝大多数是存粹的内存操作
  2. 数据结构简单,对数据操作简单(如利用了Hash的查找为O(1)的特性)
  3. 采用单线程(在处理网络请求时是单线程),单线程也能处理高并发的需求,如果想要多核的话可以启动多个实例
  4. 使用多路I/O复用模型,非阻塞I/O

下面我们便开始聊一聊Redis的多路I/O复用模型,在聊这个之前,我们首先应该清楚一个概念:

FD 文件描述符(File Descripto):一个打开的文件通过唯一的描述符进行引用,该描述符是打开文件元数据到文件本身的映射.

I/O模型有:1.传统的I/O阻塞模型 2.多路I/O复用模型(可以同时对多个fd的状态进行监控)

这是一份Redis学习总结,请查收_第2张图片
IO多路复用.png

Redis采用的I/O多路复用函数: epoll/kqueue/evport/select

优先选择O(1)的I/O多路复用函数作为底层实现,以时间复杂度为O(n)的来保底,基于react设计监听I/O事件(监听多个fd)

简单聊一下Redis的数据类型

1.String k,v 最基本的类型,二进制安全(可存图片) [incr 可用作网站用户量统计]
2.Hash String元素组成的字典,适合存储对象。hmset lilei name "lilei" age 26 title "senior"
3.List列表 按照插入顺序排序 lpush rpush. 查询0-10的记录 orange mylist 0 10
4.Set String元素的无序集合,不重复。可以进行交、差、并等操作。sadd myset 111。smembers myset —>遍历所有元素
5.Sorted Set 通过分数为元素大小排序,不重复 zadd myzset 3 abc ; zrangebyscore myset 0 10
还有一些比较高级的如用于计数的HyperLogLog和用于支持存储地理位置的Geo

此外可以看一下<>这本书中对于Redis底层数据类型基础的介绍,以上的所有对象都是基于更加底层的数据结构实现的

如何从海量数据里(2000w+)查询出某一个固定前缀的key

遇到这种问题时,应该首先问清楚数据量的范围规模,问清楚边界,再进行思考和回答

KEYS pattern : 查找所有符合给定模式pattern 的key, 例如 keys k1 查找所有以k1开头的key*

使用keys 对线上业务有什么影响?

keys指令会一次性返回所有匹配的key,键的数量太大会导致服务卡顿

为了解决这个问题 引入了SCAN cursor
  • 基于游标cursor的迭代器,需要基于上一次游标延续之前的迭代过程,以0作为游标作为一次新的迭代,直到返回游标0完成一次遍历。

  • 不保证每次执行都返回某个给定数量的元素,支持模糊查询。

  • 一次返回的数量不可控,只能是大概率符合count参数

    Scan 0 match k1* count 10

    可能获取的数据数量不是10条,可能获得重复元素,要在程序中去重

如何通过Redis实现分布式锁?

要想实现分布式锁,我们需要考虑到以下需求:

  • 互斥性
  • 安全性
  • 注意死锁问题
  • 容错

可以利用Redis如下命令的特性实现一个分布式锁

SETNX key value :如果key不存在,创建并且赋值(用key作为锁)

但这样是占有了一个锁,如何释放掉它呢?也就是说如何解决SETNX长期有效的问题

EXPIRE key seconds :设置过期时间删除模拟锁的释放

下面看一个伪代码的实现

long status = redisService.setnx(key,"1");
//注释1
if(status==1){
    redisService.expire(key,expire);
    doSomething();//......
}

上面的代码看上去似乎没有什么问题,但是仔细想想,如果执行到注释1的地方的时候,redis服务器发生了宕机怎么办? 所以我们要保证setnx操作和expire的设置是原子的

所以redis引入了以下命令

set locktarget 12345(可以写线程的标识) ex 10 nx  //该操作是个原子操作,成功返回OK,失败返回nil

伪代码实现:

String result = redisService.set(lockkey,requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,expiretime);

if("OK".equals(result)){
    doSomething();//....
}

大量的key同时过期怎么处理?

key集中过期,清除大量的key很耗时,会出现短暂的卡顿现象

解决方案: 在设置key的过期时间的时候,给每个key加上随机的值

如何用Redis做异步队列?

使用list做异步队列, rpush生产消息,lpop消费消息

缺点:没有等待队列里有值就进行直接消费

弥补:可以在应用层引入sleep机制去调用lpop重试

也可以用以下命令

BLPOP key[key...] timeout : 阻塞直到队列有消息或者超时

缺点:只能供一个消费者消费

Pub/Sub 主题订阅模式

这是一份Redis学习总结,请查收_第3张图片
主题订阅模式.png

这里有三个客户端连接 分别为cli-1、cli-2、cli-3

cli1:6379-> subscribe myTopic
cli2:6379-> subscribe myTopic
cli3:6379-> publish myTopic "Hello"

然后cli1 和 cli3便会接收到hello消息

这里请注意: 消息的发布是无状态的,无法保证可达

Redis如何做持久化?

1.RDB(快照)持久化:保存某个时间点的全量数据快照
在redis.conf 中可以配置 RDB持久化方式的持久化策略
  • save 900 1 //在900s内进行一次写操作触发持久化
  • save 300 10
  • Save 60 10000
stop-writes-on-bgsave-error yes

当备份进程出错误时,主进程停止接受新的写入操作(保证持久化的数据一致性问题)

rdbcompression no

建议设置为no,关闭压缩,降低cpu损耗(因为Redis本身就是CPU密集型)

手动持久化的命令

1.SAVE : 阻塞Redis的服务器进程直到RDB文件被创建完毕

2.BGSAVE: fork出一个子进程来创建RDB文件,不阻塞服务器进程

自动触发RDB持久化的方式
  • 根据redis.conf配置的save m n 定时触发(用的bgsave)
  • 主从复制时,主节点自动触发
  • 执行debug reload
  • 执行shutdown 且没有开启ROF持久化

什么是 Copy-On-Write 写时复制?

如果有多个调用者同时获取相同的资源的时候,他们会获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容的时候,系统才会真正复制一份专有副本给调用者,而其他调用者见到的最初资源保持不变。

当redis做持久化时,redis会fork一个子进程,将数据写入磁盘中的一个临时的rdb文件中,当子进程完成写临时文件之后,将原来的rdb替换掉,这样的好处是可以实现copy-on-write,子进程继续可以接受其他请求,确保了redis性能。

缺点:内存数据的全量同步,当数据量大的时候会由于I/O而严重影响性能,可能会因为redis挂掉而丢失从当前至最近一次快照期间的数据。

2.AOF(Append-Only-File)持久化:保存写状态
  • 记录下除了查询以外所有变更数据库状态的指令
  • 以append的形式追加保存到AOF文件中(增量)

AOF的持久化默认是关闭的. vim redis.conf

appendonlyno. —>修改为 appendonly yes 生效

appendfilename "append only.aof"

appendfsync:可以指定AOF写入方式 :

1.always 2.everysec 3.no

AOF日志重写 bgrewrite aof

日志重写解决AOF文件大小不断增大的问题,原理如下:

  1. 调用fork(),创建一个子进程
  2. 子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件
  3. 主进程持续把新的变动同时写入内存和原来的AOF里
  4. 主进程获取子进程的重写AOF的完成信号,往新的AOF同步增量变动
  5. 使用新的AOF文件替换掉旧的AOF文件

Redis数据的恢复

RDB和AOF文件共存情况下的恢复流程
这是一份Redis学习总结,请查收_第4张图片
Redis数据的恢复流程.png

RDB-AOF混合持久化的方式

BGSAVE做镜像全量持久化,AOF做增量持久化

总结RDB和AOF的优缺点

  • RDB优点:全量数据快照,文件小,恢复快
  • RDB缺点:无法保存最近一次快照后的数据
  • AOF优点:可读性高,适合保存增量数据,数据不易丢失
  • AOF缺点:文件体积大,恢复时间长

Redis主从复制同步

1.全同步过程
  1. Slave发送sync命令到master
  2. master启动一个后台进程,将redis中的数据快照保存到文件中
  3. master将保存快照期间收到的命令缓存起来
  4. master完成写文件操作后,将该文件发送给salve
  5. 使用新的AOF文件替换掉旧的AOF文件
  6. master将这期间收到的写命令发送给salve,进行回放
2.增量同步的过程
  1. master接受用户的操作指令,判断是否需要传播到salve
  2. 将操作记录追加到aof文件
  3. 将操作传播到其他slave:1.对齐主从库 2.往响应缓存中写入指令
  4. 将缓存中的数据发送给slave

主从模式不具备高可用性,当master挂掉以后,slave将无法对外提供写入操作,为解决该问题,引入redis sentinel(哨兵)解决主从同步宕机后的主从切换问题

  • 监控:检查主从服务器是否运行正常
  • 提醒:通过API向管理员或者其他应用程序发送故障通知
  • 自动故障转移:主从切换

流言协议 Gossip 在杂乱无章中寻求一致

每个节点都随机与对方通信,最终所有节点的状态达成一致。

种子节点定期随机向其他节点发送节点列表以及需要传播的信息

不保证信息一定会传递给所有的节点,但最终会趋于一致性。

关于redis集群的更多总结我会放到下一篇文章里,今天的总结就到这里了!

lhsjohn 转载请注明出处 谢谢!

你可能感兴趣的:(这是一份Redis学习总结,请查收)