目录

4-1 Redis简介

4-2 Redis常用的数据类型

4-3从海量的Key里面查询出某一固定前缀的Key

 4-4 如何实现分布式锁

4-5 如何使用Redis来做异步队列

4-6 持久化方式之RDB

4-7 持久化方式---AOF以及混合模式

 4-10 Pipeline及主从同步

4-11 Redis集群


4-1 Redis简介

常见的数据库Mysql,Oricle数据都是存放在磁盘中,虽然在数据库层做了对应的缓存,但是这种数据库层次的缓存,一般针对的是查询的内容且力度小,一般是表中的数据没有发生变更的时候,数据库中的cash才会发生作用,但不能减少业务对数据库进行增删改查的IO访问压力。

缓存数据库实现了对数据的高速缓存,提高应用的反应速度,极大缓解后端数据库的压力

缓存中间件——Memcache和Redis的区别

  • Memcache:Key-Value存储,代码层次类似Hash,简单易用,可以通过hash数据结构实现
    • 支持简单数据类型
    • 不支持数据持久化存储---一旦服务器宕机后,数据是没有办法保存下来的
    • 不支持主从
    • 不支持分片---将大数据分布到多个物理节点上的分区方案
  • Redis ---有持久化需求,或者对数据结构处理有高级要求的应用,选择Redis
    • 数据类型丰富,set,list,zset等数据类型
    • 支持数据磁盘持久化存储
    • 支持出从
    • 支持分片

为什么Redis能这么快

100000+QPS(query per second,每秒内查询的次数)

  • 完全基于内存,绝大部分请求时纯粹的内存操作,执行效率高;
    • 单线程,单进程,数据存储在内存中,读写数据的时候都不会受到IO硬盘速率的限制
  • 数据结构简单,对数据操作简单——Redis不使用表,他的数据库不会强制性对Redis存储的不同数据进行关联,因此性能比关系型数据库高很多,其存储结构就是键值对,类似于hashmap,hashmap的优势就是查找和操作的时间复杂度都是O(1)。
  • 采用单线程,单线程也能处理高并发请求,想多核也可启动多例。
    • 在遇到高并发的时候,首先想到的时用多个线程来处理,将io线程和业务线程分开,业务线程使用线程池来避免频繁创建和销毁线程,即便是一次请求阻塞,也不会影响其他线程。
    • Redis反其道而行之,主线程是单线程的,这里的主线程指的的IO事件的处理以及IO对应的相关请求的业务的处理,此外,主线程还负责过期键的处理,集群协调等等,这些IO事件的逻辑,会被封装成周期性的任务,由主线程进行周期性的调用处理。
    • 对于客户端的所有读写请求,都由主线程进行串行处理,因此多个客户端同时对于一个键进行写操作的时候,不会有并发的问题,避免了频繁的上下文切换和锁竞争,使得Redis执行起来,效率更高。
    • 并发并不是并行--- 并行性指的是服务器同时处理几个事情,具有多个计算单元。并发性IO流,意味着能够让一个计算单元来处理来自多个客户端的流请求。
    • Redis单线程 + IO流多路复用,能够大幅度提升性能。
  • 使用多路I/O复用模型,非阻塞IO——Redis是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作,等待用户输入或者输出,都是阻塞的,所以IO操作在一般情况下,往往不能直接返回,这回导致某一文件的IO阻塞,进而导致则会导致整个进程无法对其他客户端提供服务。多路I/O复用模型就是为解决这一个问题而存在的。

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

  • 因地制宜
  • 优先选择时间复杂度为O(1)的I/O多路复用函数作为底层实现
  • 以时间复杂度为O(n)的select作为保底。

4-2 Redis常用的数据类型

  1. String 最基本的数据类型,二进制安全——key-value键值对,最大存储512M,如set key "value"(或者1也行)。可以存储图片或序列化的对象
  2. Hash String元素组成的映射表(就有点像实体对象)。可以存用户信息
  3. List 简单的字符串列表,按照插入顺序排序。可实现排行榜,新插入的排前显示
  4. Set: 字符串组成的无序集合,不允许重复,增删查找复杂度为O(1)。场景:将关注人存到这里实现共同关注功能
  5. Sorted Set字符串组成的无序集合,通过分数来为集合中的成员进行从小到大的排序,不允许重复,查找复杂度为O(1)。场景:将关注人存到这里实现共同关注功能
  6. 用于计数的HyperLogLog,用于支持存储地理位置信息的Geo。

