面试时被面试官问到 Redis 了 ,当时那分钟就只知道是数据缓存
我后来百度总结了一下 分享给大家 ,希望能帮到你们
什么是Redis
Redis是一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server (远程数据服务),使用C语言编写,Redis是一个key-value存储系统(键值存储系统),支持丰富的数据类型,如:String、list、set、zset、hash。
Redis是一种支持key-value等多种数据结构的存储系统。可用于缓存,事件发布或订阅,高速队列等场景。使用C语言编写,支持网络,提供字符串,哈希,列表,队列,集合结构直接存取,基于内存,可持久化。
1.使用redis缓存的好处
- 提高并发量,不需要每次都访问数据库,提高并发量。 提高性能,redis运行在内
- 存中,内存的执行效率,远远超过数据库。
2.Redis的key和value的存储大小有限制吗?
Redis的key和value的存储大小都是有限制的,都是512M。
3.redis存储什么数据类型
(1)字符串(String)
(2)字符串列表(lists)
(3)字符串集合(sets)
(4)有序字符串集合(sorted sets)
(5)哈希(hash)
4.使用redis缓存的弊端
- 缓存穿透,大量的请求访问,查询的是数据库中不存在的数据,就会在缓存中无法命中,直接去访问数据库,数据库中也查询不到,自然无法将结果写入缓存,下一秒又有大量请求查询不存在的数据,导致数据库最终挂掉(要么系统出bug,要么黑客恶意攻击)
解决方案:
-
在数据库中没有查询到的数据,存储一个特定值到缓存中,这样下次恶意访问就可以去缓存中查询到数据,不会访问到数据库.注意的是这些特定的key,需要设置过期时间,避免黑客攻击时,大量的无效key把redis存满。
-
缓存雪崩,大规模的key失效,大量的请求通过key访问到数据库,导致数据库直接崩溃,然后系统直接瘫痪.
-
解决方案:
事前: 保证redis集群的高可用性,redis cluster,主从机制;
事中: hystrix(熔断器)限流+降级,避免数据库被打死.高并发的访问量走hystrix限流组件,让访问量保持在数据库最大负载范围内,保证数据库不被打死,系统可以继续使用.剩余的请求走降级组件,返回一些默认的值或者是友情提示等
事后: redis持久化机制,尽快回复缓存集群,一旦重启,自动从磁盘上加载数据,回复内存中的数据 -
缓存与数据库不一致,修改数据时,数据库和缓存数据不一致。 最初级的缓存不一致:
出现场景:
先修改数据库,再删除缓存,如果修改缓存失败,就会导致数据库中式新数据,缓存中是旧数据,数据不一致 解决思路:
先删除缓存,再修改数据库,如果删除缓存成功,修改数据库失败,那么数据库中是旧数据,缓存中是空的,读的时候缓存中没有,去数据库中读旧数据,然后更新到缓存中去 -
高并发场景下数据库与缓存的数据不一致:
出现场景:
读写并发请求,导致数据库和缓存中的数据不一致,在写请求删除缓存,修改数据库库存还未成功时,查询库存的请求就发来,先去缓存中查询,发现是空,然后去数据库中查,然后将结果放入缓存中,然后修改库存的操作成功,导致数据库中是新数据,缓存中是旧数据 -
解决思路:
相同的商品id,进行哈希取值,再加上对内存队列的数量进行取模,每个商品都可以路由到某一个内存队列中,然后将去请求和写请求串行化,这样就可保证一定不会出现不一致的情况,但是会导致系统的吞吐量会大幅降低 -
缓存并发竞争,同一时间,多个线程来执行,操作同一个key。 解决思路:
方式一:分布式锁,(zookeeper分布式锁),确保同一时间,只能由一个系统实例在操作某个key,别的实例不被允许读和写
方式二:乐观锁 ,每次要写之前,先判断这个value的时间戳是否比缓存里的更新,如果新就允许写.
5.为什么redis是单线程的但是还可以支撑高并发?
- redis线程模型:redis里有个处理器叫做文件事件处理器(file event handler),这个文件事件处理器是单线程的执行的,redis所以是单线程的模型。
执行流程:
-
redis启动初始化时,redis会将连接应答器和AE_READABLE事件关联
-
客户端跟redis连接,会在serverSocket产生一个AE_READABLE事件,然后由IO多路复用程序监听到事件,将serverSocket压入队列,然后连接应答处理器来处理事件,和客户端建立连接,创建对应的socket,同时将这个socket的AE_READABLE事件跟命令请求处理器关联起来
-
客户端向redis发起请求时,首先会在socket中产生一个AE_READABLE事件,然后被监听,被压入队列,然后由对应的命令请求处理器来处理
-
redis准备好给客户端响应数据之后,就会将socket的AE_WRITABLE命令跟回复处理器关联起来,当客户端准备好读取响应数据时,就会在socket上产生一个AE_WRITABLE事件,会由对应的命令回复处理器来处理
-
命令回复处理器处理完之后,就会删除这个socket的AE_WRITABLE事件和命令回复处理器的关联关系
6.Redis为什么效率高?
- 核心是基于非阻塞的IO多路复用机制,由监听程序轮询等待的事件,然后压入队列,可以达到一个线程同时处理多个io请求的目的
- 纯内存操作
- 单线程避免了多线程的频繁切换问题
7.Redis由哪些数据类型,分别在哪些场景下使用?
- String,存储基本的类型
- List,有序列表,比如之前项目中的广告缓存,用的就是list集合进行缓存的,它可以用于比如存储粉丝列表,文章的评论列表等等
- Set,无序列表,它最主要的特点就是去重,比如微博上查询两个明星的共同粉丝,就可以用set进行去重
- SortSet,它主要是在set的基础上加上了排序功能
- Hash,类似map的集合,一般可以用来存储对象,我们系统中的购物车对象就是以hash类型存储在redis中,key就是用户的id,value就是购物车对象
8.Redis过期策略
- 定期删除配合惰性删除
- 定期删除,指的是redis默认每个100ms就抽取一些设置了过期事件的key,检查是否过期,如果过期就删除.如果太多的key都设置了过期时间,每隔几百毫秒,就全部检查删除,cpu的负载会很高.实际上redis是每隔100ms随机抽取一些key来检查和删除.
- 惰性删除,就是当你获取key时,redis就会检查一下,如果key过期了,就删除,不会返回,如果没过期就返回
- 因为redis的过期策略会导致你设置了过期key,过期了很多过期key没有被删除,还堆积在内存中,此时就要设置内存淘汰机制来解决了
- 内存淘汰机制
- noevication:当内存不足以容纳写入新数据时,新写入操作就会报错
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key
- allkeys-random:在键空间中,随机移除某个key
- volatile-lru:在设置了过期时间的键空间中,移除最近最少使用的key
- volatile-random:在设置了过期时间的键空间中,随机移除某个key
- volatile-ttl:在设置了过期时间的键空间中,由更早过期时间的key优先移除
9.怎样做redis支撑高并发(读多写少用缓存,读少写多用队列)
- 搭建redis集群,做成主从架构,一主多从,通过redis replication机制实现读写分离,主负责写,并且将数据同步复制到其他slave(从)节点上,从节点负责读,所有的读请求走从节点.好处,可支持水平扩容的读高并发架构
- redis replication的核心机制
- redis采用异步方式复制数据都slave节点上,slave节点会周期性的确认自己每次复制的数据量
- 一个master(主)节点可以配置多了slave节点
- slave节点也可以连接其他slave节点
- slave节点做复制的时候,也不会阻塞对自己的查询操作,它会用旧数据向外提供服务;复制完后,它就删除旧数据,加载新数据,这个时候向外停止服务(毫秒级别)
- slave节点主要用来横向扩容,做读写分离,提高吞吐量
Master(主节点)持久化对于主从架构的安全保障的意义
- 如果仅仅用slave节点作为master节点的数据热备份,当master节点宕机后重启,数据清空,本地没有数据可以恢复,它会任为自己的数据就是空的,然后同步到slave节点上,就会导致数据百分百丢失
- 其次,如果开启了哨兵机制,在master节点挂掉之后,会迅速推选出一个slave节点去做master节点,但是当哨兵还没检测到master节点挂掉,master节点就自动重启了,还是会出现上面的情况
redis主从架构的核心原理
- 当启动一个slave节点时,会发送一个PSYNC命令给master节点
- 如果这个slave节点第一次连接master节点,就会出发full resynchronization
- 开始full resynchronization的时候,master会开启一个后台线程,开始生成一份rdb快照文件,同时还还会继续接收客户端的写命令,存在内存中.RDB文件生成完毕后,master会将这个RDB发送给slave,slave先将RDB写入本地自盘,然后再加载到内存.然后master会将内存中缓存的写命令发送给slave,slave同步这些数据
redis的高可用
- 不可用: 虽然主从架构,读写分离可以保证缓存应对高并发场景,但是如果master节点挂掉,整个缓存架构就会挂掉,大量的流量涌入数据库,超过数据库的最大承载压力,数据库挂掉,整个系统就会挂掉不可用
- 高可用: 故障转移,主备切换,当master故障时,自动检测(通过sentinal node 哨兵),将某个slave自动切换成master的过程交主备切换.这个机制实现了redis主从架构下的高可用性
10.redis的持久化
-
如果没有持久化,遇到灾难性故障时,就会丢失所有数据,如果开启持久化到磁盘,定期备份到云服务上,就能保证遇到灾难性故障,就不会丢失全部数据
-
RDB
-
当redis需要做持久化时,redis会fork一个子进程,将数据写到磁盘写上一个临时RDB文件中,当子进程完成写临时文件后,将原来的RDB换掉
-
优势:
适合做冷备份,在最坏的情况下,恢复数据要比AOF快
对redis对外提供读写服务,影响服务非常小
RDB数据快照文件更少 -
AOF
-
可以做到更精细的持久化,redis每执行一个修改数据的命令,都会将它添加到os cache中,一般会每个一秒执行一次fsync操作,保证将os cache中的数据写入磁盘中AOF日志文件中,AOF日志文件以append-only模式写入,文件不容易破损,在AOF日志过大时,就会出现后台重写
-
优势:
AOF可以更好的保护数据不丢失,丢失的数据更少
AOF日志文件以append-only模式写入,没有磁盘寻址的开销,写入性能非常高
AOF日志过大时,也不会影响客户端的读写 -
RDB和AOF到底如何抉择
-
综合使用两种持久化机制,用AOF保证数据不丢失,作为数据恢复的第一选择,用RDB来做不同程度的冷备份,在AOF文件丢失或者损坏不可用的情况下,用RDB来进行快速的数据回复
11.redis怎么设置缓存大小
打开redis配置文件
示例:maxmemory 100mb
单位:mb,gb。
默认为0,没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使redis崩溃,所以一点要设置。
设置maxmemory之后,配合的要设置缓存数据回收策略, 可以通过设置LRU算法来删除部分key,释放空间(LRU是Least Recently Used 近期最少使用算法。)。
就可以修改缓存大小为16gb.
12.redis集群主从数据如何同步
答1:
Redis主从同步有两种方式:全同步和部分同步。主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。
答2:
(1)全量同步:
Redis全量复制一般发生在S-ave初始化阶段,这时S-ave需要将Master上的所有数据都复制一份。具体步骤如下:
1)从服务器连接主服务器,发送SYNC命令;
2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;
(2)增量同步:
Redis增量复制是指S-ave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令
(3)Redis主从同步策略:
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,s-ave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
13.Redis缓存穿透和redis雪崩处理
(1)缓存穿透
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,应该去后端系统查询(比如数据库)。如果key对应的value不存在,并且对key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
解决方案(晦涩)
对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。还有最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
也可以采用一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
(2)缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如数据库)带来很大的压力。
缓存雪崩是指在我们设置缓存是采用了相同的过期时间,导致缓存在某一时期同时失效,请求全部转发到DB,DB瞬间压力过大雪崩。
解决方案:
把缓存时间分散开,比如在原来的时间基础上增加一个随机值,比如1~5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。