redis难点

Key的过期策略

  1. 定时删除 timer 让定时器在键的过期时间来临时,立即执行对键的删除操作
  2. 惰性删除 在取出键时才对键进行过期检查,如果发现过期了就会被删除
  3. 主动定期删除 是定时删除策略和惰性删除策略的一个折中

Redis为什么快

  • 内存存储
  • 单线程实现(Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销)
  • 非阻塞IO
  • 简单的数据结构(Redis有诸多可以直接应用的优化数据结构的实现,应用层可以直接使用原生的数据结构提升性能)
  • 文件事件处理器
    在这里插入图片描述

Redis 核心数据结构都有哪些?String类型都有哪些特点?

  • Redis主要支持五种数据类型:
    • string (Binary-safe二进制安全)可以包含任何数据(jpg图片或者序列化的对象)(SET stringkey value)

    • hash(字段和值都是字符串)(hmset hashkey filedkey1 filedvalue1 filedkey2 filedvalue2 filedkey3 filedvalue3 。。。)

    • list(linked lists根据插入顺序排序的字符串元素的集合 value可重复)(LPUSH listkey value)

    • set(collections of unique未排序的字符串元素的集合)(SADD setkey value)

    • sorted set(collections of unique zset:有序集合 每个字符串元素都与一个称为score的浮点值相关联可检索一系列元素 前10名或后10名)(ZADD zsetkey score value)(查询在线用户(按过期时间排序))

      • zset底层存储结构
        • zset 由两个结构ziplist skiplist组成,在同时满足以下两个条件的时候使用ziplist,其他时候使用skiplist
          • 有序集合保存的元素数量小于128个
          • 有序集合保存的所有元素的长度小于64字节
        • 当ziplist作为zset的底层存储结构时候,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值
        • 当skiplist作为zset的底层存储结构的时候,使用skiplist按序保存元素及分值,使用dict(key/value,key为元素,value为分值)来保存元素和分值的映射关系
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
    • Bit arrays (or simply bitmaps)可以使用特殊命令像位数组一样处理字符串值:您可以设置和清除单个位,计数所有设置为1的位,找到第一个设置或未设置的位(仅使用512 MB内存就可以记住40亿用户的一位信息)

Redis值类型中 hash 和 string 又什么区别呢?

  • 常用于存储一个对象(提供了直接存储map数据的接口)
  • 用string存缺点
    • value值需要开销(序列化/反序列化)
    • 修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题

Redis 事务

  • Redis命令在事务执行时可能会失败,但仍会继续执行剩余命令而不是Rollback
  • 命令进入了一个队列 只有EXEC被调用时,排队的命令才会被执行 调用DISCARD可以清除事务中的commands队列并退出事务
  • 事务执行过程中如果不出现语法上的错误(入队列并没有执行只能检测出语法错误),事务中的一个操作执行失败而另一个操作是可以执行成功的
  • 基本命令使用
    • multi 开启事务(不能嵌套使用 会报错)
    • 命令入列(返回 QUEUED字符串)
    • exec 提交事务 discard 放弃事务(清空队列)
    • watch(乐观锁 CAS 在事务的过程中,某个监控项被修改了,那么整个事务就会终止执行)
Jedis jedis = new Jedis("localhost");
Transaction transaction = jedis.multi();
transaction.lpush("key", "11");
transaction.lpush("key", "22");  
transaction.lpush("key", "33");
List<Object> list = transaction.exec();