底层数据类型基础:简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表、对象

4-3从海量的Key里面查询出某一固定前缀的Key

  • 摸清数据规模,即问清楚边界
  • keys pattern —— 查找所有符合给定模式pattern的key,
    • 一次性返回所有匹配的key
    • 数据过大,会造成卡顿
  • scan cursor [match pattern] [count count]
    • 基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程
    • 以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历
    • 不保证每次执行度返回某个给定数量的元素,支持模糊查询
    • 一次返回的数量不可控,只能大概符合count参数

 4-4 如何实现分布式锁

分布式锁是实现控制分布式系统或者不同系统之间共同访问共享资源之间的一种锁的实现。

如果不同的系统,或者同一个系统,不同的主机之间共享了某个资源时,往往需要互斥来防止彼此干扰,进而保证一致性。

分布式锁需要解决的问题

  1. 互斥性——任意时刻只能有一个客户端获取锁,不能同时有两个客户端获取锁
  2. 安全性——锁只能由持有该锁的客户端删除,不能由其他客户端删除
  3. 死锁——获取锁的客户端因为某些原因而宕机而未能释放锁,其他客户端再也无法获取到该锁而导致死锁,此时需要有机制来避免这种情况的发生
  4. 容错—— 当部分节点宕机的时候,客户端仍然能够获取到锁和释放锁

问题1 如何通过Redis来实现分布式锁。

  • SETNX key value : 如果key不存在,则创建并赋值
    • 时间复杂度0(1),原子性
    • 返回值:设置成功,返回1;设置失败,返回0

问题2:防止一直占用锁,怎样给key设定生存时间?

  • 如何解决Setnx长期有效的问题
    • EXPIRE key seconds
      • 设置key的生存时间,当key过期时(生存时间为0),会被自动删除
      • 缺点:缺乏原子性的操作

问题3:两个命令破坏了原子性,会有风险,expire命令没有执行,一直死锁,怎么办?

指令:set key value [EX seconds] [PX milliseconds] [NX|XX],满足原子性,满足分布式锁

  • EX:过期时间seconds秒,
  • PX :过期时间milliseconds毫秒
  • NX:键不存在时,进行设置
  • XX:键已经存在时,进行设置
  • Set 操作成功的时候成功返回ok,否则返回nil

问题4  大量key同时过期注意事项?

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

  • 在设置key的过期时间的时候,给每个key加上随机值,使得过期时间分散一些,这样就能在很大程度上避免卡顿现象发生

4-5 如何使用Redis来做异步队列

使用List作为队列,RPUSH生产消息,LPOP消费消息

  • 缺点——没等等待队列里有值就直接消费
  • 弥补——可以通过在应用层引入Sleep机制去调用LPOP重试
  • BLPOP key 【key...】 timeout :阻塞直到队列有消息或者超时
    • 缺点——只能提供给一个消费者消费

生产一次,就让多个消费者消费的方法

pub/sub :主题订阅者模式,可以实现一对多的消费队列

  • 发送者(pub)发送消息,订阅者(sub)接收消息
  • 订阅者可以订阅任意数量的频道
  • 缺点——消息的发布是无状态的,即发即失,无法保证可达。
  • 缺陷是无法保证能收到,比如发的时候,正好消费者掉线,后续它就收不到了。这些只能通过专业的消息队列了,如kafka

4-6 持久化方式之RDB

RDB(快照)持久化:保存某个时间点的全量数据快照

  • SAVE:阻塞Redis的服务器进程,直到RDB文件被创建完毕,很少被使用,因为save操作是在主线程保存,由于Redis是用一个主线程来处理所有的请求的,这种方式会阻塞所有客户端二点请求。
  • BGSAVEFork出一个字进程来创建RDB文件,子进程完成创建后,会发送信号给主进程(Redis主进程),主进程处理命令的时候,通过轮询来接收子进程的信号并进行处理,并立刻恢复对客户端的服务,不阻塞服务器进程

