Redis知识点总结

一. 概述

¥1. 什么是Redis?Redis的特点

Redis是一个高性能非关系型(NoSQL)的键值对数据库

Redis的特点:

  • 数据均存放在内存中,读写性能优异
  • 所有的操作都是原子操作,保证线程安全
  • 支持数据持久化,支持事务,数据结构丰富,支持主从复制
¥2. Redis的应用场景
  • 高速缓存,计数器,会话缓存(分布式Session),全页缓存
  • 消息队列(发布/订阅功能):List 通过 lpush 和 rpop 写入和读取消息
  • 分布式锁(SETNX 命令, RedLock )
  • Set 可以实现交集、并集等操作,从而实现共同好友等功能
  • ZSet 可以实现有序性操作,从而实现排行榜等功能
3. Redis与Memcached的区别
  1. memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
  2. redis的速度比memcached快很多
  3. redis可以持久化其数据
¥4. 为什么要用Redis

高性能:操作缓存就是直接操作内存,所以速度相当快。
高并发:直接操作缓存能够承受的数据库请求数量远远大于直接访问数据库的

一般像 MySQL 这类的数据库的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 redis 的情况,redis 集群的话会更高)。

¥5. Redis为什么这么块
  1. Redis完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
  2. 采用单线程,避免了不必要的上下文切换和竞争条件
¥6. 如何保证缓存与数据库双写时的数据一致性
  • 更新 DB,然后直接删除 cache

如果更新数据库成功,而删除缓存这一步失败的情况的话,简单说两个解决方案:

  1. 缓存失效时间变短(不推荐,治标不治本) :我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用。
  2. 增加cache更新重试机制(常用): 如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将缓存中对应的 key 删除即可。
7. Redis支持的Java客户端
  • Redisson、Jedis、lettuce等等,官方推荐使用Redisson。
  • Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持;Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

二. 数据结构

¥1. Redis的五大数据结构及其应用场景

在这里插入图片描述

1.5 Zset有哪些命令
  1. 添加,删除元素
  2. zscore 查找该元素的分数
  3. zrange和zrevrange :输出该排名内的元素
  4. zrank 输出元素的排名
  5. zrangebyscore和zrevrangebyscore:输出该分数断内的元素
  6. zremrangebyrank和zremrangebyscore:删除指定排名范围和指定分数范围内的元素
2. HyperLogLog的应用场景
  • HyperLogLog用于基数统计(不精确的去重),如统计IP数,用户数等
  • 其特点是仅用极小的内存即可统计很大数量的基数,但不能存储元素本身
3. Redis底层数据结构
  • 简单动态字符串SDS:包含字符串长度+未用空间长度+字符数组
    通过预留空间减少内存充分配次数,空间不足时再自动扩容
  • 链表:双向链表,包含头尾节点+链表长度
  • 整数集合:编码方式(int8,int16,int32,int64)+元素数量+元素数组
    先用较小的编码方式(int8),如果新插入的元素大于当前编码方式再升级,节省空间
  • 压缩列表:将数据紧凑存储节省空间。相当于在一个数组中存储很多节点,每个节点包含自己的内容和上一个节点的地址。
    (压缩列表存在连锁更新问题,且插入删除不便,只适用于存储较小的数据)
  • 跳表:跳表的每个节点有自己的层数数组,每层有指向该层下一个节点的指针和跨度。查询时根据跨度判断,是在该层继续走还是进入下一层。
    在这里插入图片描述
  • 字典:Redis总的来讲就是字典,字典的实现与HashMap类似
3.5 Redis压缩链表
  • 使用场景:1.列表的列表项很少,且列表项为小整数或小字符串;2. 哈希表只包含少量键值对,且键值对很小
  • 构成:连续内存数组,包含zlbytes记录总占用字节数;zltail尾节点对偏移量;zlen节点数量;entryX节点
  • 节点构成:节点可用于保存一个整数值或字节数组。包含前一个节点长度(1字节或5字节);编码类型(内容是哪种类型的数字或数组);内容
  • 遍历方式:从后往前遍历,先通过zltail定位到最后一个节点,再通过每个元素记录的前节点长度推移至上一节点,依次类推
  • 连锁更新:如果前一个节点的长度发生了变化,则需改变记录前一个节点长度的内容,可能又会影响下一个节点,导致连锁更新
4. Redis字典数据结构的扩容过程

