Java面试之redis篇

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: Java面试之redis篇

Redis为什么那么快?

(1)Redis是基于内存操作,避免了磁盘I/O的开销

(2)Redis是高效数据结构,对数据的操作也比较简单

(3)Redis是单线程模型,避免了多线程中上下文频繁切换的操作

(4)使用多路I/O复用模型,非阻塞I/O

为什么要使用Redis,Redis的使用场景?

使用Redis缓存的目的就是提升读写性能。而实际业务场景下,更多的是为了提升读性能,还可以带来更高的并发量。Redis 的读写性能比 Mysql 好的多,我们就可以把Mysql中的热点数据缓存到 Redis 中,提升读取性能,同时也减轻了 Mysql 的读取压力

①适合存储热点数据(热点商品、资讯、新闻)

②缓存

③任务队列

④消息队列

⑤分布式锁

Redis有哪些数据类型?

(1)String字符串类型

(2)列表list,常用来存储一个有序数据,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。

(3)set,集合中不能出现重复的数据。它的底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)

(4)sorted set,不允许重复的成员。每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大排序。有序集合的成员是唯一的,但分数却可以重复。

(5)Hash,也叫散列,可以将对象中的每个字段独立存储

(6)Bitmaps相当于是一个以位为单位的数组,数组的每个单位只能存储0和1,是一种统计二值状态的数据类型

(7)HyperLogLog,统计一个集合中不重复的元素个数

(8) Redis流(Stream):Redis流是Redis版的MQ消息中间件+阻塞队列

数据库缓存双写不一致问题

(1)修改DB更新缓存场景:在高并发请求场景下,若多个请求对数据库中同一个数据进行修改,修改后还需要更新缓存中相关的数据,那么就有可能会出现缓存与数据库中数据不一致的问题。

(2)修改DB删除缓存场景:在高并发读写情况下,若这些请求对数据库中同一个数据的操作既包含写也包含读,且修改后还要删除缓存中相关数据,那么就可能出现缓存与数据库不一致的情况。

如何保证Redis和MySQL的数据一致性

(1)先更新数据库, 再更新缓存

如果先更新数据库,再更新缓存,如果缓存更新失败,就会导致数据库和 Redis中的数据不一致。

(2)先删除缓存, 再更新数据库

如果是先删除缓存, 再更新数据库,理想情况是应用下次访问 Redis 的时候,发现 Redis 里面的

数据是空的, 就从数据库加载保存到 Redis 里面, 那么数据是一致的。 但是在极端情况下, 由于

删除 Redis 和更新数据库这两个操作并不是原子的,所以这个过程如果有其他线程来访问, 还是会

存在数据不一致问题。

(3)延时双删策略(推荐)

①先删除redis中的数据

②更新数据库

③再延时删除redis

例如:客户端1去修改接口的数据时,先删除已有的redis缓存;这时,客户端2查询这个接口,因为redis中没有数据,就会去查找MySQL数据库,将数据缓存到redis中。此时,客户端1更新MySQL数据后,再延时删除redis缓存(延时删除的目的是为查询后,保证构建缓存执行完毕后再去删除,以免删不掉)。当下次再有客户访问时,就又会重新查询数据库,从而保证数据的一致性。

缺点:由于它有延时操作,会造成服务器的阻塞。所以,不适合高并发的场景

(4)在极端情况下保证Redis和Mysql数据最终一致性(推荐)

第二次如果删除失败,使用mq异步重试机制。但是耦合性较高,修改、新增都要加程序。可以使用canal,canal利用MySQL的主从复制机制伪装成MySQL的一个从机,他会不断的去监听我们这个二进制日志文件,当我们的数据发生变化的时候,他会主动的给我们的mq发送一个消息。

Redis如何内存优化?

(1)尽可能的使用散列表,散列表使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。

(2)比如你的web系统中有一个用户对象,不要为这个用户的名称,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。

Redis中的管道有什么用?

一次请求/响应,服务器能处理新的请求,既使旧的请求还未被响应。这样就可以将多个命令发送到服务器,而

不用等待回复,最后在一个步骤中读取该答复,这就是管道。这是一种几十年来广泛使用的技术。例如许多POP3

协议已经实现支持这个功能,大大加快了从服务器下载新邮件的功能。

Redis的持久化

Redis持久化机制是什么?以及优缺点

Redis的持久化有RDB和AOF两中方式

(1)RDB:在指定的时间间隔内将内存中的数据集快照写入磁盘。保存备份时它执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中

a.优点

①适合大规模的数据恢复

②按照业务定时备份