自动化触发RDB持久化的方式

  • 根据redis.conf配置里的SAVE m n 定时触发(用的是BGSAVE)
    • save 900 1,900秒内有1条写入就备份
  • 主从复制的时候,主节点自动触发。
  • 执行Debug Reload
  • 执行Shutdown且没有开启AOF持久化

缺点:

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

4-7 持久化方式---AOF以及混合模式

AOF持久化:以日志的形式来记录每一个写操作,将Redis执行过的所有写指令记录下来,保存写状态

  • 记录下除了查询以外的所有变更数据库状态的指令
  • 以append的形式追加保存到AOF文件中(增量),只许追加文件,不可以改写文件。
  • 每修改同步:appenfysnc always 同步持久化,每次发生数据变更会被立即记录到磁盘,性能差但数据完整性较好
  • 每秒同步 : appenfysnc everysec 异步操作,每秒记录。如果一秒内宕机,有数据丢失
  • 不同步:appenfysnc no 从不同步

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

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

RDB和AOF的优缺点:

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

RDB-AOF混合持久化模式

使用RDB做为全量备份,使用AOF作为增量备份来提升备份效率

新的AOF文件亲半段是RDB格式的全量数据,后半段是AOF格式增量数据。

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

 4-10 Pipeline及主从同步

使用Pipeline的好处

  • Pipeline和linux的管道类似
  • Reids基于请求响应模型,单个请求处理需要一一应答。在这种情况下,如果需要执行批量的命,那就需要等待上一条命令应答后,在执行后面的命令。
  • Pipeline批量执行指令,节省多次I/O往返时间。
  • 有顺序依赖的指令建议分批发送

Reids的同步机制---主从复制原理

 全同步进程:所有写操作都是在master上进行,所有读操作都是在slave上进行的

  1. 从机(slave)发送同步信息到主机(master)
  2. 主机启动一个后台进程,进行快照数据的存储
  3. 主机进行快照数据存储的同时将客户端的写请求操作缓存起来
  4. 主机快照数据存储完成后,将该文件发送给从机
  5. 从机将新的RDB文件替换掉旧的RDB文件,加载到内存中,并通知主机
  6. 主机会将这期间缓存的增量命令(AOF)发送给从机

增量同步进程

  1. 主机(master)接收到客户端的请求操作,判断是否需要发送到从机(slave)
  2. 执行请求操作指令并记录到AOF文件中
  3. 将操作指令同步到从机(slave),首先判断是否为主机的从机,是的话进行同步

主从复制的弊端不具备高可靠性

Redis Sentinel (哨兵): 解决主从同步主机(master)宕机后的问题

  1. 监控:哨兵模式会监控主机和从机
  2. 通知:若宕机的话,哨兵模式会进行通知,以API或应用程序发送通知
  3. 主从切换:哨兵模式会通过一系列机制来选举出新的主机,并和别的从机通过流言协议进行通信,以达到最终一致性

流言协议Gossip:

  • 在杂乱无章中寻求一致,在这个协议中,每个节点会随意的向别的节点通信,但不一定保证和所有节点进行通信,理论上,最终所有的节点信息会保持一致性

4-11 Redis集群

Redis集群技术,是构建高性能网站架构的重要手段。

如何从海量的数据里快速找到所需的数据/

  • 分片:按照某种规则去划分数据,分散存储在多个节点上——每个节点都和其他节点连接,节点之间使用Gossip协议去传播信息以及发现新的节点。Redis的主要目的是要将不同的key放入到不同的Redis节点,实现的原理是获取key的哈希值,根据节点数来求mod
  • 这样常规按照哈希划分无法实现节点的动态增减。
  • 引入一致性哈希算法——对2^32取模,将哈希空间组织成虚拟圆环(欢喜环),服务器也计算在哈希环上有单独的哈希值,数据转换成哈希值在换上正时针选择最近服务器存储
    • 好处:环上某一机器宕机,会直接略过
    • 弊端:hash环数据倾斜问题:数据分布不均;
    • 增加虚拟节点可以解决数据节点少时,数据倾斜问题。——虚拟节点:虚拟出来的服务器节点,相当于服务器分身
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