什么是redis?
它是一个key-value的数据结构的非关系数据库,具有五种基础的数据类型:String(字符)、list(列表)、Hash(哈希表)、set(集合)、sort set(有序集合)。
它具有读写性能优异、数据结构丰富、支持数据持久化、支持主从复制(实现读写分离)、支持事务(redis具有原子性操作,但事务不保证原子性操作)。
缺点:数据库容量受到内存限制、不具备自动容错和数据恢复、很难进行在线扩容、主机宕机、宕机前部分数据无法同步到从机上,导致切换ip引入数据的不一致性、降低了系统的可用性
我们为什么需要使用redis的缓存?
我们可以从高性能和高并发这个两条件去解释:
当我们第一次访问数据库时,速度很慢,是从硬盘中 进行访问的,当我们使用redis缓存,将数据存储到缓存中进行读取,速度效率就大大提高了,因为缓存是存储于内存中的,我们就是直接同过去操作内存去访问资源----高性能
当我们数据库遇到并发请求访问同一条数据时,有可能会出现数据库连接问题,这个时候我们可以通过redis进行一个缓冲操作,将热点数据存储在缓存中,当缓存中没有该数据我们才会去访问数据库-----高并发
redis为什么这么快呢?
因为它是基于内存操作的,使用的是单线程避免了上下文的切换和竞争条件、使用了多路IO的复合模型,使用同步非阻塞式NIO、数据结构简单、使用了虚拟机机制(降低了浪费时间移动和请求)
为什么采用单线程?
因为多线程并发请求,你需啊考虑线程安全,数据读取是否一致,我们需要通过上锁的机制,但有可能会出现死锁的情况,并且线程之间的频繁切换加大CPU的开销。
Redis的持久化操作?
将内存数据存储到磁盘中进行保存,防止数据库宕机
RDB:按照一定时间通过快照的方式将内存数据存储到磁盘中,通过配置文件中的save参数定义快照的周期。
优点:文件体积小、紧凑、网络传输快、适合全局复制、恢复速度比AOF快。对性能影响最小
缺点:无法做到实时持久化,会出现数据丢失的情况
AOF:将redis执行每次写入的命令记录到单独的日志文件中,通过重启redis来进行持久化恢复数据
优点:数据安全、没写入一次命令就进行计入、支持秒级持久化、兼容性好
缺点:文件大、恢复速度慢、对性能影响大
redis缓存持久化建议:
可以承受数据丢失的用RDB持久化或者两者同时使用,不过优先加载AOF,兼容性有点差
持久化数据采用一致hash实现动态扩容和缩容操作
过期删除策略?
采用expire进行对key进行设置过期时间,使用PERSIST保证是永远的热点数据永不过期
定时:每个过期时间的key设置一个监视器,到时间立即进行清除,对内存十分友好,但加到了CPU的负载,影响了缓存的响应时间和吞吐量
定期:每隔一段时间,对缓存中的key进行一个抽查操作,如果过期进行删除操作。大大降低了CPU和内存的消耗
惰性:只有当访问一个可以的时候,我们才会去对其进行判断是否过期,如果过期,进行清除,减轻了CPU的负担,但对内存很不友好,如果出现大量过期key没有访问,那么就得不到清除,会占有内存
这里我们可以同时使用定期和惰性两种方法进行过期操作
缓存问题如何解决?
缓存穿透:查询一个不存在的数据,这个查不到的数据不会写入缓存,导致不存在数据会每次去数据库查询,从而使数据库挂掉
解决方案:查询返回数据为空,将空结果缓存进数据或者使用布隆过滤器
布隆过滤器:5 分钟搞懂布隆过滤器,亿级数据过滤算法你值得拥有! - 知乎 (zhihu.com)
缓存击穿:设置了过期时间的key,缓存到期时,这个key遇到并发请求,然后请求会发现过期缓存会从数据库加载后返回到缓存瞬间把数据库压垮
解决方案:使用互斥锁,通过逻辑去过期key
缓存雪崩:缓存设置了相同的过期时间到了,所有缓存同一时刻失效,数据库就崩了
解决方案:将缓存时间分散开,每个失效时间设置一个随机值,避免集体失效
事发前:搭建主从复制以及哨兵模式
事发后:通过持久化操作恢复或者设置本地缓存以及做限流处理
Redis的集群搭建?
集群:通过添加服务器数量,提供相同的服务,使服务器到达稳定、高效的状态
redis主从架构:
单机的redis能够承受的每秒查询率也就几万条,对于缓存来说,一般用于支撑高并发请求。因此使用主从架构,一主多从,主负责写,将数据异步复制到从数据上,从数据用来读。实现水平扩容,支撑读高并发请求。
作用:主从复制实现热备份、当主机出现问题,可以通过从节点进行故障恢复、高可用和负载均衡(读写分离)
注意:启用主从架构,需要开启主的持久化,不建议开启从的持久化操作。因为如果没有开启主机的持久化,主机宕机重启数据是空的,进过一系列复制到从机上,从机的数据也出现丢失了
缺点:主机宕机,整个节点就没有有可以写的节点,需要通过哨兵模式来进行监控和故障排查
主从复制的核心原理:
当从库与主库进行连接关系,会发送同步请求
当主库接收到从库发送的请求会在后台保存快照(RDB快照持久化),将期间接收到的命令存起来
当快照完成以后,主库会将快照文件和命令发送给从库
从库接收到命令,会下载快照文件和植入缓存命令
之后主库每次接收到命令都会发放给从库,到达主从一致
redis哨兵模式
管理多台服务器执行它的监控、提醒、自动故障移除
监控:哨兵会不断地检查你的主服务器和从服务器是否运作正常。
提醒:当被监控的某个 Redis 服务器出现问题时,哨兵可以通过 API 向管理员或者其他应用程序发送通知。
自动故障移除: 当一个主服务器不能正常工作时,哨兵 会开始一次自动故障迁移操作, 它会进行选举,将其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
缺陷:如果主库下只有一个从库,哨兵无法缓解并发请求
Redis-cluster集群
由多个Redis服务器组成的分布式网络服务集群;集群之中有多个Master主节点,每一个主节点都可读可写。节点之间会互相通信,两两相连,Redis集群无中心节点。
Redis事务?
什么是事务?
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
redis事务?
redis单条命令是原子性操作的,但redis事务不保证原子性操作,它具有事务的一致性和隔离性
Redis如何保证与数据库一致性?
使用两种策略:
1.先删除缓存、再更新数据库----->在高并发下表现的不如意,在原子性被破环时表现优异
2.先更新数据库,再删除缓存 ----->在高并发先表现优异,在原子性被破坏时表现不如意
第一种:如果缓存删除成功,数据库更新失败,那么缓存和数据库还是一致的
如果缓存删除失败,我们可以直接返回异常,数据还是一致的
但是在高并发环境下,A删除了缓存,B去进行查询操作,数据为空。B就去数据库读旧数据,在写入缓存,在进行更新数据库,数据就产生不一致了。
解决方案:将删除缓存、修改数据库、读取缓存等的操作积压到队列里边,实现串行化。
第二种:会出现数据库更新成功,缓存删除失败,到处数据库是新值,缓存中还是旧数据
解决方案:将需要删除的key发送到消息队列中---自己消费消息,获得需要删除的key---不断重试删除操作,直到成功
Redis分区?
分区可以让Redis管理更大的内存,Redis将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。分区使Redis的计算能力通过简单地增加计算机得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长。
Redis的分布式锁?
什么是分布式?
不同的业务模块部署在不同的服务器上或者同一个业务模块分拆多个子业务,部署在不同的服务器上,解决高并发的问题,提供可扩展性以及高可用性,业务中使用分布式的场景主要有分布式存储以及分布式计算。分布式存储中可以将数据分片到多个节点上,不仅可以提高性能(可扩展性),同时也可以使用多个节点对同一份数据进行备份。
分布式锁?
当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。
作用:随着业务的需求不断增加我们从一个单点部署系统变成了分布式集群系统。我们需要解决他跨jvm共享资源的问题
条件:
1、原子性,在分布式系统环境下,只有一个客户端能持有锁;加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
2、容错性,高可用的获取锁与释放锁。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁;
3、高性能,获取锁与释放锁的操作消耗小;
4、可重入,同一线程在未释放锁时如果再次申请锁资源不需要走申请流程,只需要将已经获取的锁继续返回并且记录上已经重入的次数即可;
5、具备锁失效机制,防止死锁,即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁;
6、可阻塞,这把锁最好是一把阻塞锁(根据业务需求考虑要不要这条),阻塞锁即没有获取到锁,则继续等待获取锁;非阻塞锁即没有获取到锁后,不继续等待,直接返回锁失败;
使用场景
数据库:锁表和排他锁
redis:保证同一时刻只有一个客户端能持有锁(互斥性)、不会出现死锁、只要大部分的redis节点正常运行,那么客户端可以加锁和解锁只能加自己的
使用方法:
使用 SETNX(set if not exist)命令插入一个键值对时,如果 Key 已经存在,那么会返回 False,否则插入成功并返回 True。因此客户端在尝试获得锁时,先使用 SETNX 向 Redis 中插入一个记录,如果返回 True 表示获得锁,返回 False 表示已经有客户端占用锁。
EXPIRE 可以为一个键值对设置一个过期时间,从而避免了死锁的发生。