Redis总结

主流应用框架

Redis总结_第1张图片

一、缓存器一般有两种缓存中间件 ——Memcache和Redis

Memcache:代码层类似于Hash,简单易用

特点:

  1. 支持简单类型
  2. 不支持数据持久化存储
  3. 不支持主从
  4. 不支持分片
Redis:

特点:

  1. 数据类型丰富(set, list)
  2. 支持数据持久化存储
  3. 支持主从
  4. 支持分片

二、Redis为什么这么快?

  1. 完全基于内存,绝大多数的请求都是纯粹的内存操作,执行效率较高
  2. 数据结构简单,对数据操作也简单(键值对)
  3. 采用单线程(对于网络请求),单线程也能处理高并发,但是单线程无法充分利用多核cup,所以可以启动多实例。(为何单线程也能这么快?其实单线程就意味着多个请求修改同一个key时,不存在阻塞问题,不需要加锁和解锁,间接也提高了效率)
  4. 使用多路I/O复用模型,非阻塞IO(解决多并发客户端连接)
Redis 单线程如何处理那么多的并发客户端连接?
使用多路I/O复用模型

Redis总结_第2张图片
文件分派器有很多种:

Selector模型
Redis总结_第3张图片
当系统要查找文件时,把请求FD交给Selector,Selector就会找到对应文件再交给Redis线程

多种文件分派器 如epoll/kqueue/evport/select

这些模型都比Selector要优秀(时间复杂度为O(1)),但是是不同平台的特定模型,而Selector是通用的模型,时间复杂度为O(n)
模型的使用原则:

  • 因地制宜(根据操作系统而定)
  • 优先选择时间复杂度为O(1)的模型
  • 以时间复杂度为O(n)的模型为保底
  • 基于react设计模式监听I/O事件

基本数据类型

  1. STRING(字符串)
  2. LIST(列表)
  3. SET(集合)
  4. HASH(哈希散列表)
  5. ZSET(有序集合)
  6. HyperLogLog(基数)

问题:如果要从海量元素中获取以 k开头的键

获取指定的key有两种方式:

  • 方式一:keys k*
    k* 为模糊匹配出一k开头的所有键
    特点:一次返回全部元素,如果数据量非常大,会造成服务器阻塞
  • 方式二:scan cursor [ MATCH pattern ] [COUNT count]
    参数说明:
    cursor:指一个游标,指定要开始遍历的位置,默认以0作为开始遍历的值,返回0代表遍历结束
    pattern代表匹配的规则
    count:代表一次返回的数据条数(不一定按指定条数返回,只是大致约束返回的条数)
    如:scan 0 MATCH key: COUNT 10
    特点:分批返回,不会占用线程造成阻塞。总执行时间会边长

如何实现分布式锁

方式一(不推荐)、用setnx 再给值设置一个过期时间,
如:
setnx k1 v1
expire k1 10
setnx:用于设置一个值,前提是该值不存在
expire:设置一个键的过期时间
该方式的缺点:把操作分成两部步,违反了原子性,如果第一个命令执行完,机器故障,第二个命令无法执行,也会造成该key无法释放回收
方式二、SET key [EX second] [PX milliseconds] [NX | XX]
Redis总结_第4张图片
如:set key1 1234·ex 10 nx
解析:如果key1不存在,设置该key1,值为1234,过期时间为10秒
好处:保证操作原子性

如何给大量的key设置同时的过期时间

如果每个key的过期时间相同,则在同一时间回收会造成卡顿。
解决:给每一个key的过期时间加一个随机值,让大量的key的过期时间有稍微的错开

如何实现异步队列

