0. redis 应用
- 为热点数据加速查询(主要场景),如热点商品,热点新闻、热点资讯、推广类等高访问量信息等
- 任务队列,如秒杀、抢购、购票排队等
- 即时信息查询,如各位排行榜,各类网站访问统计、公交到站信息、在线人数信息(聊天室、网站)、设备信号等
- 时效性信息控制,如验证码控制、投票控制等
- 分布式数据共享,如分布式集群架构中的session分离
- 消息队列
- 分布式锁
1.redis 缓存穿透,缓存雪崩,缓存击穿
-
缓存穿透
缓存穿透,是指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。
-
缓存雪崩
缓存雪崩,是指在某一个时间段,缓存集中过期失效。 产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。
-
缓存击穿
缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
2. 什么是缓存穿透?怎么解决?
缓存空值和使用布隆过滤器
https://www.cnblogs.com/jamaler/p/12453800.html
3.Redis部署模式?
1. 单节点实例
单机版三个问题
- 内存容量有限
- 处理能力有限
- 无法高可用
2. 主从模式(master/slaver)
首先谈谈我对主从模式的必要性:
- 主从模式的一个作用是备份数据,这样当一个节点损坏(指不可恢复的硬件损坏)时,数据因为有备份,可以方便恢复。
- 另一个作用是负载均衡,所有客户端都访问一个节点肯定会影响Redis工作效率,有了主从以后,查询操作就可以通过查询从节点来完成。
对主从模式必须的理解
- 一个Master可以有多个Slaves
- 默认配置下,master节点可以进行读和写,slave节点只能进行读操作,写操作被禁止
- 不要修改配置让slave节点支持写操作,没有意义,原因一,写入的数据不会被同步到其他节点;原因二,当master节点修改同一条数据后,slave节点的数据会被覆盖掉
- slave节点挂了不影响其他slave节点的读和master节点的读和写,重新启动后会将数据从master节点同步过来
- master节点挂了以后,不影响slave节点的读,Redis将不再提供写服务,master节点启动后Redis将重新对外提供写服务。
- master节点挂了以后,不会slave节点重新选一个master
主从复制的原理
当启动一个slave node的时候,它会发送一个PSYNC命令给master node。如果这是slave node初次连接到master node,会触发一次full resynchronization全量复制,此时master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端新收到的所有写命令缓存在内存中。RDB文件生成文件后,master会将这个RDB发送给slave,slave会写入本地磁盘,然后再从本地磁盘加载到内存中,接着master会将内存中缓存的写命令(不是AOF,AOF是在磁盘中的文件)发送到slave,slave也会同步这些数据。slave node如果跟master node有网络故障断开连接会自动重连,此时master node只会复制slave缺少的那部分数据。
- slave node 复制数据时不会block master node的正常工作
- slave node 复制数据时也不会 block 自己的查询操作,它会用旧的数据提供服务,但是完成复制后需要删除旧数据加载新数据,这时会暂停对外服务
主从节点的缺点
- master节点挂了以后,redis就不能对外提供写服务了,因为剩下的slave不能成为master。 这个缺点影响是很大的,尤其是对生产环境来说,是一刻都不能停止服务的,所以一般的生产坏境是不会单单只有主从模式的。所以有了下面的sentinel模式。
3. sentinel模式
- sentinel模式是建立在主从模式的基础上,如果只有一个Redis节点,sentinel就没有任何意义
- 当master节点挂了以后,sentinel会在slave中选择一个做为master,并修改它们的配置文件,其他slave的配置文件也会被修改,比如slaveof属性会指向新的master
- 当master节点重新启动后,它将不再是master而是做为slave接收新的master节点的同步数据
- sentinel因为也是一个进程有挂掉的可能,所以sentinel也会启动多个形成一个sentinel集群
- 当主从模式配置密码时,sentinel也会同步将配置信息修改到配置文件中,不需要担心。
- 一个sentinel或sentinel集群可以管理多个主从Redis。
- sentinel最好不要和Redis部署在同一台机器,不然Redis的服务器挂了以后,sentinel也挂了
- sentinel监控的Redis集群都会定义一个master名字,这个名字代表Redis集群的master Redis。
- 当使用sentinel模式的时候,客户端就不要直接连接Redis,而是连接sentinel的ip和port,由sentinel来提供具体的可提供服务的Redis实现,这样当master节点挂掉以后,sentinel就会感知并将新的master节点提供给使用者。
缺点:
- sentinel模式基本可以满足一般生产的需求,具备高可用性。但是当数据量过大到一台服务器存放不下的情况时,主从模式或sentinel模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中,就是下面要讲的。
4. cluster模式
cluster的出现是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。
- cluster可以说是sentinel和主从模式的结合体,通过cluster可以实现主从和master重选功能,所以如果配置两个副本三个分片的话,就需要六个Redis实例。
- 因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量过大时,可以新增机器进行扩容
- 这种模式适合数据量巨大的缓存要求,当数据量不是很大使用sentinel即可。
https://blog.csdn.net/java_zyq/article/details/83818341
https://www.cnblogs.com/guapiwangxi/p/10556812.html
https://www.jianshu.com/p/1a9738ddeed6
4.Redis 为什么是单线程的?为什么这么快?
- 因为CPU不是Redis的瓶颈。Redis的瓶颈最有可能是机器内存或者网络带宽,既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。关于redis的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求
- 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
二、Redis为什么这么快
-
完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
-
数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
-
采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
-
使用多路I/O复用模型,非阻塞IO;
-
使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
注意:redis 单线程指的是网络请求模块使用了一个线程,即一个线程处理所有网络请求,其他模块仍用了多个线程。
https://blog.csdn.net/u010623927/article/details/87918603
https://www.cnblogs.com/liangf27/p/10764718.html
5. Redis 支持的数据类型有哪些?zset底层结构?
- Redis自身是一个Map,其中所有的数据都是采用key:value的形式存储
- 数据类型指的是存储的数据的类型,也就是value部分的类型,key部分永远都是字符串
redis的5种数据类型:
- string 字符串(可以为整形、浮点型和字符串,统称为元素)
- list 列表(实现队列,元素不唯一,先入先出原则)
- set 集合(各不相同的元素)
- hash hash散列值(hash的key必须是唯一的)
- sort set 有序集合
zset实现:
- 有序集合对象的编码可以是ziplist或者skiplist。
同时满足以下条件时使用ziplist编码:
- 元素数量小于128个
- 所有member的长度都小于64字节
其他:
- 不能满足上面两个条件的使用 skiplist 编码。以上两个条件也可以通过Redis配置文件zset-max-ziplist-entries 选项和 zset-max-ziplist-value 进行修改
- 对于一个 REDIS_ENCODING_ZIPLIST 编码的 Zset, 只要满足以上任一条件, 则会被转换为REDIS_ENCODING_SKIPLIST 编码
https://www.cnblogs.com/yuanfang0903/p/12165394.html https://blog.csdn.net/H1517043456/article/details/89054611
6. 怎么保证缓存和数据库数据的一致性?
由于数据库层面的读写并发,引发的数据库与缓存数据不一致的问题(本质是后发生的读请求先返回了),可能通过两个小的改动解决:
- 修改服务Service连接池,id取模选取服务连接,能够保证同一个数据的读写都落在同一个后端服务上
- 修改数据库DB连接池,id取模选取DB连接,能够保证同一个数据的读写在数据库层面是串行的
7. Redis 持久化有几种方式?
- Redis作为一个键值对内存数据库(NoSQL),数据都存储在内存当中,在处理客户端请求时,所有操作都在内存当中进行,Redis服务器守护进程退出,内存中的数据也一样会消失。
- 为了避免内存中数据丢失,Redis提供了对持久化的支持,我们可以选择不同的方式将数据从内存中保存到硬盘当中,使数据可以持久化保存。
- Redis提供了RDB和AOF两种不同的数据持久化方式
RDB
RDB是一种快照存储持久化方式,具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb,而在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中恢复数据。
开启RDB持久化方式
开启RDB持久化方式很简单,客户端可以通过向Redis服务器发送save或bgsave命令让服务器生成rdb文件,或者通过服务器配置文件指定触发RDB条件。
RDB的几个优点
- 与AOF方式相比,通过rdb文件恢复数据比较快。
- rdb文件非常紧凑,适合于数据备份。
- 通过RDB进行数据备,由于使用子进程生成,所以对Redis服务器性能影响较小。
RDB的几个缺点
- 如果服务器宕机的话,采用RDB的方式会造成某个时段内数据的丢失,比如我们设置10分钟同步一次或5分钟达到1000次写入就同步一次,那么如果还没达到触发条件服务器就死机了,那么这个时间段的数据会丢失。
- 使用save命令会造成服务器阻塞,直接数据同步完成才能接收后续请求。
- 使用bgsave命令在forks子进程时,如果数据量太大,forks的过程也会发生阻塞,另外,forks子进程会耗费内存。
AOF
AOF持久化方式会记录客户端对服务器的每一次写操作命令,并将这些写操作以Redis协议追加保存到以后缀为aof文件末尾,在Redis服务器重启时,会加载并运行aof文件的命令,以达到恢复数据的目的。
三种写入策略
- always: 客户端的每一个写操作都保存到aof文件当,这种策略很安全,但是每个写请注都有IO操作,所以也很慢。
- everysec: appendfsync的默认写入策略,每秒写入一次aof文件,因此,最多可能会丢失1s的数据。
- no: Redis服务器不负责写入aof,而是交由操作系统来处理什么时候写入aof文件。更快,但也是最不安全的选择,不推荐使用。
AOF的优点
- AOF只是追加日志文件,因此对服务器性能影响较小,速度比RDB要快,消耗的内存较少。
AOF的缺点
- AOF方式生成的日志文件太大,即使通过AFO重写,文件体积仍然很大。
- 恢复数据的速度比RDB慢。
参考链接: https://juejin.im/post/5d09a9ff51882577eb133aa9 https://www.cnblogs.com/xiaoqiang-code/p/11748395.html
8. Redis 怎么实现分布式锁?
Redis的实现主要基于setnx 和给予一个超时时间(防止释放锁失败)。
多个尝试获取锁的客户端使用同一个key做为目标数据的唯一键,value为锁的期望超时时间点; 首先进行一次setnx命令,尝试获取锁,如果获取成功,则设置锁的最终超时时间(以防在当前进程获取锁后奔溃导致锁无法释放)这里利用 Redis set key 时的一个 NX 参数可以保证在这个 key 不存在的情况下写入成功。并且再加上 EX 参数可以让该 key 在超时之后自动删除。一定不要把两个命令(NX EX)分开执行,如果在 NX 之后程序出现问题就有可能产生死锁。非阻塞锁、阻塞锁、解锁、为了更好的健壮性,将该操作封装为一个lua脚本,这样即可保证其原子性 redis分布式的实现原理:
通过setNX操作,如果存在key,不操作;不存在,才会set值,保证锁的互斥性2、value设置锁的到期时间,当锁超时时,进行getAndSet操作,先get旧值,再set新值,避免发生死锁。这里也可以通过设置key的有效期来避免死锁,但是setNx和exprise(设置有效期)操作非原子性,可能发生锁没有设置有效时间的问题,从而发生死锁。实现:spring boot 通过jdeis连接redsi集群。
加锁过程:
- 获得当前系统时间,计算锁的到期时间
- setNx操作,加锁
- 如果,加锁成功,设置锁的到期时间,返回true;取锁失败,取出当前锁的value(到期时间)
- 如果value不为空而且小于当前系统时间,进行getAndSet操作,重新设置value,并取出旧value;否则,等待间隔时间后,重复步骤2;
- 如果步骤3和4取出的value一样,加锁成功,设置锁的到期时间,返回true;否则,别人加锁成功,恢复锁的value,等待间隔时间后,重复步骤2。
9. Redis中执行Lua脚本
Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。使用脚本的好处如下:
- 减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成。使用脚本,减少了网络往返时延。
- 原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。
- 复用:客户端发送的脚本会永久存储在Redis中,意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑。
redis 事务和lua 脚本的比较
相同点
很好的实现了一致性、隔离性和持久性,但没有实现原子性,无论是redis事务,还是lua脚本,如果执行期间出现运行错误,之前的执行过的命令是不会回滚的。
不同点
- redis事务是基于乐观锁,lua脚本是基于redis的单线程执行命令。
- redis事务的执行原理就是一次命令的批量执行,而lua脚本可以加入自定义逻辑。
https://www.cnblogs.com/yanghuahui/p/3697996.html
https://blog.csdn.net/u013452337/article/details/103281691
https://blog.csdn.net/fangjian1204/article/details/50585080
10. Redis 分布式锁有什么缺陷?
在工作和网络上看到过各个版本的Redis分布式锁实现,每种实现都有一些不严谨的地方,甚至有可能是错误的实现,包括在代码中,如果不能正确的使用分布式锁,可能造成严重的生产环境故障。 redis分布式锁的缺陷
https://blog.csdn.net/matt8/article/details/64442064
11. Redis 如何做内存优化?
- 使用对象共享池优化小整数对象。
- 数据优先使用整数,比字符串更节省空间。3,操作优化。尽量避免字符串的追加操作,因为字符串存在预分配机制。追加操作后字符串对象会分配一倍的容量作为于预留空间。4,编码优化。list,hash,set,zset尽可能使用ziplist编码。好处是内存下降,坏处是操作变慢。一般大小不超过1000。
12.Redis 淘汰策略有哪些?
redis 提供 6种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
13.Redis 常见的性能问题有哪些?
该如何解决?
- master写内存快照,seve命令调度rdbsave函数,会阻塞主线程的工程,当快照比较大的时候对性能的影响是非常大的,会间断性暂停服务。所以master最好不要写内存快照。
- master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响master重启时的恢复速度。master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个slave开启AOF备份数据,策略每秒为同步一次。
- master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂的服务暂停现象。4.redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,slave和master最好在同一个局域网内。
参考资料: https://www.nowcoder.com/discuss/412166?type=all&order=time&pos=&page=1