redis与mysql的数据一致性问题(事务一致性)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: redis与mysql的数据一致性问题(事务一致性)

redis与mysql的数据一致性问题(事务一致性)

案例:考虑一个在线购物应用,其中有一个购物车服务,购物车信息存储在MySQL中,同时为了提高性能,购物车中的商品数量也被缓存到了Redis。用户在购物车中添加商品时,需要保证购物车数量在MySQL和Redis中的更新是原子性的,以避免不一致的情况。

# Python代码示例 - 添加商品到购物车的逻辑
import redis
import MySQLdb
def add_to_cart(user_id, product_id, quantity):
    redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
    mysql_conn = MySQLdb.connect(host='localhost', user='user', password='password', db='ecommerce')
    cursor = mysql_conn.cursor()
    try:
        # 开始MySQL事务
        mysql_conn.begin()
        # 从MySQL中获取购物车中的商品数量
        cursor.execute(f'SELECT quantity FROM shopping_carts WHERE user_id={user_id} AND product_id={product_id}')
        result = cursor.fetchone()
        if result:
            # 商品已存在,更新数量
            new_quantity = result[0] + quantity
            cursor.execute(f'UPDATE shopping_carts SET quantity={new_quantity} WHERE user_id={user_id} AND product_id={product_id}')
        else:
            # 商品不存在,插入新记录
            cursor.execute(f'INSERT INTO shopping_carts (user_id, product_id, quantity) VALUES ({user_id}, {product_id}, {quantity})')
        # 提交MySQL事务
        mysql_conn.commit()
        # 更新Redis中购物车缓存
        redis_client.hset(f'user:{user_id}:cart', product_id, quantity)
    except Exception as e:
        # 发生异常,回滚MySQL事务
        mysql_conn.rollback()
        print(f"Error: {e}")
    finally:
        cursor.close()
        mysql_conn.close()

解决方案:

  1. 使用MySQL事务确保原子性:
    在MySQL中执行购物车更新操作时,将相关操作包裹在事务中,以确保它们的原子性。如果任何一个操作失败,整个事务将被回滚,防止不一致的数据状态。
  2. 使用Redis的WATCH和MULTI命令实现乐观锁:
    使用Redis的WATCH和MULTI命令,通过乐观锁的方式确保Redis中购物车缓存的原子性更新。如果在执行事务前发现被监视的键(购物车缓存键)被其他客户端修改,则事务会被取消。
# Python代码示例 - 使用Redis的WATCH和MULTI命令实现乐观锁
import redis
def add_to_cart_atomic(user_id, product_id, quantity):
    redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
    mysql_conn = MySQLdb.connect(host='localhost', user='user', password='password', db='ecommerce')
    cursor = mysql_conn.cursor()
    try:
        # 使用WATCH监视购物车缓存键
        with redis_client.pipeline() as pipe:
            while True:
                try:
                    # 开启Redis事务
                    pipe.watch(f'user:{user_id}:cart')
                    # 获取当前购物车中商品的数量
                    current_quantity = int(pipe.hget(f'user:{user_id}:cart', product_id) or 0)
                    # 开始Redis事务
                    pipe.multi()
                    # 计算新的商品数量
                    new_quantity = current_quantity + quantity
                    # 更新购物车缓存
                    pipe.hset(f'user:{user_id}:cart', product_id, new_quantity)
                    # 执行Redis事务
                    pipe.execute()
                    # 开始MySQL事务
                    mysql_conn.begin()
                    # 更新MySQL中购物车数量
                    cursor.execute(f'INSERT INTO shopping_carts (user_id, product_id, quantity) VALUES ({user_id}, {product_id}, {quantity}) ON DUPLICATE KEY UPDATE quantity=quantity+{quantity}')
                    # 提交MySQL事务
                    mysql_conn.commit()
                    break
                except redis.WatchError:
                    # 被监视的键被其他客户端修改,重新尝试
                    continue
    except Exception as e:
        # 发生异常,回滚MySQL事务
        mysql_conn.rollback()
        print(f"Error: {e}")
    finally:
        cursor.close()
        mysql_conn.close()
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2天前
|
canal 缓存 NoSQL
【Redis系列笔记】双写一致性
本文讨论了缓存不一致问题及其后果,如价格显示错误和订单计算错误。问题主要源于并发和双写操作的异常。解决方案包括使用分布式锁(但可能导致性能下降和复杂性增加)、延迟双删策略(通过延迟删除缓存来等待数据同步)以及异步同步方法,如通过Canal和MQ实现数据的最终一致性。面试中,可以提及这些策略来确保数据库和缓存数据的一致性。
26 1
【Redis系列笔记】双写一致性
|
16天前
|
缓存 NoSQL 关系型数据库
13- Redis和Mysql如何保证数据⼀致?
该内容讨论了保证Redis和MySQL数据一致性的几种策略。首先提到的两种方法存在不一致风险:先更新MySQL再更新Redis,或先删Redis再更新MySQL。第三种方案是通过MQ异步同步以达到最终一致性,适用于一致性要求较高的场景。项目中根据不同业务需求选择不同方案,如对一致性要求不高的情况不做处理,时效性数据设置过期时间,高一致性需求则使用MQ确保同步,最严格的情况可能涉及分布式事务(如Seata的TCC模式)。
44 6
|
23天前
|
SQL 关系型数据库 MySQL
轻松入门MySQL:保障数据完整性,MySQL事务在进销存管理系统中的应用(12)
轻松入门MySQL:保障数据完整性,MySQL事务在进销存管理系统中的应用(12)
|
24天前
|
NoSQL Redis 数据库
什么是Redis的事务?
Redis事务提供原子性和顺序性,确保命令按顺序执行且不被打断。核心概念包括原子性、顺序性、隔离性和持久性。关键指令有MULTI、EXEC、DISCARD和WATCH,用于事务的开始、执行、取消和监视。这保障了命令的完整性,防止并发操作导致的数据不一致。
12 2
|
23天前
|
NoSQL Redis
Redis事务:保证数据操作的一致性和可靠性
Redis事务:保证数据操作的一致性和可靠性
|
1天前
|
缓存 NoSQL Redis
深度解析Redis的缓存双写一致性
【4月更文挑战第20天】
10 1
|
2天前
|
存储 SQL 关系型数据库
MySQL 事务
MySQL 事务
|
16天前
|
存储 SQL 关系型数据库
【MySQL实战笔记】03.事务隔离:为什么你改了我还看不见?-02
【4月更文挑战第7天】数据库通过视图实现事务隔离,不同隔离级别如读未提交、读已提交、可重复读和串行化采用不同策略。以可重复读为例,MySQL使用多版本并发控制(MVCC),每个事务有其独立的视图。回滚日志在无更早视图时被删除。长事务可能导致大量存储占用,应避免。事务启动可显式用`begin`或设置`autocommit=0`,但后者可能意外开启长事务。建议使用`autocommit=1`并显式管理事务,若需减少交互,可使用`commit work and chain`。
30 5
|
29天前
|
关系型数据库 MySQL 测试技术
面试-MySQL的四种事务隔离级别
面试-MySQL的四种事务隔离级别
19 0
|
29天前
|
NoSQL 关系型数据库 MySQL
安装Docker&镜像容器操作&使用Docker安装部署MySQL,Redis,RabbitMQ,Nacos,Seata,Minio
安装Docker&镜像容器操作&使用Docker安装部署MySQL,Redis,RabbitMQ,Nacos,Seata,Minio
392 1