由于一般Redis存储的数据量极大,为了避免数据迁移时间过长影响服务,Redis字典采用渐进式rehash方式

  • 触发条件:负载因子>=1(BGSAVE或BGREWRITE时>=5)时扩容,<=0.1时收缩
  • 数据结构,Redis字典包含两个哈希表(一个专门用于扩容时的迁移)和一个rehash标识(正在进行rehash时为0,平时为-1)
  • 扩容过程
  1. 为新的哈希表分配空间
  2. 修改rehash标识为0
  3. 每次进行增删改查时,顺带将旧哈希表上的数据迁移到新哈希表上(删改在两个表上,查找先查旧表再查新表,插入在新表)
  4. rehash结束后修改rehash标识为-1,指针指向新表
5. Redis五大数据结构与底层数据结构的对应关系
五大数据结构底层数据结构
String1. 能用long表示的字符用整数保存 2. 一般字符串用SDS存储 3. <39字节的字符串用embstr编码的SDS
Hash1. 一般用字典结构存储 2. 键值长度数量很小时用压缩列表存储
List快速列表:将列表按段切分,每段用压缩列表紧凑存储
Set1. 集合元素都是整数且数量<512则用整数集合 2. 一般用字典结构
ZSet1. 元素数量很少且大小很小时用压缩列表存储 2. 否则用跳表+哈希表存储
¥6. ZSet结构的底层实现

包含一个跳表和一个哈希表

  • 跳表按元素的分数对元素进行排序,维护有序性,便于排名范围查找等操作
  • 哈希表键为元素,值为分数,使得查找成员分数的时间复杂度为O(1)
  • 跳表和哈希表通过共用指针避免内存浪费
7. 一个字符串类型的值的存储最大容量

512M

三. 内存使用及键的过期

¥1. Redis是如何淘汰过期键的

Redis淘汰过期键包括惰性删除和定期删除两种策略

  • 惰性删除:取出键时发现键已过期则删除
  • 定期删除:每隔一段时间 Redis例行程序serCron随机检查一部分键,过期的删除
2. Redis淘汰过期键的底层原理

Redis内部维护一个expires过期字典,键保存键,值保存过期时间,有关于过期的操作实质都是在操作过期字典

¥3. Redis内存淘汰策略

Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。

全局的键空间选择性移除:

  • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
  • allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除使用频率最少的key。(这个是最常用的)

设置过期时间的键空间选择性移除

  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除(先删除马上要过期的键)。
  • volatile-lfu:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除使用频率最少的key。
4. Redis如何做内存优化

因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。

四. 持久化

¥1. Redis 的RDB持久化机制
  • RDB(缩写快照)是Redis默认的持久化方式。其将数据以快照的形式保存到硬盘中,产生对应的数据文件为dump.rdb。
  • 持久化可以手动执行也可自动执行,自动执行时通过配置文件中的save参数来定义快照的周期(默认900 1,300 10,60 10000),serCron发现规定时间里修改了那么多次,则会执行自动持久化。
  • 底层原理:
  1. SAVE:阻塞服务直至快照创建完毕
  2. BGSAVE(自动持久化执行的是该命令):fork一个子进程,Copy on Write地创建RDB,主服务不会被阻塞

1、RDB过程中会fork一个子进程,子进程做数据备份操作,主进程继续对外提供服务,所有Redis服务不会阻塞;
2、Copy On Write 机制,备份的是开始那个时刻内存中的数据;
3、Copy On Write 机制不需要把整个内存的数据都复制一份;
4、只有在父进程发生写操作修改内存数据时,才会真正去分配内存空间,并复制内存数据,而且也只是复制被修改的内存页中的数据,并不是全部内存数据;

¥2. Redis 的AOF持久化机制
  • AOF持久化是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据(重新执行写命令)。
  • 同步频率appendfsync:执行fsync的频率(同步过程由独立线程完成)

appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步

为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF
文件,Redis
性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis
还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。

  • AOF文件重写:在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作
¥3. RDB和AOF的比较
  • RDB数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失;AOF数据安全,可以配置 appendfsync 属性为 always,每进行一次 命令操作就记录到 aof 文件中一次
  • 数据集大的时候,RDB比AOF 的启动效率更高
  • 如果两个都配了优先加载AOF
¥4. 如何选择合适的持久化方式
  • 一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
  • 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。
  • 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。
  • 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

五. 线程模型

