MST
星途 面试题库

面试题:缓存设计之中等难度:物联网场景下缓存数据一致性问题

在物联网场景中,设备会持续产生大量数据并写入缓存。假设多个设备同时更新缓存中的同一数据,可能导致数据一致性问题。请描述你会采用什么策略来保证缓存数据的一致性,并简单说明该策略的优缺点。
42.6万 热度难度
后端开发缓存设计

知识考点

AI 面试

面试题答案

一键面试

策略

  1. 分布式锁
    • 在更新缓存数据前,每个设备尝试获取分布式锁。只有获取到锁的设备才能对缓存数据进行更新操作,其他设备等待锁的释放。例如可以使用Redis的SETNX(SET if Not eXists)命令实现简单的分布式锁。
    • 代码示例(Python + Redis):
import redis

r = redis.Redis(host='localhost', port=6379, db = 0)
lock_key = "data_update_lock"
lock_value = "unique_value"
if r.set(lock_key, lock_value, nx = True, ex = 10): # 设置锁,10秒过期
    try:
        # 执行缓存更新操作
        cache_key = "shared_data"
        r.set(cache_key, "new_value")
    finally:
        r.delete(lock_key) # 释放锁
else:
    print("Failed to acquire lock, retry later.")
  1. 版本控制
    • 为缓存中的数据添加版本号。每次更新数据时,先读取当前版本号,更新数据后将版本号加1。当设备读取数据时,不仅获取数据,还获取版本号。下次更新时,比较当前版本号与之前读取的版本号,如果一致则进行更新,并递增版本号;如果不一致则说明数据已被其他设备更新,需要重新读取数据再进行操作。
    • 例如在数据库和缓存中都存储数据版本号,在缓存更新逻辑中:
import redis

r = redis.Redis(host='localhost', port=6379, db = 0)
cache_key = "shared_data"
version_key = "shared_data_version"
current_version = int(r.get(version_key) or 0)
data = r.get(cache_key)
# 假设进行数据处理得到new_data
new_data = data + " updated"
new_version = current_version + 1
if r.get(version_key) == str(current_version):
    pipe = r.pipeline()
    pipe.multi()
    pipe.set(cache_key, new_data)
    pipe.set(version_key, new_version)
    pipe.execute()
else:
    print("Data has been updated by another device, retry.")
  1. 队列处理
    • 将所有设备的缓存更新请求放入一个消息队列(如Kafka)中。消息队列按顺序处理这些请求,确保同一时间只有一个更新操作在进行,从而保证数据一致性。
    • 以Python和Kafka为例,生产者将更新请求发送到Kafka主题:
from kafka import KafkaProducer
producer = KafkaProducer(bootstrap_servers='localhost:9092')
update_request = "update_shared_data"
producer.send('update_topic', update_request.encode('utf-8'))
producer.flush()
  • 消费者从Kafka主题中读取请求并按顺序处理:
from kafka import KafkaConsumer
consumer = KafkaConsumer('update_topic', bootstrap_servers='localhost:9092')
for message in consumer:
    update_request = message.value.decode('utf-8')
    # 执行缓存更新操作
    cache_key = "shared_data"
    # 假设更新操作
    new_value = "updated_value"
    r.set(cache_key, new_value)

优缺点

  1. 分布式锁
    • 优点
      • 实现相对简单,在大多数场景下能有效保证数据一致性。
      • 可以灵活设置锁的过期时间,防止死锁情况发生。
    • 缺点
      • 可能存在锁竞争问题,当大量设备同时请求更新时,会导致性能下降。
      • 分布式锁依赖外部系统(如Redis),如果外部系统出现故障,可能影响缓存更新操作。
  2. 版本控制
    • 优点
      • 不需要额外的分布式锁服务,降低了系统复杂度。
      • 适合读多写少的场景,因为读操作不会影响版本号,不会造成额外的性能开销。
    • 缺点
      • 写操作时需要多次读取和比较版本号,增加了操作的复杂性和额外的开销。
      • 可能导致部分更新操作失败,需要设备进行重试,影响系统的实时性。
  3. 队列处理
    • 优点
      • 能很好地保证数据更新的顺序性,从而确保数据一致性。
      • 消息队列本身可以进行削峰填谷,缓解高并发更新带来的压力。
    • 缺点
      • 引入了消息队列系统,增加了系统的复杂性和维护成本。
      • 消息处理可能存在延迟,不适用于对实时性要求极高的场景。