为什么使用Redis
-
性能:在遇到数据库中经常被查询的字段可以将它存在Redis中提高效率
-
Redis为什么快
-
-
并发:防止并发状态下大量请求直达数据库,造成数据库连接异常
单线程的redis为什么这么快
-
纯内存操作
-
单线程避免了频繁的上下文切换
-
采用了非阻塞的I/O多路复用机
-
操作系统使用一个线程去监听socket连接,当监听到读操作或写操作的时候,就把数据交给应用层;
-
Redis只有单线程吗
redis的单线程主要是指Redis的网络I/O线程;Redis的持久化、集群同步等操作,都是由另外的线程执行
-
在redis的4.0版本之后:Redis添加了多线程的支持,主要体现在大数据的异步删除功能上;
-
在Redis的6.0版本之后:新增了多线程I/O读写并发的能力
采用了多个线程处理网络I/O请求,这是因为网络硬件的提升,redis的性能瓶颈有时会出现在网络I/O的处理上,所以为了提高网络请求的并行度,6.0对于多线程采用了并行处理,但是读写命令,Redis仍然是单线程处理的
缓存击穿
Redis中一个热点key在失效的同时,大量的请求过来,从而会全部到达数据库,压垮数据库。
解决
-
热点数据永不过期:对于某些场景不适合
-
定时更新:
-
互斥锁:解决缓存击穿常用的方式,当Redis根据key获取的value为空时,添加互斥锁,并从数据库更新数据,若其他线程请求该key,则获取锁失败,睡眠一段时间后重试。
缓存穿透
缓存和数据库中都没有的数据,可用户还是源源不断的发起请求,导致每次请求都会到数据库,从而压垮数据库
解决
-
业务层校验:对于错误的请求直接返回
-
不存在的数据设置短过期时间
-
布隆过滤器
缓存雪崩
Redis中缓存的数据大面积同时失效,或者Redis宕机,从而会导致大量请求直接到数据库,压垮数据库。
解决
-
设置有效期均匀分布
-
数据预热:对于即将来临的大量请求,可以提前走一遍系统,提前缓存,设置不同的过期时间
-
保证Redis服务高可用:前面我们介绍过Redis的哨兵模式和集群模式,为防止Redis集群单节点故障,可以通过这两种模式实现高可用。
为什么 Redis 将其整个数据集保存在内存中?
Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度会严重影响redis的性能。
redis客户端缓存
优点
-
数据以非常小的延迟提供
-
数据库系统接受的查询较少,从而允许他为较少节点数的相同数据集提供服务(即为热点数据提供服务)
redis过期策略及内存淘汰机制
-
redis采用的是定期删除+惰性删除策略。
-
为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.
-
定期删除+惰性删除是如何工作的呢? 定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。因此在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
-
惰性删除容易造成内存占用过多的情况
-
淘汰策略:
noeviction:内存不足以容纳新写入的数据,新写入会报错,不推荐;
allkeys-lru:内存不足以容纳新写入的数据,在键空间中,移除最近最少使用的key,推荐,都在用;
allkeys-random:内存不足以容纳新写入的数据,在键空间中,随机删除,不推荐;
volatile-lru:只从设置过期时间的key中删除最近最少使用;不推荐
volatile-random:只从设置过期时间的key中随机删除;不推荐
volatile-ttl:从设置过期时间的key中删除最早过期的;不推荐
Redis和数据库双写一致性问题
-
redis与数据库只能保证最终一致性,而不能保证强一致性;
-
先删除缓存,再更新数据库;
-
假设A查询,B更新,可能会造成双写一致性问题
B更新,删除缓存;
A查询,缓存无数据;
A从数据库中查到旧值,
A将旧值更新到缓存;
B更新数据库完毕
可以使用延时双删解决这个问题
(1)先淘汰缓存 (2)再写数据库(这两步和原来一样) (3)休眠一定时间,再次淘汰缓存
如果使用了MySQL读写分离架构
还是使用双删延时策略,只是,睡眠时间修改为在主从同步的延时时间基础上,加几百ms
吞吐量降低怎么办
将第二次删除操作设置为异步的
如果第二次删除失败则么办?
-
-
先更新数据库,再删除缓存;
失效:先把数据放入数据库中,成功后,再让缓存失效
A查询,B更新
(1)缓存刚好失效 (2)请求A查询数据库,得一个旧值 (3)请求B将新值写入数据库 (4)请求B删除缓存 (5)请求A将查到的旧值写入缓存
-
这种发生错误的概率很小
-
三种Redis持久策略
1. RDB(半持久化的方式)
-
概述
RDB的方式是将某一时刻的数据记录下来,而不是记录操作,因此在恢复的时候,只需要把RDB文件读入到内存,就可以完成快速恢复;
按照配置不定期地通过异步的方式、快照的形式直接把内存的数据持久化到磁盘的一个dump.rdb文件(二进制文件)中,
redis默认的持久化方式,它配置在它的配置文件(redis.conf)中。
-
RDB快照的时候会不会阻塞线程:
save会,bgsave会创建一个子线程,所以不会;
-
如果在bgsave快照的时候主线程执行操作,Redis怎么做
如果是读操作,则没有影响;写操作:被修改的数据会复制一份副本,然后bgsave子线程会把副本数据写入RDB文件;
-
优缺点
-
优点:只包含一个文件,讲一个单独的文件转移到其他存储媒介上,对于文件备份,灾难恢复而言,比较实用。
-
缺点:系统一旦在持久化策略之前出现宕机现象,此前还没有持久化的数据将会丢失。
-
配置
-
在配置文件6379.conf中 save 900 1 #表示在900秒之后,如果至少有一个key发生变化,则将数据集的快照dump到dump.rdb文件中。
2. AOF(全持久化的方式)
-
概述
在Redis执行命令,把数据写入到内存,将命令写入文件当中(将日志记录到文件);
Redis默认是不支持这种全持久化方式的,需要在配置文件中将appendonly no改为appendonly yes。
-
redis为什么会在命令执行成功后才写入文件,而不是之前
因为Redis在写入日志之前,是不会对命令进行检查的,所以就可以只记录成功的命令;
风险是可能会丢失数据,在命令成功之后宕机
-
优缺点
-
优点:数据安全性高,对日志文件的写入是append模式,因此即使再写入中发生了宕机问题,也不会破坏日志中已存在的内容。
-
缺点:对于数量相同的数据集来说,aof文件通常比rdb文件大,因此rdb文件回复的速度大于aof文件
-
配置
在 Redis 的配置文件中存在三种同步方式,它们分别是:
-
appendfsync always #每次有数据修改发生时都会都调用 fsync 刷新到 aof 文件,非常慢, 但是安全;
-
appendfsync everysec #每秒钟都调用 fsync 刷新到 aof 文件中,很快,但是可能丢失一秒内 的数据,推荐使用,兼顾了速度和安全;
-
appendfsync no #不会自动同步到磁盘上,需要依靠 OS(操作系统)进行刷新, 效率快,但是安全性就比较差;
3. AOF和RDB混合持久化
把数据以RDB的方式写入文件,之后的操作命令以AOF的格式存入文件,既保证了Redis的重启速度,又降低了数据丢失的风险
4. AOF和RDB持久方式的区别
AOF往往在运行效率上慢于RDB,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。
如果缓存数据安全性要求比较高的话,使用AOF持久化方式(比如项目中的购物车)
如果对于大数据集要求效率高的话,可以使用默认的RDB持久化方式。
两种持久化方式可以同时使用。
Redis的高可用
redis高可用有三种方式
1. 主从复制(最基础)
基础保证,将原本的一台Redis服务器,同步数据到多台服务器上,实现主写从读的读写分离,可以承载更多的并发服务
2. 哨兵模式
因为主从同步模式服务器宕机后需要手动恢复,Redis添加了哨兵模式监控主从服务器,并提供自动容灾恢复的功能
3. Redis集群(最常用)
一种去中心化的运行模式,将数据分布在不同的服务器上,降低对于单主节点的压力和依赖,从而提高读写效率
一个切片集群有16384个哈希槽,每个键值都会根据key值映射到对应的哈希槽中
哈希槽的映射规则:
-
平均分配:
-
手动分配:
Redis分布式锁是怎么回事
先拿setnx来争抢锁,抢到之后在使用expire给锁加一个过期时间防止忘记释放;
-
扩展:如果在setnx之后expire之前进程意外carsh或者是重启维护了,怎么办
思考之后,记得redis好像有一个把setnx和expire合成一条指令来用的