• Java面试题(6)Redis


    一、Redis两种持久化方案(RDB与AOF机制对比)

    RDB(redis dataBase):在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

    优点:

    • 整个redis数据库将只包含一个文件 dump.rdb,方便持久化
    • 容灾性好,方便备份
    • 性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化。使用单独子进程来进行持久化,主进程不会进行任何IO操作(主进程不会IO阻塞),保证了redis的高性能。
    • 相对于数据集大时,比AOF的启动效率更高。

    缺点:

    • 数据安全性低,RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候。
    • 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,fork子进程会长时间占用CPU,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

    AOF(append only file):以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

    优点:

    • 数据安全,redis中提供了三种同步策略,即每秒同步、每修改同步和不同步(保证数据和文件一致性)。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。
    • 通过append模式写文件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过redis-check-aof工具解决数据一致性的问题。
    • AOF机制的rewrite模式(AOF中存储了大量的redis命令操作),定期对AOF文件进行重写,以达到压缩的目的。

    缺点:

    • AOF文件比RDB文件大,且恢复速度慢
    • 数据集大的时候,比RDB启动效率低
    • 运行效率没有RDB高

    总结:

    1. AOF文件比RDB更新频率高,优先使用AOF还原数据。
    2. AOF比RDB更安全也更大
    3. RDB性能比AOF好
    4. 如果两个都配置了的话优先加载AOF

    二、Redis的过期键的删除策略

    redis是key-value数据库,我们可以设置redis中缓存的key的过期时间。redis的过期策略就是指当redis中缓存的key 过期了,redis如何处理。

    • 惰性过期:只有当访问一个key的时候,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源(只有访问的时候才清除),却对内存非常不友好。极端情况下可能会出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。(这种策略类似于延长加载)。
    • 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

    (expire字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该redis集群中保存的所有键。)

    Reids中同时使用了惰性过期和定期过期两种过期策略。

    三、Redis线程模型、单线程快的原因(redis处理命令的形式)

    redis基于Reactor(响应式)模式开发了网络事件处理器,这个处理器叫做文件事件处理器 file event handler。这个文件事件处理器,它是单线程的,所以redis才叫做单线程的模型。它采用IO多路复用机制来同时监听多个socket,根据socket上的事件类型来选择对应的事件处理器来处理这个事件。可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了redis内部的线程模型的简单性。

    文件事件处理器的结构包含四个部分:多个socketIO多路复用程序文件事件分派器、事件处理器(命令请求处理器、命令回复处理器、连接应答处理器等)。

    多个socket可能并发的产生不同的操作(读事件、写事件....),每个操作对应不同的文件事件,但是IO多路复用程序会监听多个socket,会将socket放入一个队列中排队,每次从队列中取出一个socket事件分派器事件分派器把socket给对应的事件处理器

    然后一个socket的事件处理完之后,IO多路复用程序才会将队列中的下一个socket给事件分派器。文件事件分派器根据每个socket当前产生的事件,来选择对应的事件处理器来处理。

    单线程快的原因:

    • 纯内存操作
    • 核心是基于非阻塞的IO多路复用机制,效率高
    • 单线程反而避免了多线程的频繁上下文切换带来的性能问题

    为什么redis使用单线程,而java中使用多线程?

    如果业务逻辑相当复杂,任务处理需要很长的时间(IO阻塞),这时候需要使用多线程去执行。

    如果一个请求相当多,但是处理速度非常快,这个时候可以使用单线程。redis都只是对key的操作,要么读数据,要么取数据。类似于nginx,只是负责转发,没有很复杂的业务,所以nginx也是单线程。

    四、缓存雪崩、缓存穿透、缓存击穿

    缓存雪崩:是指缓存同一时间大面积的失效(或redis发生故障重启或第一次启动缓存中还没有数据时),所有请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

    解决方案:

    • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
    • 给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效,则更新数据缓存。(比较消耗性能,要去实时监听)
    • 缓存预热(重启时)
    • 加互斥锁(查到一个缓存,缓存失效查数据库,查数据库的时候把缓存的key加锁,查完数据库放入缓存中,释放锁。避免同一个请求对键操作,让请求排队)

    缓存穿透:是指缓存和数据库中都没有数据,导致所有的请求都落到了数据库上,造成数据库短时间内承受大量的请求而崩掉。(一般来自攻击场景或商城系统并发比较大)

    解决方案:

    • 接口层增减校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截(参数校验)
    • 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-ull,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
    • 采用布隆过滤器(Java中可以实现),将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免对底层存储系统的查询压力。

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

    解决方案:

    • 设置热点数据永远不过期(维护修改缓存)
    • 加互斥锁

    五、简述Redis事务实现(ACID)

    Redis与MySQL事务处理机制不一样,MySQL提供了回滚机制,Redis不支持事务回滚机制(rollback操作),但是会检查每一个事务中的命令是否错误。但是redis是支持原子性、一致性、持久性(AOF和RDB最终持久化)、隔离性(redis是单线程的具备隔离性)的。redis还有一个watch机制,监控事务中的所有的key。key发生变化的化redis会通知。redis事务不支持检查那些程序员自己逻辑错误,例如对string类型的数据库键值对执行对HashMap类型的操作。

    • 事务开始:MULTI命令的执行,标识一个事务的开始。
    • 命令入队:当一个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执行不同的操作。
    • 事务执行:客户端发送EXCE命令,服务器执行EXCE命令逻辑

    Redis事务几个常用的命令:

    • WATCH:是一个乐观锁,可以为redis事务提供check-and-set(CAS)行为。可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXCE命令。
    • MULTI:用于开启一个事务,它总是返回OK,MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即执行,而是被放到一个队列中,当EXCE命令被调用时,所有队列中的命令才会被执行。
    • EXCE:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值null
    • 通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务,并且客户端会从事务状态中推出。
    • UNWATCH:可以取消watch对所有key的监控

    六、Redis集群方案

    七、Redis主从复制的核心原理(数据一致性问题及同步问题)

    Redis主从集群模式。主节点(读写),从节点(只有读,数据都是从主节点来的)。

    通过执行slaveof命令或设置slaveof选项,让一个服务器去复制另一个服务器的数据。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般只是读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。

    全量复制

    • 主节点通过bgsave命令fork子进程进行RDB持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘IO的
    • 主节点通过网络将RDB文件发送给从节点,对主节点的带宽都会带来很大的消耗
    • 从节点清空老数据,载入新的RDB文件的过程是阻塞的,无法响应客户端的命令;如果从节点执行bgrewriteaof ,也会带来额外的消耗

    部分复制

    • 复制偏移量:执行复制的双方,主从节点,分别会维护一个辅助偏移量offfset
    • 复制积压缓冲区:主节点内部维护了一个固定长度的、先进先出(FIFO)队列作为复制积压缓冲区,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。
    • 服务器运行ID(runid):每个redis节点,都有其运行ID,运行ID由节点在启动时自动生成,主节点会将自己的运行ID发送给从节点,从节点会将主节点的运行ID存起来。从节点redis断开重连的时候就会根据运行ID来判断同步的进度:
    1. 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况)
    2. 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的redis节点并不是当前的主节点,只能进行全量复制。

    过程原理

  • 相关阅读:
    物联网普而不及 仍缺杀手级应用
    05-if和switch的简单比较
    05-if使用注意
    04-关系运算符使用注意
    03-sizeof的用法
    01-scanf函数的注意点
    01-变量的内存分析
    06-自定义构造方法
    06-构造方法
    05-id的使用
  • 原文地址:https://www.cnblogs.com/nastu/p/15782694.html
Copyright © 2020-2023  润新知