首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >redis与mysql的数据一致性问题(事务一致性)

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

作者头像
GeekLiHua
发布2025-01-21 13:19:32
发布2025-01-21 13:19:32
2480
举报
文章被收录于专栏:JavaJava

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

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

代码语言:javascript
复制
# 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中购物车缓存的原子性更新。如果在执行事务前发现被监视的键(购物车缓存键)被其他客户端修改,则事务会被取消。
代码语言:javascript
复制
# 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()
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • redis与mysql的数据一致性问题(事务一致性)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档