MST

星途 面试题库

面试题:Redis预计算结果与MySQL数据一致性维护

在借助Redis预计算结果提升MySQL数据洞察能力时,如何保证Redis中的预计算数据与MySQL中的原始数据一致性?请详细描述你所知道的策略与方法,比如在MySQL数据更新后,如何同步更新Redis中的预计算结果以确保数据的准确性和实时性。
11.9万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试
  1. 双写模式
    • 操作流程
      • 当MySQL数据发生更新时,同时对Redis中的预计算数据进行更新。例如,在一个电商系统中,如果商品的价格在MySQL中被修改,那么根据价格参与计算的Redis预计算数据(如某类商品的平均价格等)也要同步修改。
      • 代码示例(以Python的Flask框架结合SQLAlchemy和Redis - Py为例):
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import redis

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] ='mysql://user:password@localhost/mydb'
db = SQLAlchemy(app)
r = redis.Redis(host='localhost', port = 6379, db = 0)

class Product(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    price = db.Column(db.Float)

@app.route('/update_product_price/<int:product_id>/<float:new_price>', methods=['POST'])
def update_product_price(product_id, new_price):
    product = Product.query.get(product_id)
    product.price = new_price
    db.session.commit()
    # 更新Redis预计算数据,假设Redis中存储了所有商品的平均价格
    all_products = Product.query.all()
    total_price = sum([p.price for p in all_products])
    average_price = total_price / len(all_products) if all_products else 0
    r.set('average_product_price', average_price)
    return 'Product price updated successfully'
  • 优缺点
    • 优点:实现相对简单,能够快速保证数据一致性。
    • 缺点:在高并发场景下,可能会因为网络等原因导致MySQL更新成功但Redis更新失败,造成数据不一致。而且每次更新都需要操作两个存储,增加了系统的开销。
  1. 消息队列模式
    • 操作流程
      • 当MySQL数据更新时,向消息队列(如Kafka、RabbitMQ等)发送一条消息,消息内容包含更新的数据及相关操作信息(如新增、修改、删除)。
      • 一个独立的消费者服务监听这个消息队列,当收到消息时,根据消息内容对Redis中的预计算数据进行相应的更新。例如,在一个社交平台中,用户的点赞数在MySQL中更新后,发送消息到消息队列,消费者服务收到消息后重新计算该用户相关的热度值(Redis预计算数据)。
    • 代码示例(以Python结合RabbitMQ和Redis - Py为例)
import pika
import redis

# 连接RabbitMQ
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='mysql_updates')

# 连接Redis
r = redis.Redis(host='localhost', port = 6379, db = 0)

def callback(ch, method, properties, body):
    # 假设消息体格式为:user_id:new_like_count
    parts = body.decode('utf - 8').split(':')
    user_id = parts[0]
    new_like_count = int(parts[1])
    # 重新计算用户热度值并更新到Redis
    # 这里假设热度值计算方式为点赞数的平方
    popularity = new_like_count * new_like_count
    r.set(f'user_{user_id}_popularity', popularity)

channel.basic_consume(queue='mysql_updates', on_message_callback = callback, auto_ack = True)

print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
  • 优缺点
    • 优点:解耦了MySQL和Redis的更新操作,提高了系统的可扩展性和稳定性。消息队列可以保证消息不丢失,即使消费者服务出现故障,重启后可以继续处理未处理的消息。
    • 缺点:引入了额外的组件(消息队列),增加了系统的复杂性和维护成本。消息处理可能存在一定的延迟,无法做到绝对实时的一致性。
  1. 数据库触发器模式(仅适用于支持触发器的数据库,如MySQL)
    • 操作流程
      • 在MySQL数据库中创建触发器。例如,当对某张表进行INSERT、UPDATE或DELETE操作时,触发器会被触发。触发器可以向一个特定的表(如redis_update_log)中插入一条记录,记录包含需要更新Redis预计算数据的相关信息(如涉及的表名、主键值等)。
      • 一个后台任务(如定时任务或常驻进程)定期检查redis_update_log表,根据记录的信息更新Redis中的预计算数据。例如,在一个博客系统中,当文章的评论数在MySQL中更新时,触发器记录更新信息到redis_update_log表,后台任务根据这些信息重新计算文章的热度值(Redis预计算数据)。
    • 优缺点
      • 优点:利用数据库自身的功能实现数据的关联操作,对应用层代码侵入性小。可以精确控制在数据库操作的同时记录相关更新信息。
      • 缺点:不同数据库的触发器语法可能不同,可移植性较差。后台任务定期检查存在一定延迟,而且如果触发器逻辑复杂,可能会影响数据库的性能。