非结构化数据库虽然平时用的比较少,但老师还是布置了一些MongoDB的实验供我们了解,其中有一个“选择合理的数据库结构设计来优化效率”的题目:
请给出打印集合 book1 中所有书的所有作者信息的程序代码和运行时间截图、打印集合 book2 中所有书的所有作者信息的程序代码和运行时间截图。 比较两种结构下的查询效率差异, 你觉得什么应用情景下使用内嵌式文档设计比较好?
集合book1的结构是内嵌式的,创建代码如下:
MongoCollection<Document> book1Collection = md.getCollection("book1");
long startMili3 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
Document d1 = new Document();
d1.append("name", "book" + i);
d1.append("publication year", 2019);
/*为了创建内嵌式文档,所以需要一个List存储书的作者,每一个作者也是一个文档*/
/*“authors”数组存储的是完整的作者信息*/
ArrayList<Document> authors = new ArrayList<Document>();
Document author1 = new Document();
author1.append("name", "author123456");
author1.append("country", "China");
authors.add(author1);
Document author2 = new Document();
author2.append("name", "author654321");
author2.append("country", "China");
authors.add(author2);
d1.append("authors", authors);
/*插入这本书的信息到book1集合中*/
book1Collection.insertOne(d1);
}
long endMili3 = System.currentTimeMillis();
System.out.println("生成book1(内嵌式文档)数据,总耗时为:" + (endMili3 - startMili3) + "毫秒");
集合book2是引用式,创建代码并插入数据代码如下:
MongoCollection<Document> book2Collection = md.getCollection("book2");
long startMili4 = System.currentTimeMillis();
/*这里要创建的是引用式文档,所以存储的是ObjectId*/
ArrayList<ObjectId> authorsOID = new ArrayList<ObjectId>();
Bson b1, b2, b;
/*首先需要在author集合中查找author123456、author654321的ObjectId*/
b1 = Filters.eq("name", "author123456");
b2 = Filters.eq("country", "China");
b = Filters.and(b1, b2);
MongoCursor<Document> mongoCursor = authorCollection.find(b).iterator();
authorsOID.add(new ObjectId(mongoCursor.next().get("_id").toString()));
b1 = Filters.eq("name", "author654321");
b2 = Filters.eq("country", "China");
b = Filters.and(b1, b2);
mongoCursor = authorCollection.find(b).iterator();
authorsOID.add(new ObjectId(mongoCursor.next().get("_id").toString()));
/*生成book2中的图书信息,全部采用引用式设计,“authors”数组只存储作者的ObjectId*/
for (int i = 0; i < 1000000; i++) {
Document d2 = new Document();
d2.append("name", "book" + i);
d2.append("publication year", 2019);
d2.append("authors", authorsOID);
book2Collection.insertOne(d2);
}
long endMili4 = System.currentTimeMillis();
System.out.println("生成book2(引用式文档)数据,总耗时为:" + (endMili4 - startMili4) + "毫秒");
两个集合都关联到的author集合结构如下:
MongoCollection<Document> authorCollection = md.getCollection("author");
long startMili2 = System.currentTimeMillis();
/*生成数据,逐个插入*/
for (int i = 0; i < 1000000; i++) {
Document author = new Document();
author.append("name", "author" + i);
author.append("country", "China");
authorCollection.insertOne(author);
}
long endMili2 = System.currentTimeMillis();
System.out.println("生成author数据,总耗时为:" + (endMili2 - startMili2) + "毫秒");
为了比较两种结构下的查询效率差异,分别查询并打印出两个集合中所有数据的author信息,代码如下:
MongoCollection<Document> collection =
mongoDatabase.getCollection("book1");
long startMili1 = System.currentTimeMillis();
MongoCursor<Document> mongoCursor = collection.find().iterator();
long endMili1 = System.currentTimeMillis();
while (mongoCursor.hasNext()) {
Document d = mongoCursor.next();
//System.out.println(d.get("name").toString() + d.get("publication year").toString());
ArrayList<Document> authors =
(ArrayList<Document>) d.get("authors");
endMili1 = System.currentTimeMillis();
for (int i = 0; i < authors.size(); i++) {
Document author = authors.get(i);
System.out.println(author.get("name").toString() + author.get("country").toString());
}
}
System.out.println("查询book1中所有书的所有作者信息完毕,总耗时为:" + (endMili1 - startMili1) + "毫秒");
为了防止无关因素可能会干扰实验结果,两个查询不要放在一个程序里一起执行。
MongoCollection<Document> collection =
mongoDatabase.getCollection("book2");
long startMili1 = System.currentTimeMillis();
MongoCursor<Document> mongoCursor = collection.find().iterator();
long endMili1 = System.currentTimeMillis();
while (mongoCursor.hasNext()) {
Document d = mongoCursor.next();
/*拿到引用的作者的 ObjectId,类似于 Mysql 中的外键,但是 mongoDB 中没有表连接关系*/
ArrayList<ObjectId> authors =
(ArrayList<ObjectId>) d.get("authors");
Bson authorFilter = Filters.in("_id", authors);
MongoCollection<Document> authorCollection =
mongoDatabase.getCollection("author");
/*利用刚才拿到的作者的 id, 再执行一次对作者信息的查询*/
MongoCursor<Document> authorCursor =
authorCollection.find(authorFilter).iterator();
endMili1 = System.currentTimeMillis();
while (authorCursor.hasNext()) {
Document author = authorCursor.next();
System.out.println(author.get("name").toString() + author.get("country").toString());/*拿到了想要的属性*/
//author.get("country");/*拿到了想要的属性*/
}
}
System.out.println("查询book2中所有书的所有作者信息完毕,总耗时为:" + (endMili1 - startMili1) + "毫秒");
最后结果如下:
查找资料分析差异原因:但是用内嵌结构通过一个集合去访问量一个集合时,优点就是不需要单独执行一条查询数据库语句去获取内嵌的内容。而缺点是无法把被内嵌的文档当做单独的实体去访问,更新不方便。
一般情况下,当数据对象之间有 “contains” (包含) 关系、数据对象之间有一对多等关系时, “多个”或者子文档会经常和父文档一起被查找。通常情况下,内嵌数据会对读操作有比较好的性能提高,也可以使应用程序在一个单个操作就可以完成对数据的读取。同时,内嵌数据也对更新相关数据提供了一个原子性写操作。
引用比内嵌要更加灵活一些。但客户端应用必须使用二次查询来解析文档内包含的引用。换句话说,对同样的操作来说,规范化模式(引用)会导致更多的网络请求发送到数据库服务器端。