Redis 持久化是如何工作的?Redis在持久化过程中和可以对外服务么?如果可以的话,Redis又是如何处理新命令的?
可以对外服务 父进程处理新命令
SAVE 直接调用rdbSave,阻塞Redis主进程看,直到保存完成为止.在主进程阻塞期间,服务器不能处理客户端的任何请求
不管是使用哪种持久化,RDB持久化或AOF重写,主进程都会fork出一个子进程,在子进程里完成rdb文件的生成或aof的重写。fork操作对于操作系统来说属于比较重的操作。fork阶段,redis会阻塞一段时间(毫秒级)

  • RDB

    • redis根据配置检查是否需要生成rdb快照文件
    • 如果需要快照生成,就fork一个子进程出来
    • 子进程将数据dump到临时的rdb快照文件中,也就是dump.rdb
    • 完成rdb快照文件的生成之后,会替换掉之前的老快照
    • conf文件配置(save设置多个,就会有多个快照检查点,每到一个检查点,就会check一下 生成文件)
      • #   save ""
        save 900 1  //900s内有一个更改
        save 300 10
        save 60 10000
        
  • AOF

    • AOF的持久化默认是关闭的 RDB的持久化默认是打开的(conf文件中配置打开)
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hH4lVPnH-1600689816295)(C:\Users\90766\Desktop\1599551566(1)].jpg)

    • redis每接收到一条写命令,就会以追加的形式写入日志文件中,当然是先写入os cache,然后每隔一定时间再fsync一下(conf文件中配置AOF的fsync策略 建议(默认)策略是fsync每秒执行一次)
      mysql #每次写入一条数据执行一次fsync,非常非常慢,非常安全 # appendfsync always #每隔一秒执行一次fsync,这个是最常用(在2.4中可能与快照速度一样快)如果发生灾难,您可能会丢失1秒的数据 appendfsync everysec #从不fsync,只需将数据交给操作系统即可。更快,更不安全的方法。通常,Linux使用此配置每30秒刷新一次数据,但这取决于内核的精确调整 # appendfsync no

    • rewrite策略 AOF会自动在后台每隔一定时间做rewrite操作(重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合(如日志里已经存放了针对100w数据的写日志;redis删掉了90w数据,还剩10w数据;这样redis会基于内存中当前的10w数据构建一套最新的日志,覆盖之前的老日志,确保AOF日志不会过大,保证和redis内存数据量一致))
      mysql #当前的aof文件超过上一次重写后aof文件的1倍触发rewite auto-aof-rewrite-percentage 100 #当前的aof文件最小size auto-aof-rewrite-min-size 64mb

      ```
      * rewrite过程
      	* 当redis检测到需要rewrite的时候,redis fork一个子进程
      	* 子进程开始往一个新的临时的AOF文件中写入日志
      	* 主进程在内存中写入日志,同时新的日志也继续写入旧的AOF文件中
      	* 子进程写完之后,主进程将内存中的新日志再次追加到新的AOF文件中
      	* 用新的日志文件替换掉旧的日志文件(这个过程中如果发生宕机等意外会导致AOF文件损坏 用redis-check-aof工具对比文件可以轻松修复它)
      ```
      
  • 同时有RDB文件和AOF日志,redis重启的时,会优先使用AOF进行数据恢复,因其日志更完整

  • Redis> = 2.4可以确保避免在RDB快照操作正在进行时触发AOF重写,或者避免在AOF重写正在进行时允许BGSAVE

  • AOF和RDB怎么选择

    • 注重数据安全性 应同时使用两种持久性方法
    • 在发生灾难的情况下仍然可以承受几分钟的数据丢失,则只需单独使用RDB
    • 单独使用AOF,不建议 因为不时地使用RDB快照对于进行数据库备份、加快重启速度以及在AOF引擎出现错误时都是一个好主意

Redis 持久化方式Rdb和Aof的优缺点

  • Rdb优势
    • RDB最大限度地提高了Redis的性能(Redis fork 子进程出去rdb快照文件生成)
    • 与AOF相比,RDB恢复快
    • 占用磁盘空间较小
  • Rdb缺点
    • 丢数据(没到执行RDB快照时间 Redis在没有正确关闭情况下停止工作 shutdown正常关闭时会触发(bgsave) 丢失这个时间范围内的数据)
    • 可能导致Redis停止为客户端服务几毫秒(fork子进程 数据集很大CPU性能不佳fork很耗时可能导致Redis停止为客户端服务几毫秒甚至一秒钟 AOF还需要fork 但可以调整要重写日志的频率,而无需权衡持久性)
      *AOF优势(参考RDB优缺点)
    • 可以使用不同的fsync策略 (使用默认策略fsync时,每秒的写入性能仍然很好(fsync是使用后台线程执行的,并且在没有进行fsync的情况下,主线程将尽力执行写入操作。)但是您只能损失一秒钟的写入时间)
    • AOF日志是仅追加的日志,因此,如果断电,则不会出现定位失败或损坏问题。即使由于某种原因(磁盘已满或其他原因)以half-written命令结束日志,redis-check-aof工具也可以轻松修复它
    • 在重写触发事件范围内发生错误操作(FLUSHALL或FLUSHDB命令)仍然可以保存数据集,只需停止服务器,删除最新命令并再次重新启动Redis(调大AOF重写参数auto-aof-rewrite-percentage和auto-aof-rewrite-minsize, 让Redis不能产生AOF自动重写)
  • AOF缺点
    • 占用磁盘空间较大(对于同一数据集,AOF文件通常大于等效的RDB文件)
    • 恢复慢(根据最优的fsync策略,AOF可能比RDB慢)
    • 有未知没有重现的错误(过去,我们在特定命令中遇到过罕见的错误)

Redis 内存不够时,如何处理新的请求?

取决于内存淘汰策略

Redis 是如何处理过期数据(淘汰策略)?

  • noeviction(默认策略):对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外)
  • allkeys-lru:从所有key中使用LRU算法进行淘汰

  • volatile-lru:从设置了过期时间的key中使用LRU算法进行淘汰

  • allkeys-random:从所有key中随机淘汰数据

  • volatile-random:从设置了过期时间的key中随机淘汰

  • volatile-lfu:在设置了过期时间的key中使用LFU算法淘汰key(4.0之后)

  • allkeys-lfu:在所有的key中使用LFU算法淘汰数据(4.0之后)

  • volatile-ttl:在设置了过期时间的key中,根据key的过期时间进行淘汰,越早过期的越优先被淘汰

当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以被淘汰,则和noeviction一样返回错误

