MST
星途 面试题库

面试题:高并发下Redis与MySQL协同处理多媒体数据查询的架构设计

假设面临高并发的多媒体数据查询场景,需要通过Redis缓存与MySQL数据库协同工作来保障高效查询。请设计一个详细的架构方案,包括数据在Redis和MySQL之间的流动、缓存预热策略、缓存失效处理,以及如何应对Redis缓存雪崩、穿透和击穿问题,确保系统的高可用性和高性能。
40.3万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

1. 架构总体设计

  1. 客户端:发起多媒体数据查询请求。
  2. 应用层:接收请求,先查询Redis缓存。若缓存命中,直接返回数据;若未命中,则查询MySQL数据库,将从MySQL获取的数据存入Redis缓存,并返回给客户端。
  3. Redis层:存储高频访问的多媒体数据,提供快速查询。
  4. MySQL层:作为数据的持久化存储,保存所有多媒体数据。

2. 数据在Redis和MySQL之间的流动

  1. 读操作
    • 客户端发送查询请求到应用层。
    • 应用层首先查询Redis,若缓存命中,直接返回数据给客户端。
    • 若Redis未命中,应用层查询MySQL,获取数据后,将数据写入Redis缓存(设置合适的过期时间),然后返回数据给客户端。
  2. 写操作
    • 客户端发送写请求到应用层。
    • 应用层先更新MySQL数据,成功后删除Redis中对应的缓存数据,确保数据一致性。

3. 缓存预热策略

  1. 启动时预热:在系统启动阶段,从MySQL批量读取热门多媒体数据,将其写入Redis缓存。可以通过定时任务或者一次性脚本完成。例如,利用Python的pymysql连接MySQL,redis - py连接Redis,批量查询并写入。
import pymysql
import redis

# 连接MySQL
mysql_conn = pymysql.connect(host='mysql_host', user='user', password='password', database='database')
mysql_cursor = mysql_conn.cursor()

# 连接Redis
redis_conn = redis.StrictRedis(host='redis_host', port=6379, db=0)

# 查询热门数据
mysql_cursor.execute('SELECT * FROM multimedia_data WHERE is_popular = 1')
rows = mysql_cursor.fetchall()

for row in rows:
    # 假设row的第一个字段为id,其他为数据,以JSON格式存储
    data_id = row[0]
    data = json.dumps(row[1:])
    redis_conn.set(data_id, data)

mysql_conn.close()
  1. 定时预热:使用定时任务(如Linux的crontab或Spring Boot的@Scheduled),定期从MySQL中查询更新热门数据到Redis,保证缓存中数据的时效性。

4. 缓存失效处理

  1. 主动更新:当MySQL数据发生变化(如更新、删除操作)时,及时删除Redis中对应的缓存数据。在应用层的写操作逻辑中,先更新MySQL,成功后执行Redis删除操作。
  2. 被动更新:当缓存过期,客户端请求时,应用层从MySQL重新读取数据并更新Redis缓存。

5. 应对Redis缓存雪崩、穿透和击穿问题

  1. 缓存雪崩
    • 设置不同过期时间:避免大量缓存同时过期。在设置Redis缓存过期时间时,采用随机时间,例如原本过期时间为1小时,可以设置为50 - 70分钟之间的随机值。
    • 启用持久化:开启Redis的持久化机制(RDB或AOF),当Redis重启时,能够快速恢复缓存数据,减少因缓存大量丢失导致的雪崩问题。
    • 搭建集群:采用Redis集群,增加系统的可用性和容错性,即使部分节点故障,其他节点仍能提供服务。
  2. 缓存穿透
    • 布隆过滤器:在应用层使用布隆过滤器,在查询Redis和MySQL之前,先通过布隆过滤器判断数据是否存在。如果布隆过滤器判断不存在,则直接返回,避免无效查询穿透到MySQL。例如,使用Python的pybloom - filter库。
from pybloomfilter import BloomFilter

# 初始化布隆过滤器,预计元素数量10000,误判率0.01
bloom = BloomFilter(capacity=10000, error_rate=0.01)

# 假设从MySQL获取所有数据id,添加到布隆过滤器
mysql_cursor.execute('SELECT id FROM multimedia_data')
ids = mysql_cursor.fetchall()
for id in ids:
    bloom.add(str(id[0]))

# 查询时先检查布隆过滤器
def query_data(data_id):
    if str(data_id) not in bloom:
        return None
    # 继续查询Redis和MySQL逻辑
- **空值缓存**:当查询MySQL发现数据不存在时,将空值也缓存到Redis中,并设置较短的过期时间,避免下次相同的无效查询穿透到MySQL。

3. 缓存击穿: - 互斥锁:在应用层,当缓存失效,查询MySQL前,使用互斥锁(如Redis的SETNX命令实现分布式锁)。只有获取到锁的线程才能查询MySQL并更新缓存,其他线程等待,避免大量请求同时查询MySQL。

import time

def get_data_with_mutex(data_id):
    lock_key = 'lock:' + data_id
    lock_acquired = redis_conn.setnx(lock_key, 1)
    if lock_acquired:
        try:
            data = redis_conn.get(data_id)
            if not data:
                data = get_data_from_mysql(data_id)
                redis_conn.set(data_id, data)
            return data
        finally:
            redis_conn.delete(lock_key)
    else:
        # 等待一段时间后重试
        time.sleep(0.1)
        return get_data_with_mutex(data_id)
- **热点数据不过期**:对于一些极其热点的数据,不设置过期时间,同时使用后台线程定期更新缓存数据,保证数据的实时性。

6. 确保系统的高可用性和高性能

  1. 高可用性
    • Redis集群:搭建Redis集群,采用主从复制和哨兵模式或Cluster模式,提高Redis的可用性,自动进行故障转移。
    • MySQL主从复制:配置MySQL主从复制,主库负责写操作,从库负责读操作,提高MySQL的可用性和读写性能。同时可以使用MHA(Master High Availability)等工具实现自动故障切换。
    • 负载均衡:在应用层前端部署负载均衡器(如Nginx),将请求均匀分配到多个应用服务器实例,避免单个服务器压力过大。
  2. 高性能
    • 优化SQL查询:对MySQL中的多媒体数据查询语句进行优化,添加合适的索引,避免全表扫描。
    • 缓存优化:合理设置Redis的缓存策略,如采用LRU(Least Recently Used)等淘汰策略,确保热点数据常驻缓存。同时,根据业务需求调整Redis的内存配置,提高缓存命中率。
    • 异步处理:对于一些非关键的操作,如缓存预热、缓存更新等,可以采用异步方式处理,减少对主线程的影响,提高系统的响应速度。例如,使用消息队列(如Kafka)将缓存更新任务异步化。