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 条评论
下一页
为你推荐
查看更多