reids 企业级开发知识点总结
2023-12-03 18:06:08   0  举报             
     
         
 AI智能生成
  Redis企业级开发中使用知识点总结。Redis高可用架构、持久化策略、缓存穿透、缓存击穿、数据库缓存不一致等问题解决方案
    作者其他创作
 大纲/内容
  Redis介绍    
     Redis 是开源的(BSD许可),内存中数据结构的数据存储系统(数据库)。
  
     Redis 常用数据结构    
     字符串(String)  
     哈希结构(Hash)  
     列表(List)  
     集合(Sets)  
     有序集合sorted Sets(Zsets)  
     Redis线程模型    
     Redis 单线程    
     Redis 对应数据读写操作命令都是单线程操作的。
Redis数据操作都在内存中进行,单线程操作可以减少线程创建、线程上下文件切换带来的新能消耗。
  
    Redis数据操作都在内存中进行,单线程操作可以减少线程创建、线程上下文件切换带来的新能消耗。
 Redis所有操作并发完全单线程,如:数据持久化、缓存删除、数据同步都是异步进行的。  
     Redis  I/O多路复用    
     Redis处理多个客服端时采用I/O多路复用模式。Redis 利用epoll模型实现I/O多路复用  
     Redis持久化    
     RDB快照持久化    
     默认情况下Redis将内存中的数据以RDB快照文件的形式保存在当前目录下的dump.rdb的二进制文件中。
可以通过Redis配置文件redis-conf进行修改
  
    可以通过Redis配置文件redis-conf进行修改
 RDB持久化策略:通过配置SAVEA M N  生成RDB快照文件。表示M秒内数据集做了N次改动进行一次持久化。
如: SAVE 10 1000 ,在 10s内数据集做了1000次改动。
    如: SAVE 10 1000 ,在 10s内数据集做了1000次改动。
 RDB可通过save或者bgsave命令手动生成RDB文件。Redis自动生成快照使用的时bgsave 异步方式生成。save 为同步方式生成,会阻塞读写  
     RDB写时复制机制:Redis 利用操作系统的写时复制机制,在进行bgsave操作时fork 一个字进程,共享主进程数据。
客服端在读操作时并不影响RDB生产,如果是客服端写,子进程会生成一份数据的副本进行RDB操作。不影响主进程写操作。
  
    客服端在读操作时并不影响RDB生产,如果是客服端写,子进程会生成一份数据的副本进行RDB操作。不影响主进程写操作。
 AOF 追加持久 化    
     AOF 是将每一条修改命令追加写入appendonly.aof文件中。相比与RDB方式,数据更加安全。如果Redis异常宕机,RDB方式会丢失最近写入Redis内存的数据 (未达到触发SAVA配置的条件)。  
     AOF持久化策略    
     通过配置Redis-config开启AOF           
     appendfsync always:每次命令追加到 AOF 文件 ,非常慢,非常安全。
appendfsync everysec(推荐):每秒 fsync 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。
appendfsync no:从不 fsync ,数据交给操作系统来处理。更快,不安全的选择。
    appendfsync everysec(推荐):每秒 fsync 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。
appendfsync no:从不 fsync ,数据交给操作系统来处理。更快,不安全的选择。
 AOF重新机制    
     AOF文件在记录修改命令时,会出现很多无用命令(对最终结果没有影响)。如:incr 多次相加,中间相加过程都可以省略,只记录最终结果即可。所有AOF会根据内存中的数据定期重写AOF文件  
     AOF重写配置    
     auto‐aof‐rewrite‐min‐size 64mb //aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大  
     auto‐aof‐rewrite‐percentage 100 //aof文件自上一次重写后文件大小增长了100%则再次触发重写  
     AOF也可以手动重写,进入redis客户端执行命令bgrewriteaof重写AOF
注意:AOF重新也是通过fork子进程进行,不会影响主进程写操作。
    注意:AOF重新也是通过fork子进程进行,不会影响主进程写操作。
 混合持久化策略RDB+AOF    
     Redis 在重启时很少使用到RDB来恢复内存中的数据,RDB存在丢失大量数据的风险。从而采用AOF重放命令来恢复内存数据。但是对比RDB的内存恢复效率降低了很多。所以在Redis 4.0 之后新增了混合持久化方式。  
     通过图中方式开启混合持久化(必须先开启AOF)           
     混合持久化是AOF在做重写操作时,进行生产RDB快照操作,将这一刻之前的数据生成RDB数据和这一刻之后的数据命令追加到RDB数据后。生成为一个同时包含RDB快照和AOF追加命令的文件aof文件。这样Redis在做内存恢复时就能通过二者结合快速安全的恢复了。  
     Redis内存淘汰策略    
     被动删除策略: Redis在读数据时会判断key是否过期,过期会删除数据  
     主动删除策略: Redis会定期对过期的key进行批量删除。保证冷数据能被及时清除。
