1. Redis有哪些数据类型?
参考答案
Redis支持5种核心的数据类型,分别是字符串、哈希、列表、集合、有序集合;
Redis还提供了Bitmap、HyperLogLog、Geo类型,但这些类型都是基于上述核心数据类型实现的;
Redis在5.0新增加了Streams数据类型,它是一个功能强大的、支持多播的、可持久化的消息队列。
2. redis过期策略和内存淘汰
redis有三种过期策略,8种内存淘汰机制
2.1 过期策略
有三种过期策略,也可以说是主动回收方式
定时删除:有定时器的存在会消耗CPU,降低Redis性能
惰性删除:如果访问不到的key,则永远不会删除
定期删除:随机选择,保证过期的key在一定的比例,但是不太好控制。
所以生产中Redis采用的策略是惰性删除加定期删除的组合方式。
2.2 内存淘汰
当现有内存大于设置的maxmemory时,便会触发内存被动淘汰机制,设置参数 maxmemory-policy ,有如下6种淘汰方式:
noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略
volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值
volatile-lfu:淘汰所有设置了过期时间的键值中使用最低频的键值(4.0以后版本新增)
volatile-random:随机淘汰设置了过期时间的任意键值
volatile-ttl:优先淘汰更早过期的键值
allkeys-lru:淘汰整个键值中最久未使用的键值(4.0以后版本新增)
allkeys-random:随机淘汰任意键值
allkeys-lfu:淘汰整个键值中使用最低频的键值
3. redis持久化
3.1. AOF日志
AOF是写后日志,先执行命令在写日志,刷盘fsync时机有三种:
always:同步写盘,影响主线程性能
everysec:每秒写盘,宕机丢失1秒的数据,
no:写命令执行完,将日志写到AOF内存缓冲区,宕机丢数据
AOF重写:AOF以文件的形式记录的执行命令,文件过大会进行重写,由主线程fork出一个子线程bgrewriteaof完成,fork过程会拷贝主线程里最新的缓存,并不阻塞主线程。
3.2. RDB快照
有两种,redis提供save和bgsave两个命令来全量生成RDB快照,前者由主线程执行,会阻塞客户端请求,后者是主线程fork一个子线程后台执行,对于新进来的写请求,采用写时复制(copy-on-write cow)的技术写入RDB,fork的过程是阻塞的。另外,Redis主从同步使用的是RDB,相比于AOF,RDB是压缩的二进制文件,传输更快,从库可以直接加载,而AOF回放执行会相对慢些。
数据恢复需要RDB全量+AOF增量
4.redis为什么这么快
可以说五点原因:IO基于内存、数据结构简单、单线程、IO多路复用
5.redis为什么用单线程
redis是基于内存操作,CPU可以说不是性能的瓶颈,性能瓶颈最有可能的是内存和网络带宽,既然单线程容易实现,而且CPU不会构成瓶颈,那就顺理成章的采用单线程的方案了,如果是多线程可能还会引入复杂的事务机制。
6.redis集群模式
大概有以下几种:
主从复制集群,手动切换
带有哨兵的HA主从复制集群
客户端实现的索引路由分片集群
中间代理实现的索引路由的分片集群
redis官方实现的cluster分片集群
7. 分片集群
为了解决读写分离模型的缺陷,可以将数据分片模型引用进来,可以将每个节点看成是独立的master,然后通过业务实现数据分片,结合主从架构和分片,可以将每个master设计成有一个master和多个slave组成的模型
8. redis热点数据怎么解决
热点数据会导致某个节点的物理网卡负载很高,解决方案有四种:
- 服务端本地缓存:本地采用 LRU 算法来缓存热点数据,需要提前知道哪些属于热点数据,可以通过sparkstreaming接受大量请求日志做实时统计分析,然后将热点数据写入zookeeper的znode节点上,服务端对zookeeper的节点做监听,一旦有新节点,服务端就会感应到,并加载到本地缓存
- Proxy 上做缓存:
- Redis读写分离
- 限流熔断保护:在服务端做限流保护措施,一旦热点数据请求量超过了redis的限制,则拒绝redis的访问
9. redis提供的高级工具
慢查询分析、性能测试、pipeline、事务、lua自定义命令、Bitmaps、HyperLogLog、Geo、订阅
pipeline:管道命令,可以用pipeline一次传输很多个命令,一次执行,当不是原子操作
10. Redis中,sexnx命令的返回值是什么,如何使用该命令实现分布式锁?
参考答案
setnx命令返回整数值,当返回1时表示设置值成果,当返回0时表示设置值失败(key已存在)。
一般我们不建议直接使用setnx命令来实现分布式锁,因为为了避免出现死锁,我们要给锁设置一个自动过期时间。而setnx命令和设置过期时间的命令不是原子的,可能加锁成果而设置过期时间失败,依然存在死锁的隐患。对于这种情况,Redis改进了set命令,给它增加了nx选项,启用该选项时set命令的效果就会setnx一样了。
采用Redis实现分布式锁,就是在Redis里存一份代表锁的数据,通常用字符串即可。采用改进后的setnx命令(即set…nx…命令)实现分布式锁的思路,以及优化的过程如下:
加锁:
第一版,这种方式的缺点是容易产生死锁,因为客户端有可能忘记解锁,或者解锁失败。
setnx key value
第二版,给锁增加了过期时间,避免出现死锁。但这两个命令不是原子的,第二步可能会失败,依然无法避免死锁问题。
setnx key value expire key seconds
第三版,通过“set…nx…”命令,将加锁、过期命令编排到一起,它们是原子操作了,可以避免死锁。
set key value nx ex seconds
解锁:
解锁就是删除代表锁的那份数据。
del key
问题:
看起来已经很完美了,但实际上还有隐患,如下图。进程A在任务没有执行完毕时,锁已经到期被释放了。等进程A的任务执行结束后,它依然会尝试释放锁,因为它的代码逻辑就是任务结束后释放锁。但是,它的锁早已自动释放过了,它此时释放的可能是其他线程的锁。
想要解决这个问题,我们需要解决两件事情:
在加锁时就要给锁设置一个标识,进程要记住这个标识。当进程解锁的时候,要进行判断,是自己持有的锁才能释放,否则不能释放。可以为key赋一个随机值,来充当进程的标识。
解锁时要先判断、再释放,这两步需要保证原子性,否则第二步失败的话,就会出现死锁。而获取和删除命令不是原子的,这就需要采用Lua脚本,通过Lua脚本将两个命令编排在一起,而整个Lua脚本的执行是原子的。
按照以上思路,优化后的命令如下:
#加锁
set key random-value nx ex seconds
# 解锁
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end
当然以上还会存在问题
11. 说一说Redis的持久化策略
参考答案
Redis支持RDB持久化、AOF持久化、RDB-AOF混合持久化这三种持久化方式。
RDB:
RDB(Redis Database)是Redis默认采用的持久化方式,它以快照的形式将进程数据持久化到硬盘中。RDB会创建一个经过压缩的二进制文件,文件以“.rdb”结尾,内部存储了各个数据库的键值对数据等信息。RDB持久化的触发方式有两种:
-
手动触发:通过SAVE或BGSAVE命令触发RDB持久化操作,创建“.rdb”文件;
-
自动触发:通过配置选项,让服务器在满足指定条件时自动执行BGSAVE命令。
其中,SAVE命令执行期间,Redis服务器将阻塞,直到“.rdb”文件创建完毕为止。而BGSAVE命令是异步版本的SAVE命令,它会使用Redis服务器进程的子进程,创建“.rdb”文件。BGSAVE命令在创建子进程时会存在短暂的阻塞,之后服务器便可以继续处理其他客户端的请求。总之,BGSAVE命令是针对SAVE阻塞问题做的优化,Redis内部所有涉及RDB的操作都采用BGSAVE的方式,而SAVE命令已经废弃!
BGSAVE命令的执行流程,如下图:

BGSAVE命令的原理,如下图:

RDB持久化的优缺点如下:
-
优点:RDB生成紧凑压缩的二进制文件,体积小,使用该文件恢复数据的速度非常快;
-
缺点:BGSAVE每次运行都要执行fork操作创建子进程,属于重量级操作,不宜频繁执行,
所以RDB持久化没办法做到实时的持久化。
AOF:
AOF(Append Only File),解决了数据持久化的实时性,是目前Redis持久化的主流方式。AOF以独立日志的方式,记录了每次写入命令,重启时再重新执行AOF文件中的命令来恢复数据。AOF的工作流程包括:命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load),如下图:

AOF默认不开启,需要修改配置项来启用它:
appendonly yes # 启用AOF
appendfilename "appendonly.aof" # 设置文件名
AOF以文本协议格式写入命令,如:
*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
文本协议格式具有如下的优点:
- 文本协议具有很好的兼容性;
- 直接采用文本协议格式,可以避免二次处理的开销;
- 文本协议具有可读性,方便直接修改和处理。
AOF持久化的文件同步机制:
为了提高程序的写入性能,现代操作系统会把针对硬盘的多次写操作优化为一次写操作。
-
当程序调用write对文件写入时,系统不会直接把书记写入硬盘,而是先将数据写入内存的缓冲区中;
-
当达到特定的时间周期或缓冲区写满时,系统才会执行flush操作,将缓冲区中的数据冲洗至硬盘中;
这种优化机制虽然提高了性能,但也给程序的写入操作带来了不确定性。
-
对于AOF这样的持久化功能来说,冲洗机制将直接影响AOF持久化的安全性;
-
为了消除上述机制的不确定性,Redis向用户提供了appendfsync选项,来控制系统冲洗AOF的频率;
-
Linux的glibc提供了fsync函数,可以将指定文件强制从缓冲区刷到硬盘,上述选项正是基于此函数。
appendfsync选项的取值和含义如下:

