1、redis事务本质是什么?事务命令有哪些?事务机制是怎样的?

1.1Redis事务是基于队列实现的,创建一个事务队列,然后将事务操作都放入队列中,最后依次执行。(Redis是弱事务,也称伪事务)
Redis开启事务命令:**multi**
Redis执行事务命令:**exec**
Redis关闭事务命令:**discard**
1.2Redis事务处理机制有两种
在语法错误情况下:(执行命令的语法错误)
会直接返回错误,正确的命令也不执行。
在执行错误情况下:(命令在运行过程中出现错误)
只会错误的命令不执行,而正确的命令仍然能够正常操作。
1.3.springboot实现事务的操作

  1. //开启redis事务控制
  2. redisTemplate.setEnableTransactionSupport(true);

2、持久化方案,原理,有什么问题

方案一:RDB

基于快照思想,当符合一定条件时,Redis会将这一刻的内存数据进行快照并保存在磁盘上,产生一个经过压缩的二进制文件,后缀名为.rdb。
1.触发条件*

save 3600 1 #表示1小时内至少1个键被更改则进行快照。
save 300 100 #表示5分钟(300秒)内至少100个键被更改则进行快照。
save 60 10000 #表示1分钟内至少10000个键被更改则进行快照。

2.手动执行命令
save:同步处理,阻塞redis服务进程,服务器不会处理任何命令,直到RDB文件保存完毕。
bgsave:异步子线程执行,不会阻塞redis服务进程,操作RDB文件的同时仍然可以处理命令。
3.RDB原理*
RDB执行流程.png
1)Redis服务进程判断,当前是否有子线程在执行save或bgsave的命令;
2)如果有,则直接返回,不做处理;
3)如果没有,则以阻塞式创建线程,在创建子线程期间,Redis不处理任何命令;
4)创建子线程后,取消阻塞,Redis服务继续响应其他命令;
5)同时基于子线程操作RDB文件,将此刻数据保存到磁盘。
4.存在的问题
1)无法保证数据完整性,会丢失最后一次快照后的所有数据;
2)bgsave命令每次执行都会阻塞Redis服务进程创建子线程,频繁执行影响系统吞吐率。

方案二:AOF

当开启AOF持久化后,Redis会将客户端发送的所有更改数据的命令(即写命令),记录到磁盘中的AOF文件。(记录的是增删改命令

  1. # 是否开启AOF,默认为no
  2. appendonly yes

1.执行原理
AOF执行原理.png
1)客户端向Redis发送写命令;
2)Redis将接收到的写命令保存到缓冲文件aof_buf的末尾;这个过程是命令追加
3)Redis将缓冲区文件内容写入到AOF文件;这个过程是文件写入
4)Redis根据策略将AOF文件保存到磁盘;这个过程是文件同步
2.触发条件*
always:每次执行写入命令都会将aof_buf缓冲区文件全部内容写入到AOF文件中,并将AOF文件同步到磁盘。该方式效率最低,安全性最高。即每次都会执行
everysec:每次执行写入命令都会将aof_buf缓冲区文件全部内容写入到AOF文件中。 并且每隔一秒会由子线程将AOF文件同步到磁盘。该方式兼备了效率与安全,即使出现宕机重启,也只会丢失不超过两秒的数据。系统默认
no:每次执行写入命令都会将aof_buf缓冲区文件全部内容写入到AOF文件中,但并不对AOF文件进行同步磁盘。 同步操作交由操作系统完成(默认是每30秒一次),该方式最快,但最不安全。交由系统去执行
比较:
AOF同步策略比较.png
3.AOF优化*
存在的问题:随着保存的内容越来越多,会占用大量存储空间,数据还原花费的时间越来越多。
解决:提供AOF文件重写功能
1)当前aof文件大小超过上一次aof文件大小的百分之多少时会进行重写。如果之前没有重写过,以
启动时aof文件大小为准,默认为100
2)限制允许重写最小aof文件大小,也就是文件大小小于64MB的时候,不需要进行优化

3、两种持久化机制对比

