MST

星途 面试题库

面试题:如何在复杂业务场景下优雅处理Redis切换数据库异常

假设你正在开发一个涉及多个Redis数据库切换操作的复杂业务系统,在高并发情况下,Redis切换数据库可能出现连接超时、数据不一致等异常。请阐述你会如何设计一个优雅的异常处理机制,以确保业务的连续性和数据的完整性,可结合代码示例说明。
46.3万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试
  1. 连接超时处理
    • 重试机制:当出现连接超时异常时,可以设置重试逻辑。例如,使用Python的redis - py库:
import redis
import time

redis_client = redis.StrictRedis(host='localhost', port=6379, db = 0)

def execute_with_retry(func, *args, **kwargs):
    max_retries = 3
    retry_delay = 1
    for attempt in range(max_retries):
        try:
            return func(*args, **kwargs)
        except redis.ConnectionError as e:
            if attempt < max_retries - 1:
                time.sleep(retry_delay)
                retry_delay *= 2
            else:
                raise e


# 示例操作
def set_key_value(key, value):
    return redis_client.set(key, value)


execute_with_retry(set_key_value, 'test_key', 'test_value')
  • 异步处理:可以将一些非关键的Redis操作放到异步任务队列(如Celery)中执行。当出现连接超时,异步任务可以在后台不断重试,而不会阻塞主线程的业务流程。
  1. 数据不一致处理
    • 事务和Watch机制:在Redis中,可以使用事务和WATCH命令来确保数据一致性。例如,假设要在两个数据库之间转移数据:
def transfer_data(from_db, to_db, key):
    from_redis = redis.StrictRedis(host='localhost', port=6379, db = from_db)
    to_redis = redis.StrictRedis(host='localhost', port=6379, db = to_db)
    pipe = from_redis.pipeline()
    pipe.watch(key)
    value = pipe.get(key)
    if value is not None:
        pipe.multi()
        pipe.delete(key)
        to_redis.set(key, value)
        try:
            pipe.execute()
        except redis.WatchError:
            # 数据已被其他客户端修改,重新执行操作
            transfer_data(from_db, to_db, key)
  • 数据版本控制:在应用层给数据添加版本号。每次读取数据时,同时获取版本号,在写入数据时,检查版本号是否匹配。如果不匹配,说明数据在读取后被其他操作修改,需要重新读取数据并重新操作。例如:
def update_data(redis_client, key, new_value):
    current_version = redis_client.get(f'{key}_version')
    if current_version is None:
        current_version = 0
    else:
        current_version = int(current_version)
    while True:
        value = redis_client.get(key)
        new_version = current_version + 1
        pipe = redis_client.pipeline()
        pipe.watch(key)
        if int(redis_client.get(f'{key}_version')) == current_version:
            pipe.multi()
            pipe.set(key, new_value)
            pipe.set(f'{key}_version', new_version)
            try:
                pipe.execute()
                break
            except redis.WatchError:
                current_version = int(redis_client.get(f'{key}_version'))
        else:
            current_version = int(redis_client.get(f'{key}_version'))


  1. 全局异常捕获
    • 在应用的入口处(如Flask或Django的视图函数外层,或者在Python的asyncio事件循环中),设置全局异常捕获。这样可以捕获未处理的Redis异常,并进行统一的日志记录和错误处理,确保业务不会因为未处理的异常而崩溃。例如,在Flask应用中:
from flask import Flask

app = Flask(__name__)

@app.errorhandler(Exception)
def handle_exception(e):
    # 记录详细的异常日志
    import logging
    logging.exception('Unhandled exception')
    # 返回给客户端友好的错误信息
    return 'An error occurred', 500