写点什么

不是吧阿 sir,你这 Redis 太熟了吧,震惊面试官第六年

用户头像
极客good
关注
发布于: 刚刚

? 本地缓存非常高效,本地缓存会占用堆内存,影响垃圾回收、影响系统性能。


本地缓存设计:


? 以 Java 为例,使用自带的 map 或者 guava 实现的是本地缓存,最主要的特点是轻量以及快速,生命周期随着 jvm 的销毁而结束,并且在多实例的情况,每个实例都需要各自保存一份缓存,缓存不具有一致性。


解决缓存过期:


? 1、将缓存过期时间调为永久


? 2、将缓存失效时间分散开,不要将缓存时间长度都设置成一样;比如我们可以在原有的失效时间基础上增加一个随机值,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。


解决内存溢出:


? 第一步,修改 JVM 启动参数,直接增加内存。(-Xms,-Xmx 参数一定不要忘记加。)


第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。


第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

3、redis 和 Memcached 的区别

| redis | Memcached |


| --- | --- |


| 内存高速数据库 | 高性能分布式内存缓存数据库,可缓存图片、视频 |


| 支持 hash、list、set、zset、string 结构 | 只支持 key-value 结构 |


| 将大部分数据放到内存 | 全部数据放到内存中 |


| 支持持久化、主从复制备份 | 不支持数据持久化及数据备份 |


| 数据丢失可通过 AOF 恢复 | 挂掉后,数据不可恢复 |


使用场景:


? 1、如果有持久方面的需求或对数据类型和处理有要求的应该选择 redis。? 2、如果简单的 key/value 存储应该选择 memcached。

4、redis 常用数据结构和使用场景

Redis 主要有 5 种数据类型,包括 String,List,Set,Zset,Hash


| 类型 | 存储值 | 应用场景 |


| --- | --- | --- |


| String | 字符串、整数、浮点数 | 简单的键值对缓存 |


| List | 列表 | 存储列表型数据结构,例如:评论列表、商品列表 |


| Set | 无序集合 | 适合交集、并集、查集操作,例如朋友关系 |


| Zset | 有序集合 | 去重后排序,适合排名场景 |


| Hash | 哈希 | 结构化数据,比如存储对象 |


Redis 的应用场景:


白嫖资料


计数器


? 可以对 String 进行自增自减运算,从而实现计数器功能。Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。


缓存


? 将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。


会话缓存


? 可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。


其它


? Set 可以实现交集、并集等操作,从而实现共同好友等功能。ZSet 可以实现有序性操作,从而实现排行榜等功能。

5、Zset 底层实现?跳表搜索插入删除过程?

? 跳表(skip List)是一种随机化的数据结构,基于并联的链表,实现简单,插入、删除、查找的复杂度均为 O(logN)。简单说来跳表也是链表的一种,只不过它在链表的基础上增加了跳跃功能,正是这个跳跃的功能,使得在查找元素时,跳表能够提供 O(logN)的时间复杂度


? Zset 数据量少的时候使用压缩链表 ziplist 实现,有序集合使用紧挨在一起的压缩列表节点来保存,第一个节点保存 member,第二个保存 score。ziplist 内的集合元素按 score 从小到大排序,score 较小的排在表头位置。


? 数据量大的时候使用跳跃列表 skiplist 和哈希表 hash_map 结合实现,查找删除插入的时间复杂度都是 O(longN)


搜索


? 跳跃表按 score 从小到大保存所有集合元素,查找时间复杂度为平均 O(logN),最坏 O(N) 。


插入


之前就说了,之所以选用链表作为底层结构支持,也是为了高效地动态增删。单链表在知道删除的节点是谁时,时间复杂度为 O(1),因为跳表底层的单链表是有序的,为了维护这种有序性,在插入前需要遍历链表,找到该插入的位置,单链表遍历查找的时间复杂度是 O(n),同理可得,跳表的遍历也是需要遍历索引数,所以是 O(logn)。


白嫖资料


删除


删除的节点要分两种情况,如果该节点还在索引中,那删除时不仅要删除单链表中的节点,还要删除索引中的节点;另一种情况是删除的节点只在链表中,不在索引中,那只需要删除链表中的节点即可。但针对单链表来说,删除时都需要拿到前驱节点才可改变引用关系从而删除目标节点。

6、redis 过期淘汰策略

1)全局的键空间选择性移除


? noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。


? allkeys-lru:在键空间中,移除最近最少使用的 key。(这个是最常用的)


? allkeys-random:在键空间中,随机移除某个 key。


2)设置过期时间的键空间选择性移除


? volatile-lru:在设置了过期时间的键空间中,移除最近最少使用的 key。


? volatile-random:在设置了过期时间的键空间中,随机移除某个 key。


? volatile-ttl:在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。


总结


? Redis 的内存淘汰策略的选取并不会影响过期的 key 的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据。

7、redis 持久化机制?都有什么优缺点?持久化的时候还能接受请求吗?

持久化就是把内存中的数据持久化到本地磁盘,防止服务器宕机了内存数据丢失


Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制


**RDB:**是 Redis DataBase 缩写快照


? RDB 是 Redis 默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为 dump.rdb。通过配置文件中的 save 参数来定义快照的周期。


? 优点:


白嫖资料


? 1)只有一个文件 dump.rdb,方便持久化;


? 2)容灾性好,一个文件可以保存到安全的磁盘。


? 3)性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能。


? 4)相对于数据集大时,比 AOF 的启动效率更高。


? 缺点:


? 1)数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候


AOF:持久化


? AOF 持久化(即 Append Only File 持久化),则是将 Redis 执行的每次写命令记录到单独的日志文件中,当重启 Redis 会重新将持久化的日志中文件恢复数据。


? 优点:


? 1)数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。


? 2)通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。


缺点:


? 1)AOF 文件比 RDB 文件大,且恢复速度慢。


? 2)数据集大的时候,比 rdb 启动效率低。

8、redis 事务

? 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。


Redis 事务的概念


? Redis 事务的本质是通过 MULTI、EXEC、WATCH 等一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。总结说:redis 事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。


Redis 事务功能是通过 MULTI、EXEC、DISCARD 和 WATCH 四个原语实现的


Redis 会将一个事务中的所有命令序列化,然后按顺序执行。


Redis 的事务总是具有 ACID 中的一致性和隔离性,其他特性是不支持的。当服务器运行在 AOF 持久化模式下,并且 appendfsync 选项的值


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


为 always 时,事务也具有耐久性。


白嫖资料


事务命令:


MULTI: 用于开启一个事务,它总是返回 OK。MULTI 执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当 EXEC 命令被调用时,所有队列中的命令才会被执行。


EXEC: 执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil 。


WATCH : 是一个乐观锁,可以为 Redis 事务提供 check-and-set (CAS)行为。可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到 EXEC 命令。


DISCARD: 调用该命令,客户端可以清空事务队列,并放弃执行事务,且客户端会从事务状态中退出。


UNWATCH:命令可以取消 watch 对所有 key 的监控。

9、缓存雪崩和缓存穿透,以及解决方法

【1】缓存雪崩:


? 指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。


? 解决方案:


? 1)缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。


? 2)一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。


? 3)给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效,则更新数据缓存。


【2】缓存穿透:


? 缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。


? 解决方案:


白嫖资料


? 1)接口层增加校验,如用户鉴权校验,id 做基础校验,id<=0 的直接拦截;


? 2)从缓存取不到的数据,在数据库中也没有取到,这时也可以将 key-value 对写为 key-null,缓存有效时间可以设置短点,如 30 秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个 id 暴力攻击;


? 3)采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。


【3】缓存击穿:


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


? 解决方案:


? 1)设置热点数据永远不过期

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
不是吧阿sir,你这Redis太熟了吧,震惊面试官第六年