1. Redis的线程模型
  • Redis基于Reactor单线程模式开发了网络事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。
  • 文件事件处理器使用 I/O 多路复用程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
  • 当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
  • 虽然文件事件处理器以单线程方式运行, 但通过使用 I/O 多路复用程序来监听多个套接字, 文件事件处理器既实现了高性能的网络通信模型, 又可以很好地与 redis 服务器中其他同样以单线程方式运行的模块进行对接, 这保持了 Redis 内部单线程设计的简单性。
¥2. Redis是单线程的吗
  • 从主服务上讲,Redis是单线程的。因为Redis基于Reactor单线程模式,采用I/O 多路复用方式,即获取连接,处理连接,返回连接都在一个线程内完成
  • 从辅助服务上讲,Redis是多线程的。Redis除了主服务,还有其他线程辅助主服务,如持久化线程执进行持久化
  • 此外,Redis 在 4.0 之后的版本中就已经加入了对多线程的支持,主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主处理之外的其他线程来“异步处理”。
2.5 Redis6.0 之后为何引入了多线程?

Redis6.0 引入多线程主要是为了提高网络 IO 读写性能,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈主要受限于内存和网络)。

虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了, 执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。

Redis6.0 的多线程默认是禁用的,只使用主线程。如需开启需要修改 redis 配置文件 redis.conf :

io-threads-do-reads yes

开启多线程后,还需要设置线程数,否则是不生效的。同样需要修改 redis 配置文件 redis.conf :

io-threads 4
3. Redis时间事件

serCron函数负责执行一些需要定期处理的时间事件,默认周期是100ms一次,但有可能会因为正在处理文件事件而延迟

时间事件包括:清理过期键,尝试持久化,集群同步等等

4. Redis事件调度规则

Redis事件调度以以下方式进行循环:

  1. Redis等待文件事件的产生(如果超过最长等待时间,则先去看看有没有到达的时间事件)
  2. 处理已产生的文件事件
  3. 处理已到达的时间事件

处理时间事件时会尽量减小程序阻塞时间,必要时会让出主动权,耗时很长的会放到子线程中去执行

六. Redis事务

¥1. Redis事务的概念

Redis 事务的本质是通过MULTI、EXEC、WATCH等一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中

¥2. Redis事务的三个阶段
  1. 事务开始 MULTI
  2. 命令入队
  3. 事务执行 EXEC

事务执行过程中,如果服务端收到有EXEC、DISCARD、WATCH、MULTI之外的请求,将会把请求放入队列中排队

¥3. Redis事务相关命令

Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的

  • WATCH 命令是一个乐观锁,可以监控一个或多个键,事务执行前一旦其中有一个键被修改(或删除),则整个事务取消。
  • MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行
  • EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil
  • 通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。
¥4. Redis事务的回滚
  1. redis 不支持回滚,“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。
  2. 如果在一个事务中的命令出现错误(入队过程中,还没开始执行),那么所有的命令都不会执行;
  3. 如果在一个事务中出现运行错误,其他命令依旧会继续执行。
¥5. Redis事务的ACID特性
  • 原子性:Redis事务不支持回滚,因此不保证原子性
  • 一致性:支持
  • 隔离性:Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的
  • 持久性:在appendfsync选项的值为always时,事务具有持久性

七. 缓存异常

¥1. 缓存雪崩及其解决方案

缓存雪崩是指缓存同一时间大面积的失效,造成数据库短时间内承受大量请求而崩掉。

解决方案:

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  2. 一般并发量不是特别多的时候,可以加锁排队
  3. 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
  4. 设置二级缓存
¥2. 缓存穿透及其解决方案

缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案:

  1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;或加密处理,请求参数解密失败则直接返回
  2. 缓存空值:从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
  3. 布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
3. 缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库

解决方案

  1. 设置热点数据永远不过期。
  2. 加互斥锁,互斥锁
  3. 缓存预热
4. 缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

5. 缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

对于一些不重要的缓存数据进行降级处理,如Redis出现问题,不去数据库查询,而是直接返回默认值给用户

6. 缓存热点问题

在服务端读数据进行访问时,往往会对数据进行分片切分,此过程中会在某一主机 Server 上对相应的 Key 进行访问,当访问超过 Server 极限时,就会导致热点 Key 问题的产生。

危害:

  1. 流量集中,达到物理网卡上限。
  2. 请求过多,缓存分片服务被打垮。
  3. DB 击穿,引起业务雪崩。

解决方案:

  1. 给热点key加随机值,将其分散到不同的节点上
  2. 对热点节点做读写分离,分配多个只读节点
    (以上方法需要提前知道热点key)