除此之外当Redis内存达到上限时也会触发主动删除策略。
    除此之外当Redis内存达到上限时也会触发主动删除策略。
 设置了过期时间的key    
     volatile-ttl : 在筛选时根据过期时间的长短删除,越早过期的key 越先被删除  
     volatile-random: 对设置了过期数据的key进行随机删除  
     volatile-lru: 使用LRU算法进行过期键删除    
     LRU策略:删除最近最少访问的key  
     volatile-lfu:使用LFU算法进行过期键删除    
     LFU策略:删除最不经常使用的key  
     对所有key    
     allkeys-random:从所有键值对中随机选择并删除数据  
     allkeys-lru:使用 LRU 算法在所有数据中进行筛选删除  
     allkeys-lfu:使用 LFU 算法在所有数据中进行筛选删除  
     不做任何处理    
     不删除任何key,当Redis内存满时,决绝所有写入操作。读操作不受影响  
     Redis高可用架构    
     Redis主从架构    
     介绍:Redis 主从架构由一主多从节节点构成,主节点提供读写,从节点提供读,实现读写分离,提高读性能。
从节点同时可以做为数据的容灾备份节点。
  
    从节点同时可以做为数据的容灾备份节点。
 主从配置:修改redis-conf 中的replicaof 192.168.0.3 6379 # 从192.168.0.3机器6379的redis实例复制数据,Redis 5.0之前使用slaveof  
     主从工作原理    
     主从数据同步    
     当一个从节点slave连接上master主节点后,不论是否第一次连接都会发送一个psync命令,请求数据同步    
     1. master收到psync命令后,后台进行RDB持久化,这个过程中master还是能提供读写操作  
     2. 如果在进行RDB的过程中出现写操作,master主节点会将操作数据命令缓存在内存中  
     3. master做完RDB后,会将RDB数据发送到从节点。  
     4.从节点收到RDB数据后,会先进行持久化,再将数据存储到内存数据库中  
     5. 最后master会将第2步中缓存的数据集发送给slave节点  
     redis数据部分复制(断点续传)    
     当maser和slave节点在进行主从同步的过程中,出现网络断链恢复或者宕机重启。
一般情况下会进行全量复制,但是在redis2.8之后,redis支持部分数据的psync命令。从数据断开的地方继续同步
    
    一般情况下会进行全量复制,但是在redis2.8之后,redis支持部分数据的psync命令。从数据断开的地方继续同步
 1. 在主从同步的过程中,master主节点会在内存中创建一个缓存队列,保存最近一段时间的数据。  
     2. master和slave节点都会维护maser节点的进程id 和当前复制数据的下标offset。
当前slave节点再次连接进行复制数据时会从主节点的offset下标开始复制数据
  
    当前slave节点再次连接进行复制数据时会从主节点的offset下标开始复制数据
 3. 如果断开时间太长或者maser节点进程id发生了变化,在maser节点中找不到slave所维护的maser id 或者 offset下标。那么会进行一次全量数据同步  
     Redis 哨兵架构    
     介绍:哨兵架构是在主从的基础上进行的增强。相对于主从架构,它可以实现在主节点宕机的情况下,选举出新的主节点,对外提供服务。  
     配置 : 在主从的基础上启动哨兵服务sentinel。在哨兵配置文件中修改配置:sentinel monitor mymaster 192.168.0.60 6379 2     
     mymaster:名称随便取,在进行客服端连接时会使用到  
     数字2 : 表示多少个哨兵节点认为主节点宕机,进行maser选举(一般配置为(哨兵实例数 /  2 + 1)过半机制)  
     当某个sentinel哨兵认为maser节点宕机后,会同其他sentinel协商出一个sentinel的leader节点进行故障转移。
由leader从存活的slave节点中选举出一个master节点。
    
    由leader从存活的slave节点中选举出一个master节点。
 sentinel哨兵leader 选举流程    
     当认为master节点下线的sentinel都可以投票自己为leader,每一个sentinel都有一个选举周期,每一次投票选举周期加1。
