面试题答案
一键面试性能优化
- 网络延迟
- 优化网络拓扑:确保Redis和MySQL服务器位于低延迟的网络环境中,减少不必要的网络跳数,例如部署在同一数据中心的同一机架或相邻机架。
- 连接池复用:在应用程序中使用连接池,减少每次与Redis和MySQL建立新连接的开销。如在Java中可以使用Jedis连接池管理Redis连接,使用HikariCP管理MySQL连接。
- 异步处理:将对Redis和MySQL的操作异步化,使用线程池或异步框架(如Java的CompletableFuture、Node.js的async/await),避免网络I/O阻塞主线程,提高整体吞吐量。
- 内存使用
- 合理配置Redis内存:根据业务需求和数据量,合理设置Redis的最大内存(
maxmemory
)。例如,对于社交互动系统中短期的消息缓存,可根据平均消息量和缓存时间估算所需内存。使用maxmemory-policy
设置内存淘汰策略,如allkeys - lru
,优先淘汰最近最少使用的键,防止内存溢出。 - 数据结构优化:在Redis中选择合适的数据结构存储数据。如使用
list
作为消息队列时,避免存储过大的消息,若消息较大可考虑存储消息ID到MySQL的映射,在需要时再从MySQL读取完整消息。对于频繁查询的用户关系等数据,可使用hash
结构,减少内存碎片化。 - 内存监控与调整:使用Redis的
INFO
命令定期监控内存使用情况,根据监控数据动态调整内存分配和数据存储策略。例如,当发现某些热数据占用过多内存时,考虑将其迁移到持久化存储(如MySQL),并在Redis中保留索引。
- 合理配置Redis内存:根据业务需求和数据量,合理设置Redis的最大内存(
- 持久化策略
- 选择合适的持久化方式:Redis有RDB和AOF两种持久化方式。对于社交互动系统,可采用AOF(Append - Only - File)为主,RDB为辅的策略。AOF以日志形式记录写操作,能保证数据的高可用性,可设置
appendfsync
为everysec
,每秒进行一次同步,在性能和数据安全性间取得平衡。RDB定期生成快照,适合用于数据备份和灾难恢复,可设置合理的快照保存条件,如save 900 1
表示900秒内至少有1个键被修改则进行快照。 - 优化AOF重写:随着AOF文件的增长,会占用大量磁盘空间且重写时可能影响性能。可通过设置
auto - aof - rewrite - min - size
和auto - aof - rewrite - percentage
来优化重写。例如,设置auto - aof - rewrite - min - size 64mb
表示AOF文件大小超过64MB时触发重写,auto - aof - rewrite - percentage 100
表示当前AOF文件大小超过上次重写后文件大小的100%时触发重写。 - 定期清理无效数据:在MySQL中,定期清理已处理且不再需要的历史数据,以减少磁盘占用,提高查询性能。例如,对于已过期的社交互动消息,可通过定时任务执行
DELETE
语句进行清理。
- 选择合适的持久化方式:Redis有RDB和AOF两种持久化方式。对于社交互动系统,可采用AOF(Append - Only - File)为主,RDB为辅的策略。AOF以日志形式记录写操作,能保证数据的高可用性,可设置
故障应对策略
- Redis消息队列消息丢失
- 开启持久化:如上述提到的AOF持久化,确保消息在Redis重启后不会丢失。即使Redis崩溃,AOF文件中的记录可用于恢复未处理的消息。
- 生产者确认机制:在生产者端,使用Redis的
RPUSH
命令发送消息后,等待Redis返回确认信息。若未收到确认,可进行重试。例如在Python中使用redis - py
库:
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
result = r.rpush('message_queue', 'new_message')
if result is None:
# 重试逻辑
pass
- **消费者手动确认**:在消费者端,从Redis队列获取消息后,先处理消息,处理成功后再使用`LREM`命令从队列中删除消息。若处理失败,可将消息重新放回队列或记录到错误队列中,后续进行重试处理。
2. Redis消息队列重复消费
- 幂等性处理:在业务处理逻辑上保证幂等性,即多次处理同一条消息的结果与处理一次相同。例如,在社交互动系统中点赞操作,可在数据库中添加唯一索引,防止重复点赞记录。对于数据库插入操作,使用INSERT IGNORE
或ON DUPLICATE KEY UPDATE
语句。
- 消息去重:在消费者端维护一个已处理消息ID的集合(如Redis的set
),每次处理消息前先检查消息ID是否已存在于集合中。若存在则跳过处理,若不存在则处理消息并将ID添加到集合中。例如在Java中:
Set<String> processedSet = jedis.smembers("processed_messages");
String messageId = getMessageId(message);
if (processedSet.contains(messageId)) {
return;
}
processMessage(message);
jedis.sadd("processed_messages", messageId);
- MySQL数据库主从复制延迟
- 监控复制延迟:使用
SHOW STATUS LIKE 'Seconds_Behind_Master'
命令监控从库的复制延迟,可通过脚本定时执行并设置阈值报警。例如,当延迟超过10秒时发送邮件或短信通知运维人员。 - 优化主库写入性能:减少主库上的大事务,将大事务拆分成多个小事务执行。优化主库的SQL查询,添加合适的索引,减少主库的I/O和CPU负载,从而降低主从复制延迟。
- 增加从库数量:在读写压力较大的情况下,增加从库数量,分担读请求,减少单个从库的压力,降低复制延迟。同时,合理分配读请求到不同从库,可使用负载均衡器(如HAProxy)根据从库的负载情况分配请求。
- 选择合适的复制模式:MySQL有多种复制模式,如基于语句的复制(SBR)、基于行的复制(RBR)和混合模式复制(MBR)。对于社交互动系统,由于数据量较大且可能存在复杂的更新操作,可考虑使用基于行的复制模式,减少主从数据不一致的风险,提高复制效率。
- 监控复制延迟:使用