具体拓展:基于流式计算技术的缓存热点自动发现+热点缓存自动加载为JVM本地缓存
流式计算系统发现某个热点请求过多,将该热点放入Zookeeper中,应用服务器对Zookeeper进行监听,发现Znode发生变化,系统层就可以立马把相关的缓存数据从数据库加载出来,然后直接放在自己系统内部的本地缓存即可

八. Redis实践及应用

¥1. Redis分布式锁
  • Redis可以使用SETNX命令实现分布式锁
  • SETNX 是『SET if Not exists』(如果不存在,则 SET)的简写;当且仅当 key 不存在,将 key 的值设为 value。 若给定的 key 已经存在,则 SETNX 不做任何动作
  • 注意:为了防止获取锁后程序出现异常,导致其他线程/进程调用SETNX命令总是返回0而进入死锁状态,需要为该key设置一个“合理”的过期时间(Redis支持setnx和expire指令一起执行)
  • 解决方案:可将value值设为过期时间,其他线程获取锁时如果发现以过期,则获取成功。释放时需判断值是否与获取时设置的value一致,只有一致才能释放
2. 集群模式下Redis分布式锁存在的问题及解决方案

主节点申请锁后未同步至从节点就挂了,从节点成为主节点后没有原来的锁。该问题发生的几率很小,一般可以容忍

解决方案:
Redlock:加锁时向多个节点发送setnx指令,过半节点成功才成功;因此Redlock的容错性也更好

¥3. 使用Redis做过异步队列吗,是如何实现的
  • 使用list类型保存数据信息,rpush生产消息,lpop消费消息,当lpop没有消息时,可以sleep一段时间,然后再检查有没有信息
  • 如果不想sleep的话,可以使用blpop, 在没有信息的时候,会一直阻塞,直到信息的到来。
  • redis可以通过pub/sub主题订阅模式实现一个生产者,多个消费者,当然也存在一定的缺点,当消费者下线时,生产的消息会丢失。
4. Redis如何实现延时队列

使用Zset,使用时间戳做score, 消息内容作为key,调用zadd来生产消息,消费者使用zrangbyscore获取n秒之前的数据做轮询处理。

5. Redis位图的应用

适合处理boolean类型数据,如签到表,可大大节省空间

¥6. 布隆过滤器

原理就是大型位图+几个无偏hash函数

7. 限流

Redis-Cell模块可提供分布式的漏斗限流

8. GEO

Redis提供GEO模块,可实现地理位置定位。实现如附近的人等功能

9. Scan

查找符合条件的Key

10. 假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?
  • 使用keys指令可以扫出指定模式的key列表。
  • 但redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。
  • 这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。
11. Redis如何做大量数据插入
  • Redis2.6开始redis-cli支持一种新的被称之为pipe mode的新模式用于执行大量数据插入工作。
  • pipeline通过在客户端调整I/O读写顺序,一次执行多条指令,大幅节省IO时间(写读写读——>写写读读)

九. 分布式方案

¥1. Redis主从架构
  • 单机的 redis,能够承载的 QPS 大概就在上万到几万不等。对于缓存来说,一般都是用来支撑读高并发的。
  • 因此架构做成主从(master-slave)架构一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发。
  • 此外,如果主节点宕机,从节点可以进行替代
  • 因此,通过主从架构,可以实现Redis的高并发和高可用
¥2. Redis主从复制原理

过程原理(前4步是初次同步过程,第5步是建立完成后持续的同步过程):

  1. 当从库和主库建立MS关系后,会向主数据库发送SYNC命令
  2. 主库接收到SYNC命令后会开始在后台保存快照(RDB持久化过程),并将期间接收到的写命令缓存起来
  3. 当快照完成后,主Redis会将快照文件和所有缓存的写命令发送给从Redis
  4. 从Redis接收到后,会载入快照文件并且执行收到的缓存的命令
  5. 之后,主Redis每当接收到写命令时就会将命令发送从Redis,从而保证数据的一致
3. Redis增量复制
  • slave node 如果跟 master node 有网络故障,断开了连接,会自动重连,重连后如果发现连接断开期间的写命令还存在于缓冲区中,那么仅复制连接断开期间的命令(通过偏移量检查实现)
  • 否则还是要执行RDB的全量复制
¥4. Redis哨兵模式(sentinel)

