面试题答案
一键面试架构设计
-
数据结构设计:使用Redis的哈希(Hash)结构存储每个用户的购物车数据。哈希的field为商品ID,value为商品数量。例如:
HSET user:1:cart product1 5
,表示用户1的购物车中商品product1的数量为5。 -
持久化策略:
- 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
(由操作系统决定何时同步,性能最高但数据安全性最低)。
- RDB(Redis Database):RDB是一种快照持久化方式,它在指定的时间间隔内将内存中的数据集快照写入磁盘。适合用于灾难恢复,因为它可以快速恢复大量数据。配置方式可以通过修改
-
缓存一致性:
- 读写分离:在读取购物车数据时,从Redis缓存中读取;在写入操作(添加、更新、删除商品)后,立即更新Redis缓存,并考虑使用发布 - 订阅(Pub/Sub)机制通知其他可能依赖该数据的服务。例如,当用户在购物车中添加商品后,除了更新Redis中的购物车数据,还可以发布一条消息,告知库存服务更新库存。
- 版本控制:为购物车数据添加版本号,每次购物车数据发生变化时,版本号递增。读取数据时,同时读取版本号,在进行更新操作前,先检查版本号是否一致,如果不一致则重新读取数据。这样可以避免在并发操作时出现数据覆盖的问题。
-
并发控制:
- Redis事务:Redis事务使用
MULTI
、EXEC
、DISCARD
等命令。例如,在添加商品、更新商品数量和删除商品等操作时,可以使用事务来保证这些操作的原子性。示例代码如下:
- Redis事务:Redis事务使用
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
代码实现思路
- 添加商品:
- 使用
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)
- 更新商品数量:
- 使用
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)
- 删除商品:
- 使用
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)
- 计算总价:
- 遍历购物车哈希结构,获取每个商品的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)
通过以上架构设计和代码实现思路,可以构建一个高性能、高可用且数据准确的电商购物车系统。