文章目录
- 1.介绍一下Redis
- 2.Redis为什么这么快?
- 3.Redis支持哪些数据类型?
- 4.Redis与Memcached的区别?
- 5.Redis跳表是什么?
- 6.什么是冷热数据分离?什么是VM机制?
- 7.Redis过期键的删除策略有哪些?
- 8.说说Redis的同步机制?
- 9.Pipeline有什么好处?
- 10.Redis实现分布式锁?
- 11.Redis实现幂等性?
- 12. 接触过哪些Redis客户端?
- 13.Redis哈希槽?
- 14.Redis 集群会有写操作丢失吗?
- 15.如何保证Redis中存的都是热点数据?
- 16.缓存穿透、击穿、雪崩?
- 17.热点key?
- 18.大Key(Big Key)?
- 19.无底洞问题?
- 20.缓存与数据库一致性问题?
1.介绍一下Redis
Redis 是一款使用 C 语言编写的高性能 key-value 数据库。
特点:
- 性能极高,能到 100000 次/s 读写速度
- 支持数据的持久化,对数据的更新采用Copy-on-write技术,可以异步地保存到磁盘上
- 丰富的数据类型,String(字符串)、List(列表)、Hash(字典)、Set(集合)、Sorted Set(有序集合)
- 原子性:Redis 的所有操作都是原子性的,多个操作通过 MULTI 和 EXEC 指令支持事务
- 丰富的特性:key 过期、publish/subscribe、notify
- 支持数据的备份,快速的主从复制
- 节点集群,很容易将数据分布到多个Redis实例中
- 单线程:避免了不必要的上下文切换和竞争条件
用途:
高速缓存。
分布式锁。
单点登录共享session。
计数器。
2.Redis为什么这么快?
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单高效,如 Hash、跳表等。
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
3.Redis支持哪些数据类型?
Redis 支持五种数据类型
string:字符串
hash:哈希
list:列表
set:集合
sorted set:有序集合
4.Redis与Memcached的区别?
暂不做了解。
5.Redis跳表是什么?
跳跃表基于有序单链表,在链表的基础上,每个结点不只包含一个指针,还可能包含多个指向后继结点的指针,这样就可以跳过一些不必要的结点,从而加快查找、删除等操作。
6.什么是冷热数据分离?什么是VM机制?
Redis使用到了VM, 通过VM功能可以实现冷热数据分离。使热数据仍在内存中,冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。
冷数据即是那些不经常访问、但又无法删除的信息。
7.Redis过期键的删除策略有哪些?
惰性删除:不主动删除过期键,从键空间中获取键时,都检查取得的键是否过期,过期则删除;没过期则返回
定期删除:每隔一段时间对数据库进行一次检查,删除里面的过期键。删除多少过期键、检查多少个数据库,由算法决定。
参考:Java架构直通车——Redis缓存过期处理与内存淘汰机制
8.说说Redis的同步机制?
2.8 版以前
Redis 通过同步(sync)和指令传播(command propagate)两个操作完成同步
同步(sync):将从节点的数据库状态更新至与主节点的数据库状态一致
从节点向主节点发送 SYNC 指令
收到 SYNC 指令,主节点执行 BGSAVE 指令,在后台生成一个 RDB 文件,并使用一个缓冲区记录从现在开始执行的所有写指令
主节点 BGSAVE 指令执行后,会将生成的 RDB 文件发送给从节点
从节点接收、载入 RDB 文件,将数据库状态更新至主节点执行 BGSAVE 指令时的数据库状态
从节点加载完 RDB 文件,通知主节点将记录在缓冲区里面的所有写指令发送给从节点,从节点执行这些写指令,将数据库状态更新至主节点当前数据库状态
指令传播(command propagate):主节点数据被修改,会主动向从节点发送执行的写指令,从节点执行之后,两个节点数据状态又保持一致
为了解决主从节点断线复制低效的问题(SYNC过程中生成、传输、载入 RDB 文件耗费大量 CPU、内存、磁盘 IO 资源),2.8 版开始新增 PSYNC 指令。
PSYNC 具有两种模式
完整重同步(full resynchronization),与SYNC过程基本一致
部分重同步(partial resynchronization),借助复制偏移量、复制积压缓冲区、服务器运行 ID ,完成主从节点断开连接后,从节点重连主节点后,条件允许,主节点将连接断开期间执行的写指令发送给从节点,从节点接收并执行写指令,将数据库更新至主节点当前状态
9.Pipeline有什么好处?
多个指令之间没有依赖关系,可以使用 pipeline 一次性执行多个指令,减少 IO,缩减时间。
10.Redis实现分布式锁?
参考:Java架构直通车——基于Redis的Set NX实现分布式锁
11.Redis实现幂等性?
为需要保证幂等性的每一次请求创建一个唯一标识token, 先获取token, 并将此token存入redis, 请求接口时, 将此token放到header或者作为请求参数请求接口, 后端接口判断redis中是否存在此token:
如果存在, 正常处理业务逻辑, 并从redis中删除此token, 那么, 如果是重复请求, 由于token已被删除, 则不能通过校验, 返回请勿重复操作提示。
12. 接触过哪些Redis客户端?
Redisson
优点:
实现了分布式特性和可扩展的 Java 数据结构,适合分布式开发
API 线程安全
基于 Netty 框架的事件驱动的通信,可异步调用
缺点:
API 更抽象,学习使用成本高
Jedis
优点:
提供了比较全面的 Redis 操作特性的 API
API 基本与 Redis 的指令一一对应,使用简单易理解
缺点:
同步阻塞 IO
不支持异步
线程不安全
Lettuce
优点:
线程安全
基于 Netty 框架的事件驱动的通信,可异步调用
适用于分布式缓存
缺点:
API 更抽象,学习使用成本高
13.Redis哈希槽?
Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念。
Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 算法计算的结果,对 16384 取模后放到对应的编号在 0-16383 之间的哈希槽,集群的每个节点负责一部分哈希槽
14.Redis 集群会有写操作丢失吗?
以下情况可能导致写操作丢失:
过期 key 被清理
最大内存不足,导致 Redis 自动清理部分 key 以节省空间
主库故障后自动重启,从库自动同步
单独的主备方案,网络不稳定触发哨兵的自动切换主从节点,切换期间会有数据丢失
15.如何保证Redis中存的都是热点数据?
Redis存储在内存中的数据升到配置大小时,就进行数据淘汰
使用 allkeys-lru 策略,从数据集(server.db[i].dict)中挑选最近最少使用的数据优先淘汰,即可满足保存热点数据
16.缓存穿透、击穿、雪崩?
17.热点key?
缓存中的某些Key(可能对应用与某个促销商品)对应的value存储在集群中一台机器,使得所有流量涌向同一机器,成为系统的瓶颈,该问题的挑战在于它无法通过增加机器容量来解决。(当key失效的时候,大量的线程构建缓存,导致负载增加)
解决方案:
- 客户端热点key缓存:将热点key对应value并缓存在客户端本地,并且设置一个失效时间。对于每次读请求,将首先检查key是否存在于本地缓存中,如果存在则直接返回,如果不存在再去访问分布式缓存的机器。
- 将热点key分散为多个子key,然后存储到缓存集群的不同机器上,这些子key对应的value都和热点key是一样的。当通过热点key去查询数据时,通过某种hash算法随机选择一个子key,然后再去访问缓存机器,将热点分散到了多个子key上。
- 为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存。缓存层面没有设置过期时间(这种方案可以解决大量线程等待的问题,但它可能导致数据不一致的问题)
- 针对热点key失效,大量线程构建缓存(使用分布式锁,只让一个线程构建缓存,可以解决这个问题)。
18.大Key(Big Key)?
Redis使用过程中经常会有各种大key的情况, 比如单个简单的key存储的value很大。
由于redis是单线程运行的,如果一次操作的value很大会对整个redis的响应时间造成负面影响,导致IO网络拥塞。
解决:
将整存整取的大对象,分拆为多个小对象。可以尝试将对象分拆成几个key-value, 使用multiGet获取值,这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;
19.无底洞问题?
暂时不做了解。
20.缓存与数据库一致性问题?
参考:缓存架构
参考:分布式DB与Cache一致性