AOF持久化的优缺点如下:
-
优点:与RDB持久化可能丢失大量的数据相比,AOF持久化的安全性要高很多。通过使用everysec选项,用户可以将数据丢失的时间窗口限制在1秒之内。
-
缺点:AOF文件存储的是协议文本,它的体积要比二进制格式的”.rdb”文件大很多。AOF需要通过执行AOF文件中的命令来恢复数据库,其恢复速度比RDB慢很多。AOF在进行重写时也需要创建子进程,在数据库体积较大时将占用大量资源,会导致服务器的短暂阻塞。
RDB-AOF混合持久化:
Redis从4.0开始引入RDB-AOF混合持久化模式,这种模式是基于AOF持久化构建而来的。用户可以通过配置文件中的“aof-use-rdb-preamble yes”配置项开启AOF混合持久化。Redis服务器在执行AOF重写操作时,会按照如下原则处理数据:
-
像执行BGSAVE命令一样,根据数据库当前的状态生成相应的RDB数据,并将其写入AOF文件中;
-
对于重写之后执行的Redis命令,则以协议文本的方式追加到AOF文件的末尾,即RDB数据之后。
通过使用RDB-AOF混合持久化,用户可以同时获得RDB持久化和AOF持久化的优点,服务器既可以通过AOF文件包含的RDB数据来实现快速的数据恢复操作,又可以通过AOF文件包含的AOF数据来将丢失数据的时间窗口限制在1s之内。
12. 如何实现Redis的高可用?
参考答案
实现Redis的高可用,主要有哨兵和集群两种方式。
哨兵:
Redis Sentinel(哨兵)是一个分布式架构,它包含若干个哨兵节点和数据节点。每个哨兵节点会对数据节点和其余的哨兵节点进行监控,当发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它就会与其他的哨兵节点进行协商,当多数哨兵节点都认为主节点不可达时,它们便会选举出一个哨兵节点来完成自动故障转移的工作,同时还会将这个变化实时地通知给应用方。整个过程是自动的,不需要人工介入,有效地解决了Redis的高可用问题!
一组哨兵可以监控一个主节点,也可以同时监控多个主节点,两种情况的拓扑结构如下图:

哨兵节点包含如下的特征:
- 哨兵节点会定期监控数据节点,其他哨兵节点是否可达;
- 哨兵节点会将故障转移的结果通知给应用方;
- 哨兵节点可以将从节点晋升为主节点,并维护后续正确的主从关系;
- 哨兵模式下,客户端连接的是哨兵节点集合,从中获取主节点信息;
- 节点的故障判断是由多个哨兵节点共同完成的,可有效地防止误判;
- 哨兵节点集合是由多个哨兵节点组成的,即使个别哨兵节点不可用,整个集合依然是健壮的;
- 哨兵节点也是独立的Redis节点,是特殊的Redis节点,它们不存储数据,只支持部分命令。
集群:
Redis集群采用虚拟槽分区来实现数据分片,它把所有的键根据哈希函数映射到0-16383整数槽内,计算公式为slot=CRC16(key)&16383,每一个节点负责维护一部分槽以及槽所映射的键值数据。虚拟槽分区具有如下特点:
- 解耦数据和节点之间的关系,简化了节点扩容和收缩的难度;
- 节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据;
- 支持节点、槽、键之间的映射查询,用于数据路由,在线伸缩等场景。
Redis集群中数据的分片逻辑如下图:

13.数据库和redis缓存一致性
延时双删:delete key -> update MySql -> thread.sleep(1s) -> delete key
设置过期时间:设置过期时间 -> update MySql -> delete key
binlog同步到redis
加分布式锁
事务框架处理
参考:https://blog.csdn.net/qq_35022142/article/details/124346030
37万+

被折叠的 条评论
为什么被折叠?



