4. aioredis 库入门:安装、连接池配置、基本命令的异步调用
在 Python 异步生态中,aioredis 是连接 Redis 的标准异步驱动。本章将围绕其安装、连接池配置以及核心命令的异步调用展开,为后续的缓存任务编排打下基础。
4.1 安装与版本选择
aioredis 目前有两个主要分支:aioredis 1.x(基于 asyncio)和 aioredis 2.x(基于 asyncio 并整合了 redis-py 的异步接口)。推荐使用 aioredis 2.x,它提供了更现代的 API 和更好的性能。
| 版本 | 安装命令 | 特点 | 推荐场景 |
|---|---|---|---|
| aioredis 1.x | pip install aioredis==1.3.1 |
独立驱动,API 较旧 | 遗留项目维护 |
| aioredis 2.x | pip install aioredis |
与 redis-py 接口对齐,支持连接池、哨兵、集群 | 新项目首选 |
版本提示: 本课程所有示例均基于
aioredis 2.x。安装后可通过 pip show aioredis 确认版本。
4.2 连接池配置
连接池是异步 Redis 客户端的核心组件,它复用 TCP 连接,避免每次操作都创建新连接的开销。aioredis 2.x 使用 redis.asyncio.ConnectionPool 来管理连接。
4.2.1 基础连接池创建
import asyncio
from redis.asyncio import ConnectionPool, Redis
async def create_pool():
# 创建连接池:最大连接数 10,最小空闲连接 2
pool = ConnectionPool(
host='localhost',
port=6379,
db=0,
password=None,
max_connections=10,
min_idle_connections=2,
socket_connect_timeout=5,
socket_timeout=5,
decode_responses=True # 自动将 bytes 解码为 str
)
return pool
pool = asyncio.run(create_pool())
4.2.2 连接池参数详解
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
host |
str | 'localhost' | Redis 服务器地址 |
port |
int | 6379 | Redis 服务器端口 |
db |
int | 0 | 数据库编号 |
max_connections |
int | None | 连接池最大连接数,None 表示不限制 |
min_idle_connections |
int | 0 | 保持的最小空闲连接数 |
socket_connect_timeout |
float | None | 连接超时(秒) |
socket_timeout |
float | None | 读写超时(秒) |
decode_responses |
bool | False | 是否自动解码响应为字符串 |
注意: 生产环境中务必设置
max_connections 和 socket_timeout,防止连接泄漏或无限等待。
4.3 基本命令的异步调用
aioredis 2.x 的 API 与同步 redis-py 高度一致,只需在调用前加上 await 即可。下面演示最常用的字符串、哈希、列表操作。
4.3.1 字符串操作
async def string_ops(redis: Redis):
# SET / GET
await redis.set('user:1001:name', 'Alice')
name = await redis.get('user:1001:name')
print(f'Name: {name}') # 输出: Name: Alice
# SETEX(带过期时间)
await redis.setex('session:token:abc123', 3600, 'valid')
ttl = await redis.ttl('session:token:abc123')
print(f'TTL: {ttl}') # 输出: TTL: 3600
# MSET / MGET(批量操作)
await redis.mset({'key1': 'val1', 'key2': 'val2'})
vals = await redis.mget('key1', 'key2')
print(f'Values: {vals}') # 输出: Values: ['val1', 'val2']
4.3.2 哈希操作
async def hash_ops(redis: Redis):
# HSET / HGET
await redis.hset('user:1001', mapping={'name': 'Bob', 'age': 30})
name = await redis.hget('user:1001', 'name')
print(f'Name: {name}') # 输出: Name: Bob
# HGETALL
user_data = await redis.hgetall('user:1001')
print(f'User data: {user_data}') # 输出: User data: {'name': 'Bob', 'age': '30'}
# HEXISTS / HDEL
exists = await redis.hexists('user:1001', 'email')
print(f'Email exists: {exists}') # 输出: Email exists: False
4.3.3 列表操作
async def list_ops(redis: Redis):
# LPUSH / RPUSH
await redis.lpush('queue:task', 'task1', 'task2')
await redis.rpush('queue:task', 'task3')
# LRANGE
tasks = await redis.lrange('queue:task', 0, -1)
print(f'Tasks: {tasks}') # 输出: Tasks: ['task2', 'task1', 'task3']
# LPOP / RPOP
first = await redis.lpop('queue:task')
print(f'Popped: {first}') # 输出: Popped: task2
# LLEN
length = await redis.llen('queue:task')
print(f'Length: {length}') # 输出: Length: 2
4.4 连接池与命令调用的完整流程
下面的 SVG 图展示了从连接池创建到命令执行、连接归还的完整生命周期。
4.5 完整示例:连接池 + 命令调用
将连接池创建与命令调用整合到一个异步函数中,展示完整的“获取连接 → 执行命令 → 归还连接”流程。
import asyncio
from redis.asyncio import ConnectionPool, Redis
async def main():
# 1. 创建连接池
pool = ConnectionPool(
host='localhost',
port=6379,
db=0,
max_connections=10,
decode_responses=True
)
# 2. 从连接池获取 Redis 客户端实例
redis = Redis(connection_pool=pool)
try:
# 3. 执行命令
await redis.set('course:title', 'Redis Async Task Orchestration')
title = await redis.get('course:title')
print(f'Course title: {title}')
# 4. 批量操作
await redis.hset('course:info', mapping={
'instructor': 'Expert',
'duration': '30 chapters'
})
info = await redis.hgetall('course:info')
print(f'Course info: {info}')
finally:
# 5. 关闭连接池(释放所有连接)
await redis.close()
await pool.disconnect()
if __name__ == '__main__':
asyncio.run(main())
最佳实践: 在应用启动时创建一次连接池,全局复用;在应用关闭时统一释放。避免在每次请求中创建新的连接池。
4.6 连接池配置对比:单机 vs 哨兵 vs 集群
不同部署模式下,连接池的配置略有差异。下表总结了三种常见场景的配置要点。
| 部署模式 | 连接池类 | 关键参数 | 示例 |
|---|---|---|---|
| 单机 | ConnectionPool |
host, port |
ConnectionPool(host='localhost', port=6379) |
| 哨兵 | SentinelConnectionPool |
service_name, sentinel_kwargs |
SentinelConnectionPool(service_name='mymaster', sentinel_kwargs={'password': 'xxx'}) |
| 集群 | RedisCluster(内置连接池) |
startup_nodes, cluster_error_retry_attempts |
RedisCluster(startup_nodes=[{'host': '127.0.0.1', 'port': 7000}]) |
注意: 集群模式下不要手动创建
ConnectionPool,应使用 RedisCluster 自动管理连接池。
4.7 常见问题与避坑
- 连接池耗尽: 如果
max_connections设置过小,高并发时会出现ConnectionError: max connections reached。建议根据并发量估算,一般设置为CPU核心数 × 2或更高。 - 连接泄漏: 使用
Redis实例后未调用close(),会导致连接无法归还池中。务必使用try...finally或async with上下文管理器。 - 响应解码: 设置
decode_responses=True后,所有响应自动转为str;如果后续需要bytes,请单独处理。 - 超时设置: 不设置
socket_timeout可能导致命令永久阻塞。建议根据业务容忍度设置 3-10 秒。
4.8 本章小结
通过本章学习,你已掌握:
- aioredis 2.x 的安装与版本选择
- 连接池的创建、参数配置与生命周期管理
- 字符串、哈希、列表等基本命令的异步调用
- 连接池与命令调用的完整流程
- 不同部署模式下的连接池配置差异