Этот вопрос проверяет понимание проблем параллельного доступа к кэшу и способов обеспечения консистентности данных в распределённых системах.
Race condition (состояние гонки) при работе с кэшем — это классическая проблема параллелизма, когда результат операции зависит от неуправляемого порядка выполнения потоков или процессов. В контексте кэширования это часто проявляется, когда устаревшие данные перезаписывают свежие, или когда несколько запросов одновременно пытаются обновить одну запись, вызывая потерю обновлений.
INCR, DECR или SETNX (Set if Not eXists). Наиболее мощной является операция Compare-And-Swap (CAS), которая обновляет значение только если оно не изменилось с момента чтения.Redis не имеет встроенной команды CAS, но её можно эмулировать с помощью WATCH, MULTI и EXEC (транзакции с оптимистической блокировкой).
import redis
r = redis.Redis()
def update_user_balance(user_id, amount):
key = f"user:{user_id}:balance"
while True: # Повторяем, если транзакция не удалась
try:
# Начинаем наблюдение за ключом
r.watch(key)
current = int(r.get(key) or 0)
new_balance = current + amount
# Начинаем транзакцию
pipe = r.pipeline()
pipe.multi()
pipe.set(key, new_balance)
# Если за время наблюдения ключ не изменился, exec выполнится
if pipe.execute():
print(f"Balance updated to {new_balance}")
break
except redis.WatchError:
# Ключ изменился другим клиентом, повторяем попытку
print("Race detected, retrying...")
continue
finally:
r.unwatch()
# Пример вызова
update_user_balance(123, 100)Вывод: Выбор стратегии зависит от требований к консистентности, производительности и сложности реализации. Атомарные операции кэш-системы предпочтительны для простых сценариев. Для сложных обновлений с зависимостями используйте оптимистичные блокировки или очереди. Всегда оценивайте, не проще ли отказаться от синхронной инвалидации в пользу короткого TTL.