java面试题之-Redis篇(持续更新)
2021/9/28 19:12:21
本文主要是介绍java面试题之-Redis篇(持续更新),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- Redis基础
- 01、Redis里有哪些数据类型?
- 02、Redis为什么能够快速执行?
- 03、那Redis怎样防止异常数据不丢失的(Redis的如何持久化)?
- 04、缓存穿透、缓存击穿、缓存雪崩解决方案
- 05、Redis的io有什么优化
- 06、Redis的sds 是什么
- Redis进阶
- 01、Redis主从复制模式和哨兵模式?
- 02、Redis故障时哨兵模式下自动切换过程?
- 03、Redis的集群方案怎么做?
- 04、Redis 主从复制、哨兵和集群原理与区别
- 05、Redis的过期策略与内存淘汰机制?
- 06、Redis内存用完会发生什么情况?
- 07、Redis的事务(Transactions)
- 08、非关系型数据的CAP
- 09、项目中有没有用Redis事务
- 10、Redis中的分布式锁
- 11、Redis常见性能问题和解决方案:
- 12、MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据
- 13、Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
- 14、关系型数据库和非关系型数据库的区别
Redis基础
01、Redis里有哪些数据类型?
Redis 有 5 种基础数据结构,分别为:string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)。
string:
- string是redis最基本的类型,一个key对应一个value。
- string类型是二进制安全的,意思是redis的string可以包含任何数据,比如jpg图片或者序列化的对象 。
- string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
list:
- Redis 的列表相当于 Java 语言里面的
LinkedList
,底层实现结构是链表。 - 这意味着 list 的插入和删除操作非常快,时间复杂度为 O(1),但是查询很慢,时间复杂度为 O(n)。
- 当列表弹出了最后一个元素之后,该数据结构自动被删除,内存被回收。
hash:
- Redis 的哈希相当于 Java 语言里面的
HashMap
,它是无序字典。 - 内部实现结构上同 Java 的 HashMap 也是一致的,同样的数组 + 链表二维结构。
- 第一维 hash 的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。
set:
- Redis 的集合相当于 Java 语言里面的
Set
,它内部的键值对是无序的唯一的
,好比HashSet只要键值,value值都为NULL。 - 它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值NULL。 当集合中最后一个元素移除之后,数据结构自动删除,内存被回收。
zset :
- Redis的zset类似于 Java 的 SortedSet 和 HashMap 的结合体,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权重。
- 简单说就是
有序的Set
02、Redis为什么能够快速执行?
1、绝大部分请求是纯粹的内存操作(非常快速)
2、采用单线程
,避免了不必要的上下文切换和竞争条件(所以不需考虑并发安全性)
3、非阻塞IO - IO多路复用
注意: Redis实际上是采用了线程封闭的观念,把任务封闭在一个线程,自然避免了线程安全问题,不过对于需要依赖多个redis(集群)操作的复合操作来说,依然需要锁,而且有可能是分布式锁
。
先说一下同步阻塞IO:
- 同步阻塞IO模型是最简单的IO模型
- 如果一个线程请求服务器,在这个线程请求期间其他线程阻塞
说以下I/O多路复用(多路网络连接复用一个io线程。):
I/O就是指的我们网络I/O,多路指多个TCP连接(或多个Channel),复用指复用一个或少量线程。串起来理解就是很多个网络I/O复用一个或少量的线程来处理这些连接
03、那Redis怎样防止异常数据不丢失的(Redis的如何持久化)?
大家是否知道,MySQL服务器如果突然宕机,怎样保证数据的不丢失:
- 使用的就是MySQL日志,MySQL的redo日志会在数据库宕机重启之后,对其进行前滚,保证MySQL数据的一致性
Redis中也是采用这种方式,先说结论,然后在做分析?
- 结论:Redis使用
持久化的方式来保证异常之后数据不丢失
,持久化使用的文件有两种:RBD文件
和AOF文件
。
何为Redis的持久化?
- 在指定的时间间隔内将内存中的数据集
快照
写入磁盘,它恢复时是将快照文件直接读到内存里 - Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。
- 整个过程中,
主进程是不进行任何IO操作的,这就确保了极高的性能
。
RDB持久化(默认的持久化方式)
-
RDB持久化即
通过创建快照(压缩的二进制文件)的方式进行持久化
,保存某个时间点的全量数据。 -
RDB持久化的触发包括手动触发与自动触发两种方式。
-
优势
- 适合大规模的数据恢复
- 对数据
完整性
和一致性
要求不高
-
劣势
- 在一定
间隔
时间做一次备份,所以如果Redis服务器意外down掉的话,就会丢失最后一次快照后的所有修改(因为有时间间隔
) - Fork的时候,内存中的数据被克隆了一份,会占用更多的内存空间
- 在一定
AOF持久化
- AOF(Append-Only-File)持久化即记录所有变更数据库状态的指令,以append的形式追加保存到AOF文件中,在服务器下次启动时,就可以通过载入和执行AOF文件中保存的命令,来还原服务器关闭前的数据库状态。
AOF和RDB持久化方式对比:
-
RDB方式在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他I0操作,所以RDB持久化方式可以最大化redis的性能。
-
与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些,但是数据丢失风险大。
-
RDB需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候fork的过程是非常耗时间的,可能会导致Redis在一些毫秒级不能回应客户端请求
-
RDB的恢复,直接把RDB文件里面的内容读进来就可以了
-
AOF文件里面存放的都是指令,如果那条指令存入时有问题了,或者被人为修改了,恢复的时候就会出问题,就算不出问题把全部执行执行一遍非常耗时。
RDB、AOF混合持久化
- Redis从4.0版开始支持RDB与AOF的混合持久化方案。
- 首先由RDB定期完成内存快照的备份,然后再由AOF完成两次RDB之间的数据备份,由这两部分共同构成持久化文件。
- 该方案的优点是充分利用了RDB加载快、备份文件小及AOF尽可能不丢数据的特性。
- 缺点是兼容性差,一旦开启了混合持久化,在4.0之前的版本都不识别该持久化文件,同时由于前部分是RDB格式,阅读性较低。
Redis 持久化方案的建议
-
如果Redis
只是用来做缓存服务器
,比如数据库查询数据后缓存,那可以不用考虑持久化,因为缓存服务失效还能再从数据库获取恢复。 -
如果你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。如果你可以接受灾难带来的几分钟的数据丢失,那么可以仅使用RDB。
-
通常的设计思路是利用主从复制机制来弥补持久化时性能上的影响。即Master上RDB、AOF都不做,保证Master的读写性能,而Slave上则同时开启RDB和AOF(或4.0以上版本的混合持久化方式)来进行持久化,保证数据的安全性。
04、缓存穿透、缓存击穿、缓存雪崩解决方案
缓存穿透:
-
指查询一个在Redis中不存在的数据,而且在存储层还查不到该数据无法写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,可能导致 DB 挂掉。
-
简言之:
在Redis和MySQL中都不存在
,导致每次都去访问DB,导致MySQL挂掉。
解决方案:
- 查询返回的数据为空时,仍把这个空结果进行缓存,但过期时间会比较短
缓存击穿:
- 对于设置了过期时间的 key,缓存在某个时间点过期的时候,恰好这时间点对这个 Key 有大量的并发请求过来,这些请求发现缓存过期一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把 DB 压垮。
- 简言之:
在Redis中不存在MySQL中存在
,导致大量请求都去访问DB,导致MySQL挂掉。
解决方案:
-
使用互斥锁:当缓存失效时,不立即去 load db,先使用如 Redis 的 setnx 去设置一个互斥锁,当操作成功返回时再进行 load db 的操作并回设缓存,否则重试 get 缓存的方法。
-
永远不过期:物理不过期,但逻辑过期(后台异步线程去刷新)。
缓存雪崩:
- 设置缓存时采用了相同的过期时间,导致缓存在某一时刻
同时失效
,请求全部转发到 DB, DB 瞬时压力过重雪崩。 - 与缓存击穿的区别:雪崩是很多 key,击穿是某一个key 缓存。
- 简言之:
在Redis中不存在MySQL中存在
,导致大量请求都去访问DB,导致MySQL挂掉。
解决方案:
- 将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
05、Redis的io有什么优化
Redis中采用的是非阻塞IO
06、Redis的sds 是什么
String类型的底层实现,动态字符串
Redis进阶
01、Redis主从复制模式和哨兵模式?
先说一下主从复制模式是咋回事:
- 主从复制是为了数据备份,和MySQL一样,主从复制之后可以进行读写分离(写主库,读从库),因为是复制,所以里面的数据都一样。
主从复制的工作原理:
-
Slave启动成功连接到Master后会发送一个sync命令
-
Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,Master将传送整个数据文件到Slave,以完成一次完全同步
-
全量复制(
首次
):Slave服务在接收到数据库文件数据后,将其存盘并加载到内存中 -
增量复制(
继续
):Master继续将新的所有收集到的修改命令依次传给Slave,完成同步 -
但是只要是重新连接Master,一次完全同步(全量复制)将被自动执行
-
主从复制是
乐观复制
,当客户端发送写执行给主,主执行完立即将结果返回客户端,并异步的把命令发送给从,从而不影响性能(主先执行完,在传给从,乐观复制,认为期间不会宕机
)。也可以设置至少同步给多少个从主才可写。 -
缺点:
一旦主节点由于故障不能提供服务, 需要人工将从节点晋升为主节点
, 同时还要通知应用方更新主节点地址
为了解决主从复制模式的这种缺点
,Redis从2.8开始正式提供了Redis Sentinel
(哨兵) 机制来解决这个问题
哨兵模式(在从
数据库的配置文件
中设置要监控主库的IP地址即可)
- Sentinel(哨兵)进程是用于
监控Master主服务器工作的状态
。 - 在Master主服务器发生故障的时候,可以实现Master和Slave服务器的自动切换,保证系统的高可用(HA);
- 注意:哨兵 + redis 主从的部署架构,是不保证数据零丢失的,
只能保证 redis集群的高可用性
。
哨兵进程的作用:
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 当哨兵监测到
master
宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。 - 然而
一个哨兵进程对Redis服务器进行监控,不是太可靠
,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式,如下图。
02、Redis故障时哨兵模式下自动切换过程?
先说一下什么是主观下线和客观下线?
主观下线
(Subjectively Down, 简称 SDOWN):指的是单个 Sentinel 实例
对服务器做出的下线判断。客观下线
(Objectively Down, 简称 ODOWN):指的是多个 Sentinel 实例
在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。- 从
主观下线
状态切换到客观下线状态并没有使用严格的法定人数算法(strong quorum algorithm), 而是使用了流言协议:如果 Sentinel 在给定的时间范围内, 从其他 Sentinel 那里接收到了足够数量的主服务器下线报告, 那么 Sentinel 就会将主服务器的状态从主观下线改变为客观下线
。,如果之后其他 Sentinel 不再报告主服务器已下线, 那么客观下线状态就会被移除。 客观下线
条件只适用于主服务器, 对于任何其他类型的 Redis 实例(例如:slave实例), Sentinel 在将它们判断为下线前不需要进行协商(单个哨兵就可以将其判断为下线状态
), 所以从服务器或者其他 Sentinel永远不会达到客观下线条件
。
在说一下哨兵是如何进行监控的?
-
每个Sentinel(哨兵)进程以
每秒钟一次
的频率向整个集群中的Master主服务器
,Slave从服务器
以及其他Sentinel(哨兵)
进程发送一个 PING 命令。 -
如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过down-after-milliseconds选项所指定的值,则这个实例会被 Sentinel(哨兵)进程标记为
主观下线
(SDOWN)。 -
如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有
Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态。 -
当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)。
-
在一般情况下, 每个Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
-
当Master主服务器被 Sentinel(哨兵)进程标记为
客观下线
(ODOWN)时,Sentinel(哨兵)进程向被标记为客观下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。 -
若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。
最后来说一下Redis故障时哨兵模式下自动切换过程:
- 假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为
主观下线
。 - 当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。
- 切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为
客观下线
。
03、Redis的集群方案怎么做?
当我们的需要缓存的数据非常多的时候,一个Redis实例可能就容不下了,这时候我们需要多Redis进行横向扩容,把数据分到若Redis中,这样就形成一个多master(每个master有自己的slave)的结构,每个master中存放的数据还不一样
,如何实现那,redis从3.0版本
开始引入了redis-cluster,就使用这个redis-cluster来实现。
redis-cluster:
- 支撑N个redis master node ,每个master node 都可以挂载多个slave node ,读写分离的架构,对于每个master 来说,写就写到master ,然后读就从master对应的slaver去读,实现高可用。
- 因为每个master都有slaver节点,那么如果master挂掉,redis-cluster 这套机制,就会自动将某个slaver切换成master,
- redis cluster(多master+读写分离+高可用),
横向扩展Redis内存
。
redis-cluster vs
replication +sentinel
- 如果数据量很少,主要是承载
高并发性能
的场景,比如几个G的缓存,使用单个master就可以,也就是主从复制+哨兵模式
- redis-cluster:
主要针对海量数据+高并发的场景
04、Redis 主从复制、哨兵和集群原理与区别
我们先看一下Redis的发展历程:
- 主从模式:因为无法实现自动切换,引入了哨兵模式。
- 主从-哨兵:如果需要缓存的数量太多,这种方式就有点难顶,引入了redis cluster集群。
- 主从-哨兵-集群:可以看到redis的不断完善
主从模式
:读写分离,备份,一个Master可以有多个Slaves。
哨兵sentinel
:监控,自动转移,哨兵发现主服务器挂了后,就会从slave中重新选举一个主服务器。
集群
:为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器,内存/QPS不受限于单机,可受益于分布式集群高扩展性。
05、Redis的过期策略与内存淘汰机制?
注意:Redis的过期策略和内存淘汰机制不是一个概念:
- Redis的过期策略是指:如果处理过期的key。
- Redis的内存淘汰机制指:当Redis的内存不足时如何处理内存里面的key。
Redis的过期策略,在Redis中过期的key不会立刻从内存中删除
,而是会同时以下面两种策略进行删除:
定期删除
:每隔一段时间,随机检查设置了过期的key并删除已过期的key;维护定时器消耗CPU资源;惰性删除
:当key被访问时检查该key的过期时间,若已过期则删除;已过期未被访问的数据仍保持在内存中
,消耗内存资源;
redis 内存淘汰机制有以下几个(常用的):
- noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。
allkeys-lru
:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的
)。- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key
优先移除
。
英文解释:
- LRU(Least recently used):淘汰最近最少使用的
- Random:随机的
总结:Redis的内存淘汰策略的选取并不会影响过期的key的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据。
06、Redis内存用完会发生什么情况?
如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)或者你可以配置内存淘汰机制
,当Redis达到内存上限时会删除一部分内容
。
07、Redis的事务(Transactions)
事务:当一次执行多个命令,本质是一组命令的集合,一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞,类似于synchronsized锁一样。
相比于MySQL中的事务来说,Redis中的事务都不能称为真正的事务,所以一般也不用Redis中的事务
注意:Redis的事务不是原子性
- 在redis中,对于一个存在问题的命令,如果在
入队的时候就已经出错
,整个事务内的命令将都不会被执行(其后续的命令依然可以入队),如果这个错误命令在入队的时候并没有报错
,而是在执行的时候出错了
,那么redis默认跳过这个命令执行后续命令。也就是说,redis只实现了部分事务。
Redis事务的三阶段三特性
- 三阶段:
- 开启:以MULTI开始一个事务
- 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
- 执行:由EXEC命令触发事务
- 三特性
单独的隔离操作
:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。没有隔离级别的概念
:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行, 也就不存在事务内的查询要看到事务里的更新,在事务外查询不能看到这个让人万分头痛的问题不保证原子性
:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
。
08、非关系型数据的CAP
在说非关系型数据库的CAP原理之前,先说一下关系型数据库的ACID原理
- 原子性:
- 一致性:数据库要一直处于一致的状态
- 隔离性:指
并发
的事务之间不会互相影响 - 持久性:持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使服务器出现宕机也不会丢失
分布式数据库的CAP原理:
- C:Consistency(强一致性)
- A:Availability(可用性)
- P:Partition tolerance(分区容错性)
CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。
而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的
。所以我们只能在一致性
和可用性
之间进行权衡,没有NoSQL系统能同时保证这三点
。
- CP :Redis、Mongodb
09、项目中有没有用Redis事务
-
如果项目中采用的是
Redis Cluster
(集群架构),不同的key是有可能分配在不同的Redis节点上的,在这种情况下Redis的事务机制是不生效的。 -
其次,
Redis事务不支持回滚操作
,所以基本不用
10、Redis中的分布式锁
分布式锁:是控制分布式系统不同进程
共同访问共享资源
的一种锁的实现,秒杀下单、抢红包等等业务场景,都需要用到分布式锁,我们项目中经常使用Redis作为分布式锁
。
选了Redis分布式锁的几种实现方法,大家来讨论下,看有没有啥问题哈。
- 命令setnx + expire分开写
- setnx + value值是过期时间
- set的扩展命令(set ex px nx)
- set ex px nx + 校验唯一随机值,再删除
命令setnx + expire分开写:
if(jedis.setnx(key,lock_value) == 1){ //加锁 expire(key,100); //设置过期时间 try { do something //业务请求 }catch(){ } finally { jedis.del(key); //释放锁 } }
- 出现的问题:如果执行完
setnx
加锁,正要执行
expire设置过期时间时,进程crash掉或者要重启维护了,那这个锁就长生不老
了,别的线程永远获取不到锁啦,所以分布式锁不能这么实现。
setnx + value值是过期时间:
long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间 String expiresStr = String.valueOf(expires); // 如果当前锁不存在,返回加锁成功 if (jedis.setnx(key, expiresStr) == 1) { return true; } // 如果锁已经存在,获取锁的过期时间 String currentValueStr = jedis.get(key); // 如果获取到的过期时间,小于系统当前时间,表示已经过期 if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) { // 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间(不了解redis的getSet命令的小伙伴,可以去官网看下哈) String oldValueStr = jedis.getSet(key_resource_id, expiresStr); if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { // 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁 return true; } } //其他情况,均返回加锁失败 return false; }
- 问题:
- 过期时间是客户端自己生成的,分布式环境下,每个客户端的时间必须同步。
- 没有保存持有者的唯一标识,可能被别的客户端释放/解锁。
- 锁过期的时候,并发多个客户端同时请求过来,都执行了
jedis.getSet()
,最终只能有一个客户端加锁成功,但是该客户端锁的过期时间,可能被别的客户端覆盖。
set的扩展命令(set ex px nx):
if(jedis.set(key, lock_value, "NX", "EX", 100s) == 1){ //加锁 try { do something //业务处理 }catch(){ } finally { jedis.del(key); //释放锁 } }
- 问题:锁过期释放了,业务还没执行完,锁被别的线程误删。
set ex px nx + 校验唯一随机值,再删除:
if(jedis.set(key, uni_request_id, "NX", "EX", 100s) == 1){ //加锁 try { do something //业务处理 }catch(){ } finally {// 代码块中不是原子操作 //判断是不是当前线程加的锁,是才释放 if (uni_request_id.equals(jedis.get(key))) { jedis.del(key); //释放锁 } } }
-
问题:在这里,判断当前线程加的锁和释放锁是不是一个原子操作。如果调用jedis.del()释放锁的时候,可能这把锁已经不属于当前客户端,会解除他人加的锁。
-
一般也是用lua脚本代替。lua脚本如下
11、Redis常见性能问题和解决方案:
-
Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件(Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照,AOF文件过大会影响Master重启的恢复速度)
-
如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
-
为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
-
尽量避免在压力很大的主库上增加从库
-
主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…,这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
12、MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据
这个问题主要考察了以下几点内容:
1.Redis的内存淘汰策略。
2.Redis的最大内存设置。
分析题目:保证Redis 中的 20w 数据都是热点数据 说明是被频繁访问的数据,并且要保证Redis的内存能够存放20w数据,要计算出Redis内存的大小。
**a、保留热点数据:对于保留 Redis 热点数据来说,我们可以使用 Redis 的内存淘汰策略来实现,可以使用allkeys-lru淘汰策略,**该淘汰策略是从 Redis 的数据中挑选最近最少使用的数据删除,这样频繁被访问的数据就可以保留下来了。
**b、保证 Redis 只存20w的数据:**1个中文占2个字节,假如1条数据有100个中文,则1条数据占200字节,20w数据 乘以 200字节 等于 4000 字节(大概等于38M);所以要保证能存20w数据,Redis 需要38M的内存。
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(
这个是最常用的
)。 - volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。
volatile和allkeys规定了是对已设置过期时间的数据集淘汰数据
还是从全部数据集淘汰数据
,后面的lru
、ttl
以及random
是三种不同的淘汰策略,再加上一种no-enviction
永不回收的策略。
使用策略规则:
-
1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用
allkeys-lru
-
2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用
allkeys-random
13、Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
- 使用keys指令可以扫出指定模式的key列表。
- 对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
- 这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。
- 这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。
14、关系型数据库和非关系型数据库的区别
关系型数据库最典型的数据结构是表
,由二维表及其之间的联系所组成的一个数据组织
- 优点:
- 1、易于维护:都是使用表结构,格式一致;
- 2、使用方便:SQL语言通用,可用于复杂查询;
- 3、复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。
- 缺点:
- 1、读写性能比较差,尤其是海量数据的高效率读写;
- 2、固定的表结构,灵活度稍欠;
- 3、高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。
非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。
-
优点:
- 1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
- 2、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
- 3、高扩展性;
- 4、成本低:nosql数据库部署简单,基本都是开源软件。
-
缺点:
- 1、不提供sql支持,学习和使用成本较高;
- 2、无事务处理;
- 3、数据结构相对复杂,复杂查询方面稍欠。
这篇关于java面试题之-Redis篇(持续更新)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南