Redis 内存淘汰算法 LRU/ LFU 可以讲一下原理么?

  • LRU(Least Recently Used 最近最少使用),是一种缓存置换算法。当缓存被占满,这个时候继续往缓存里面添加数据,就需要淘汰一部分老的数据。
    核心思想是:如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉
    弊端(LFU解决) 假如你使用的是LRU算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了。
  • LFU(Least Frequently Used) 核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来

Redis 位图应用场景及实现原理可以讲一下么?

  • 原理
    *位图不是实际的数据类型,而是在String类型上定义的一组面向位的操作 由于字符串是二进制安全Blob,并且最大长度为512 MB,因此它们适合设置多达2 32个不同的位 时间复杂度都是O(1)

  • 位操作分为两类

    • 固定时间的单个位操作(如将一个位设置为1或0或获取其值)
    • 计算给定位范围内设置的位的数量(例如,人口计数)
  • 应用场景

    • 位图计数
      • 1.我们需要记录用户一年的签到记录
      • 2.用户在线状态
      • 3.统计几天用户的活跃度
  • 使用

    • setbit key offset value(命令的第一个参数是位数,第二个参数是要设置位的值,value即1或0。如果寻址的位超出当前字符串长度,该命令将自动放大字符串 偏移量较大时可能有较大耗时)
    • getbit key 10(GETBIT只是返回指定索引处的位的值。超出范围的位(寻址超出存储在目标键中的字符串长度的位)始终被视为零)
    • 统计命令 bitcount bittop
  • 理解

    • 下面记录了 22这个userid在 20160502 访问了pagehelloword
    • setbit pagehelloword::20160502 22 1
    • setbit pagehelloword::20160502 23 1
    • setbit pagehelloword::20160502 24 1
    • setbit pagehelloword::20160502 25 1
    • setbit pagehelloword::20160502 26 1
    • bitcount pagehelloword::20160502 返回5

Redis 的缓存穿透/缓存雪崩/缓存重建?

  • 缓存穿透 查数据在Redis中没查到就会向持久层数据库去查数据,很多这种操作时给持久层数据库非常大的压力,这情况就叫缓存穿透(是指在缓存和数据库中都查询不到)**缓存穿透解决方案 **

    • 1、使用布隆过滤器进行过滤
      将所有存在的数据哈希到位数组(bit array)中,如果访问的不存在,则会被拦截掉,从而保证不会对持久层进行查询。
      优点
      不需要存储元素本身(数据保密)
      Hash函数相互之间没有关系,方便由硬件并行实现(多个hash函数来降低冲突)
      布隆过滤器存储空间和插入/查询时间复杂度都是常数阶
      使用位数组,占用数据空间小
      缺点
      有误算
      不支持删除操作
      应用场景
      网页URL 去重
      垃圾邮件识别
      黑名单
      集合元素重复的判断
      缓存穿透

    • 2、如果查询的数据不存在,则也对这个数据进行缓存(缓存为空)。

  • 缓存雪崩 指缓存层本身出现了问题,比如:缓存层宕机,或者缓存数据在同一时间过期。那么所有的查询请求都会指向数据库。这种情况我们称之为缓存雪崩。

    **缓存雪崩解决方案 **

    • 1、搭建Redis集群,保证Redis提供正常的服务。

    • 2、给数据设置不同的过期时间。

    • 3、限流访问

  • 缓存击穿 是指一个热点key,大并发集中对这个key进行访问,当这个key在失效的瞬间,仍然有大批量的访问这个key,但是由于这个key已经失效,所有的请求就都会直接向持久层数据库进行访问
    **缓存击穿解决方案 **

    • 1、热点数据设置为永不过期
    • 2、根据业务去维护热点数据的过期时间

Redis 单线程和Redis6.0 的多线程是如何工作的?

  • 单线程高速执行

    • 多命令进入队列 逐个执行( (socket 读)、解析、执行、内容返回这个过程为单线程 其他淘汰算法 无用连接的释放 大key的删除等是多线程)
    • 避免了线程切换开销
    • 非阻塞IO(Redis采用epoll I/O多路复用技术 相当于nio中的selector)
    • 基于内存速度快
    • CPU不是Redis的瓶颈 瓶颈是机器内存或网络带宽(但是不能对cpu多核更好的利用)
  • reids6(改动较大 主要列举)

    • ACL(用户权限管理 分配可以操作的指令和key)
    • SSL
    • IO多线程支持

Redis日志

redis在默认情况下,是不会生成日志文件的,所以需要配置(conf文件中配置logfile“”里面填充路径即可)

Redis 集群分片原理是怎样的?

三种集群方式
三种集群方式1

  • Redis Cluster(Redis 的分布式解决方案)主从模型
    • 三个为从节点 三个主节点会分配槽,处理客户端的命令请求,而从节点可用在主节点故障后,顶替主节点
      Redis集群数据分片
    • 密钥的CRC16取模16384 每个node分摊
    • 一致性hash算法
      • 直播
      • 手写
      • 步骤
        • 物理节点 虚拟节点个数
        • Map<String,List>real2VirtrualMap 物理节点 虚拟节点 对应
        • SortedMap<Integer, T> circle = new TreeMap<Integer, T>(); 虚拟节点放到环上
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值