前言
在当今大数据时代,传统的关系型数据库已无法满足所有场景的需求,NoSQL数据库因其灵活的数据模型和出色的扩展性成为技术栈中不可或缺的部分。本文将带您深入浅出地学习两大主流NoSQL数据库——文档型数据库MongoDB和键值存储Redis的核心概念与实战应用,通过Python生态的PyMongo和redis-py库,快速掌握NoSQL开发技能。
一、MongoDB基础与PyMongo实战
1.1 MongoDB核心概念
MongoDB作为最流行的文档型数据库,与传统SQL数据库有着显著差异:
概念 | SQL | MongoDB |
---|---|---|
数据库 | Database | Database |
表 | Table | Collection |
行 | Row | Document |
列 | Column | Field |
主键 | Primary Key | _id (ObjectId) |
索引 | Index | Index |
外键 | Foreign Key | 引用/嵌入文档 |
核心优势:
灵活模式:文档结构可动态变化
水平扩展:分片集群轻松应对海量数据
高性能:内存映射文件提高IO效率
丰富查询:支持复杂条件与聚合操作
1.2 PyMongo基础操作
安装与环境配置:
pip install pymongo
连接数据库:
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure
# 基础连接
client = MongoClient('mongodb://localhost:27017/')
# 带认证的连接
auth_client = MongoClient(
'mongodb://username:password@localhost:27017/',
authSource='admin'
)
# 连接测试
try:
client.admin.command('ping')
print("MongoDB连接成功")
except ConnectionFailure:
print("MongoDB连接失败")
CRUD操作示例:
# 获取数据库和集合
db = client['school'] # 使用school数据库
students = db['students'] # 使用students集合
# 插入文档
student1 = {
"name": "张三",
"age": 20,
"major": "计算机科学",
"courses": ["数据结构", "算法"],
"gpa": 3.8
}
insert_result = students.insert_one(student1)
print(f"插入ID: {insert_result.inserted_id}")
# 批量插入
student_list = [
{"name": "李四", "age": 21, "major": "数学"},
{"name": "王五", "age": 19, "major": "物理"}
]
students.insert_many(student_list)
# 查询文档
# 查找单个文档
zhangsan = students.find_one({"name": "张三"})
print(zhangsan)
# 查找多个文档
cs_students = students.find({"major": "计算机科学"})
for student in cs_students:
print(student)
# 更新文档
update_result = students.update_one(
{"name": "张三"},
{"$set": {"gpa": 3.9}, "$push": {"courses": "数据库"}}
)
print(f"匹配{update_result.matched_count}条, 修改{update_result.modified_count}条")
# 删除文档
delete_result = students.delete_one({"name": "王五"})
print(f"删除{delete_result.deleted_count}条文档")
1.3 高级查询与聚合
复杂查询:
# 比较操作符
# 年龄大于20且小于25的学生
students.find({"age": {"$gt": 20, "$lt": 25}})
# 逻辑操作符
# 计算机科学或数学专业的学生
students.find({"$or": [{"major": "计算机科学"}, {"major": "数学"}]})
# 数组查询
# 选修了算法的学生
students.find({"courses": "算法"})
# 正则表达式
# 名字以"张"开头的学生
students.find({"name": {"$regex": "^张"}})
聚合管道:
pipeline = [
{"$match": {"major": "计算机科学"}}, # 筛选条件
{"$group": { # 分组统计
"_id": "$age",
"count": {"$sum": 1},
"avg_gpa": {"$avg": "$gpa"}
}},
{"$sort": {"avg_gpa": -1}}, # 排序
{"$limit": 5} # 限制结果数
]
results = students.aggregate(pipeline)
for r in results:
print(f"年龄{r['_id']}: 人数{r['count']}, 平均GPA{r['avg_gpa']:.2f}")
二、Redis核心数据结构与实战
2.1 Redis核心特性
Redis作为内存型键值数据库,提供多种数据结构:
数据结构 | 特点 | 典型应用场景 |
---|---|---|
String | 二进制安全的字符串 | 缓存、计数器 |
Hash | 字段-值映射表 | 对象存储 |
List | 双向链表 | 消息队列、最新列表 |
Set | 无序唯一集合 | 标签、好友关系 |
ZSet | 有序集合 | 排行榜 |
Stream | 消息流 | 事件日志 |
核心优势:
超高性能:内存操作,10万+ QPS
持久化选项:RDB快照和AOF日志
丰富数据结构:超越简单键值存储
原子操作:单线程模型保证原子性
2.2 redis-py基础操作
安装与连接:
pip install redis
import redis
# 基础连接
r = redis.Redis(host='localhost', port=6379, db=0)
# 连接池(推荐生产环境使用)
pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
r = redis.Redis(connection_pool=pool)
# 测试连接
try:
r.ping()
print("Redis连接成功")
except redis.ConnectionError:
print("Redis连接失败")
2.3 String与Hash实战
String操作:
# 基本操作
r.set("user:1000:name", "张三") # 设置键值
name = r.get("user:1000:name") # 获取值
print(name.decode('utf-8')) # 返回bytes需要解码
# 带过期时间的缓存
r.setex("article:1234:content", 3600, "文章内容...") # 1小时后过期
# 计数器
r.set("page:views", 0)
r.incr("page:views") # +1
r.incrby("page:views", 10) # +10
views = r.get("page:views")
print(f"总访问量: {views.decode()}")
# 批量操作
r.mset({"user:1000:email": "zhangsan@example.com",
"user:1000:age": 25})
values = r.mget(["user:1000:name", "user:1000:email"])
print(values) # [b'\xe5\xbc\xa0\xe4\xb8\x89', b'zhangsan@example.com']
Hash操作:
# 存储用户对象
user_data = {
"name": "李四",
"age": 28,
"email": "lisi@example.com"
}
r.hset("user:1001", mapping=user_data)
# 获取字段
name = r.hget("user:1001", "name")
print(name.decode('utf-8'))
# 获取所有字段
user = r.hgetall("user:1001")
print({k.decode('utf-8'): v.decode('utf-8') for k, v in user.items()})
# 字段操作
r.hincrby("user:1001", "age", 1) # 年龄+1
r.hset("user:1001", "phone", "13800138000") # 添加字段
r.hdel("user:1001", "email") # 删除字段
# 批量设置
r.hmset("user:1002", {"name": "王五", "age": 22})
三、综合应用场景
3.1 用户会话管理(Redis)
import secrets
from datetime import timedelta
def create_session(user_id):
"""创建用户会话"""
session_id = secrets.token_hex(16)
session_key = f"session:{session_id}"
# 存储会话数据
r.hset(session_key, mapping={
"user_id": user_id,
"ip": "192.168.1.100",
"login_time": datetime.now().isoformat()
})
# 设置30分钟过期
r.expire(session_key, timedelta(minutes=30))
return session_id
def get_user_id(session_id):
"""获取会话用户ID"""
session_key = f"session:{session_id}"
if r.exists(session_key):
r.expire(session_key, timedelta(minutes=30)) # 续期
return r.hget(session_key, "user_id").decode()
return None
3.2 商品评论系统(MongoDB)
from datetime import datetime
# 评论文档结构
comment = {
"product_id": "p10086",
"user_id": "u1001",
"rating": 5,
"text": "质量非常好!",
"created_at": datetime.now(),
"tags": ["好评", "物流快"]
}
# 插入评论
db.comments.insert_one(comment)
# 聚合查询:计算商品平均评分
pipeline = [
{"$match": {"product_id": "p10086"}},
{"$group": {
"_id": "$product_id",
"avg_rating": {"$avg": "$rating"},
"count": {"$sum": 1}
}}
]
result = db.comments.aggregate(pipeline).next()
print(f"商品{p10086}: 平均评分{result['avg_rating']:.1f}, 共{result['count']}条评论")
四、性能优化与最佳实践
4.1 MongoDB优化技巧
索引优化:
# 创建单字段索引
db.students.create_index([("name", pymongo.ASCENDING)])
# 创建复合索引
db.students.create_index([("major", 1), ("gpa", -1)])
# 查看查询执行计划
explain = db.students.find({"major": "计算机科学"}).explain()
print(explain["executionStats"]["executionTimeMillis"])
批量操作:
# 批量插入比单条插入高效10倍以上
operations = [InsertOne(doc1), UpdateOne(filter, update), DeleteMany(filter)]
db.collection.bulk_write(operations)
读写分离:
# 从secondary节点读取
client = MongoClient(
'mongodb://primary,secondary1,secondary2/',
replicaSet='myReplSet',
readPreference='secondaryPreferred'
)
4.2 Redis优化建议
管道(Pipeline):
pipe = r.pipeline()
pipe.set("counter", 0)
pipe.incr("counter")
pipe.incrby("counter", 10)
pipe.get("counter")
results = pipe.execute() # 单次网络往返
print(results) # [True, 1, 11, b'11']
Lua脚本:
script = """
local key = KEYS[1]
local increment = tonumber(ARGV[1])
local current = tonumber(redis.call('GET', key) or 0)
local new = current + increment
redis.call('SET', key, new)
return new
"""
new_value = r.eval(script, 1, "my_counter", 5)
内存优化:
使用Hash代替多个String存储对象
对长字符串进行压缩
设置合理的过期时间
五、技术选型对比
特性 | MongoDB | Redis |
---|---|---|
数据模型 | 文档型 | 键值型 |
持久化 | 磁盘存储 | 内存+持久化选项 |
查询能力 | 丰富 | 有限 |
事务支持 | 多文档ACID事务 | 单命令原子性 |
扩展性 | 水平扩展(分片) | 主从复制/集群 |
适用场景 | 内容管理、产品目录 | 缓存、会话、消息队列 |
典型应用场景:
选择MongoDB:
需要灵活模式的产品目录
内容管理系统
用户生成内容平台
实时分析系统
选择Redis:
会话缓存
排行榜和计数器
消息队列系统
实时数据处理
结语
通过本文的学习,您应该已经掌握了MongoDB和Redis这两大NoSQL数据库的核心概念与实战技能。在实际项目中:
混合使用:多数系统会同时使用SQL和NoSQL技术
扬长避短:根据业务特点选择合适的数据存储
持续学习:关注MongoDB的Atlas服务和Redis模块扩展
进一步学习建议:
研究MongoDB的Change Streams实现实时数据流
学习Redis的Stream数据结构实现消息队列
探索MongoDB Atlas云服务部署
了解Redis集群搭建与故障转移
如果您在NoSQL实践中遇到任何问题,欢迎在评论区留言讨论。觉得本文有帮助的话,请点赞收藏支持!