一、基础概念
MongoDB 的特点是什么?
MongoDB是一种NoSQL数据库,具有以下特点:
-
文档存储模型
- MongoDB 使用 BSON(Binary JSON) 格式存储数据,数据以
文档
的形式组织,类似于JSON对象。 - 文档可以包含嵌套结构(如数组和对象),非常适合存储复杂、非结构化的数据。
- MongoDB 使用 BSON(Binary JSON) 格式存储数据,数据以
-
高性能
- MongoDB支持索引,能够快速查询数据。
- 写入性能高,支持内存映射文件,能够高效处理大量数据。【内存映射文件是一种高效的文件访问技术,通过将文件直接映射到内存中,减少了传统文件 I/O 的开销。】
-
水平扩展能力
- MongoDB支持分片(Sharding),可以将数据分布到多个服务器上,实现水平扩展,适合处理大规模数据和高并发场景。
-
高可用性
- MongoDB通过 复制集 提供高可用性。复制集包含多个节点(主节点和从节点),当主节点故障时,系统会自动选举新的主节点,确保服务不中断。
-
丰富的查询功能
- 支持丰富的查询操作,包括范围查询、正则表达式查询、地理空间查询等。
- 提供 聚合管道,支持复杂的数据分析和处理。
-
事务支持
- 从 MongoDB 4.0 开始,支持多文档事务,能够在分布式环境中保证数据的一致性。
MongoDB 和关系型数据库术语对比
概念 | MongoDB | 关系型数据库 |
---|---|---|
数据库层级 | 数据库(Database) | 数据库(Database) |
数据集合 | 集合(Collection) | 表(Table) |
数据单元 | 文档(Document) | 行(Row) |
数据格式 | BSON(Binary JSON) | 行数据(Row Data) |
数据结构 | 字段(Field) | 列(Column) |
嵌套结构 | 嵌套文档(Embedded Document) | 关联表(Related Table) |
数组支持 | 数组(Array) | 多值列(Multi-value Column) |
唯一标识 | _id 字段 |
主键(Primary Key) |
查询语言 | MongoDB 查询语言 | SQL(Structured Query Language) |
聚合操作 | 聚合管道(Aggregation Pipeline) | SQL聚合函数(如GROUP BY) |
索引 | 索引(Index) | 索引(Index) |
复合索引 | 复合索引(Compound Index) | 复合索引(Composite Index) |
事务 | 多文档事务(Multi-Document Transactions) | 事务(Transactions) |
数据一致性 | 最终一致性(Eventual Consistency) | 强一致性(Strong Consistency) |
水平扩展 | 分片(Sharding) | 分区(Partitioning) |
高可用性 | 复制集(Replica Set) | 主从复制(Master-Slave Replication) |
BSON 格式的优势有哪些?
1. 二进制编码,高效存储和传输
- BSON 是二进制编码的 JSON,比文本格式的 JSON 更紧凑,减少了存储空间和网络传输的开销。
- 二进制格式解析速度更快,适合高性能场景。
2. 支持丰富的数据类型
- BSON 支持比 JSON 更多的数据类型,如 日期(Date)、二进制数据(BinData)、ObjectId、正则表达式(Regex)等。
3. 支持嵌套和复杂结构
- BSON可以表示嵌套的文档和数组,适合存储复杂的数据结构。
- 例如,一个文档中可以包含另一个文档或数组,而无需额外的关联表。
4. 高效查询
- BSON格式在存储时会记录字段的长度和类型信息,使得查询时可以直接定位数据,无需解析整个文档。
5. 支持索引
- BSON 格式允许 MongoDB 在文档的特定字段上创建索引,从而加速查询。
- 例如,可以在嵌套字段或数组字段上创建索引。
MongoDB 支持哪些数据类型?
1. 基本数据类型
- 字符串(String)
- 整数(Integer)
- 双精度浮点数(Double)
- 布尔值(Boolean
- 日期(Date)**
- 用于存储日期和时间,例如:
"createdAt": ISODate("2023-10-01T12:00:00Z")
。
- 用于存储日期和时间,例如:
- 空值(Null)
2. 特殊数据类型
-
ObjectId
- 用于唯一标识文档的12字节ID,例如:
"_id": ObjectId("507f1f77bcf86cd799439011")
。
- 用于唯一标识文档的12字节ID,例如:
-
二进制数据(BinData)
- 用于存储二进制数据,例如图片或文件,例如:
"file": BinData(0, "SGVsbG8gd29ybGQ=")
。
- 用于存储二进制数据,例如图片或文件,例如:
-
正则表达式(Regex)
- 用于存储正则表达式,例如:
"pattern": /^[A-Za-z]+$/
。
- 用于存储正则表达式,例如:
-
JavaScript代码(JavaScript)
- 用于存储JavaScript代码,例如:
"script": "function() { return this.age > 18; }"
。
- 用于存储JavaScript代码,例如:
-
时间戳(Timestamp)
- 用于存储时间戳,通常用于内部操作,例如:
"ts": Timestamp(1696156800, 1)
。
- 用于存储时间戳,通常用于内部操作,例如:
3. 复杂数据类型
- 数组(Array)**
- 用于存储一组值,例如:
"hobbies": ["reading", "traveling", "coding"]
。
- 用于存储一组值,例如:
- 嵌套文档(Embedded Document)**
- 用于存储嵌套的文档,例如:
"address": { "city": "New York", "zip": "10001" }
- 用于存储嵌套的文档,例如:
MongoDB 和 MySQL 哪个扩展性好一些?
一般来说,MongoDB的扩展性要优于MySQL,以下是从多个方面的分析:
- 数据模型
- MongoDB:采用文档型数据模型,数据以JSON或BSON格式存储,具有高度的灵活性和可扩展性。文档结构可以随意变化,无需事先定义固定的表结构,非常适合处理不断变化的业务需求和非结构化数据。例如,在一个内容管理系统中,随着业务发展,需要为文章添加新的字段,如摘要、关键词等,在MongoDB中只需直接在文档中添加相应字段即可,无需对整个数据库结构进行修改。
- MySQL:是关系型数据库,基于固定的表结构存储数据,表中的列和数据类型在创建时就需要明确指定。当业务需求发生变化,需要添加或修改列时,可能需要执行复杂的表结构变更操作,如
ALTER TABLE
语句,这在大型生产环境中可能会带来一些风险和性能问题。
- 分布式架构
- MongoDB:原生支持分布式架构,其分片(Sharding)功能可以将数据分散存储在多个节点上,实现水平扩展。通过自动数据均衡和故障转移机制,能够轻松应对大规模数据和高并发读写场景。例如,一个大型电商系统的订单数据量非常大,使用MongoDB的分片功能可以将订单数据按照一定的分片键(如用户ID)分散到多个节点上,从而提高系统的读写性能和可扩展性。
- MySQL:虽然也可以通过一些分布式方案来实现扩展,如主从复制、分布式数据库中间件等,但这些方案相对来说较为复杂,需要额外的配置和管理。而且,在分布式环境下,MySQL的事务处理和数据一致性保证可能会面临一些挑战。
- 读写性能
- MongoDB:在处理高并发的读写请求时,MongoDB具有较好的性能表现。它采用了内存映射文件、异步I/O等技术,能够快速地读写数据。同时,由于文档型数据模型的特点,对于一些复杂的查询操作,可以通过索引和聚合管道等功能来高效地实现。例如,在一个社交媒体应用中,需要实时统计用户的点赞、评论等数据,MongoDB可以利用聚合管道快速地进行数据处理和分析。
- MySQL:在处理大规模数据和高并发读写时,性能可能会受到一定的限制。尤其是在进行复杂的关联查询和事务处理时,可能会导致性能下降。为了提高性能,通常需要进行一些优化措施,如索引优化、查询语句优化、数据库参数调整等。
虽然MongoDB在扩展性方面具有一定的优势,但MySQL也有其自身的特点和适用场景。在实际应用中,需要根据具体的业务需求、数据量、并发量等因素来综合考虑选择合适的数据库。
二、数据操作
如何创建数据库和集合?
创建数据库:
- 使用
use
命令切换到指定数据库,如果数据库不存在则会自动创建。use myDatabase
创建集合:
- 使用
db.createCollection()
方法显式创建集合。db.createCollection("myCollection")
- 如果集合不存在,插入文档时会自动创建集合。
使用 insertOne()
和 insertMany()
插入文档?
{ name: “Alice”, age: 25 } 表示一个文档
insertOne()
:
- 插入单个文档。
db.myCollection.insertOne({ name: "Alice", age: 25 })
- 注意事项:
- 如果文档未指定
_id
字段,MongoDB 会自动生成一个唯一的ObjectId
。 - 如果文档已存在
_id
字段,且_id
已存在,则会抛出重复键错误。
- 如果文档未指定
insertMany()
:
- 插入多个文档。
db.myCollection.insertMany([ { name: "Bob", age: 30 }, { name: "Charlie", age: 35 } ])
- 注意事项:
- 如果插入的文档中有重复的
_id
,整个操作会失败(默认行为)。 - 可以通过
ordered: false
选项忽略重复键错误,继续插入其他文档。db.myCollection.insertMany([ { _id: 1, name: "Bob" }, { _id: 1, name: "Charlie" } ], { ordered: false })
- 如果插入的文档中有重复的
如何编写多条件查询语句(如年龄>25且性别为男)?
- 使用
$and
操作符或直接在查询对象中指定多个条件。db.myCollection.find({ age: { $gt: 25 }, gender: "male" })
如何使用投影操作符返回部分字段?
- 在
find()
的第二个参数中指定需要返回的字段(1
表示返回,0
表示不返回)。db.myCollection.find( { age: { $gt: 25 } }, { name: 1, age: 1, _id: 0 } )
如何对查询结果进行排序?
- 使用
sort()
方法,1
表示升序,-1
表示降序。db.myCollection.find().sort({ age: 1 })
如何使用聚合管道进行复杂数据处理?
- 使用
aggregate()
方法,结合多个阶段(如$match
、$group
、$sort
等)处理数据。
db.users.aggregate([
// 1. 过滤年龄大于 25 的用户
{
$match: {
age: {
$gt: 25 } } },
// 2. 只保留 name 和 gender 字段
{
$project: {
name: 1, gender: 1, _id: 0 } },
// 3. 按 gender 分组,计算每组人数
{
$group: {
_id: "$gender", total: {
$sum: 1 } } },
// 4. 按 total 字段降序排序
{
$sort: {
total: -1 } },
// 5. 只返回前 5 条结果
{
$limit: 5 }
])
如何更新满足条件的多个文档?
- 使用
updateMany()
方法。db.myCollection.updateMany( { age: { $gt: 25 } }, { $set: { status: "active" } } )
如何删除满足条件的文档?
- 使用
deleteMany()
方法。db.myCollection.deleteMany({ age: { $lt: 18 } })
如何实现文档的部分更新?
- 使用
$set
操作符更新指定字段。db.myCollection.updateOne( { name: "Alice" }, { $set: { age: 26 } } )
三、索引
索引底层原理?
索引数据通过 B 树来存储,所有节点都有 Data 域,只要找到指定索引就可以进行访问,
单次查询从结构上来看要快于MySql(B+ 树)。
B 树结构:
<