③对数据完整性和一致性要求不高

④RDB文件在内存中的加载速度要比AOF快得多

b.缺点

①rdb是在一定间隔时间做一次备份,如果redis意外down掉的话,就会丢失当前最近一次快照期间的数据

②rdb是全量同步,如果数据量太大就会导致I/O阻塞,严重影响服务器性能

③rdb依赖于主进程的fork(通过复制父进程的内存,创建一个完全相同的子进程。这个子进程伴随着父进程一起运行,但有自己独立的内存空间),在更大的数据集中,可能会导致服务请求的瞬时延迟

(2)AOF:以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

a.优点:更好的保护数据不丢失,可做紧急恢复

b.缺点:相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb;aof运行效率也要慢于rdb

RDB原理是什么?

原理是redis会单独创建(fork)一个与当前进程一模一样的子进程来进行持久化,这个子进程的数据和原进程一模一样,会先将数据写到一个临时文件中,持久化结束后,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程不进行任何io操作,确保极高的性能

RDB文件在哪?

redis.conf配置文件的dbfilename指定了rdb文件的名字(默认dump.rdb),dir指定dbfilename文件存放目录。

哪些情况会触发RDB快照

(1)配置文件中默认的快照配置

(2)手动save/bgsave命令

(3)执行flushall/flushdb命令也会产生dump.rdb文件,但里面是空的,无意义

(4)执行shutdown且没有设置开启AOF持久化

(5)主从复制时,主节点自动触发

AOF原理是什么?

原理是将redis的操作记录(写操作)以追加的方式写入文件。文件默认名称是appendonly.aof,redis重启时重新执行aof文件中的写操作

布隆过滤器BloomFilter

布隆过滤器是什么、能干嘛

布隆过滤器是由一个初值都为零的bit数组和多个无偏(无偏表示分布均匀)哈希函数构成,用来快速判断集合中是否存在某个元素。

①布隆过滤器能够高效的插入和查询,占用空间少,但返回的结果是不确定、不够完美。一个元素如果判断存在,元素不一定存在;但是判断结果为不存在,则一定不存在。

②布隆过滤器不能删除元素,由于涉及hashcode判断依据,删掉元素会导致误判率增加

原理

布隆过滤器是一种专门用来解决去重问题的高级数据结构。实质就是一个大型位数组和几个不同的无偏hash函数(无偏表示分布均匀)。由一个初值都为零的bit数组和多个个哈希函数构成,用来快速判断某个数据是否存在。但是跟 HyperLogLog 一样,它也一样有那么一点点不精确,也存在一定的误判概率

(1)添加key时:使用多个hash函数对key进行hash运算得到一个整数索引值,对位数组长度进行取模运算得到一个位置,每个hash函数都会得到一个不同的位置,将这几个位置都置1就完成了add操作。

(2)查询key时:只要有其中一位是零就表示这个key不存在,但如果都是1,则不一定存在对应的key。

hash冲突导致数据不精确

(1)哈希函数的概念是:将任意大小的输入数据转换成特定大小的输出数据的函数,转换后的数据称为哈希值或哈希编码,也叫散列值。如果两个散列值是不相同的(根据同一函数)那么这两个散列值的原始输入也是不相同的。这个特性是散列函数具有确定性的结果,具有这种性质的散列函数称为单向散列函数。散列函数的输入和输出不是唯一对应关系的,如果两个散列值相同,两个输入值很可能是相同的,但也可能不同,这种情况称为“散列碰撞(collision)”。用 hash表存储大数据量时,空间效率还是很低,当只有一个 hash 函数时,还很容易发生哈希碰撞。

(2)布隆过滤器的误判是指多个输入经过哈希之后在相同的bit位 置1了,这样就无法判断究竟是哪个输入产生的,这种情况也造成了布隆过滤器的删除问题,因为布隆过滤器的每一个 bit 并不是独占的,很有可能多个元素共享了某一位。如果我们直接删除这一位的话,会影响其他的元素

缓存预热

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

缓存雪崩

缓存雪崩的发生

redis中有大量key同时失效

预防

(1)redis缓存集群实现高可用

(2)多缓存结合:使用本地缓存+redis缓存

(3)服务降级,如sentine限流或降级

缓存穿透

(1)缓存穿透是什么

请求去查询一条记录,先查redis无,后查mysql无,都查不到该条记录,每次查询都要访问数据库,就会导致数据库压力剧增,这种现象我们称为缓存穿透。

解决

(1)空对象或者缺省值(零、负数、defaultNull)缓存

