کتابخانه aioredis در پایتون
aioredis یک کتابخانه غیرهمزمان (asynchronous) برای کار با Redis در پایتون است که با استفاده از asyncio اجازه میدهد عملیات I/O مرتبط با دیتابیس Redis بدون مسدودسازی حلقه رویداد اجرا شوند. با رشد نیاز به برنامههای همزمان (مانند وبسرورها، بوتها و سرویسهای real-time)، استفاده از رابطهای async برای Redis اهمیت ویژهای یافته است.
توضیح تاریخی و حالت فعلی اکوسیستم
در طی زمان دو مسیر وجود داشت: نسخهٔ اولیهٔ پروژه aioredis (از گروه aio-libs) و سپس ادغام قابلیتهای async داخل بستهٔ رسمی redis-py (نسخههای 4.x به بعد) که با خروجی redis.asyncio ارائه میشود. امروزه توصیه میشود برای پروژههای جدید از پیادهسازی رسمی redis.asyncio استفاده کنید اما مفاهیم و الگوهای aioredis سنتی همچنان کاربردی هستند.
مزایا و موارد استفاده
- عدم مسدودسازی حلقهٔ رویداد (event loop) برای بارهای همزمان بالا.
- مناسب برای وبفریمورکهای async مانند FastAPI، Starlette، Aiohttp.
- پشتیبانی از Pub/Sub، پایتلاینها، تراکنشها و اسکریپتهای Lua به صورت async.
- قابلیت استفاده به عنوان cache، message broker ساده، و store برای session/lock.
نمونهٔ پایه: اتصال و عملیات ساده با redis.asyncio
import asyncio
import redis.asyncio as redis
async def main():
r = redis.Redis(host='localhost', port=6379, db=0)
await r.set('mykey', 'hello')
val = await r.get('mykey')
print(val) # b'hello'
await r.close()
await r.connection_pool.disconnect()
asyncio.run(main())در این مثال از بستهٔ رسمی redis.asyncio استفاده شده است. ابتدا اتصال ایجاد میشود، سپس با await عملیات SET و GET اجرا میشوند. در پایان اتصال بسته و connection pool جدا میشود تا منابع آزاد شوند. استفاده از await باعث میشود این فراخوانیها غیرهمزمان باشند و حلقهٔ رویداد تا دریافت پاسخ بلوکه نشود.
استفادهٔ امنتر با connection pool و context manager
import asyncio
import redis.asyncio as redis
async def main():
pool = redis.ConnectionPool.from_url("redis://localhost:6379/0")
async with redis.Redis(connection_pool=pool) as r:
await r.set('k', 'v')
print(await r.get('k'))
# pool در خروج از context manager همچنان قابل استفاده است، اما میتوانید disconnect کنید:
await pool.disconnect()
asyncio.run(main())در این کد از ConnectionPool و context manager استفاده شده تا مدیریت منابع بهتر انجام شود. Context manager برای Redis باعث بسته شدن اتصالات مرتبط با کلاینت میشود و pool.disconnect برای قطع کامل اتصالات استفاده میشود.
Pub/Sub به صورت غیرهمزمان
import asyncio
import redis.asyncio as redis
async def reader(channel):
async for msg in channel.iter_messages():
if msg is None:
continue
print("Received:", msg['data'])
async def main():
r = redis.Redis()
pubsub = r.pubsub()
await pubsub.subscribe('my-channel')
ch = pubsub.channels['my-channel']
await reader(ch)
asyncio.run(main())در این مثال یک Subscriber غیرهمزمان ساخته شده که با استفاده از iter_messages پیامها را دریافت میکند. توجه داشته باشید که Pub/Sub در شرایط بار بالا باید با دقت مدیریت شود (مثلاً پردازش پیامها در تسکهای جداگانه تا از مسدودسازی جلوگیری شود).
پایپلاین و تراکنشها
import asyncio
import redis.asyncio as redis
async def main():
r = redis.Redis()
# pipeline به صورت atomic با transaction=True
async with r.pipeline(transaction=True) as pipe:
pipe.set('a', 1)
pipe.incr('a')
results = await pipe.execute()
print(results)
asyncio.run(main())pipeline در اینجا به شکل تراکنش اجرا میشود تا مجموعهای از عملیات به صورت atomic انجام شوند. استفاده از pipeline باعث کاهش round-tripها و افزایش کارایی میشود.
اجرای اسکریپت Lua
import asyncio
import redis.asyncio as redis
async def main():
r = redis.Redis()
lua = """
local v = redis.call('GET', KEYS[1])
if not v then
redis.call('SET', KEYS[1], ARGV[1])
return ARGV[1]
end
return v
"""
res = await r.eval(lua, 1, 'mykey', 'default')
print(res)
asyncio.run(main())با استفاده از EVAL میتوان منطق پیچیده را داخل سرور Redis با Lua اجرا کرد که کارآمد و atomic است. در مثال بالا اگر کلید وجود نداشته باشد مقدار جدید قرار داده و برگردانده میشود؛ در غیر این صورت مقدار موجود بازگردانده میشود.
بهینهسازی، خطاها و الگوهای عملیاتی
- مدیریت reconnect: هنگام استفاده در محیطهای پراکسیمیتو یا شبکهٔ ناپایدار از مکانیزم retry با backoff استفاده کنید.
- اندازهٔ pool را متناسب با concurrency تنظیم کنید؛ مقدار خیلی کم باعث صف شدن و مقدار زیاد باعث مصرف منابع میشود.
- برای عملیات سنگین روی پیامها، پردازش را به تسکهای جداگانه واگذار کنید تا subscriber بلوکه نشود.
- در خوشه Redis یا Sentinel از پارامترهای مربوطه (cluster/sentinel) در پیکربندی connection استفاده کنید.
مقایسهٔ مختصر: aioredis (کهنه) vs redis.asyncio (توصیهشده)
| ویژگی | aioredis (قدیمی) | redis.asyncio (امروزی) |
|---|---|---|
| پشتیبانی رسمی | کمی قدیمی، پروژه اصلی کمتر فعال | بستهٔ رسمی redis-py با پشتیبانی جامع |
| سازگاری | سازگار با asyncio اما تفاوت API دارد | سازگاری کامل با redis-py و مستندات رسمی |
| مهاجرت | نیاز به تغییر API در پروژههای جدید | پیشنهادی برای پروژههای جدید |
نتیجهگیری و توصیهها
برای برنامههای جدید توصیه میشود از redis.asyncio (نسخهٔ رسمی redis-py با پشتیبانی async) استفاده کنید. در هر حال مفاهیم اتصال غیرهمزمان، connection pool، pub/sub، pipeline و اجرای Lua در هر دو راهکار وجود دارند. حتماً مدیریت منابع، retry و مانیتورینگ را در طراحی سیستم مدنظر قرار دهید تا از پایداری و کارایی مطلوب برخوردار شوید.
آیا این مطلب برای شما مفید بود ؟




