1. 认识Redis
Redis与Memcached 有什么区别
为什么采用Redis作为MySQL的缓存?
2. Redis 数据结构
常见的五种数据类型:String,Hash,Set,Zset,List
随着Redis新版本的发布,有支持以下四种方式:Bitmap,Hyperloglog,GEO,Stream
五种数据类型的应用场景:
数据类型的内部实现:
String:
List:
Hash:
Set
Zset
Redis 的线程模型
Redis是单线程吗?
Redis是单线程指的是 接收客户端请求-—》解析请求—》进行数据读写—》发送数据给客户端---》 这个过程是由一个线程(主线程)来完成的。Redis并不是单线程的,在Redis启动时,会启动后台线程。
Redis的持久化
Redis是如何实现数据不丢失的?
一共由三种方式:
AOF 日志:
先执行写操作,后将该命令计入到AOF日志中。
两个好处:
两个风险:
AOF写回策略:
过程图
Redis写回硬盘策略:
当AOF文件过大时,会触发AOF重写机制。当AOF文件的大小超过所设阈值时,就会触发AOF重写机制,来压缩AOF文件。
重写AOF日志的过程是怎样的?
重写AOF日志过程是由后台子进程 bgrewriteaof 来完成的。这样做有两个好处:
1)子进程在进行重写期间,主进程可以继续处理请求,从而避免阻塞主进程。
2)子进程带有主进程的数据副本。如果采用线程的形式,多线程之间会修改共享数据。需要通过来保证线程安全。会降低性能。而使用子进程时,由于是父子进程,父子进程之间可以共享数据。但是只能以只读的形式。父子进程任意一方修改了共享数据,就会发生写时复制。于是父子进程就会有独立的数据副本,就不需要加锁。
触发重写机制后,主进程就会创建子进程。此时父子进程共享内存数据。重写子进程只会对这个内存数据进行只读。子进程会读取数据库的所有键值对,并以一条命令的形式保存到AOF文件中。
但是在子进程重写过程中,主进程依然可以正常处理命令。 但是,在重写AOF日志中,如果主进程已经修改了存在的key-value,就会发生写时复制。那么key-value在AOF文件中的数据与数据库中的数据就会不一致。
为了解决以上问题,Redis设置了一个AOF重写缓冲区。这个缓冲过去在创建子进程bgrewriteaof 开始使用。
在重写AOF日志时,当Redis执行一个命令之后,就会将该命令写入到AOF文件和AOF重写缓冲区。
在重写子进程重写期间,主进程会有以下工作:
当子进程完成重写工作后(扫描数据库中的所有键值对,逐一将数据库中的键值对转化为命令存入AOF文件中)会向主进程发送一条信号。
当主进程收到信号后,会调用一个函数处理这个信号。它主要做一下两个工作:
信号函数执行之后,主进程就会运行啦
RDB快照是如何实现的?
RDB做快照时会阻塞线程吗?
Redis提供了两个命令save 和 bgsave 来生成RDB文件。它们的区别在于是否在主线程执行。
save:执行了save命令,就会在主线程生成RDB文件,可能会阻塞主线程.
bgsave:执行了bgsave命令,就会创建一个子进程来生成RDB文件,这样可以避免主线程的阻塞。
RDB快照是全量快照。每次执行快照,就会把内存中的所有数据存入到磁盘中,这个任务十分巨大,执行太频繁,可能会对Redis性能造成影响。
RDB在执行快照时,数据能修改吗?
在执行 bgsave Redis仍然可以执行数据修改操作。原因就在于写时复制技术。
执行bgsave过程中,会通过fork 创建子进程,此时子进程是和主进程共享内存数据。因为创建子进程时,会复制主进程的页表,但是页表指向的物理地址还是同一个。
如果主进程执行读操作,则主进程和子进程互不影响。
如果主进程执行写操作,则被修改的数据会复制一份副本,然后bgsave子进程沪江副本数据写入到RDB文件中。在这个过程中,主进程仍然可以修改原来的数据。
为什么会有混合持久化?
RDB的优点是数据恢复速度快,但是恢复的频率不好把握。频率太低,丢失的数据会变多,频率变快,会影响Redis的性能。
AOF日志的优点是丢失数据少,但是恢复速度不快。
为了集成两者的优点,采用混合使用AOF和内存快照。也叫混合持久化。既保证了Redis重启速度,又降低了数据丢失风险。
混合持久化工作在AOF重写过程中,当开启了混合持久化,在AOF子进程重写日志时,fork创建出来的子进程会先将于主进程共享的内存数据以RDB的形式写入到AOF文件中,然后后面主进程的操作命令会写入到AOF重写缓冲区,重写缓冲区的增量命令会以AOF的格式写入到AOF文件中。写入完成后会将新的含有RDB格式和AOF格式的AOF文件替换掉旧的AOF文件。
混合持久化的优点是 前面时RDB格式的文件,加载的速度会很快,而后面以AOF的形式,可以丢失更少的数据。
Redis集群
Redis如何实现服务高可用?
要想实现Redis服务高可用,一定要从Redis多服务节点考虑,比如Redis的主从复制,哨兵模式,切片集群等。
主从复制
将从前一台的服务器数据,同步数据到其他几个服务器。即一主多从的模式,主从服务器之间采用读写分离模式。
主服务器可以进行读写操作,且写操作之后,会将命令同步到从服务器。而从服务器一般是只读,并执行主服务器发送过来的命令。
也就是说数据只能在主服务器上修改,然后将最新的数据同步到从服务器。这样使得主从服务器数据保持一致性。
但是,主从服务器之间的命令复制是异步进行的,所以无法实现强一致性。
当主服务器接收到新命令后,就会将命令发送给从服务器,但是,当主服务器完成命令之后,不会等待从服务器返回执行结果。会直接向客户端返回结果。如果此时从服务器还未执行发送过来的命令,那么主从服务之间数据也就不一致了。
哨兵模式
在使用主从服务器服务时,当主从服务器进行故障宕机的时候,需要手动恢复,这时候就需要哨兵模式。因为哨兵模式可以监控故障节点,并且提供主从节点转移故障的功能
切片集群模式
当Redis上的缓存数据量大到一台服务器无法缓存时,需要使用Redis切片集群,他将数据分配到不同的服务器之上,以此来降低系统对主节点的依赖,从而提高Redis的读写性能。
集群脑裂导致数据丢失怎么办
脑裂
解决方案
在Redis的配置文件中有两个参数可以设置:
我们可以搭配使用,比如,主服务器连接的从库至少要有N个,和主库进行主从复制时ACK消息延迟不能超过T秒,否则,禁止主节点读取数据。
Redis过期删除和内存淘汰策略。
Redis使用的过期删除策略是什么?
Redis对Key设置对应的过期时间,Redis将Key和对应的过期时间都存到了过期字典上面。就是说过期字典上面保存了所有Key的过期时间。
挡我们查询一个Key时,需要有以下两部分:
Redis使用的过期策略一般是惰性删除+定期删除
惰性删除:
优点:
缺点:
定期删除:
每隔一段时间从过期字典中抽取一部分Key检查,并删除其中过期的Key。
定期删除策略的优点:
定期删除策略的缺点:
所以一般选择二者搭配使用。
Redis持久化时,对过期键是如何处理的?
来看一下过期键在RDB格式和AOF文件下的状态:
在RDB格式下,分为RDB文件生成阶段和文件加载阶段:
RDB文件生成阶段:从内存状态持久化到RDB文件生成状态,会对Key进行过期间检查,过期的键不会被保存到新的RDB文件中
RDB加载状态:RDB加载阶段,要看是在主服务器还是从服务器
在主服务器上:在载入RDB文件时,会对文件中保存的Key进行检查,如果过期。则不会被保存到数据库中。所以过期键对在主服务器上加载的RDB文件没有影响。
在从服务器上:无论是否过期。都会被存入到从数据库,但是在主从服务器进行数据同步时,从服务器数据会被清空,所以也不会有影响。
AOF文件,AOF文件写入阶段和AOF重写阶段:
AOF文件写入阶段:当某个过期间未被删除。,那么AOF文件会保存此过期键。当此过期键被删除之后,Redis 会追加一条DELETE命令来存到AOF文件中,显式的删除该过期键。
AOF重写阶段:执行AOF重写时,已过期的键不会被保存。不会被重写到新的AOF文件中。
Redis主从模式下,对过期键如何处理?
从库不会进行过期扫描,从库对于过期键的处理是被动的,当主服务器删除一条过期键时,会在AOF文件中追加一条del指令,该指令会被同步到所有从库,从库通过执行这条指令来删除过期键。
Redis内存满了会发生什么?
当Redis 的运行内存达到某个阈值时,就会触发内存淘汰机制,那个阈值就是我们设置的最大运行内存·。
Redis的内存淘汰策略:
1. 不进行内存淘汰,直接向客户端返回错误。
2. 在设置了过期时间的数据范围内淘汰
在所有数据中淘汰:
Redis缓存设计?
如何避免缓存击穿,缓存穿透。缓存雪崩?
避免缓存雪崩
为了保证Redis中的数据与数据库中的数据的一致性,会给在Redis中的数据设置过期时间,当缓存过了过期时间,用户需要访问的数据不在缓存中,缓存会重新生成,因此需要重新访问数据库。并将数据库中的数据更新到Redis缓存中。但是,当大量的缓存在同一时间失效,如果此时有大量的请求来访问,此时无法在缓存中处理,于是请求全部转到数据库中,导致数据库的压力增大,可能会出现数据库宕机。导致一系列连锁反应,可能导致系统崩溃,这就是缓存雪崩。
两种解决方案:
1. 将缓存失效时间打散。(在原有的缓存失效时间的基础上加一个随机值,这样就不会导致缓存的过期时间重复)
2. 设置缓存不过期(通过后台服务 来更新缓存数据)
如何避免缓存击穿?
有些数据比如秒杀商品属于热点数据。当热点数据缓存过期,就会有大量的请求来到数据库,数据库很容易被高并发请求击垮,这就叫缓存击穿。
两种解决方案:
1. 设置互斥锁方案,保证同一时间只能有一个业务线程请求缓存,未能获取互斥锁的请求,要么等待互斥锁释放后重新读取缓存,要么就返回空值 或者默认值。
2. 不给热点数据设置过期时间。或者在热点数据快要过期前,提前通知后台线程更新缓存并重新设置过期时间。
如何避免缓存穿透?
当在缓存和数据库中,都不存在要访问的数据。导致大量请求一直访问数据库,可能会导致数据库宕机。
两种可能实现:
业务误操作。缓存中的数据和数据库中的数据都被误操作删除。
黑客恶意访问大量未存在的数据。
解决方式:
非法请求限制:在API入口判断请求参数是否合理,
设置空值和默认值
使用布隆过滤器来快速判断数据是否存在。避免通过查询数据库在判断数据是否存在。
如何设置一个缓存策略,可以动态缓存热点数据?
通过数据最新访问时间排名,并过滤掉不常访问的数据,只留下经常访问的数据。
常见的缓存更新策略:
一般采用旁路缓存策略。
旁路缓存策略:
应用程序可以与数据库,缓存直接交互。并负责对缓存的维护。该策略也可以被分为写策略和读策略。
写策略:先更新数据库数据,再更新缓存中的数据。
读策略:如果请求的数据命中了缓存,则直接返回数据,如果没有命中,则直接访问数据库中的数据,并将数据写入到缓存中,返回给客户端。
Redis实战
Redis如何实现延迟队列?
使用有序集合Zset来实现
Redis的大key如何处理?
大Key指的是Key对应的value值很大:
String类型的值大于10kb
Hash,List,Set,ZSet,元素的类型个数超过5000个
会造成什么影响?
1. 客户端超时阻塞
2. 引发网络阻塞
3. 阻塞工作线程
4. 内存分布不均、