1)RDB默认开启,AOF需手动开启;
2)RDB性能优于AOF;
3)AOF安全性优于RDB;
4)AOF优先级高于RDB(在两种模式都开启的情况下,RDB是用来兜底的);
5)RDB存储某个时刻的数据快照,AOF存储写命令;
6)RDB在配置触发状态会丢失最后一次快照以后更改的所有数据,AOF默认使用everysec,每秒保存一次,最多丢失两秒以内的数据;

4、高可用-主从复制的原理

1).原理:
1)Slave服务启动,主动连接Master,并发送SYNC命令,请求初始化同步;
2)Master收到SYNC后,执行bgsave命令生成RDB文件,并缓存该时间段内的写命令;
3)Master完成RDB文件后,将其发送给所有Slave服务器;
4)Slave服务器接收到RDB文件后,删除内存中旧的缓存数据,并装载RDB文件;
5)Master在发送RDB后,即刻向所有Slave服务器发送缓存中的写命令。
2).作用:
读写分离:主写从读;注意:从服务器不负责写,只负责读
负载均衡:由slave分担master负载,并根据需求变化,改变slave的数量,通过多个从节点分担数据读取负载,提高redis服务器的并发量和数据吞吐量;
故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复;
数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式;
高可用基石:基于主从复制,构建哨兵模式与集群,实现redis的高可用。
高可用概念:高可用就是单位时间的可用性,(5个9)行业的追求,5.256;*

5、主从存在问题

当master发生故障后,会直接影响从库的数据同步操作,并且当客户端发送写请求需要master提供服务,此时写服务中断,此时就需要一个新的主库。怎么选择主库?需要考虑三个问题?1.主库真的挂了吗?2.该选择哪个从库作为主库?3.怎么把新主库的相关信息通知给从库和客户端?

6、哨兵模式的原理是什么?

哨兵也是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master。
注意点:
1)哨兵也是一台服务器,只是不提供数据服务;
2)通常哨兵配置数量为单数;
3)如果配置启用单节点哨兵,如果有哨兵实例在运行时发生了故障,主从库无法正常切换,所以需要哨兵集群。
sentinel工作任务:
1)监控
sentinel会不断的检查主服务器和从服务器是否正常运行;
2)通知
当被监控的某个redis服务器出现问题时,sentinel可以通过API向管理员或其他应用程序发送通知
3)自动故障迁移
当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器
当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器
主观下线和客观下线
1)主观下线:哨兵进程会使用 PING -PONG通信机制检测它自己和主、从库的网络连接情况,用来判断实例的状态。如果哨兵发现主库或从库对 PING 命令的响应超时了,那么,哨兵就会先把它标记为“主观下线”。存在误判的情况
2)客观下线:指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。客观下线条件只适用于主服务器
3)仲裁qurum:当拥有认为主观下线的哨兵达到sentinel monitor所配置的数量时,就会发起一次投票,进行failover。这个【足够数量】就是配置文件里面的值,一般是Sentinel个数的一半加1,比如3个Sentinel则就设置为2

