一、为什么需要MongoDB和Redis一起用?

想象你正在开发一个电商网站。商品详情页需要快速加载(比如库存、价格),但每次都要从数据库查会很慢;同时订单数据必须安全存储,不能因为服务器重启就丢失。这时候,Redis就像你的临时记事本,把热点数据记在内存里随时取用;MongoDB则是保险柜,确保重要信息永久保存。

举个具体场景:当用户疯狂抢购限量球鞋时:

  1. Redis实时扣减库存,响应速度在毫秒级
  2. MongoDB在后台默默记录完整的订单流水
# 技术栈:Python + Redis + MongoDB
import redis
from pymongo import MongoClient

# 连接Redis和MongoDB
r = redis.Redis(host='localhost', port=6379)
mongo = MongoClient('mongodb://localhost:27017/')
db = mongo['ecommerce']

def purchase_item(user_id, item_id):
    # 先用Redis检查库存
    stock_key = f"item:{item_id}:stock"
    if r.decr(stock_key) < 0:  # 原子性减库存
        r.incr(stock_key)  # 恢复库存
        return "库存不足"
    
    # 再写入MongoDB持久化
    order = {
        "user_id": user_id,
        "item_id": item_id,
        "status": "paid",
        "created_at": datetime.now()
    }
    db.orders.insert_one(order)
    
    return "购买成功"

二、这对黄金搭档各自擅长什么?

Redis的特长

  • 闪电般的读写速度(10万+ QPS)
  • 支持丰富的数据结构(字符串/哈希/列表等)
  • 自带过期机制,适合临时数据
  • 原子操作避免并发问题

MongoDB的强项

  • 类JSON的文档结构,和代码对象天然匹配
  • 灵活的字段增减,适合需求频繁变更
  • 强大的聚合查询能力
  • 自动分片应对海量数据

看个实际例子:社交平台的用户动态流

# 技术栈:Python + Redis + MongoDB
def post_activity(user_id, content):
    # 先持久化到MongoDB
    activity = {
        "user_id": user_id,
        "content": content,
        "timestamp": datetime.now()
    }
    activity_id = db.activities.insert_one(activity).inserted_id
    
    # 再推送到Redis时间线
    timeline_key = f"user:{user_id}:timeline"
    r.lpush(timeline_key, str(activity_id))
    r.ltrim(timeline_key, 0, 999)  # 只保留最新1000条
    
def get_timeline(user_id):
    # 先从Redis获取最近的活动ID
    timeline_key = f"user:{user_id}:timeline"
    activity_ids = [ObjectId(id) for id in r.lrange(timeline_key, 0, 50)]
    
    # 再从MongoDB获取完整内容
    return list(db.activities.find({"_id": {"$in": activity_ids}}))

三、配合使用时要注意的坑

  1. 数据一致性:Redis内存数据可能丢失,重要操作要有补偿机制
# 技术栈:Python + Redis + MongoDB
def update_product_price(product_id, new_price):
    # 先更新数据库
    db.products.update_one(
        {"_id": product_id},
        {"$set": {"price": new_price}}
    )
    
    # 再失效Redis缓存
    r.delete(f"product:{product_id}")
  1. 内存管理:Redis别让它吃太多内存,记得设置maxmemory-policy

  2. 连接池配置:频繁连接数据库会拖慢速度,记得复用连接

  3. 错误处理:网络波动时要重试,比如这样:

# 技术栈:Python + Redis + MongoDB
from tenacity import retry, stop_after_attempt

@retry(stop=stop_after_attempt(3))
def safe_get_user(user_id):
    # 先查Redis
    user = r.get(f"user:{user_id}")
    if user:
        return json.loads(user)
        
    # 再查MongoDB
    user = db.users.find_one({"_id": user_id})
    if user:
        # 回填缓存
        r.setex(f"user:{user_id}", 3600, json.dumps(user))
    return user

四、经典组合方案实战

方案1:热点数据缓存

  • Redis作为MongoDB的查询缓存
  • 设置合理的TTL(比如30分钟)
# 技术栈:Python + Redis + MongoDB
def get_product_details(product_id):
    cache_key = f"product:{product_id}"
    # 先查缓存
    cached = r.get(cache_key)
    if cached:
        return json.loads(cached)
    
    # 缓存未命中时查数据库
    product = db.products.find_one({"_id": product_id})
    if product:
        # 设置缓存并返回
        r.setex(cache_key, 1800, json.dumps(product))
    return product

方案2:计数器+持久化

  • 用Redis的INCR统计实时点击量
  • 定时同步到MongoDB
# 技术栈:Python + Redis + MongoDB
from apscheduler.schedulers.background import BackgroundScheduler

def sync_clicks():
    # 获取所有需要同步的键
    keys = r.keys("click:*")
    for key in keys:
        item_id = key.decode().split(":")[1]
        count = int(r.get(key))
        
        # 批量更新MongoDB
        db.items.update_one(
            {"_id": item_id},
            {"$inc": {"clicks": count}}
        )
        # 清空计数器
        r.set(key, 0)

# 每5分钟同步一次
scheduler = BackgroundScheduler()
scheduler.add_job(sync_clicks, 'interval', minutes=5)
scheduler.start()

五、什么情况下不该用这个组合?

  1. 数据量很小(比如就几百条记录)时,单独用MongoDB就够了
  2. 需要复杂事务的场景(虽然MongoDB 4.0+支持事务,但性能会下降)
  3. 对一致性要求极高的系统(比如银行账户余额)

六、总结使用心法

  1. Redis管快,MongoDB管稳:热数据放Redis,完整数据放MongoDB
  2. 建立数据通道:通过消息队列或定时任务保持数据同步
  3. 监控不能少:关注Redis内存使用率和MongoDB慢查询
  4. 做好兜底方案:Redis挂了要能降级直接查数据库

记住这个组合就像快餐店的前台和后厨:

  • 前台(Redis)快速响应顾客简单需求
  • 后厨(MongoDB)处理复杂的订单制作
  • 两者通过订单系统(你的代码)紧密配合