一、为什么需要MongoDB和Redis一起用?
想象你正在开发一个电商网站。商品详情页需要快速加载(比如库存、价格),但每次都要从数据库查会很慢;同时订单数据必须安全存储,不能因为服务器重启就丢失。这时候,Redis就像你的临时记事本,把热点数据记在内存里随时取用;MongoDB则是保险柜,确保重要信息永久保存。
举个具体场景:当用户疯狂抢购限量球鞋时:
- Redis实时扣减库存,响应速度在毫秒级
- 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}}))
三、配合使用时要注意的坑
- 数据一致性: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}")
内存管理:Redis别让它吃太多内存,记得设置maxmemory-policy
连接池配置:频繁连接数据库会拖慢速度,记得复用连接
错误处理:网络波动时要重试,比如这样:
# 技术栈: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()
五、什么情况下不该用这个组合?
- 数据量很小(比如就几百条记录)时,单独用MongoDB就够了
- 需要复杂事务的场景(虽然MongoDB 4.0+支持事务,但性能会下降)
- 对一致性要求极高的系统(比如银行账户余额)
六、总结使用心法
- Redis管快,MongoDB管稳:热数据放Redis,完整数据放MongoDB
- 建立数据通道:通过消息队列或定时任务保持数据同步
- 监控不能少:关注Redis内存使用率和MongoDB慢查询
- 做好兜底方案:Redis挂了要能降级直接查数据库
记住这个组合就像快餐店的前台和后厨:
- 前台(Redis)快速响应顾客简单需求
- 后厨(MongoDB)处理复杂的订单制作
- 两者通过订单系统(你的代码)紧密配合
评论