(2)可以使用布隆过滤器解决缓存穿透的问题把已存在数据的key存在布隆过滤器中,相当于redis前面挡着一个布隆过滤器。当有新的请求时,先到布隆过滤器中查询是否存在:

①如果布隆过滤器中不存在该条数据则直接返回;

②如果布隆过滤器中已存在,才去查询缓存redis,如果redis里没查询到则再查询Mysql数据库

缓存击穿

(1)缓存击穿是什么:大量的请求同时查询一个key,此时这个key正好失效了,就会导致数据库压力剧增

(2)发生:热点key突然失效

缓存击穿解决方案

(1)对于频繁访问的热点key,干脆就不设置过期时间

(2)采用双检加锁策略:如果多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,做了缓存。后面的线程进来发现已经有缓存了,就直接走缓存。

工作中哪里使用过redis

(1)创建任务编号

private int createCodeExec(){
        String key = "COUNTER:LINE_TASK_CODE_SERIAL";
        Long serial = redisTemplate.opsForValue().increment(key); //如果这个key在缓存中不存在则初始化值为0,并返回1作为增加后的值
        Long counterExpire = redisTemplate.getExpire(key);
        if(counterExpire<0){
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.HOUR_OF_DAY,1);
            calendar.set(Calendar.MINUTE,0);
            calendar.set(Calendar.SECOND,0);
            calendar.set(Calendar.MILLISECOND,0);
            redisTemplate.expireAt(key, calendar.getTime()); //设置key失效时间为当前时间的下一个小时整点的时间
        }
        // 获取当前时间,并按照 "MMddHH000" 的格式转换为字符串,然后将其解析为一个整数。
        int num = Integer.parseInt(DateTime.now().toString("MMddHH000"));
        // String.format("%1$-" + 9 + "s", num) 的作用是将 num 格式化为一个长度为9的字符串,并使用空格在右侧进行填充,以实现右对齐
        // replace(' ', '0') 将空格替换为0
        num = Integer.valueOf(String.format("%1$-" + 9 + "s", num).replace(' ', '0'));
        return (int) (num+serial); // 将num和redis生成的value值进行相加
    }

Redis的过期键删除策略

(1)惰性删除策略:只会在获取键时才对键进行过期检查,不会在删除其它无关的过期键花费过多的CPU时间。

①优点:对CPU时间非常友好

②缺点:对内存非常不友好

举个例子,如果数据库有很多的过期键,而这些过期键又恰好一直没有被访问到,那这些过期键就会一直占用着宝贵的内存资源,造成资源浪费。

(2)定期删除策略:由activeExpireCycle函数实现,每当Redis服务器的周期性操作serverCron函数执行时,activeExpireCycle函数就会被调用,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。

①activeExpireCycle函数的大体流程为:

函数每次运行时,都从一定数量的数据库中随机取出一定数量的键进行检查,并删除其中的过期键,比如先从0号数据库开始检查,下次函数运行时,可能就是从1号数据库开始检查,直到15号数据库检查完毕,又重新从0号数据库开始检查,这样可以保证每个数据库都被检查到。

redis的集群有几种实现方式?

分布式锁的实现方法有很多,常见的有以下几种:

数据库锁:使用数据库中的行锁或表锁来实现分布式锁。

文件锁:使用文件来实现分布式锁。

Zookeeper锁:使用Zookeeper来实现分布式锁。

Redis锁:使用Redis来实现分布式锁。

消息队列锁:使用消息队列来实现分布式锁

什么是Redis哨兵模式

Redis Sentinel 是一个分布式系统, 你可以在一个架构中运行多个 Sentinel 进程(progress), 这些进程使用流言协议(gossip protocols)来接收关于主服务器是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。

Redis Sentinel作用

(1)监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。

(2)提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

(3)自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作,它会将失效主服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

Sentinel的工作方式

(1)每个 Sentinel 以每秒钟一次的频率向它所知的 Master,Slave 以及其他 Sentinel 实例发送一个 PING 命令。

(2)如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值,则这个实例会被 Sentinel 标记为主观下线。

(3)如果一个 Master 被标记为主观下线,则正在监视这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 的确进入了主观下线状态。

(4)当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认 Master 的确进入了主观下线状态,则 Master 会被标记为客观下线。

(5)在一般情况下,每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令。

(6)当 Master 被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次。

(7)若没有足够数量的 Sentinel 同意 Master 已经下线,Master 的客观下线状态就会被移除。

(8)若 Master 重新向 Sentinel 的 PING 命令返回有效回复,Master 的主观下线状态就会被移除。

Redis Sentinel优缺点

(1)优点

①监控主数据库和从数据库是否正常运行

②主数据库出现故障时,可以自动将从数据库转换为主数据库,实现自动切换

③如果 redis 服务出现问题,会发送通知

(2)缺点

①主数据库出现故障时,选举切换的时候容易出现瞬间断线现象

②不能自动扩容

Redis Sentinel原理

(1)哨兵模式是一种特殊的模式,它是 Redis 高可用的一种实现方案。哨兵也是一个独立的进程, 可以实现对 Redis 实例的监控、通知、自动故障转移。

(2)实际上,每个哨兵节点每秒通过 ping 去进行心跳监测(包括所有 redis 实例和 sentinel 同伴),并根据回复判断节点是否在线。

(3)如果某个 sentinel 线程发现主库没有在给定时间( down-after-milliseconds)内响应这个 PING,则这个 sentinel 线程认为主库是不可用的,这种情况叫 “主观失效”(即SDOWN);这种情况一般不会引起马上的故障自动转移,但是当多个 sentinel 线程确实发现主库是不可用并超过 sentinel.conf 里面的配置项 sentinel monitor mymaster {#ip} {#port} {#number} 中的 #number 时候(这里实际上采用了流言协议),一般其余 sentinel 线程会通过 RAFT 算法推举领导的 sentinel 线程负责主库的客观下线并同时负责故障自动转移,这种情况叫 “客观失效”(即 ODOWN)。

redis主从同步(主从复制)

为什么需要主从复制,主从复制的作用

(1)假设我们只部署了一台Redis服务器,某个时刻redis服务挂了,就会有大量的请求直接打到数据库,造成数据库cpu飙升,严重的可能导致数据库直接挂掉,这就是我们常说的单点故障。

(2)主从复制的作用

①解决单机故障,master节点出现故障的时候,从节点可以提供服务

②读写分离,master节点主要是写,slave节点主要是读

③负载均衡,特别是读多写少的情况下,通过多个从节点分担读的的负载,提高redis服务的并发量。

④高可用的基石。哨兵和集群底层都依赖的是主从复制

什么是redis主从复制

(1)主从复制,是将一台redis服务器的数据,复制到其他redis服务器,前者称为主节点(master),后者称为从节点(slave),数据的复制的单向的,只能由主节点到从节点,一个主节点可以有多个从节点,但一个从节点只能有一个主节点。

(2)主从模式下,redis采用读写分离模式

①读操作:主节点与从节点均可执行读操作,主要从节点读为主

②写操作:主节点可以执行写操作,然后把数据同步给各个从节点,保证数据一致性

主从复制的过程

(1)同步阶段:从节点与主节点建立连接后,进行初始化同步,主节点将所有数据发送给从节点。

(2)增量复制阶段:在同步完成后,主节点会将接收到的写入操作发送给从节点,从节点将接收到的写入操作重新执行,保持数据的一致性。

(3)持续复制阶段:从节点持续监听主节点的写入操作,并按照接收顺序执行,以保持与主节点数据的一致性。

数据同步方式

(1)全量复制:主节点将所有数据发送给从节点,从节点将接收到的数据保存在本地,完成全量复制。

(2)部分复制:主节点仅发送写操作给从节点,从节点根据接收到的写操作进行数据更新,即进行增量复制。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
14 2
|
6天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
8天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
13天前
|
存储 缓存 Oracle
Java I/O流面试之道
NIO的出现在于提高IO的速度,它相比传统的输入/输出流速度更快。NIO通过管道Channel和缓冲器Buffer来处理数据,可以把管道当成一个矿藏,缓冲器就是矿藏里的卡车。程序通过管道里的缓冲器进行数据交互,而不直接处理数据。程序要么从缓冲器获取数据,要么输入数据到缓冲器。
Java I/O流面试之道
|
9天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
33 4
|
7天前
|
存储 消息中间件 NoSQL
使用Java操作Redis数据类型的详解指南
通过使用Jedis库,可以在Java中方便地操作Redis的各种数据类型。本文详细介绍了字符串、哈希、列表、集合和有序集合的基本操作及其对应的Java实现。这些示例展示了如何使用Java与Redis进行交互,为开发高效的Redis客户端应用程序提供了基础。希望本文的指南能帮助您更好地理解和使用Redis,提升应用程序的性能和可靠性。
22 1
|
10天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
50 4
|
18天前
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
SQL 缓存 安全
Java高频面试题目
面试时面试官最常问的问题总结归纳!
142 0
JAVA高频面试题目集锦(6)
JAVA高频面试题目集锦(6)
141 0
JAVA高频面试题目集锦(6)