哨兵模式工作原理:

  • 哨兵集群中的多个实例共同判断,可以降低对主库下线误判率**

    • 哨兵集群组成: 基于 pub/sub 机制
      • 哨兵间发现:
        • 主库频道“sentinel:hello”,不同哨兵通过它相互发现,实现互相通信
      • 哨兵发现从库
        • 向主库发送 INFO 命令
    • 基于 pub/sub 机制的客户端事件通知
      • 事件:主库下线事件
        • +sdown : 实例进入“主观下线”状态
        • -sdown : 实例退出“主观下线”状态
        • +odown : 实例进入“客观下线”状态
        • -odown : 实例退出“客观下线”状态
      • 事件:从库重新配置事件
        • +slave-reconf-sent : 哨兵发送 SLAVEOF 命令重新配置从库
        • +slave-reconf-inprog : 从库配置了新主库,但尚未进行同步
        • +slave-reconf-done : 从库配置了新主库,且完成同步
      • 事件:新主库切换
        • +switch-master : 主库地址发生变化
    • 由哪个哨兵执行主从切换?例如,现在有 5 个哨兵,quorum 配置的是 3,那么,一个哨兵需要 3 张赞成票,就可以标记主库为“客观下线”了。这 3 张赞成票包括哨兵自己的一张赞成票和另外两个哨兵的赞成票。
      • 一个哨兵获得了仲裁所需的赞成票数后,就可以标记主库为“客观下线”
        • 所需的赞成票数 <= quorum 配置项
      • “Leader 选举”
        • 两个条件:
          • 拿到半数以上的赞成票
          • 拿到的票数>= quorum
        • 如果未选出,则集群会等待一段时间(哨兵故障转移超时时间的 2 倍),再重新选举
    • 经验:要保证所有哨兵实例的配置是一致
      • 尤其是主观下线的判断值 down-after-milliseconds

        7、经典主从集群解决了什么问题,还存在什么问题

        解决的问题:
        1)主从集群间可以实现自动切换,可用性更高;
        2)数据更大限度的防止丢失;
        3)解决哨兵的集群高可用问题,减少误判率;
        还存在的问题:
        主节点的写能力和存储能力受限。

        8、高可扩-分片集群的原理

        关键词: 槽、无中心结构、CRC16算法%16384=槽、高可用、各个主节点投票
  • 数据切片和实例的对应分布关系

    • Redis Cluster 方案:无中心化
      • 采用哈希槽(Hash Slot)来处理数据和实例之间的映射关系
      • 一个切片集群共有 16384 个哈希槽,只给Master分配**
      • 具体的映射过程
        1. 根据键值对的 key,按照CRC16 算法计算一个 16 bit 的值(Hash值);
        2. 再用这个 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽
    • 哈希槽映射到具体的 Redis 实例上注意:需要把 16384 个槽都分配完否则 Redis 集群无法正常工作
      • 用 cluster create 命令创建集群,Redis 会自动把这些槽平均分布在集群实例上
      • 也可以使用 cluster meet 命令手动建立实例间的连接,形成集群,再使用 cluster addslots 命令,指定每个实例上的哈希槽个数
  • 客户端如何定位数据
    • Redis 实例会把自己的哈希槽信息发给和它相连接的其它实例,来完成哈希槽分配信息的扩散
    • 客户端和集群实例建立连接后,实例就会把哈希槽的分配信息发给客户端
    • 客户端会把哈希槽信息缓存在本地。当请求键值对时,会先计算键所对应的哈希槽
    • 但集群中,实例和哈希槽的对应关系并不是一成不变的
      • 实例新增或删除
      • 负载均衡
    • 实例之间可以通过相互传递消息,获得最新的哈希槽分配信息,但客户端是无法主动感知这些变化
  • 重定向机制
    • 如果实例上没有该键值对映射的哈希槽,就会返回 MOVED 命令
    • 客户端会更新本地缓存
    • 迁移部分完成情况下,返回ASK
    • 表明 Slot 数据还在迁移中
    • ASK 命令把客户端所请求数据的最新实例地址返回给客户端
    • 并不会更新客户端缓存的哈希槽分配信息

搭建redis分片集群,一共需要几台服务器,为什么?
6台,一个主节点对应一个从节点,各个主节点的投票机制决定了主节点只能选择奇数,所以光主节点就需要3台服务器。若要保证redis的高可用6台服务器是最低的配置。

9、异常测试*

  • 异常关闭主节点,查看集群节点变化和日志
  • 结论:对应的从节点升级为主节点
  • 恢复主节点,查看集群节点变化
  • 结论:会添加到最新的主节点上,成为从节点
  • 异常关闭从节点,查看集群状态
  • 结论:不影响集群使用,但是可能会存在单点故障问题
  • 同时关闭节点的主和从,本案例中的7001(主) —— 7006(从),get取值,观察
  • 结论:导致整个Redis集群不可用

    10、如何实现redis动态的扩缩容

    扩容先添加主节点,记录主节点的ID—>给新添加的主节点分配哈希槽—>添加从节点
    缩容:先删除从节点—>被删除的主节点哈希槽迁移到其他主节点(哈希槽一定要全部迁移走,否则造成分片集群的瘫痪)—>删除主节点

    11、redis常用的5种数据类型的应用场景*(在面试中一定要结合项目来讲)

