主从模式由一个主节点(Master)以及多个子节点(Slave)组成,主要是为了解决单点故障以及多读少写的问题。当主节点出现故障的时候,可以将Slave切换成主节点,但是需要人工手动切换。
此模式几乎不用。
哨兵模式主要是为了解决主从模式中需要人为手工切换主节点的问题,引入了哨兵节点。哨兵节点主要是监控Master、Slave节点的状态。哨兵节点定期向各大节点发送PING命令,确保各大节点的正常运行。当未收到PING消息回复时,则系统认为该节点异常,则作为节点【主观下线】操作。
主节点被多数哨兵标记为主观下线,则该主节点标记为【客观下线】。哨兵节点开始进行故障迁移操作,从健康的Slave节点中选择一个作为Master节点可以自动的将某一个从节点升级为Master节点,并且通知其他节点以及应用程序客户端进行更新节点信息。应用程序客户端接收到更新节点信息后,自动更新连接池信息,与新的Master节点进行操作。
集群模式将Key数据自动分片分别存储在多个不同的应用节点上,每个节点存储一部分数据。解决横向向扩容和容错的问题。
集群模式总共有2^14方(16384)个分片。
注意点:不能跨多个节点进行Key处理。
因为Redis集群内置插槽为16384个,所以Redis会将每个键的键名的有效部分使用CRC16算法计算出散列值,然后对16384的取余。余数为多少就表示该键应该被分配到哪个节点,这样的话,每个键都可以被分配到16384个插槽中,而集群中的每个节点都会被分配一定的插槽。
因为Redis是根据每个键的键名的有效部分使用CRC16算法计算出的散列值来分配键的,那么如果想要让相关键都分配到集群中的同一个节点里,只需要让相关键的键名的有效部分相同即可。
键值有效部分定义:
如果键名包含{符号,且在{符号后面存在}符号,并且{和}之间有至少一个字符,则有效部分是指{和}之间的内容。
例如:{仓库ID}:SKUCode 则Redis会将{仓库ID}下的所有Key都存储在一个节点中。通过集群模式可以快速扩容库存占用+释放能力。
数据类型 | 注意点 | 使用场景 |
String | 不要超过10M,若超过10M需要拆分Key值处理 | |
Hash | Key值可以重复,但是不要超过10000个Key,若超过则需要引入二级索引进行处理 | |
List | 数据量不要超过10000,若超过则需要引入二级索引处理 | |
Set | Key值不可以重复 | |
ZSet | ZSet Score值只允许16位。超过16位则出现失真,导致排序错乱 | 订单排序(乱序订单变成有序订单) 用户排序 |
Redis主从复制过程中采用的是异步数据复制方式,因此Redis属于最终一致性,并不是强一致性。
4.1 RDB
RDB是将Redis内存中的数据定期保存在磁盘上,以防止Redis异常情况下数据丢失。
优点:快照文件小,恢复速度快。
4.2 AOF
将Redis的所有操作命令存储(Append Only File)。极端情况下丢失1秒数据
4.3 AOF 文件落库方式
Always:同步执行,每执行一个命令进行落库操作
EverySec:每秒写回数据。执行完Redis操作命令,写入AOF内存缓冲区,每隔一秒落库文件
No:由操作系统来进行控制回写文件
在Redis采用RDB + AOF 同时持久化的情况下,当出现故障时,极端会丢失一秒数据。
不淘汰 : 如果内存满了,则直接报错;
设置了过期时间:随机淘汰 、最近最少使用(LRU = Least Recently Used)、根据过期时间的先后进行删除,越早过期的越先被删除(TTL)、LFU(最少访问 = Least Frequently Used)
所有数据淘汰机制:随机淘汰、最近最少使用(LRU)、最少访问-LFU
Redis Key值过期策略支持 惰性删除、定期删除。
定期删除:Redis默认每隔100ms时间随机删除标记了过期时间,并且检查确认已经过期的的Key值。定期删除会占用Redis CPU资源。
惰性删除:Redis访问过期时间Key时,如果确认已经过期,则再进行删除操作。但是带来的问题是:这部分过期的Key会占用内存资源,如果长期不访问有可能长期占用。
Redis删除了Key,也不会立马释放内存。被删除的内存空间会被标记为【可重用的内存】
1、如果是Sentinel模式,则增加多个副本,通过多副本的访问拆分访问流量
2、如果是集群模式,则将Redis的热点Key同步复制到多个不同的节点下,通过集群的节点来分担访问流量
3、增加本地二级缓存:在本地通过HashMap等框架来增加相应的二级缓存,从而降低Redis的访问量。但是需要注意本地二级缓存与Redis的一致性问题,同时需要防止本地二级缓存过大,占用应用内存,导致OOM
4、将Redis的热点Key拆分成多个Key,根据不同的访问策略访问不同的Key
1、将BigKey拆分成多个小Key,通过Hash或者List来维护Redis 二级缓存信息
2、增加本地二级缓存,降低Redis的访问。如果是高频率访问,也不进行二级缓存,会将Redis宽带打满
3、设置BigKey的TTL时间,定期删除BigKey
Redis同一时间大量的Key过期或者Redis服务器宕机,导致直接访问数据库,进而导致数据库宕机。
解决方法:
1、将过期时间设置为不同的时间范围内,防止大量Key过期
2、通过集群模式,防止Redis服务器宕机
3、感知数据库访问量突然过大,进行自动限流操作。例如:可以通过Sentinel控制
应用访问Redis的缓存,若缓存数据不存在则访问数据库,数据库也不存在。大量的访问数据时,导致缓存服务器失效,直接访问数据库,从而导致数据库过载,导致数据库服务器宕机或者不可正常使用。
解决方法:
1、若发现数据库不存在,则设置一个带有过期时间的Null缓存。
2、使用BloomFilter过滤器来判断是否存在,如果存在再进行访问Redis缓存。但是BloomFilter存在误判的几率。
3、熔断、限流、降级等措施
应用访问某一个Redis Key的时候,当前Key过期了,若有大量的访问,则会直接访问数据库,从而导致数据库访问量过大。
解决方法:
1、提前预判Redis Key的访问量是否过大,如果过大,则提前延期Redis Key的过期时间
2、若Redis Key不存在时,则先通过分布式锁来防止大量的访问数据库(等待数据库执行完成后,再允许其他应用访问)
3、熔断、限流、降级等措施
Redis BoolmFilter 提前初始化好数据大小,根据Key的内容进行多次Hash算法得到不同的位置,然后将此位置的占有位设置为1。当需要判断Key是否存在时,判断所有的Hash 位置是否都为1,若都为1则认为存在。
BoolmFilter优点:
1、占用内存少
2、查询写入速度快
缺点:
1、数据越多,误判概率越大
2、无法删除已经存在的Key
1、Redis数据库和缓存数据不一致是因为这2步操作无法保障原子性。
2、高并发访问的时候,缓存不存在,读取数据库进行缓存时,数据库数据发生了变化。缓存不知道
1、在一定的时间范围内需要忍受一定的缓存数据不一致的问题
2、缓存双删的操作
数据库更新时,按照以下步骤处理
A、删除缓存(这边操作主要是为了防止其他线程访问缓存时读不到老数据,然后可以进行相互互斥的等待操作;以及防止数据库更新成功,删除缓存失败的场景)
B、更新数据库
C、删除缓存(这步操作主要是为了解决在更新数据库之前,有线程读取到了老数据,然后缓存了老数据)
3、通过CDC方案来进行解决处理
但是这个方案有个弊端就是:当CDC数据产生大量的数据时,会有比较高的延迟
1、避免大Key存在,若存在则需要拆分成多个小Key或者多级缓存来解决
2、在生产环境中禁止使用Keys命令
3、在生产环境中禁止使用FlushDb命令
4、在LUA脚本中禁止使用死循环,从而导致Redis线程卡死
5、若Redis存在关键性的数据,则需要考虑Redis的持久化存储,RDB + AOF双重持久化存储,这样可防止最大丢失1秒数据的
6、高可用服务器的搭建,防止单点问题
7、若频繁的更新Key中的部分数据,可将Key的内容拆分成多个Key或者使用Hash存储,降低Redis网络带宽占用
Redis RedLock主要解决Redis单点故障进行转移的过程中,锁Key未进行及时的同步,从而导致锁Key的丢失。
使用Redis RedLock主要是解决多节点Key的一致性问题,在多个Redis节点中设置锁Key,锁可以在多个节点中设置成功后才认为上锁成功。
此设计思路与Zookeeper很类似,若系统对此种锁的要求很高,个人建议使用Zookeeper来实现分布式锁更合适。
Redis Pipeline解决客户端多次执行命令,减少网络IO的开销问题,提升Redis执行效率。但是Pipeline是无法保障此批次执行命令的原子性。
Redis事务不支持回滚。也就是说:Redis 执行成功 Exec后,在后续节点中出现异常,也不会进行回滚操作。
Redis 事务执行方法
1、Multi:标记事务块开始
2、Exec:执行事务,可以保障原子性,但是不支持回滚
3、Discard:放弃事务
4、Watch:监视某个Key,当这个Key发生变化时,事务不会进行相关执行。若不执行Watch,当Key变化时,事务也会进行执行
5、Unwatch:取消监视所有Key
1、 利用SetNx来进行处理分布式锁
2、如果程序运行超时,锁被释放了,该如何处理?
参照Redisson的WatchDog来进行监视线程的运行,如果未执行完成,但是锁即将超时,则重新设置锁的过期时间。
Redis提供的虚拟内存机制主要用于将不常用的内存数据存储在硬盘上,避免过多的内存占用以及提高Redis数据存储量。
Redis内存超过指定的阈值后,虚拟内存机制自动将一部分内存存储在硬盘上,然后将此部分内存进行释放。当需要访问此部分内存时,再从磁盘上读取到内存中提供使用。
虚拟内存机制会带来性能的损耗。