一、Redis 简介
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供string、list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
二、Redis 优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来(基于redis分布式锁:Redisson、Redlock)-lua脚本。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
三、redis的持久化方式
Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为“半持久化模式”);也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为“全持久化模式”)。
由于Redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据。
- RDB:在指定的时间间隔能对你的数据进行快照存储(默认下,持久化到dump.rdb文件、)。
- AOF:将Reids的操作日志以追加的方式写入文件,当服务器重启的时候会重新执行这些命令来恢复原始的数据。
1、RDB持久化原理(Redis默认的持久化方式)
通过bgsave命令触发,然后父进程执行fork操作创建子进程,子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换(定时一次性将所有数据进行快照生成一份副本存储在硬盘中)
优点:是一个紧凑压缩的二进制文件,Redis加载RDB恢复数据远远快于AOF的方式。
缺点:由于每次生成RDB开销较大,非实时持久化
2、AOF持久化原理
开启后,Redis每执行一个修改数据的命令,都会把这个命令添加到AOF文件中。
优点:实时持久化。
缺点:所以AOF文件体积逐渐变大,需要定期执行重写操作来降低文件体积,加载慢
3、RDB 和 AOF 对比
- | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
4.、如何选择使用哪种持久化方式?
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。 有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。
四、redis击穿,穿透,雪崩以及解决方案
①、穿透
一般是出现这种情况是因为恶意频繁查询才会对系统造成很大的问题: key缓存并且数据库不存在,所以每次查询都会查询数据库从而导致数据库崩溃。
解决办法
- 最好对于每一个缓存key都有一定的规范约束,这样在程序中对不符合parttern的key 的请求可以拒绝。(但一般key都是通过程序自动生成的)
- 将可能出现的缓存key的组合方式的所有数值以hash形式存储在一个很大的bitmap中<布隆过滤器>(需要考虑如何将这个可能出现的数据的hash值之后同步到bitmap中, eg. 后端每次新增一个可能的组合就同步一次,或者 穷举),一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力
- 常用: 如果对应在数据库中的数据都不存在,我们将此key对应的value设置为一个默认的值,比如“NULL”,并设置一个缓存的失效时间。当然这个key的时效比正常的时效要小的多
②、雪崩
雪崩指的是多个key查询并且出现高并发,缓存中失效或者查不到,然后都去db查询,从而导致db压力突然飙升,从而崩溃。
出现原因: 1 key同时失效
2 redis本身崩溃了
解决办法:
- 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。(跟击穿的第一个方案类似,但是这样是避免不了其它key去查数据库,只能减少查询的次数)
- 可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存
- 不同的key,设置不同的过期时间,具体值可以根据业务决定,让缓存失效的时间点尽量均匀
- 做二级缓存,或者双缓存策略(redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃)。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。
③、击穿(热点Key)
某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
注意: 这里指的是单个key发生高并发!!!
解决办法:
- 使用互斥锁(mutex key):这种解决方案思路比较简单,就是只让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据就可以了;
- "提前"使用互斥锁(mutex key):在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中;
- "永远不过期": 这里的“永远不过期”包含两层意思
- 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期;
- 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期
- 资源保护:可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。
五、Redis 高可用(主从、哨兵、集群)
①、master-slave(主从)
Redis主从复制模式可以将主节点的数据同步给从节点,从而保障当主节点不可达的情况下,从节点可以作为
后备顶上来,并且可以保障数据尽量不丢失(主从复制可以保障最终一致性)。第二,从节点可以扩展主节点的读
能力,一旦主节点不能支持大规模并发量的读操作,从节点可以在一定程度上分担主节点的压力。
主从复制面临的问题:
1.当主节点发生故障的时候,需要手动的将一个从节点晋升为主节点,同时通知应用方修改主节点地址并重启
应用,同时需要命令其它从节点复制新的主节点,整个过程需要人工干预。
2.主节点的写能力受到单机的限制。
3.主节点的存储能力受到单机的限制。
②、原始的故障迁移
1.主节点发生故障后,客户端连接主节点失败,两个从节点与主节点连接失败造成复制中断。
2.如果主节点无法正常启动,需要选出一个从节点(slave-1),对其执行slaveof no one命令使其成为新的主节
3.原来的从节点(slave-1)成为新的主节点后,更新应用方的主节点信息,重新启动应用方。
4.客户端命令另一个从节点(slave-2)去复制新的主节点
5.待原来的主节点恢复后,让它去复制新的主节点
③、Redis Sentinel的高可用(哨兵)
当主节点出现故障时,Redis Sentinel能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。
RedisSentine是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和
其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是“主节点”,它还会和
其他的Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举一个Sentinel节点来完
成自动故障转移的工作,同时会将这个变化实时通知给Redis应用方。整个过程是自动的,不需要人工干预,解决了
Redis的高可用问题。
Redis Sentinel包含了若干个Sentinel节点,这样做也带来了两个好处:
1. 对节点的故障判断是由多个Sentinel节点共同完成,这样可以有效的防止误判。
2. Sentinel节点集合是由若干个Sentinel节点组成的,这样即使个别Sentinel节点不可用,整个Sentinel节点集合依
然是健壮的。
Redis Sentinel具有以下几个功能:
1.监控:Sentinel会定期检测Redis数据节点、其余Sentinel节点是否可到达
2.通知:Sentinel会将故障转移的结果通知给应用方。
3.主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。
4.配置提供者:在RedisSentinel结构中,客户端在初始化的时候连接的是Sentinel节点集合,从中获取主节点信息。
④、集群
通过主从与哨兵,redis即可实现高可用,然额,仍然存在一个问题,单台服务器的内存是有限的,不够用怎么办?redis有缓存淘汰机制,可以解决一部分问题,但业务需求是无限的,当不能过期与淘汰的数据大到一台主机不够用时,怎么办呢?SO,跟所有其它分布式系统一样,还需要横向扩展能力,自redis3.0开始,开始提供集群功能