数据类型 应用场景 注意点

String结构 | 1.验证码
2.计数器、发号器
3.订单重复提交令牌
4.热点商品卡片(序列化JSON对象存储)
5.分布式锁 |

1.值的长度不能超过512MB
2.key的命名规范,不要过长,冒号分割,比如:业务名:表名:ID | |

List结构 |

1.简单队列
2.最新评论列表
3.非实时排行榜;定时计算器,如手机日销榜单 | 1.通常添加一个元素到列表的头部(左边)或者尾部(右边);
2.存储的都是string字符串类型;
3.支持分页操作,高并发项目中,第一页数据都是来源list,第二页和更多信息则是通过数据库加载;
一个列表最多可以包含232-1个元素(4294967295,每个列表不超过40亿个元素) | |

Hash结构 | 1.购物车
2.用户个人信息
3.商品详情 |

每个hash可以存储40多亿键值对 | |

Set结构 | 1.去重;
2.社交应用(关注、粉丝、共同好友);
3.统计网站的PV、UV、IP,大数据里面的用户画像标签集合 |

集合是通过哈希表实现的 | |

Sorted Set结构 | 1.实时排行榜:商品热销榜、体育类热门球队、积分榜
2.优先级任务、队列
3.朋友圈 文章点赞-取消点赞:用户只能点赞或取消,统计一篇文章被点赞了多少次,可以直接取里面多少个成员 |

1.底层使用到了ziplist压缩列表和“跳跃表”两种存键结构;
2.如果重复添加相同的数据,score值将被反复覆盖,保留最后一次修改的结果 |

12.redis的key过期会直接删除吗?有哪些策略可以选择?

不是直接删除的。redis对于过期的key提供了三种删除策略:

key的过期删除策略 原理 优缺点


定时删除
它会在设置键的过期时间的同时,创建一个定时器,当键到了过期时间,定时器会立即对key进行删除 该策略对内存空间足够友好,但对CPU不友好,会拉低系统性能,不建议使用该策略。

惰性删除 | 它不关注key的过期时间,而是在获取key时,才会检查key是否过期,如果过期则删除该key。 | 对CPU足够友好了,但是对内存空间非常不友好,会造成大量内存空间的浪费 | |

定期删除 | 1.默认每秒运行10次对具有过期时间的key进行一次扫描,但是并不会扫描全部的key,因为大大延长扫描时间;
2.每次默认只会随机扫描20个key,同时删除这20个key中,已经过期的key;
3.如果这20个key中过期key的比例超过25%,则将过期的key删除,没超过则继续扫描 |

扫描次数可以在配置文件配置,但是不要超过100。
这种方案是定时删除和懒惰删除的一个折中方案 |

结论:redis 定期+惰性删除 配合使用来解决内存中过期key问题

13.redis为什么要设置内存淘汰策略?它有哪些内存淘汰策略?

redis的key过期淘汰策略还存在的问题:
有一些已经过期的key,定期扫描一直都没有扫描到它,而且这些key也一直没有被使用,那么他们就会一直在内存中存在。同时继续向Redis不断插入新数据,最终造成内存空间不足的问题。

解决方案:使用内存淘汰策略:
redis的内存淘汰策略.png

内存淘汰策略 实现方式

对设置了过期时间数据淘汰 | 1.volatile-lru:删除设置了过期时间,且最近最久未被使用的key;
2.volatile-lfu:删除设置了过期时间,且最近最少未被使用的key;
3.volatile-ttl:删除设置了过期时间,且即将过期的key;
4.volatile-random:从删除设置了过期时间的key中随机删除; | | |

