错误处理
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/clients/error-handling/
在使用 Redis 时,可能会因为网络问题、无效命令或资源限制等各种原因而发生错误。本指南介绍了您可能遇到的错误类型以及如何有效处理它们。
错误分类
Redis 错误主要分为四大类。下表快速概述了每种类型。单击任何错误类型可跳转到其详细章节,其中包含常见原因、示例、处理策略和代码示例。
| 错误类型 | 常见原因 | 何时处理 | 示例 |
|---|---|---|---|
| 连接错误 | 网络问题、服务器宕机、认证失败、超时、连接池耗尽 | 几乎总是需要 | ConnectionError、TimeoutError、AuthenticationError |
| 命令错误 | 命令拼写错误、参数错误、类型无效、不支持的命令 | 很少(通常表示代码缺陷) | ResponseError、WRONGTYPE、ERR unknown command |
| 数据错误 | 序列化失败、数据损坏、类型不匹配 | 有时(取决于数据来源) | JSONDecodeError、SerializationError、WRONGTYPE |
| 资源错误 | 内存限制、连接池耗尽、连接数过多、键驱逐 | 有时(部分为临时性) | OOM、连接池超时、LOADING |
连接错误
当您的应用程序无法与 Redis 通信时,会发生连接错误。这些错误通常是临时性的,并且通常是可以恢复的。
常见原因:
- 网络连接问题
- Redis 服务器宕机或不可达
- 认证失败
- 连接超时
- 连接池耗尽
示例:
ConnectionError:网络故障或服务器不可达TimeoutError:操作超过配置的超时时间AuthenticationError:凭据无效
何时处理: 几乎总是需要。连接错误通常是临时性的,因此建议实现重试逻辑或后备策略。
示例策略:
命令错误
当 Redis 收到无效或格式错误的命令时,会发生命令错误。这些通常表示您的代码中存在缺陷。
常见原因:
示例:
ResponseError:无效命令或语法错误WRONGTYPE Operation against a key holding the wrong kind of valueERR unknown command
何时处理: 很少需要。这些通常表示编程错误,因此您应该修复代码中的错误,而不是在运行时尝试处理它们。但在某些情况下(如用户输入无效),可能值得处理。
示例:
数据错误
当数据本身存在问题(如序列化失败或数据损坏)时,会发生数据错误。
常见原因:
- 对象无法序列化为 JSON
- 缓存数据损坏
- 尝试反序列化无效数据
示例:
JSONDecodeError:无法反序列化 JSON 数据SerializationError:无法序列化对象
何时处理: 有时需要。如果错误由用户输入或外部数据引起,请优雅地处理。如果由您的代码引起,请修复代码。
示例:
资源错误
当 Redis 资源耗尽或达到限制时,会发生资源错误。
常见原因:
- 达到内存限制
- 连接池耗尽
- 连接数过多
- 内存压力导致键驱逐
示例:
OOM command not allowed when used memory > 'maxmemory'- 连接池超时
LOADING Redis is loading the dataset in memory
何时处理: 有时需要。某些资源错误是临时性的(如 Redis 加载中),而另一些则表明配置问题。
示例:
错误处理模式
模式 1:快速失败
当错误不可恢复或表示代码中存在缺陷时使用此模式。
适用场景:
- 命令错误(无效语法)
- 认证错误
- 编程错误
示例:
try:
result = r.get(key)
except redis.ResponseError as e:
# 这表示我们的代码中有缺陷
raise # 重新抛出异常模式 2:优雅降级
当您有替代方式获取所需数据时,可以回退到替代方案而非首选代码路径。
适用场景:
- 缓存读取(回退到数据库)
- 会话读取(回退到默认值)
- 非必需数据(不可用时跳过)
示例:
try:
cached_value = r.get(key)
if cached_value:
return cached_value
except redis.ConnectionError:
logger.warning("缓存不可用,使用数据库")
# 回退到数据库
return database.get(key)模式 3:带退避的重试
当错误可能由网络负载或其他临时状况引起时使用此模式。
适用场景:
- 连接超时
- 临时网络问题
- Redis 加载数据
示例:
import time
max_retries = 3
retry_delay = 0.1
for attempt in range(max_retries):
try:
return r.get(key)
except redis.TimeoutError:
if attempt < max_retries - 1:
time.sleep(retry_delay)
retry_delay *= 2 # 指数退避
else:
raise请注意,客户端库通常已为您实现重试逻辑,因此您可能只需提供正确的配置,而无需自行实现重试。请参阅下面的客户端特定的错误处理以获取描述每个客户端库重试配置的页面链接。
模式 4:记录日志并继续
当操作对您的应用程序不关键时使用此模式。
适用场景:
- 缓存写入(可接受数据丢失)
- 非关键更新
- 指标收集
示例:
try:
r.setex(key, 3600, value)
except redis.ConnectionError:
logger.warning(f"无法缓存 {key},继续执行而不使用缓存")
# 应用程序继续正常运行决策树:如何处理错误
日志记录与监控
在生产环境中,您可能会发现记录错误发生时的信息并监控日志以发现模式很有帮助。这可以帮助您识别哪些错误最常见,以及您的重试和回退策略是否有效。请注意,某些 Redis 客户端库内置了检测功能,可以为您提供这些信息(完整描述请参见可观测性)。
记录哪些信息
- 错误类型和消息: 发生了什么问题?
- 上下文: 哪个键?哪个操作?
- 时间戳: 何时发生?
- 重试信息: 这是重试吗?尝试了多少次?
示例:
logger.error(
"Redis 操作失败",
extra={
"error_type": type(e).__name__,
"operation": "get",
"key": key,
"attempt": attempt,
"timestamp": datetime.now().isoformat(),
}
)监控哪些指标
- 错误率: 每分钟有多少错误?
- 错误类型: 哪些错误最常见?
- 恢复成功率: 多少次重试成功?
- 回退使用率: 我们多频繁使用回退策略?
这些指标有助于您识别模式并发现潜在问题。
常见错误
捕获所有异常
问题: 如果捕获所有异常,可能会捕获意外错误并掩盖缺陷。
错误示例:
try:
result = r.get(key)
except Exception: # 过于宽泛——某些错误表示代码存在问题。
pass更好的做法: 捕获特定的异常类型。
正确示例:
try:
result = r.get(key)
except redis.ConnectionError:
# 处理连接错误
pass不区分错误类型
问题: 不同类型的错误需要不同的处理方式。例如,重试语法错误无济于事。
错误示例:
try:
result = r.get(key)
except redis.ResponseError:
# 重试?如果是语法错误,这不会有帮助。
retry()更好的做法: 根据错误是否可恢复,对每种错误类型进行不同处理。
正确示例:
try:
result = r.get(key)
except redis.TimeoutError:
retry() # 超时时重试
except redis.ResponseError:
raise # 语法错误时失败忽略连接池错误
问题: 连接池错误表明存在需要解决的配置或并发问题。
错误示例:
# 连接池已耗尽,但我们没有处理
result = r.get(key) # 可能会在等待连接时超时更好的做法: 监控连接池使用情况,并在需要时增加池大小。
客户端特定的错误处理
有关客户端库中异常的详细信息,请参阅: