MongoDB是一种面向文档的NoSQL数据库,它把数据存储为类似JSON的BSON格式。与关系型数据库中的表不同,MongoDB使用集合来组织文档,每个文档可以拥有不同的字段结构。这意味着在同一集合里,一条记录可以有姓名和年龄字段,另一条记录可以有姓名、邮箱和地址字段,而不需要提前修改表结构。这种灵活性让MongoDB特别适合快速迭代、数据模型多变的应用场景。

一、安装与启动本地MongoDB
在Windows系统上,可以从MongoDB官网下载MSI安装包。安装过程中如果选择Custom可以调整数据目录和日志目录。默认情况下,数据文件保存在C:\data\db,如果该目录不存在,需要手动创建。安装完成后,打开命令提示符,输入mongod --dbpath C:\data\db启动数据库服务。反斜杠路径必须保留,因为Windows使用反斜杠作为目录分隔符。
macOS用户可以使用Homebrew安装。依次执行brew tap mongodb/brew和brew install mongodb-community。安装完成后,运行brew services start mongodb-community即可启动后台服务。无论哪种系统,启动后可以新开一个终端输入mongosh进入交互式命令行。如果能看到版本号和提示符,说明连接成功。
# Windows 启动 MongoDB mongod --dbpath C:\data\db # 另一终端连接 mongosh
首次使用mongosh时,可以执行show dbs查看默认数据库。如果没有报错,本地环境就准备好了。后续所有基础操作都可以在这个命令行中完成。
二、理解数据库、集合与文档的层级
MongoDB的结构可以概括为数据库包含集合,集合包含文档。数据库对应一个应用或一个业务模块,集合类似于关系型数据库中的表,文档则相当于表中的行。但文档不是扁平的行,而是一个可以嵌套的BSON对象。下面是一个用户文档示例:
{
_id: ObjectId("64c8a1e2f1a2b3c4d5e6f789"),
name: "张三",
age: 25,
email: "zhangsan@ipipp.com",
tags: ["读书", "编程"],
address: {
city: "上海",
street: "南京西路"
}
}每个文档都会有一个_id字段作为主键,如果插入时不指定,MongoDB会自动生成一个ObjectId值。ObjectId由时间戳、机器标识、进程号和随机数组成,既能保证全局唯一,又能大致反映插入时间。这与关系型数据库常见的自增整数主键完全不同。
集合没有强制的模式校验,但并不意味着可以随意设计。一个好的实践是让同一集合中的文档在逻辑上保持相似结构,这样查询和索引才能高效。例如用户集合中大部分文档都应该有姓名和邮箱字段,而地址可以嵌套保存,也可以拆成独立集合并通过引用关联。
三、常用CRUD操作与查询运算符
CRUD是数据库入门的基本功。在mongosh中,插入单条文档使用insertOne,插入多条使用insertMany。查询使用find,可以传入一个条件对象。更新使用updateOne或updateMany,删除使用deleteOne或deleteMany。以下示例演示了基础操作:
// 插入用户
db.users.insertOne({ name: "李四", age: 30, tags: ["电影", "音乐"] })
// 查询年龄在18到35之间的用户
db.users.find({ age: { $gte: 18, $lte: 35 } })
// 更新李四的年龄并增加登录次数
db.users.updateOne(
{ name: "李四" },
{ $set: { age: 31 }, $inc: { loginCount: 1 } }
)
// 删除名字为李四的用户
db.users.deleteOne({ name: "李四" })查询运算符是MongoDB强大能力的基础。$gte表示大于等于,$lte表示小于等于,$in用于匹配数组中的任意值,$and和$or可以组合多个条件。更新时,$set只修改指定字段,$inc对数字字段做原子递增,这比传统SQL的UPDATE语句更直观。
如果要查询嵌套字段,需要使用点号路径。例如db.users.find({ "address.city": "上海" })可以找到地址中城市为上海的用户。对于数组字段,db.users.find({ tags: "编程" })可以匹配数组中包含编程的用户。这些查询方式使得文档模型在处理复杂结构时非常自然。
四、索引设计与简单聚合
当集合中的数据量变大时,全表扫描会严重影响查询性能。索引可以显著减少需要检查的文档数量。MongoDB使用B树索引,创建索引的语法是createIndex。例如为邮箱字段创建唯一索引:
db.users.createIndex({ email: 1 }, { unique: true })数字1表示升序索引,-1表示降序索引。唯一索引可以防止重复邮箱插入。创建索引后,可以执行db.users.find({ email: "test@ipipp.com" }).explain("executionStats")查看执行计划,确认查询是否命中了索引。对于复合查询,可以创建包含多个字段的复合索引,但字段顺序会影响索引效率,需要结合具体查询模式设计。
聚合管道是另一个重要功能。它可以把多个操作串联起来,对数据进行过滤、分组、排序和变换。一个常见的场景是按用户统计订单总金额:
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$userId", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } }
])在这个管道中,$match相当于WHERE条件,$group按用户ID分组并计算总金额,$sort按照总额降序排列。与find相比,聚合管道可以做更复杂的数据处理,而且大部分操作在服务端完成,减少了网络传输。理解管道的执行顺序对性能优化很有帮助。
五、通过驱动程序连接MongoDB
实际项目中,通常不会直接使用mongosh操作数据库,而是通过编程语言的驱动程序。MongoDB官方提供了Node.js、Python、Java、Go等多种语言的驱动。下面是一个Node.js连接示例:
const { MongoClient } = require("mongodb");
async function main() {
const client = new MongoClient("mongodb://localhost:27017");
await client.connect();
const db = client.db("test");
const users = db.collection("users");
await users.insertOne({ name: "王五", age: 28 });
const user = await users.findOne({ name: "王五" });
console.log(user);
await client.close();
}
main().catch(console.error);Python版本同样简洁,核心思路是先创建客户端,然后获取数据库和集合对象,再执行相应操作。示例代码如下:
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017")
db = client["test"]
users = db["users"]
users.insert_one({"name": "赵六", "age": 27})
print(users.find_one({"name": "赵六"}))
client.close()连接字符串中的mongodb://localhost:27017表示连接本机默认端口。如果MongoDB运行在远程服务器或使用副本集,需要相应修改主机地址和参数。无论哪种语言,驱动都遵循类似的连接、操作、关闭流程。掌握基础命令后,切换到具体项目语言并不会太困难。