对所有数据淘汰 | 1.allkeys-lru:从所有key中,删除最近最久未被使用的key;
2.allkeys-lfu:从所有key中,删除最近最少未被使用的key:
3.allkeys-random:从所有key中随机删除。 | | | 不淘汰 | 当内存空间不足时,直接返回错误信息。 | |

tips:对于LRU和TTL相关策略,每次触发时,redis默认从5个key中一个key符合条件的key进行删除,如果要修改的话,可以修改redis.conf中maxmemory-samples属性值。

14.请讲一下Redis6.X的新特性。

  1. 支持多线程

redis6.X的多线程只是用来处理网络数据的读写和协议解析上的,底层数据操作还是单线程。
开启多线程后,是否存在线程并发安全问题?不会有线程安全问题,Redis的多线程部分只是用来处理网络数据的读写和解析,执行命令仍然是单线程顺序执行的。

  1. 引入了ACL(Access Control List 权限控制)

1)可以给每个用户分配不同的权限;
2)通过限制命令和密钥的访问来提高安全性,阻止不受信任的客户端的访问;
3)防止由于软件错误或人为错误而导致不受信任的请求来访问redis,从而损坏数据或配置。
~*:表示可以访问的key(正则匹配)
+@:表示用户的权限,“+”表示授权,“-”表示没有权限,“@ ”表示给权限分类。

  1. client side caching(客户端缓存)

a.默认模式
Server端全局唯一的表(Invalidation Table)记录每个Client访问的key,当发生变更时,向client推送数据过期消息。
b.广播模式
客户端订阅key前缀的广播,服务端记录key前缀与client的对应关系。当相匹配的key发生变化时通知client。

15.redis的缓存穿透

现象:
已查询红包记录为例:王五没有抢到红包,但是用户恶意频繁去查询抢红包记录,此时Redis缓存中一直都没有数据,因此每次都会去查询数据库。
解决方案:
a.布隆过滤器
原理:在其内部维护一个全为0的bit数组,这里有一个误判率的概念(误判率越低,数组越长,占用的空间也会越大;误判率越高数组越短,占用空间越小);根据误判率,我们生成一个10位的bit数组,以及2个hash函数(f_1,f_2),假设输入集合为(N_1,N_2),经过计算f_1(n_1)得到的数值为2,f_2(n_2)的数值为5,则将下标为2和下标为5的位置置为1;同理,经过计算f_1(n_2)得到的数值为3,f_2(n_1)的数值为6,则将下标为3和下标为6的位置置为1,这时进来第三个数N_3,我们判断N_3是否在集合(N_1,N_2)中,不在就进行上述的计算:1.若值刚好都在上述位置中,则认为N_3在集合(N_1,N_2)中;2.若值有一个不在上述位置中,则认为N_3不在集合(N_1,N_2)中。

16.redis的缓存击穿

现象:
缓存在某个时间点过期,恰好在这个时间点对这个key有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,但是大并发请求可能会瞬间把后端DB压垮(宕机)。
解决方案:
a.多级缓存
首先通过程序将缓存存入到Redis中,且永不过期,用户查询的时候,先查询nignx缓存,如果nignx缓存没有,则查询Redis缓存,并将Redis缓存存入到nignx一级缓存中,并设置更新时间。
b.分布式锁
1)引入redisson依赖包;2)定义锁的操作方法(获取锁的方法/解锁的方法);3)创建链接Redis集群的配置文件,在里面做相关配置;4)RedissonClient,实现锁的操作->(获取锁和解锁);5)创建工厂对象Redisson的工厂

17.redis的缓存雪崩

现象:
由于缓存层承载着大量请求,有效的保护了存储层,但是如果缓存层出现故障不能提供服务,于是所有的请求都会达到存储层,存储层的调用量会保证,造成存储层也会挂掉的情况。
解决方案:(同缓存击穿一样)
a.多级缓存
b.分布式锁

redis 建议存在数据: 热点、数据量不大数据 ,建议100M cpu 线程阻塞

穿透是redis和mysql都没有数据,击穿是redis没有数据,mysql有数据。雪崩:redis出现故障,大量请求直接访问mysql