通常用一个LIST列表来存放消息队列,生产者向队列尾部中放入数据,消费者在队列头部放入数据,当队列为空时,消费者不会等待生产者放入数据。
如果想要消费者在生产者生产出数据后获取到的话,有以下几种方式:

  1. 消费者线程sleep阻塞等待:即通过sleep阻塞当前线程,直到生产者生产出数据取出再离开。
  2. 使用blpop命令,指定超时时间: blpop testlist 20 会等待20秒,期间获取到就直接退出或等到20秒或退出。如果多个客户端等待同一个生产者的数据,客户端会进入消费队列,轮流消费
  3. pub/sub 主题订阅者模式。发送者pub发送消息,订阅者接收消息,订阅者可以订阅任意数量的消息(类似于SOA)
    如:
    订阅单个:subscribe myTopic
    订阅多个:subscribe myTopic yourTopic
    发布消息:publish myTopic hello
    Redis总结_第5张图片

数据持久化

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

  • 如何持久化:
  1. SAVE命令(不用):阻塞Redis的服务器进程,知道RDB文件被创建完成。
  2. BGSAVE命令(常用):Fock出一个子进程创建RDB文件,不阻塞服务器进程

lastsave命令:返回最后一次保存的时间戳
Fock出一个子进程的同时会生成一个生成快照,但是不会立即复制全部数据,而是产生一个指针指向原来数据位置,如果只是对新的快照读操作只是读原来的数据,如果对数据写操作,此时才会对原来数据进行复制,即 Write-On-Copy写时复制。当写入结束后会删除原来的dump.rdb文件,然后把插入数据后的.rdb文件放到服务器中
(思考:如多个客户端同时对服务器执行bgsave操作,后面执行结束产生的.rdb文件是否会覆盖前一个客户端生成的.rdb文件,数据修改岂不是丢失了?
来看看BGSAVE的原理图:
Redis总结_第6张图片
客户端想要执行bgsave时服务器会判断是否有子进程正在保存,如果有则命令失败,否则进行持久化

  • 如何自动触发:
  1. 根据redis.conf 配置里的save m n 定时触发(用的是BGSAVE)
  2. 主从复制时,主节点自动触发
  3. 执行Debug Reload
  4. 执行Shutdown且没有开启AOF持久化

优点:全量数据快照,数据文件小,恢复快
缺点:无法保存最近一次快照之后的数据

AOF (Append-Only file)

通过记录每一步的操作命令,来还原数据
原理:

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

优点:可读性高,适合保存增量数据,数据不易丢失
缺点:文件体积大,恢复时间长

同时使用RDB和AOF两种持久化方式,系统应该如何选择?

先检查AOF是否开启,如果有开启直接使用AOF持久化方式,如果不存在,则尝试使用RDB方式持久化

使用RDB-AOF混合持久化

结合RDB和AOF的优点,使用RDB-AOF混合持久化

  • BGSAVE做镜像全量持久化,AOF做增量持久化。意思是使用文件中原本保留的是RDB格式的原始数据,在操作redis时增加的数据已AOF的方式记录Redis格式命令,持久化这些命令到文件中。当系统发生意外时,文件既有原来的数据,也记录了意外前操作的命令,这样恢复数据就不会大量丢失数据。所以说数据持久文件中保存的数据前半部分是RDB格式的数据,后半部分是Redis格式的命令

Pipeline 管道技术

客户端和服务端通过网络进行连接。这样的连接可能非常快(在一个回路网络中),也可能非常慢(在广域网上经过多个结点才能互通的两个主机)。但是无论是否存在网络延迟,数据包从客户端传输到服务端,以及客户端从服务端获得相应都需要花费一些时间。这段时间就成为往返时延(Round Trip Time)。因此当客户端需要执行一串请求的时候,很容易看出它对性能的影响(例如往同一个队列中加入大量元素,或者往数据库中插入大量的键)。如果RTT时长为250毫秒(在基于广域网的低速连接环境下),即使服务器每秒可以处理10万个请求,但是实际上我们依然只能每秒处理最多4个请求。
如果处于一个回路网络中,RTT时长则相当短(我的主机ping 127.0.0.1时只需要0.044ms),但是如果你执行一大串写入请求的时候,还是会有点长。
幸运的是,redis给我们提供了管道技术。

  • Redis管道技术
    一个请求/相应服务可以实现为,即使客户端没有读取到旧请求的响应,服务端依旧可以处理新请求。通过这种方式,可以完全无需等待服务端应答地发送多条指令给服务端,并最终一次性读取所有应答。管道技术最显著的优势是提高了redis服务的性能。
    通过pipeline方式当有大批量的操作时候。我们可以节省很多原来浪费在网络延迟的时间。需要注意到是用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并是不是打包的命令越多越好。具体多少合适需要根据具体情况测试。

摘自:https://www.cnblogs.com/pxuan/p/8058299.html

主从同步

redis集群结构:
Redis总结_第7张图片

  • 全过程同步
  1. Slave 发送sycn 命令到主节点Master中
  2. Master启动一个后台线程,将Redis中的数据快照保存到文件中
  3. Master将保存数据快照期间接收到的写命令缓存起来
  4. Master完成写文件操作后将该文件发送给Slave
  5. 使用新的AOF文件替换掉旧的AOF文件
  6. Master将期间收集到的增量写命令发送给
  • 增量过程同步
  1. Master接收到用户的操作指令,判断是否需要同步到Slave中(是否是写操作)
  2. 将操作记录追加到AOF文件中
  3. 将操作传播到其他Slave:1. 对齐主从库;2. 往响应缓存写入指令
  4. 将缓存中的数据发送给Slave。

Redis Sentinel

主服务器和从服务器之间虽然可以进行数据同步,但是两者并不清楚对方的状况,比如主服务器是否运行正常,等信息,则需要一个管理者来管理这些节点,这个管理者就是Sentinel。Sentinel解决了以下问题:

  • 监控:检查服务器是否运行正常。
  • 提示:通知API向管理员或其他应用发送故障通知。
  • 自动故障迁移:主从切换。
    Sentinel是基于留言协议(gossip)来获取节点的情况和发送通知的

Gossip

gossip 优势

  • 可扩展性(Scalable)
    gossip 协议是可扩展的,一般需要 O(logN) 轮就可以将信息传播到所有的节点,其中 N 代表节点的个数。每个节点仅发送固定数量的消息,并且与网络中节点数目无法。在数据传送的时候,节点并不会等待消息的 ack,所以消息传送失败也没有关系,因为可以通过其他节点将消息传递给之前传送失败的节点。系统可以轻松扩展到数百万个进程。

  • 容错(Fault-tolerance)
    网络中任何节点的重启或者宕机都不会影响 gossip 协议的运行。

  • 健壮性(Robust)
    gossip 协议是去中心化的协议,所以集群中的所有节点都是对等的,没有特殊的节点,所以任何节点出现问题都不会阻止其他节点继续发送消息。任何节点都可以随时加入或离开,而不会影响系统的整体服务质量(QOS)

  • 最终一致性(Convergent consistency)
    Gossip 协议实现信息指数级的快速传播,因此在有新信息需要传播时,消息可以快速地发送到全局节点,在有限的时间内能够做到所有节点都拥有最新的数据。

Redis的集群原理

当要在海量的数据中查找到某个数据,则必须要把海量的数据分片存储在多个节点上,组成集群。那如何知道一个数据应该保存在哪个节点上或者取数据时应该在哪个节点上寻找呢?Redis使用一致性哈希算法来分配数据到每一个节点上。

  • 一致性哈希算法:对 2^32取模,将哈希值空间组织成虚拟的圆环
  • 节点有自己的在圆环上对应的位置,一般是用节点的IP地址,端口等有标志性的参数,用一致性哈希算法,来计算出自己在圆环中的位置
    Redis总结_第8张图片
  • 将数据的key用相同的Hash算法,算出key在环中对应位置,然后顺时针地找到离自己最近的节点,然后存储在节点中
    Redis总结_第9张图片
一致性哈希算法存在的问题
  • Hash环的数据倾斜问题:当节点的数量很少的时候,数据容易集中地存在一个节点中
    Redis总结_第10张图片
  • 解决:用虚拟节点,即一个节点计算多个Hash值,让节点虚拟分配在环中的多个位置,使得减少数据集中在一个节点上的问题
    Redis总结_第11张图片

你可能感兴趣的:(Redis)