直到过半的sentinel选举某个sentinel为leader时,该sentinel就会作为leader进行故障转移。
  
    直到过半的sentinel选举某个sentinel为leader时,该sentinel就会作为leader进行故障转移。
 Redis集群架构    
     介绍:在redis哨兵模式中,只有一个主节点可对外提供写服务,并发写只能单节点操作性能相对较低。而且在内存上不宜设置过大的内存,持久化文件过大,降低主从同步和数据恢复速度。
redis的集群架构可以看做是对哨兵的增强,它提供多主多从模式,数据分片存储。相较于哨兵其写并发可以同时在多个主节点上操作。而且由于数据是分配存储的,支持更大的数据存储内存空间。
    redis的集群架构可以看做是对哨兵的增强,它提供多主多从模式,数据分片存储。相较于哨兵其写并发可以同时在多个主节点上操作。而且由于数据是分配存储的,支持更大的数据存储内存空间。
 集群架构主要配置。已3主3从为例    
     修改配置文件:cluster‐enabled yes(启动集群模式)
protected‐mode no (关闭保护模式)
appendonly yes
  
    protected‐mode no (关闭保护模式)
appendonly yes
 执行如下命令加入集群:
src/redis-cli --cluster create --cluster-replicas 1 192.168.0.120:8001 192.168.0.120:8004 192.168.0.121:8002 192.168.0.121:8005 192.168.0.122:8003 192.168.0.122:8006
其中 数字 1 表示 为每一个主节点设置一个从节点
    src/redis-cli --cluster create --cluster-replicas 1 192.168.0.120:8001 192.168.0.120:8004 192.168.0.121:8002 192.168.0.121:8005 192.168.0.122:8003 192.168.0.122:8006
其中 数字 1 表示 为每一个主节点设置一个从节点
 Redis集群的水平扩展(为已有集群进行添加或移除新的主从节点)    
     添加新的主从节点    
     1. 创建2 个Redis节点8007 (主)和 8008(从)  
     2. 使用命令添加节点:src/redis-cli --cluster add-node 192.168.0.5:8007 192.168.0.2:8001  
     3. 为8007 节点分配槽位:src/redis-cli --cluster reshard 192.168.0.5:8007    
     How many slots do you want to move (from 1 to 16384)? 2048  #需要为8007 分配的槽位个数 
What is the receiving node ID? 7237dcdf08fd98b21d477be295a9a9d5444f02ca #接收槽位的节点id
Source node #1: all # 从集群中其他主节点上迁移共2048个节点到8007节点
Do you want to proceed with the proposed reshard plan (yes/no)? yes
  
    What is the receiving node ID? 7237dcdf08fd98b21d477be295a9a9d5444f02ca #接收槽位的节点id
Source node #1: all # 从集群中其他主节点上迁移共2048个节点到8007节点
Do you want to proceed with the proposed reshard plan (yes/no)? yes
 4. 登录集群,查看8007的槽位信息
