一、NoSQL数据库的默认选型逻辑
刚接触NoSQL的朋友们经常会问:这么多数据库该怎么选?其实就像选手机一样,不同场景需要不同特性。我们以MongoDB为例,看看它最适合什么场景。
先看一个电商商品管理的例子:
// MongoDB示例:商品文档结构
{
_id: ObjectId("5f8d8a7b2f4a7d1e2c3b4c5d"),
name: "智能手表",
sku: "SM-WATCH-2023",
price: 1299,
inventory: 150,
attributes: {
color: ["黑色", "银色"],
size: ["38mm", "42mm"]
},
reviews: [
{
user: "张三",
rating: 5,
comment: "续航很棒!"
},
{
user: "李四",
rating: 4,
comment: "表带有点硬"
}
]
}
这种嵌套文档结构特别适合商品这类具有复杂属性的数据,传统关系型数据库需要拆分成多表,而MongoDB可以直接存储。
二、五大常见使用问题解决方案
1. 事务支持不足怎么办?
虽然MongoDB 4.0+已经支持多文档事务,但性能会受影响。我们来看个库存扣减的替代方案:
// MongoDB乐观锁实现库存扣减
const session = db.getMongo().startSession();
try {
session.startTransaction();
const product = db.products.findOne(
{ _id: productId, inventory: { $gte: quantity } },
{ session }
);
if (!product) throw "库存不足";
const result = db.products.updateOne(
{ _id: productId, version: product.version },
{
$inc: { inventory: -quantity, version: 1 },
$push: { sales: { date: new Date(), qty: quantity } }
},
{ session }
);
if (result.modifiedCount === 0) throw "并发冲突";
session.commitTransaction();
} catch (e) {
session.abortTransaction();
throw e;
}
2. 复杂查询性能优化
给经常查询的字段加索引是基本操作,但要注意:
// 创建复合索引示例
db.orders.createIndex({
userId: 1, // 第一排序条件
createDate: -1, // 第二排序条件
status: 1 // 第三排序条件
});
// 查询示例:获取用户最近10个待发货订单
db.orders.find({
userId: "user123",
status: "pending"
}).sort({ createDate: -1 }).limit(10);
3. 数据结构变更管理
NoSQL虽然schema-less,但也要考虑变更。比如要给所有用户加积分字段:
// 批量更新文档结构
db.users.updateMany(
{ points: { $exists: false } }, // 筛选没有points字段的文档
{ $set: { points: 0 } }, // 设置默认值
{ multi: true } // 更新多个文档
);
三、关联技术的巧妙组合
单独使用NoSQL有时会力不从心,这时候需要组合技:
1. MongoDB + Redis缓存
// Node.js示例:缓存商品详情
async function getProduct(id) {
const cacheKey = `product:${id}`;
let product = await redis.get(cacheKey);
if (!product) {
product = await db.products.findOne({ _id: id });
await redis.setex(cacheKey, 3600, JSON.stringify(product)); // 缓存1小时
} else {
product = JSON.parse(product);
}
return product;
}
2. MongoDB + Elasticsearch搜索
// 商品搜索实现
const { body } = await elasticsearch.search({
index: 'products',
body: {
query: {
multi_match: {
query: '智能 防水',
fields: ['name^3', 'description', 'tags']
}
}
}
});
四、避坑指南与最佳实践
- 连接池配置:默认连接数往往不够
// Node.js MongoDB驱动配置
const client = new MongoClient(uri, {
poolSize: 50, // 连接池大小
connectTimeoutMS: 30000, // 连接超时
socketTimeoutMS: 45000 // 操作超时
});
- 批量操作优化:避免N+1查询
// 批量插入比单条插入快10倍以上
const bulkOps = products.map(product => ({
insertOne: { document: product }
}));
await db.products.bulkWrite(bulkOps, { ordered: false });
- 监控关键指标:这些指标要盯紧
- 内存使用率(常驻集大小)
- 页面错误率
- 队列中的操作数
- 复制延迟(如果用了副本集)
五、场景选择的黄金法则
- 适合NoSQL的场景:
- 需要灵活模式的用户画像
- 物联网设备的海量时序数据
- 内容管理系统的嵌套评论
- 实时分析的热点数据
- 不适合的场景:
- 需要复杂事务的金融核心系统
- 需要多表关联的报表系统
- 需要严格一致性的库存系统
记住:没有银弹,只有合适的工具。就像你不能用螺丝刀切菜,虽然理论上也能做到。
评论