Redis哨兵模式及通过哨兵程序对Redis的主从节点进行监控,其主要功能有:

  1. 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
  2. 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  3. 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
  4. 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

哨兵的缺点:

  • 哨兵需要消耗额外的资源
  • 哨兵至少需要 3 个实例,来保证自己的健壮性。否则单点哨兵发生故障将导致整个集群不可用
¥5. Redis集群架构(Redis Cluster)

Redis Cluster是一种无中心的集群结构,每个主节点保存的数据不同,每个主节点有各自的从节点

  • Redis Cluster采用哈希槽的概念对数据进行分片,默认分配了16384 个槽位每个节点均分存储一定哈希槽(哈希值)区间的数据,每份数据分片会存储在多个互为主从的多节点上

每个节点会记录哪个哈希槽属于哪个节点
每个key通过CRC16校验后对16384取模来决定放置哪个槽

  • 数据写入时:先写主节点,再同步到从节点(支持配置为阻塞同步),因此同一分片多个节点间的数据不保持一致性
  • 读取数据时:客户端随机地请求任意一个redis节点,当客key没有分配在该节点上时,redis会返回转向指令(MOVED),指向正确的节点
  • 扩容时时需要需要把旧节点的数据迁移一部分到新节点

redis cluster 节点间采用 gossip 协议进行通信。

¥6. 什么情况下整个Redis集群不可用
  • 某个主节点宕机且其没有从节点
  • 半数以上的主节点宕机
7. 如何判断集群中的某个节点发生故障
  • 如果集群中半数以上的节点均连接不上某个节点,则认为该节点发生故障
  • 然后会通过选举的方式从其从节点中选一个进行替换
8. Redis分区的实现方案
  • 客户端分区就是在客户端就已经决定数据会被存储到哪个redis节点或者从哪个redis节点读取。大多数客户端已经实现了客户端分区。
  • 代理分区 意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些Redis实例,然后根据Redis的响应结果返回给客户端。redis和memcached的一种代理实现就是Twemproxy
  • 查询路由(Query routing) 的意思是客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点。
9. 分布式Redis是前期做还是后期规模上来了再做好?为什么?
  • 既然Redis是如此的轻量(单实例只使用1M内存),为防止以后的扩容,最好的办法就是一开始就启动较多实例。即便你只有一台服务器,你也可以一开始就让Redis以分布式的方式运行,使用分区,在同一台服务器上启动多个实例。
  • 一开始就多设置几个Redis实例,例如32或者64个实例,对大多数用户来说这操作起来可能比较麻烦,但是从长久来看做这点牺牲是值得的。
  • 这样的话,当你的数据不断增长,需要更多的Redis服务器时,你需要做的就是仅仅将Redis实例从一台服务迁移到另外一台服务器而已(而不用考虑重新分区的问题)。一旦你添加了另一台服务器,你需要将你一半的Redis实例从第一台机器迁移到第二台机器。
10. 集群方案下Redis常见性能问题和解决方案?
  1. Master最好不要做任何持久化工作(?有的说Master必须做持久化),包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。
  2. 如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
  3. 为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。
  4. 尽量避免在压力较大的主库上增加从库
  5. Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
  6. 为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,即主从关系为:Master<–Slave1<–Slave2<–Slave3…,这样的结构也方便解决单点故障问题,实现Slave对Master的替换,也即,如果Master挂了,可以立马启用Slave1做Master,其他不变。
11. Redis各集群方案优缺点
1. 主从模式

优点:

  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离
  • Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力
  • Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
  • Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据

缺点:

  1. Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
  2. 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
  3. Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
2. 哨兵模式

优点:

  1. 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
  2. 主从可以自动切换,系统更健壮,可用性更高。

缺点:

  1. Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
  2. 哨兵额外的性能消耗
  3. 若哨兵故障,则整个系统故障,需要对哨兵作集群保证可用性
3. 集群模式

优点:

  1. 无中心架构;数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布;
  2. 可扩展性:可线性扩展到 1000 多个节点,节点可动态添加或删除;
  3. 高可用性:部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副本,能够实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave 到 Master 的角色提升;
  4. 降低运维成本,提高系统的扩展性和可用性。

缺点:

  1. Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性。目前仅 JedisCluster 相对成熟,异常处理部分还不完善,比如常见的“max redirect exception”。
  2. 节点会因为某些原因发生阻塞(阻塞时间大于 clutser-node-timeout),被判断下线,这种 failover 是没有必要的。
  3. 数据通过异步复制,不保证数据的强一致性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值