MongoDB是一种开源的、面向文档的非关系型数据库管理系统,于2009年首次发布。它使用BSON 类似JSON 风格的文档来存储数据,而不是传统的行和列的表格形式。
MongoDB的设计目标是在处理大量数据时提供高性能和可扩展性。它旨在满足现代应用程序对灵活性、可伸缩性和数据复杂性的要求。
面向文档的数据模型:MongoDB使用了一种称为BSON(Binary JSON)的二进制表示形式来存储数据。文档是一个类似于JSON的结构,可以包含键值对、数组和嵌套文档。这种灵活的数据模型使得MongoDB能够轻松地存储和处理各种类型和结构的数据。
高性能和可扩展性:MongoDB采用了内存映射存储引擎,将数据存储在物理内存和磁盘之间进行交换,实现了快速的读写操作。此外,MongoDB还支持水平扩展,通过在集群中添加更多的服务器节点,可以增加处理能力和存储容量,以应对大规模数据和高并发访问的需求。
强大的查询功能:MongoDB提供丰富的查询语言和灵活的查询方式。它支持范围查询、排序、聚合、分组等各种类型的查询操作。同时,MongoDB还支持全文搜索、地理空间查询和图形查询等特殊类型的查询,满足不同应用场景下的数据检索需求。
数据复制和故障恢复:MongoDB支持数据复制和故障恢复机制,以保证数据的可靠性和高可用性。通过配置副本集(replica set),可以将数据复制到多个节点,并自动进行故障切换和故障恢复,确保系统在出现故障时能够继续提供服务。
可扩展性和弹性伸缩性:MongoDB通过分片(sharding)来实现水平扩展。分片将数据按照某种规则分割成多个片段,并将每个片段存储在不同的服务器上,实现了负载均衡和数据的无缝扩展。
安全性和权限控制:MongoDB提供了安全机制来保护数据的机密性和完整性。它支持身份验证、访问控制和数据加密等功能,可以限制用户对数据库的访问权限,并保护敏感数据的安全。
文档型数据库是一种非关系型数据库(NoSQL),它以文档的形式来组织和存储数据。
文档:文档是文档型数据库中最基本的单位,它可以是一个JSON格式或类似于JSON的结构化文本。文档不需要遵循固定的模式,可以是半结构化的数据。文档通常使用键值对的形式表示,其中键是字段名,值可以是各种数据类型,例如字符串、数字、布尔值、数组和嵌套的对象。
集合:集合是文档的容器,可以将多个相关文档组织在一起。集合类似于关系型数据库中的表的概念,但在文档型数据库中,集合中的文档可以具有不同的结构,因此灵活性更高。
模式自由:文档型数据库是模式自由的,意味着不同的文档可以有不同的字段和结构。这使得文档型数据库非常适合存储半结构化和多变的数据,因为不需要预定义的表结构或模式,可以根据需要灵活地添加或修改字段。
嵌套文档:文档型数据库支持嵌套文档,也就是在一个文档中嵌套另一个文档。这意味着可以以层次化的方式组织数据,使得复杂的数据结构可以直接映射到数据库中。
查询语言:文档型数据库提供了丰富的查询语言和灵活的查询方式,可以对文档进行各种类型的查询操作,包括范围查询、排序、聚合、分组等。查询语言通常使用类似于SQL的语法,但也可以使用其他专门为文档型数据库设计的查询语言。
扩展性:文档型数据库具有良好的可扩展性,可以通过添加更多的服务器节点来增加存储容量和处理能力。一些文档型数据库还支持分片(sharding)技术,将数据水平分割并存储在不同的机器上,以实现更高的吞吐量和负载均衡。
高性能:由于文档型数据库将数据存储为文档格式,并且通常使用内存映射技术将数据存储在内存中,因此具有快速的读写性能。同时,文档型数据库还支持索引和查询优化技术,可以加快查询速度。
总的来说,文档型数据库以文档为单位存储数据,具有灵活的数据模型、模式自由、嵌套文档、强大的查询能力和良好的扩展性。它适用于存储半结构化和多变的数据,提供高性能和灵活性,并广泛应用于各种类型的应用程序。
在文档型数据库中,集合(Collection)和文档(Document)是两个基本概念,用于组织和存储数据。
集合没有固定的结构:与关系型数据库中的表不同,集合不需要事先定义固定的模式或字段列表。集合中的文档可以具有不同的结构,可以根据需要灵活地添加、修改和删除字段。这使得集合非常适合存储半结构化和多变的数据。
集合具有独立的权限控制:集合可以定义独立的权限和访问控制,可以精确地控制对集合中数据的读写权限。这使得在多用户或多应用程序环境中更容易管理和保护数据。
文档使用键值对表示:文档由一组键值对组成,其中键是字段名,值可以是各种数据类型,例如字符串、数字、布尔值、数组、嵌套的对象等。这使得文档非常灵活,可以存储复杂的数据结构。
文档没有固定的模式:与关系型数据库中的行不同,文档可以具有不同的字段和结构。每个文档可以根据需要定义自己的字段,并且可以随时添加、删除或修改字段。这种灵活性允许开发人员动态调整数据模型,适应不断变化的需求。
文档可以嵌套:文档支持嵌套的结构,也就是在一个文档中嵌套另一个文档。这意味着可以以层次化的方式组织和表示数据,使得复杂的数据结构可以直接映射到数据库中。
集合和文档是文档型数据库中重要的概念,它们提供了灵活、动态和分层的数据存储方式。集合用于组织多个相关的文档,而文档则是最基本的单位,用于存储和表示实际的数据内容。通过集合和文档的组合使用,文档型数据库可以满足半结构化和多变的数据存储需求,并提供高度的灵活性和可伸缩性。
BSON(Binary JSON)是一种二进制的序列化数据格式,用于在文档型数据库中存储和交换数据。它是一种轻量级、高效的数据表示格式,类似于JSON,但以二进制形式存储数据,具有以下特点:
二进制格式:BSON使用二进制编码来表示数据,相比于纯文本的JSON格式,BSON在存储和传输数据时占用更少的空间,并且在解析和处理数据时更高效。
支持的数据类型:BSON支持包括字符串、整数、浮点数、布尔值、日期时间、正则表达式、数组、嵌套文档等常用的数据类型。此外,BSON还支持特殊类型如二进制数据、对象ID、时间戳、长整型等。
嵌入文档和数组:与JSON类似,BSON允许在文档中嵌套其他文档和数组,使得可以表示复杂的数据结构。嵌套文档和数组会被递归地编码为嵌套的BSON对象。
字段顺序:BSON中的字段顺序是有意义的,因为BSON数据编码时是按照字段的顺序进行的。这意味着字段的顺序在存储和传输数据时保持不变,可以确保数据的一致性。
索引和查询:由于BSON数据通常使用二进制格式存储在磁盘上,文档型数据库可以利用索引和查询优化技术来加速数据的访问和查询操作。例如,可以为某个字段创建索引,在查询时能够快速定位匹配的文档。
语言支持:BSON作为一种通用的数据序列化格式,被广泛支持和使用。许多编程语言和数据库系统都提供了与BSON交互的库和驱动程序,方便开发人员在不同的环境中使用BSON进行数据处理。
总的来说,BSON是一种二进制的数据格式,用于在文档型数据库中存储和交换数据。它具有高效、紧凑的存储形式,支持多种数据类型和嵌套结构,并且通过索引和查询优化实现快速的数据访问。BSON作为文档型数据库的基础之一,在大数据量和复杂数据结构的场景下发挥着重要的作用。
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
public class DocumentCreationExample {
public static void main(String[] args) {
// 连接到MongoDB数据库
MongoClient mongoClient = new MongoClient("localhost", 27017);
// 选择数据库
MongoDatabase database = mongoClient.getDatabase("mydatabase");
// 选择集合
MongoCollection<Document> collection = database.getCollection("mycollection");
// 创建文档
Document document = new Document();
document.append("name", "John Doe");
document.append("age", 30);
document.append("email", "[email protected]");
// 将文档插入集合
collection.insertOne(document);
// 打印插入的文档ID
System.out.println("Inserted document ID: " + document.get("_id"));
// 关闭数据库连接
mongoClient.close();
}
}
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
public class DocumentReadExample {
public static void main(String[] args) {
// 连接到MongoDB数据库
MongoClient mongoClient = new MongoClient("localhost", 27017);
// 选择数据库
MongoDatabase database = mongoClient.getDatabase("mydatabase");
// 选择集合
MongoCollection<Document> collection = database.getCollection("mycollection");
// 创建查询条件
Document query = new Document();
query.append("name", "John Doe");
// 执行查询操作
MongoCursor<Document> cursor = collection.find(query).iterator();
while (cursor.hasNext()) {
Document document = cursor.next();
// 读取文档的字段值
String name = document.getString("name");
int age = document.getInteger("age");
String email = document.getString("email");
// 打印文档字段值
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Email: " + email);
}
// 关闭游标
cursor.close();
// 关闭数据库连接
mongoClient.close();
}
}
在上述示例中,我们创建一个查询条件的Document
对象,这里使用name
字段为"John Doe"进行查询。接着,使用find
方法执行查询操作,并通过迭代MongoCursor
获取检索到的文档。
对于每个文档,我们使用getString
和getInteger
等方法读取相应字段的值,然后将其打印出来。最后,关闭游标和数据库连接。
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
public class DocumentUpdateExample {
public static void main(String[] args) {
// 连接到MongoDB数据库
MongoClient mongoClient = new MongoClient("localhost", 27017);
// 选择数据库
MongoDatabase database = mongoClient.getDatabase("mydatabase");
// 选择集合
MongoCollection<Document> collection = database.getCollection("mycollection");
// 定义更新条件
Document query = new Document();
query.append("name", "John Doe");
// 定义更新操作
Document update = new Document();
update.append("$set", new Document("age", 35));
// 执行更新操作
collection.updateOne(query, update);
// 关闭数据库连接
mongoClient.close();
}
}
在上述示例中,我们更新条件的Document
对象,这里使用name
字段为"John Doe"进行匹配。接着,定义更新操作的Document
对象,使用$set
操作符将age
字段更新为35。最后,使用updateOne
方法执行更新操作。
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
public class DocumentDeleteExample {
public static void main(String[] args) {
// 连接到MongoDB数据库
MongoClient mongoClient = new MongoClient("localhost", 27017);
// 选择数据库
MongoDatabase database = mongoClient.getDatabase("mydatabase");
// 选择集合
MongoCollection<Document> collection = database.getCollection("mycollection");
// 定义删除条件
Document query = new Document();
query.append("name", "John Doe");
// 执行删除操作
collection.deleteOne(query);
// 关闭数据库连接
mongoClient.close();
}
}
在上述示例中,我们使用deleteOne
方法执行删除操作。
如果要删除多个文档,可以使用deleteMany
方法,并提供适当的查询条件。使用该方法将会删除所有满足查询条件的文档。
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
public class GetAllDocumentsExample {
public static void main(String[] args) {
// 连接到MongoDB数据库
MongoClient mongoClient = new MongoClient("localhost", 27017);
// 选择数据库
MongoDatabase database = mongoClient.getDatabase("mydatabase");
// 选择集合
MongoCollection<Document> collection = database.getCollection("mycollection");
// 执行查询操作
MongoCursor<Document> cursor = collection.find().iterator();
// 遍历结果
while (cursor.hasNext()) {
Document document = cursor.next();
System.out.println(document.toJson());
}
// 关闭游标
cursor.close();
// 关闭数据库连接
mongoClient.close();
}
}
// 等于条件查询示例
Document query = new Document("name", "Alice");
MongoCursor<Document> cursor = collection.find(query).iterator();
// 不等于条件查询示例
Document query = new Document("age", new Document("$ne", 30));
MongoCursor<Document> cursor = collection.find(query).iterator();
// 大于条件查询示例
Document query = new Document("age", new Document("$gt", 18));
MongoCursor<Document> cursor = collection.find(query).iterator();
// 小于条件查询示例
Document query = new Document("age", new Document("$lt", 40));
MongoCursor<Document> cursor = collection.find(query).iterator();
// 大于等于条件查询示例
Document query = new Document("age", new Document("$gte", 20));
MongoCursor<Document> cursor = collection.find(query).iterator();
// 小于等于条件查询示例
Document query = new Document("age", new Document("$lte", 50));
MongoCursor<Document> cursor = collection.find(query).iterator();
// 包含条件查询示例
List<String> names = Arrays.asList("Alice", "Bob");
Document query = new Document("name", new Document("$in", names));
MongoCursor<Document> cursor = collection.find(query).iterator();
// 不包含条件查询示例
List<String> names = Arrays.asList("Charlie", "Dave");
Document query = new Document("name", new Document("$nin", names));
MongoCursor<Document> cursor = collection.find(query).iterator();
// 正则表达式条件查询示例
Pattern pattern = Pattern.compile("^A.*e$");
Document query = new Document("name", pattern);
MongoCursor<Document> cursor = collection.find(query).iterator();
投影查询是指在查询过程中只返回需要的字段,而不是返回整个文档。这种方式可以提高查询效率,减少网络传输的数据量。
// 包含投影查询示例
Document query = new Document();
Document projection = new Document("name", 1).append("age", 1);
MongoCursor<Document> cursor = collection.find(query).projection(projection).iterator();
在上述示例中,只返回了文档中的"name"和"age"字段,其他字段将被排除在结果之外。
// 排除投影查询示例
Document query = new Document();
Document projection = new Document("name", 0).append("address", 0);
MongoCursor<Document> cursor = collection.find(query).projection(projection).iterator();
在上述示例中,返回所有字段,除了"name"和"address"字段。
// 嵌套字段的投影查询示例
Document query = new Document();
Document projection = new Document("name", 1).append("address.city", 1);
MongoCursor<Document> cursor = collection.find(query).projection(projection).iterator();
在上述示例中,返回"name"字段和嵌套字段"address.city",而不返回其他嵌套字段。
// 数组字段的投影查询示例
Document query = new Document();
Document projection = new Document("name", 1).append("hobbies.0", 1);
MongoCursor<Document> cursor = collection.find(query).projection(projection).iterator();
在上述示例中,返回"name"字段和数组字段"hobbies"中的第一个元素,而不返回其他数组元素。
// 升序排序查询示例
Document query = new Document();
Document sort = new Document("age", 1); // 按照年龄升序排列
MongoCursor<Document> cursor = collection.find(query).sort(sort).iterator();
// 降序排序查询示例
Document query = new Document();
Document sort = new Document("name", -1); // 按照姓名降序排列
MongoCursor<Document> cursor = collection.find(query).sort(sort).iterator();
在上述示例中,我们使用sort
方法对查询结果进行排序。1
表示升序,-1
表示降序。
int pageNumber = 1; // 第一页
int pageSize = 10; // 每页10条记录
Document query = new Document();
Document sort = new Document("name", 1); // 按照姓名升序排列
MongoCursor<Document> cursor = collection.find(query).sort(sort)
.skip((pageNumber-1) * pageSize)
.limit(pageSize)
.iterator();
在上述示例中,我们使用skip
方法指定要跳过的记录数(即上一页的记录数),使用limit
方法指定每页的记录数。
聚合查询是在文档型数据库中用于对数据进行聚合操作的一种查询方式。它可以根据指定的条件对数据进行分组、筛选、计算和排序等操作,以生成统计结果或者按照特定的聚合规则输出数据。
要进行聚合查询,通常需要使用聚合管道(aggregation pipeline),聚合管道是一个由多个聚合操作组成的管道,每个操作依次处理数据并将结果传递给下一个操作。以下是几个常见的聚合操作:
$match
:根据指定的条件筛选文档。// 聚合查询示例 - $match操作
List<Bson> pipeline = Arrays.asList(
Aggregates.match(Filters.eq("status", "active"))
);
AggregateIterable<Document> results = collection.aggregate(pipeline);
在上述示例中,使用$match
操作筛选出"status"字段值为"active"的文档。
$group
:按照指定的字段分组数据,并进行分组操作,如计数、求和等。// 聚合查询示例 - $group操作
List<Bson> pipeline = Arrays.asList(
Aggregates.group("$category", Accumulators.sum("totalQty", "$quantity"))
);
AggregateIterable<Document> results = collection.aggregate(pipeline);
在上述示例中,使用$group
操作按照"category"字段对数据进行分组,并使用$sum
操作符计算每个分组的"quantity"字段总和。
$project
:投影操作用于选择输出的字段。// 聚合查询示例 - $project操作
List<Bson> pipeline = Arrays.asList(
Aggregates.match(Filters.eq("status", "active")),
Aggregates.project(Projections.include("name", "price"))
);
AggregateIterable<Document> results = collection.aggregate(pipeline);
在上述示例中,使用$project
操作选择输出的字段,只包含"name"和"price"字段。
$sort
:按照指定字段对结果进行排序。// 聚合查询示例 - $sort操作
List<Bson> pipeline = Arrays.asList(
Aggregates.match(Filters.eq("status", "active")),
Aggregates.sort(Sorts.descending("price"))
);
AggregateIterable<Document> results = collection.aggregate(pipeline);
在上述示例中,使用$sort
操作按照"price"字段降序排序。
文档设计:MongoDB使用文档来表示数据,一个文档类似于关系型数据库中的一行记录。在设计文档时,需要考虑如何将相关数据组织在一个文档中,以满足应用程序的查询需求。
冗余数据:MongoDB鼓励在文档中包含冗余数据,以便更高效地满足查询需求。这意味着可以将相关的数据存储在同一个文档中,而不是通过关联多个表。在决定是否添加冗余数据时,需要权衡数据的更新频率和查询性能。
嵌入和引用:在MongoDB中,可以选择将相关数据嵌入到文档中,或者使用引用来关联其他文档。嵌入数据可以提高查询性能,但会增加文档的大小。使用引用可以减小文档的大小,但可能需要多次查询才能获取完整的信息。根据数据之间的关系和查询需求,需要权衡使用嵌入和引用的优劣。
数据范式化:与传统关系型数据库不同,MongoDB并不强制要求进行严格的数据范式化。可以根据应用程序的需求,将数据组织成适合查询的形式。这意味着可以将不同实体的相关信息存储在一个文档中,以减少查询时的数据访问次数。
查询优化:在设计数据模型时,需要考虑常见的查询操作,并相应地优化数据结构和索引。使用合适的字段索引可以提高查询性能,将经常一起使用的数据放在一个文档中可以减少查询的次数。
可扩展性:MongoDB可以通过分片技术实现水平扩展。在数据建模时,可以考虑如何设计数据模型以支持分片部署,并将数据均匀分布到各个分片上。
数据完整性:MongoDB支持一些数据完整性约束,如唯一索引、复合索引、验证规则等。在建模时,可以使用这些约束来确保数据的完整性和一致性。
考虑查询模式:在设计数据模型时,需要考虑频繁进行的查询操作,并相应地优化数据结构和查询模式。这可能需要创建不同的集合或使用不同的索引来支持特定类型的查询。
MongoDB中有两种常见的数据组织方式:嵌入(Embedding)和引用(Referencing)。这两种方式在数据建模中具有不同的应用场景和优缺点。
嵌入(Embedding):
引用(Referencing):
选择嵌入还是引用取决于具体的应用场景和需求。通常情况下,嵌入适用于经常在一起使用的相关数据,可以提高查询性能和数据局部性;而引用适用于数据一致性要求更高的场景,或者需要支持复杂的查询关联操作。
MongoDB 索引是一种用于提高查询性能的数据结构,它可以加速数据库中的读取操作。索引通过在集合的一个或多个字段上创建数据结构,使得数据库可以更快地定位和检索数据。
索引类型:
索引原理:
索引的优势:
创建索引:
createIndex()
方法创建索引。索引使用注意事项:
MongoCollection<Document> collection = database.getCollection("myCollection");
collection.createIndex(Indexes.ascending("fieldName"));
MongoCollection<Document> collection = database.getCollection("myCollection");
collection.createIndex(Indexes.compoundIndex(Indexes.ascending("field1"), Indexes.ascending("field2")));
MongoCollection<Document> collection = database.getCollection("myCollection");
collection.createIndex(Indexes.text("textField"));
MongoCollection<Document> collection = database.getCollection("myCollection");
collection.createIndex(Indexes.hashed("fieldName"));
MongoCollection<Document> collection = database.getCollection("myCollection");
collection.createIndex(Indexes.geo2d("locationField"));
MongoCollection<Document> collection = database.getCollection("myCollection");
collection.createIndex(Indexes.ascending("fieldName"), new IndexOptions().unique(true));
在 Java 中,MongoDB 4.0 版本开始引入了原生的事务管理支持。MongoDB 事务管理允许开发者将多个操作(如插入、更新和删除)组合成一个原子性的操作单元,要么同时成功执行,要么全部回滚。
ClientSession
)对象。ClientSession session = mongoClient.startSession();
startTransaction()
方法即可启动事务。session.startTransaction();
collection.insertOne(session, document); // 在事务中插入文档
collection.updateOne(session, filter, update); // 在事务中更新文档
collection.deleteOne(session, filter); // 在事务中删除文档
commitTransaction()
方法来提交事务。session.commitTransaction();
abortTransaction()
方法来回滚事务。session.abortTransaction();
session.close();
需要注意的是,为了使用事务管理,确保 Mongo 驱动程序的版本为 4.0 或更高版本,并且 MongoDB 服务器的副本集开启了写入操作确认(write concern)功能。此外,事务还需要在同一个数据库中的多个集合之间执行,不能跨越多个数据库。
复制集(Replica Set)是 MongoDB 中用于提供数据冗余和高可用性的一种机制。它通过在多个 MongoDB 实例之间复制数据来实现数据的冗余存储,并允许在主节点(Primary)故障时自动选举新的主节点,从而实现高可用性。
复制集的组成:
复制集的工作原理:
复制集的配置:
复制集的应用场景: