团队提效
- 统一开发工具。
- 代码评审,一致化大家的认知,统一代码风格。
- 新人业务名词 / 概念培训
- 任务拆分,最大可能并行开发
- 人员备份
架构图


MQ(RocketMQ)
为什么需要使用 MQ
- 异步处理(多线程和 MQ)
- 实现解耦
- 流量削峰(MQ 可以实现抗高并发)
MQ 如何避免消息堆积的问题
- 提高消费者消费的速率;(对我们的消费者实现集群)
- 消费者应该批量形式获取消息,减少网络传输的次数;
如何高可用?
- NameServer 因为是无状态,且不相互通信的,所以只要集群部署就可以保证高可用。
- Master 和 Slave,Master 角色的 Broker 支持读和写,Slave 角色的 Broker 只支持读,Master 会向 Slave 同步消息。 Consumer 的读请求会被自动切换
有哪些消息模型?
- 队列模型
- 发布/订阅模型
MQ 如何保证消息不丢失?
- MQ 服务器端消息持久化到 CommitLog(日志文件)中
- 生产者 消息确认机制 必须确认消息成功刷盘到硬盘中,才能够认为消息投递成功。
- Broker 通过主从模式来保证高可用
Broker 支持 Master 和 Slave 同步复制、Master 和 Slave 异步复制模式,生产者的消息都是发送> 给 Master,但是消费既可以从 Master 消费,也可以从 Slave 消费。同步复制模式可以保证即使 > Master 宕机,消息肯定在 Slave 中有备份,保证了消息不会丢失。
- Broker 的刷盘机制:同步刷盘和异步刷盘,不管哪种刷盘都可以保证消息一定存储在 pagecache 中(内存中),但是同步刷盘更可靠,它是 Producer 发送消息后等数据持久化到磁盘之后再返回响应给 Producer。
- 消费者 必须确认消息消费成功 。
处理消息重复
- 业务幂等:第一种是保证消费逻辑的幂等性,也就是多次调用和一次调用的效果是一样的。这样一来,不管消息消费多少次,对业务都没有影响。
- 消息去重:第二种是业务端,对重复的消息就不再消费了。这种方法,需要保证每条消息都有一个惟一的编号,通常是业务相关的,比如订单号,消费的记录需要落库,而且需要保证和消息确认这一步的原子性。
延时消息了解吗?
电商的订单超时自动取消,就是一个典型的利用延时消息的例子,用户提交了一个订单,就可以发送一个延时消息,1h 后去检查这个订单的状态,如果还是未付款就取消订单释放库存。
四部分组成
对应到 RocketMQ 中,这四个角色就是 Producer、 Consumer、 Broker 、NameServer
- Producer 在发送消息前从 NameServer 中获取 Topic 的路由信息也就是发往哪个 Broker,Consumer 也会定时从 NameServer 获取 Topic 的路由信息,Broker 在启动时会向 NameServer 注册,并定时进行心跳连接,且定时同步维护的 Topic 到 NameServer。
- Broker 消息存储和中转角色,负责存储和转发消息,Broker 内部维护着一个个 Consumer Queue,用来存储消息的索引,真正存储消息的地方是 CommitLog(日志文件)。
Redis
Redis 的持久化机制是什么?各自的优缺点?
RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。 优点: 1、只有一个文件 dump.rdb,方便持久化。 2、容灾性好,一个文件可以保存到安全的磁盘。 3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能。 4、相对于数据集大时,比 AOF 的启动效率更高。 缺点: 1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)
AOF:持久化 AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。 优点: 1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。 2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。 缺点: 1、AOF 文件比 RDB 文件大,且恢复速度慢。 2、数据集大的时候,比 rdb 启动效率低。
如何选择
- 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据
- 可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化
缓存扩容
缓存扩容主要关注的是缓存的容量和缓存命中率:
- 增加缓存容量:直接增加Redis实例的内存以容纳更多的数据。这可以通过增加服务器内存或者将缓存数据分布到更多的Redis实例来实现。
- 水平扩展:和持久化数据一样,可以通过Redis Cluster进行水平扩展,将缓存数据分布在多个节点上。
- 缓存策略优化:调整缓存淘汰策略(如LRU、LFU、TTL等)以确保高价值的数据能长期保存在缓存中,并减少不必要的缓存占用。
- 使用多级缓存:可以结合使用本地缓存(如Guava、Caffeine等)和Redis缓存,形成多级缓存结构,减轻Redis的压力。
- 热点数据优化:针对热点数据进行优化,确保热门数据的缓存效率,通过调整缓存TTL或者增加特定数据的缓存容量来处理。
Redis的过期键的删除策略
- 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
- 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
- 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果
Redis的内存淘汰策略有哪些
- noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的)
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
Redis 的内存优化可以从以下几个方面入手:
- 数据结构选择 例如,对于小集合数据,如果元素数量较少,可以使用 ziplist 编码而不是 hashtable ,以节省内存。 合理选择字符串的编码方式,如 int 、 embstr 或 raw 。
- 键值对精简 缩短键的长度,去除不必要的前缀或后缀。 减少值的大小,例如只存储必要的字段。
- 定期清理过期数据 设置合理的过期时间策略,及时删除不再需要的数据。
- 限制数据量 使用 maxmemory 配置限制 Redis 使用的最大内存。 结合 LRU (Least Recently Used)或 LFU (Least Frequently Used)算法淘汰数据。
- 数据压缩 对于某些值,可以在存储前进行压缩,读取时解压缩。
- 优化数据存储方式 例如,将频繁访问但不常修改的数据存储在 Redis 中,其他数据存储在其他数据库中。
三大集群模式
主从复制模式
优点:
- 配置简单,易于实现。
- 实现数据冗余,提高数据可靠性。
- 读写分离,提高系统性能。 缺点:
- 主节点故障时,需要手动切换到从节点,故障恢复时间较长。
- 主节点承担所有写操作,可能成为性能瓶颈。
- 无法实现数据分片,受单节点内存限制。
哨兵模式
优点:
- 自动故障转移,提高系统的高可用性。
- 具有主从复制模式的所有优点,如数据冗余和读写分离。 缺点:
- 配置和管理相对复杂。
- 依然无法实现数据分片,受单节点内存限制。
Cluster模式
在Cluster模式下,Redis将所有的键值对数据分散在多个节点上。每个节点负责一部分数据,称为槽位。通过对数据的分片,Cluster模式可以突破单节点的内存限制,实现更大规模的数据存储。 数据分配
- 通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了16384 个槽位
- 每份数据分片会存储在多个互为主从的多节点上
- 数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)
- 同一分片多个节点间的数据保持一致性
- 读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
- 扩容时时需要需要把旧节点的数据迁移一部分到新节点
优点:
- 数据分片,实现大规模数据存储。
- 负载均衡,提高系统性能。
- 自动故障转移,提高高可用性。 缺点:
- 配置和管理较复杂。
- 一些复杂的多键操作可能受到限制。
主节点选举
- 过滤:“不健康”(主观下线、断线)、5 秒内没有回复过 Sentinel 节 点 ping 响应、与主节点失联超过 down-after-milliseconds*10 秒。
- 选择 slave-priority(从节点优先级)最高的从节点列表,如果存在则返回,不存在则继续。
- 选择复制偏移量最大的从节点(复制的最完整),如果存在则返 回,不存在则继续。
- 选择 runid 最小的从节点。
缓存击穿、缓存穿透、缓存雪崩?
缓存击穿 缓存击穿是指某一个或少数几个数据被高频访问,当这些数据在缓存中过期的那一刻,大量请求就会直接到达数据库,导致数据库瞬间压力过大。
- 加锁更新,⽐如请求查询 A,发现缓存中没有,对 A 这个 key 加锁,同时去数据库查询数据,写⼊缓存,再返回给⽤户,这样后⾯的请求就可以从缓存中拿到数据了。
- 将过期时间组合写在 value 中,通过异步的⽅式不断的刷新过期时间,防⽌此类现象
缓存穿透 缓存穿透是指查询不存在的数据,由于缓存没有命中(因为数据根本就不存在),请求每次都会穿过缓存去查询数据库
- 缓存空值/默认值
- 在存储和缓存之前,加一个布隆过滤器
缓存雪崩 大量的缓存数据同时过期或缓存服务器突然宕机了,导致所有的请求都落到了数据库上
- 集群部署
- 多级缓存
- 随机值过期时间
- 限流和降级(令牌桶或漏斗算法)
怎么处理热 key?
- 客户端多级缓存
- 把热 key 打散到不同的服务器