MST
星途 面试题库

面试题:复杂业务场景下Redis多选项执行顺序深度优化

假设你负责一个电商购物车系统,使用Redis存储购物车数据。购物车涉及多项操作,如添加商品、更新商品数量、删除商品、计算总价等,并且不同用户的操作可能并发进行。同时,系统还需要考虑数据持久化和缓存一致性问题。请详细阐述你将如何优化这些操作的执行顺序,结合Redis的持久化策略、事务机制以及可能用到的其他技术手段,以实现高性能、高可用且数据准确的购物车系统,要求有完整的架构设计和代码实现思路。
20.6万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

架构设计

  1. 数据结构设计:使用Redis的哈希(Hash)结构存储每个用户的购物车数据。哈希的field为商品ID,value为商品数量。例如:HSET user:1:cart product1 5,表示用户1的购物车中商品product1的数量为5。

  2. 持久化策略

    • RDB(Redis Database):RDB是一种快照持久化方式,它在指定的时间间隔内将内存中的数据集快照写入磁盘。适合用于灾难恢复,因为它可以快速恢复大量数据。配置方式可以通过修改redis.conf文件中的save参数,例如save 900 1表示900秒内如果至少有1个键被修改,则进行快照。
    • AOF(Append - Only - File):AOF是一种追加式持久化方式,它将写命令追加到文件末尾。由于是追加操作,因此数据更完整,适合数据安全性要求较高的场景。配置方式通过修改redis.conf文件,开启appendonly yes,并可以选择不同的同步策略,如appendfsync always(每次写操作都同步到磁盘,数据最安全但性能最低)、appendfsync everysec(每秒同步一次,兼顾性能和数据安全)、appendfsync no(由操作系统决定何时同步,性能最高但数据安全性最低)。
  3. 缓存一致性

    • 读写分离:在读取购物车数据时,从Redis缓存中读取;在写入操作(添加、更新、删除商品)后,立即更新Redis缓存,并考虑使用发布 - 订阅(Pub/Sub)机制通知其他可能依赖该数据的服务。例如,当用户在购物车中添加商品后,除了更新Redis中的购物车数据,还可以发布一条消息,告知库存服务更新库存。
    • 版本控制:为购物车数据添加版本号,每次购物车数据发生变化时,版本号递增。读取数据时,同时读取版本号,在进行更新操作前,先检查版本号是否一致,如果不一致则重新读取数据。这样可以避免在并发操作时出现数据覆盖的问题。
  4. 并发控制

    • Redis事务:Redis事务使用MULTIEXECDISCARD等命令。例如,在添加商品、更新商品数量和删除商品等操作时,可以使用事务来保证这些操作的原子性。示例代码如下:
import redis

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

# 开启事务
pipe = r.pipeline()
pipe.multi()
# 添加商品操作
pipe.hincrby('user:1:cart', 'product1', 1)
# 更新商品数量操作
pipe.hset('user:1:cart', 'product2', 3)
# 删除商品操作
pipe.hdel('user:1:cart', 'product3')
# 执行事务
pipe.execute()
- **乐观锁**:使用`WATCH`命令实现乐观锁。例如,在更新购物车商品数量时,先使用`WATCH`监控购物车数据,然后开启事务进行更新操作。如果在`EXEC`执行前,被监控的键被其他客户端修改,事务将被取消。示例代码如下:
import redis

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

with r.pipeline() as pipe:
    while True:
        try:
            # 监控购物车数据
            pipe.watch('user:1:cart')
            # 获取当前商品数量
            current_count = int(pipe.hget('user:1:cart', 'product1'))
            new_count = current_count + 1
            # 开启事务
            pipe.multi()
            pipe.hset('user:1:cart', 'product1', new_count)
            # 执行事务
            pipe.execute()
            break
        except redis.WatchError:
            # 重试
            continue

代码实现思路

  1. 添加商品
    • 使用HINCRBY命令增加商品数量,如果商品不存在则创建并设置数量为1。
    • 示例代码(Python):
def add_product_to_cart(user_id, product_id, quantity=1):
    r = redis.Redis(host='localhost', port=6379, db = 0)
    r.hincrby(f'user:{user_id}:cart', product_id, quantity)
  1. 更新商品数量
    • 使用HSET命令直接设置商品的新数量。
    • 示例代码(Python):
def update_product_quantity(user_id, product_id, new_quantity):
    r = redis.Redis(host='localhost', port=6379, db = 0)
    r.hset(f'user:{user_id}:cart', product_id, new_quantity)
  1. 删除商品
    • 使用HDEL命令删除购物车中的商品。
    • 示例代码(Python):
def remove_product_from_cart(user_id, product_id):
    r = redis.Redis(host='localhost', port=6379, db = 0)
    r.hdel(f'user:{user_id}:cart', product_id)
  1. 计算总价
    • 遍历购物车哈希结构,获取每个商品的ID和数量。
    • 根据商品ID从商品信息存储(如数据库或另一个Redis哈希)中获取商品单价。
    • 计算每个商品的总价并累加。
    • 示例代码(Python):
def calculate_cart_total(user_id):
    r = redis.Redis(host='localhost', port=6379, db = 0)
    cart = r.hgetall(f'user:{user_id}:cart')
    total = 0
    for product_id, quantity in cart.items():
        product_price = get_product_price(product_id.decode('utf - 8'))
        total += int(quantity) * product_price
    return total

def get_product_price(product_id):
    # 这里假设从另一个Redis哈希获取商品价格
    r = redis.Redis(host='localhost', port=6379, db = 0)
    return r.hget('products:prices', product_id)

通过以上架构设计和代码实现思路,可以构建一个高性能、高可用且数据准确的电商购物车系统。