cluster node 查看集群节点信息,8007有节点信息和槽位信息表示节点加入成功       
    cluster node 查看集群节点信息,8007有节点信息和槽位信息表示节点加入成功
 5. 加入8008从节点:加入8008为8007的从节点slave,执行:src/redis-cli --cluster add-node 192.168.0.5:8008 192.168.0.2:800
  
     6.登陆到集群中的8008节点,输入命令:CLUSTER REPLICATE 7237dcdf08fd98b21d477be295a9a9d5444f02ca  # 后面的id 是需要加入的主节点的id           
     移除已有主从节点    
     移除8008从节点,输入命令:src/redis-cli --cluster del-node 192.168.0.5:8008 1a77003381a378a0e156bcbea2cd517bfe567bdb  
     移除主节点8007,需要先迁移主节点上的数据槽位到其他节点:src/redis-cli --cluster reshard 192.168.0.5:8007  
     移除8007主节点:src/redis-cli --cluster del-node 192.168.0.5:8007 7237dcdf08fd98b21d477be295a9a9d5444f02ca  
     Redis集群原理    
     Redis集群会将集群中的所有主节点划分为16384个槽位,每一个主节点负责一部分槽位,槽位信息存储在各自的节点中,当客服端连接集群时,会将槽位信息荤菜在本地。当客服端操作某个key时可以直接定位到节点上  
     槽位定位算法:集群默认使用crc16哈希算法获取一个整数值,使用该整数%16384 定位槽位:HASH_SLOT = CRC16(key) % 16384  
     跳转定位: 客服端操作key时,发现key不归直接管理,,这时会向客服端发起一个特殊的跳转指令,并且携带正确的目标地址,让客服端去这个节点去操作key。同时客服端还会纠正本地的槽位映射表缓存,后续的key将使用新的槽位映射表。  
     Redis集群主从选举    
     当slave节点发现自己的主节点变成fail状态时,发起failover,尝试成为主节点。由于master下有多个从节点,这时需要竞争成为主节点    
     1.  slave发现masterfail, 记录集群中的currentEpoch = 1 (选举周期) ,发起failover。  
     2.  其他节点收到slave发送的信息,只有master响应 ack ,每个周期只能响应一次  
     3. 当slave收到过半的ack时,自己变为主节点,同时广播发生PONG消息通知其他节点  
     网络抖动问题    
     真实网络环境非常复杂,网络抖动很常见的问题,即很短的时间出现网络断链恢复的情况。Redis集群中提供cluster-node-timeout配置项解决该问题。避免由于网络抖动造成频繁的主从切换。  
     Redis集群脑裂问题    
     Redis集群没有过半机制、出现网络分区都有脑裂问题。即出现网络分区时,slave选举出了新的主节点,但是原来的主节点并没有fail,只是出现网络分区,slave认为fail。这时就有了两个主节点对外提供服务。当网络分区恢复后,老的主节点会作为从节点加入新的主节点中。老的主节点上的数据会被丢弃,重新从新的主节点同步数据,从而造成数据丢失  
     网络分区脑裂问题解决:redis提供min-replicas-to-write 配置 可以解决脑裂问题,min-replicas-to-write 1  // 写数据到redis成功需要最少同步多少个slave ,可以模仿半数机制配置(节点个数 / 2 )  
     Redis 企业级开发    
     Redis分布式缓存    
     缓存穿透问题    
     缓存穿透是指查询一个不存在的数据,请求在缓存层和存储层都没有命中数据,一般情况下在数据库中查询不到的数据不会建立缓存,导致这类数据每次查询都会到达存储层。  
     解决方案    
     加一个带过期时间的空缓存,对于不存在的数据查询直接走空缓存  
     布隆过滤器:如果是恶意攻击,每次都是不一样的查询,空缓存就解决不了这个问题,可以使用布隆过滤器进行判断(当布隆过滤器判断你的查询值不存在时,那么值一定不存在,当布隆过滤器判断查询值存在时,值有可能不存在)  
     缓存击穿问题    
     缓存击穿是指在同一时间大量缓存失效,导致的请求直接到达数据库,导致的数据库宕机问题。  
     解决方案: 可以通过对同一类型的缓存在同一时间段内加不同的过期时间,避免缓存集中失效  
     缓存雪崩问题    
     缓存层承受不住请求压力导致的缓存层宕机,大量请求到达存储层,进而导致存储层跟随宕机,整个服务雪崩问题。  
     解决缓存雪崩问题    
     1. 保证缓存层的高可用,如:Redis哨兵和集群架构  
     2. 做好后端服务的熔断降级。如:使用 sentinel 或者Hystrix限流降级组件  
     3. 提前做好缓存宕机、服务高负载防范措施  
     缓存不一致问题    
     高并发读写的情况下,出现的数据库数据和缓存数据不一致的情况。
如: 线程1 更新了数据库但是没有立即更新缓存,线程2 在同样的逻辑下更新了数据库就立即更新了缓存之后,线程1此时才更新缓存。导致数据库和缓存不一致。(双写不一致的情况)
    如: 线程1 更新了数据库但是没有立即更新缓存,线程2 在同样的逻辑下更新了数据库就立即更新了缓存之后,线程1此时才更新缓存。导致数据库和缓存不一致。(双写不一致的情况)
 1.对于并发不高的情况下可以对缓存key加过期时间,在读取缓存时重新从数据库更新缓存  
     2. 对于并发量高但是允许短期不一致出现,也可以通过添加过期时间解决。达到最终一致   
     3. 对于读多写少的情况可以使用添加读写锁的方式解决,读操作不加锁,写操作加锁避免数据的不一致  
     4. 阿里的开源canal通过监听数据库的binlog日志及时的去修改缓存,但引入了新的中间接导致系统的复杂度增加。  
     热点缓存key重建    
     使用缓存key+ 过期时间的方式在大多少情况下可以提供数据的读写性能,但是在大并发的热点key的情况下这种操作会存在问题。
如果大量线程访问过期的key,此时需要去重建缓存,就会出现大量线程直接访问到数据库,去重建缓存导致数据库宕机。
    如果大量线程访问过期的key,此时需要去重建缓存,就会出现大量线程直接访问到数据库,去重建缓存导致数据库宕机。
 解决方案: 可以通过加锁的方式来解决,在进行热点key缓存重建时,加上互斥锁,同一时间只允许一个线程去访问数据库重建缓存。类似与双重检测锁机制。这样就能避免大量线程重建缓存的出现。  
     开发规范    
     key 设计    
     保证key的可读性,可以使用业务名、功能名 以冒号分割命名如“login:user:1”  
     在保证语义的前提下 控制key的长度。在key很多的情况下key所占的内存空间也要考虑  
     不要在key中写特殊字符:如空格、@、?、*等  
     value值设计    
     不要出现bigkey,防止流量过大,导致慢查询    
     在redis中一个字符串最大可以由512M,一个二级数据结构(list hash set zset )可以存储大约40亿个元素(2^32-1),但在实际开发中如果出现下列情况,任务存储这些数据的key是一个bigkey  
     字符串类型:单个value值很大时就是bigkey, 如一般情况下value值大于10kb 时  
     非字符串类型,如hash 、list  、Set 、 zset 等 value 存储的数据元素也不宜太多,最好不要超过5000个元素  
     bigkey优化    
     如果一点有bigkey 存在,避免一次性取出所有数据,支取需要用的一部分数据  
     选择合理的数据接口进行存储,如: hash  、 list 、 set。如对象存储可以使用hash 结构。  
     对list 、set 等集合如果出现大量数据可以拆分成多个list结构  
     命令使用    
     O(N)时间复杂度命令,关注N的值
hgetall lrange smembers zrange sinter 等命令在使用时需要明确N的大小
  
    hgetall lrange smembers zrange sinter 等命令在使用时需要明确N的大小
 禁用命令,如: keys 、 flushdb 、 flushall 等全量操作,通过rename 命令进行重命名  
     使用批量操作提供效率: 如 mset、mget 、pipeline管道操作,一次性操作多条命令减少网络IO。一次批量操作数据量不宜太大 500个以内   
     Redis 事务较弱,官方推荐使用lua脚本来解决事务问题  
     Redis连接池设置    
     使用代连接池的客服端,可以有效的控制连接,提升性能。三个重要的参数设置:    
     maxtotal :资源池中最大连接数 ,可以根据业务希望Redis达到的并发量+ 命令执行时间+ Redis资源确定。
如 业务要求qps 为 50000, 一次命令执行时间为1ms,那么QPS为1000. maxtotal = 50000 / 1000 = 50
    如 业务要求qps 为 50000, 一次命令执行时间为1ms,那么QPS为1000. maxtotal = 50000 / 1000 = 50
 maxIdle : 资源池允许最大空闲的连接数。maxIdle实际才是业务需要的最大连接数,maxTotal是为了给出余量,一般maxIdle设置为上面根据Qps计算出的值,Maxtotal在该值的基础上增加一倍。  
     minIdle : 源池确保最少空闲的连接数。超过该数的连接执行完任务后,会慢慢移除连接池释放掉  
     分布式环境下的高并发接口设计    
     1. 接口所有请求先走缓存层,获取不到数据再走持久层  
     2.  只对热点数据加Redis缓存,通时加缓存过期时间,当请求命中缓存后对过期时间进行重设  
     3. 缓存失效(缓存击穿)。在对缓存加过期时间时,在一点范围内使用随机时间设置缓存。防止缓存集中过期  
     4. 缓存穿透。访问数据库和缓存都不存在的数据时,向缓存层中放入空缓存,防止下次访问再次缓存击穿  
     5. 热点缓存重建: 使用双重检测锁机制,保证同一时间只有一个线程能去数据库层重建缓存  
     6. 数据库缓存不一致问题:根据实际情况选择 加过期时间或者是加读写锁、或者使用第三方框架。如:阿里的canal框架,通过监听binlog日志更新缓存  
     
    收藏 
      
    收藏 
     
 
 
 
 
  0 条评论
 下一页
  
   
   
  
  
  
  
  
  
  
  
  
  
 