根据你对软件的要求,你可能优先考虑灵活性、可扩展性、性能或速度。因此,开发者和企业在为他们的需求挑选数据库时常常感到困惑。如果你需要一个能提供高灵活性和可扩展性的数据库,以及用于客户分析的数据聚合,那么MongoDB可能是你的最佳选择!
在这篇文章中,我们将讨论MongoDB数据库的结构以及如何创建、监控和管理你的数据库!
MongoDB数据库是如何结构化的?
MongoDB是一个无模式的NoSQL数据库。这意味着你不像SQL数据库那样为表/数据库指定一个结构。
你知道吗,NoSQL数据库实际上比关系型数据库更快?这是由于索引、分片和聚合管道等特性。MongoDB也因其快速的查询执行而闻名。这就是为什么它被谷歌、丰田和福布斯等公司所青睐。
下面,我们将探讨MongoDB的一些关键特征。
文档
MongoDB有一个文档数据模型,将数据存储为JSON文档。文档自然地映射到应用程序代码中的对象,使开发人员更直接地使用它。
在关系型数据库表中,你必须添加一个列才能添加一个新字段。而JSON文档中的字段则不是这样的。JSON文档中的字段可以因文档而异,所以它们不会被添加到数据库的每一条记录中。
文档可以存储像数组这样的结构,可以嵌套来表达层次关系。此外,MongoDB将文档转换为二进制JSON(BSON)类型。这确保了更快的访问速度,并增加了对各种数据类型的支持,如字符串、整数、布尔型数字等等
复制集
当你在MongoDB中创建一个新的数据库时,系统会自动为你的数据再创建至少两个副本。这些副本被称为 “复制集”,它们之间不断复制数据,确保提高你的数据的可用性。它们还提供保护,防止在系统故障或计划维护期间出现停机。
集合
一个集合是一组与一个数据库相关的文件。它们类似于关系型数据库中的表。
然而,集合要灵活得多。首先,它们不依赖于一个模式。其次,这些文件不需要是相同的数据类型。
要查看属于一个数据库的集合的列表,请使用listCollections
命令。
聚合管道
你可以使用这个框架来会几个运算符和表达式。它很灵活,因为它允许你处理、转换和分析任何结构的数据。
正因为如此,MongoDB允许快速的数据流和跨越150个运算符和表达式的功能。它也有几个阶段,比如联合阶段,可以灵活地将多个集合的结果放在一起。
索引
你可以对MongoDB文档中的任何字段进行索引,以提高其效率并改善查询速度。索引通过扫描索引来限制所检查的文档,从而节省时间。这不比读取集合中的每一个文档要好得多吗?
你可以使用各种索引策略,包括多个字段的复合索引。例如,假设你有几个文档在不同的字段中包含了雇员的名字和姓氏。如果你希望返回姓和名,你可以创建一个包括 “姓 “和 “名 “的索引。这比在 “姓 “和 “名 “上建立一个索引要好得多。
你可以利用性能顾问等工具来进一步了解哪些查询可以从索引中受益。
分片
分片将一个单一的数据集分布在多个数据库中。然后,该数据集可以存储在多台机器上,以增加系统的总存储容量。这是因为它将较大的数据集分割成较小的块,并将它们存储在不同的数据节点中。
MongoDB在集合层面对数据进行分片,将集合中的文档分布在集群中的分片上。这确保了可扩展性,允许该架构处理最大的应用程序。
如何创建MongoDB数据库
你需要首先安装适合你的操作系统的MongoDB包。转到 ‘Download MongoDB Community Server‘ 页面。从可用的选项中,选择最新的 “version”,”package”格式为zip文件,”platform”为你的操作系统,然后点击 “Download”,如下图所示。
MongoDB社区服务器的下载过程 (图片来源:MongoDB Community Server)
这个过程非常简单,所以你很快就能在你的系统中安装MongoDB!
一旦你完成了安装,打开你的命令提示符,输入 mongod -version
来验证它。如果你没有得到下面的输出,而是看到一串错误,你可能要重新安装它。
验证MongoDB的版本 (图片来源:configserverfirewall)
使用MongoDB Shell
在我们开始之前,请确保:
- 你的客户端有传输层安全,并且在你的IP允许列表中。
- 你在所需的MongoDB集群上有一个用户账户和密码。
- 你已经在你的设备上安装了MongoDB。
步骤1:访问MongoDB外壳
要访问MongoDB的外壳,请键入以下命令。
net start MongoDB
这应该会有以下输出:
MongoDB服务器初始化 (Image source: c-sharpcorner)
前面的命令初始化了MongoDB服务器。要运行它,我们必须在命令提示符中键入 mongo
。
运行MongoDB服务器 (Image source: bmc)
在MongoDB shell中,我们可以执行命令来创建数据库、插入数据、编辑数据、发布管理命令和删除数据。
第2步:创建你的数据库
与SQL不同,MongoDB没有一个数据库创建命令。相反,有一个叫做 use
的关键词,它可以切换到一个指定的数据库。如果该数据库不存在,它将创建一个新的数据库,否则,它将链接到现有数据库。
例如,要启动一个名为 “company”的数据库,请键入
use Company
在MongoDB中创建数据库
你可以键入 db
来确认你刚刚在系统中创建的数据库。如果你创建的新数据库弹出,说明你已经成功连接到它。
如果你想检查现有的数据库,输入 show dbs
,它将返回你系统中的所有数据库:
查看MongoDB中的数据库
默认情况下,安装MongoDB会创建管理、配置和本地数据库。
你是否注意到,我们创建的数据库没有显示?这是因为我们还没有将数值保存到数据库中!这就是我们的数据库。我们将在数据库管理部分讨论插入的问题。
使用Atlas UI
你也可以开始使用MongoDB的数据库服务Atlas。虽然你可能需要付费才能使用Atlas的某些功能,但大多数数据库功能都可以通过免费层获得。免费层的功能对于创建一个MongoDB数据库来说是绰绰有余的。
在我们开始之前,请确保:
- 你的IP是在允许列表中。
- 你在你想使用的MongoDB集群上有一个用户账户和密码。
要用AtlasUI创建一个MongoDB数据库,请打开一个浏览器窗口并登录到https://cloud.mongodb.com。从你的集群页面,点击Browse Collections。如果集群中没有数据库,你可以通过点击Add My Own Data按钮创建你的数据库。
提示将要求你提供一个数据库和集合的名称。一旦你命名了它们,点击Create,你就完成了! 现在你可以输入新的文件或使用驱动程序连接到数据库。
如何管理MongoDB数据库
在本节中,我们将介绍一些有效管理MongoDB数据库的巧妙方法。你可以通过使用MongoDB指南针或通过集合来实现。
使用集合
虽然关系型数据库拥有定义明确的表,有指定的数据类型和列,但NoSQL有集合而不是表。这些集合没有任何结构,而且文档可以变化–你可以有不同的数据类型和字段,而不需要在同一个集合中匹配另一个文档的格式。
为了演示,让我们创建一个名为 “Employee “的集合,并向其中添加一个文档。
db.Employee.insert( { "Employeename" : "Chris", "EmployeeDepartment" : "Sales" } )
如果插入成功,它将返回 WriteResult({ "nInserted" : 1 })
:
在MongoDB中成功插入
这里,”db “指的是当前连接的数据库。”Employee “是在公司数据库中新创建的集合。
我们没有在这里设置主键,因为MongoDB会自动创建一个名为”_id “的主键字段,并为其设置一个默认值。
运行下面的命令来检查JSON格式的集合。
db.Employee.find().forEach(printjson)
输出:
{ "_id" : ObjectId("63151427a4dd187757d135b8"), "Employeename" : "Chris", "EmployeeDepartment" : "Sales" }
虽然”_id “值是自动分配的,但你可以改变默认主键的值。这一次,我们将在 “Employee”数据库中插入另一个文档,”_id “值为 “1”。
db.Employee.insert( { "_id" : 1, "EmployeeName" : "Ava", "EmployeeDepartment" : "Public Relations" } )
在运行 db.Employee.find().forEach(printjson)
命令时,我们得到如下输出:
集合中的文件与它们的主键
在上面的输出中,”Ava “的”_id “值被设置为 “1”,而不是被自动分配一个值。
现在我们已经成功地将数值添加到数据库中,我们可以使用以下命令检查它是否显示在我们系统中的现有数据库下:
show dbs
显示数据库的列表
然后就可以了! 你已经成功地在你的系统中创建了一个数据库!
使用MongoDB Compass
尽管我们可以通过Mongo shell来处理MongoDB服务器,但有时会很乏味。在生产环境中,你可能会遇到这种情况。
然而,有一个由MongoDB创建的Compass工具(适当地命名为Compass)可以使它更容易。它有一个更好的GUI,并增加了一些功能,如数据可视化、性能分析,以及对数据、数据库和集合的CRUD(创建、读取、更新、删除)访问。
你可以为你的操作系统下载Compass IDE,并通过其简单的过程安装它。
接下来,打开应用程序,通过粘贴连接字符串创建与服务器的连接。如果你找不到它,你可以点击Fill in connection fields individually。如果你在安装MongoDB时没有改变端口号,只需点击连接按钮,你就可以进去了 否则,只要输入你设置的值,然后点击Connect。
MongoDB的新连接窗口 (图片来源: mongodb)
接下来,在新连接窗口中提供主机名、端口和认证。
在MongoDB Compass中,你可以同时创建一个数据库并添加其第一个集合。下面是你如何做的。
- 点击Create Database,打开提示。
- 输入数据库的名称和它的第一个集合。
- 点击Create Database。
你可以通过点击你的数据库名称在数据库中插入更多的文件,然后点击集合的名称,看到Documents标签。然后,你可以点击Add Data按钮,将一个或多个文档插入你的集合中。
在添加你的文档时,你可以一次输入一个,也可以在一个阵列中输入多个文档。如果你要添加多个文档,请确保这些以逗号分隔的文档被括在方括号中。比如说:
{ _id: 1, item: { name: "apple", code: "123" }, qty: 15, tags: [ "A", "B", "C" ] }, { _id: 2, item: { name: "banana", code: "123" }, qty: 20, tags: [ "B" ] }, { _id: 3, item: { name: "spinach", code: "456" }, qty: 25, tags: [ "A", "B" ] }, { _id: 4, item: { name: "lentils", code: "456" }, qty: 30, tags: [ "B", "A" ] }, { _id: 5, item: { name: "pears", code: "000" }, qty: 20, tags: [ [ "A", "B" ], "C" ] }, { _id: 6, item: { name: "strawberry", code: "123" }, tags: [ "B" ] }
最后,单击 “Insert“,将文件添加到你的集合中。这就是一个文件的正文的样子:
{ "StudentID" : 1 "StudentName" : "JohnDoe" }
这里,字段名是 “StudentID “和 “StudentName”。字段值分别为 “1 “和 “JohnDoe”。
有用的命令
你可以通过角色管理和用户管理命令来管理这些集合。
用户管理命令
MongoDB用户管理命令包含与用户有关的命令。我们可以使用这些命令创建、更新和删除用户。
(1)DropUser
该命令从指定的数据库中删除一个用户。下面是其语法。
db.dropUser(username, writeConcern)
这里,username
是一个必填字段,它包含了用户的认证和访问信息的文件。可选的字段 writeConcern
包含了对创建操作的写关注程度。写入关注的级别可以由可选字段 writeConcern
决定。
在放弃一个拥有 userAdminAnyDatabase
角色的用户之前,请确保至少有一个其他用户拥有用户管理权限。
在这个例子中,我们将放弃测试数据库中的用户 “user26″:
use test db.dropUser("user26", {w: "majority", wtimeout: 4000})
输出:
> db.dropUser("user26", {w: "majority", wtimeout: 4000}); true
(2)createUser
这条命令为指定的数据库创建一个新的用户,如下所示:
db.createUser(user, writeConcern)
这里,user
是一个必填字段,包含了要创建的用户的认证和访问信息的文件。可选的字段writeConcern
包含了对创建操作的写关注程度。写入关注的级别可以由可选字段writeConcern
决定。
如果用户已经存在于数据库中,createUser
将返回一个重复的用户错误。
你可以在测试数据库中创建一个新的用户,方法如下:
use test db.createUser( { user: "user26", pwd: "myuser123", roles: [ "readWrite" ] } );
输出结果如下:
Successfully added user: { "user" : "user26", "roles" : [ "readWrite", "dbAdmin" ] }
(3)grantRolesToUser
你可以利用这个命令来授予用户额外的角色。要使用它,你需要记住以下语法:
db.runCommand( { grantRolesToUser: "<user>", roles: [ <roles> ], writeConcern: { <write concern> }, comment: <any> } )
你可以在上面提到的角色中指定用户定义的和内置的角色。如果你想指定一个存在于 grantRolesToUser
运行的同一个数据库中的角色,你可以用一个文件来指定这个角色,如下所述:
{ role: "<role>", db: "<database>" }
或者,你可以简单地用角色的名称来指定角色。比如说:
"readWrite"
如果你想指定存在于不同数据库中的角色,你就必须用不同的文件来指定这个角色。
要在一个数据库上授予一个角色,你需要在指定的数据库上使用grantRole
动作。
这里有一个例子可以让你清楚地了解到。以产品数据库中的用户productUser00为例,其角色如下。
"roles" : [ { "role" : "assetsWriter", "db" : "assets" } ]
grantRolesToUser
操作为 “productUser00 “提供股票数据库的 readWrite
角色和产品数据库的读取角色。
use products db.runCommand({ grantRolesToUser: "productUser00", roles: [ { role: "readWrite", db: "stock"}, "read" ], writeConcern: { w: "majority" , wtimeout: 2000 } })
产品数据库中的用户productUser00现在拥有以下角色:
"roles" : [ { "role" : "assetsWriter", "db" : "assets" }, { "role" : "readWrite", "db" : "stock" }, { "role" : "read", "db" : "products" } ]
(4)usersInfo
你可以使用 usersInfo
命令来返回一个或多个用户的信息。下面是语法:
db.runCommand( { usersInfo: <various>, showCredentials: <Boolean>, showCustomData: <Boolean>, showPrivileges: <Boolean>, showAuthenticationRestrictions: <Boolean>, filter: <document>, comment: <any> } ) { usersInfo: <various> }
在访问方面,用户总是可以查看自己的信息。要看另一个用户的信息,运行命令的用户必须有包括在另一个用户的数据库中 viewUser
的动作的权限。
在运行 userInfo
命令时,根据指定的选项,你可以获得以下信息。
{ "users" : [ { "_id" : "<db>.<username>", "userId" : <UUID>, // Starting in MongoDB 4.0.9 "user" : "<username>", "db" : "<db>", "mechanisms" : [ ... ], // Starting in MongoDB 4.0 "customData" : <document>, "roles" : [ ... ], "credentials": { ... }, // only if showCredentials: true "inheritedRoles" : [ ... ], // only if showPrivileges: true or showAuthenticationRestrictions: true "inheritedPrivileges" : [ ... ], // only if showPrivileges: true or showAuthenticationRestrictions: true "inheritedAuthenticationRestrictions" : [ ] // only if showPrivileges: true or showAuthenticationRestrictions: true "authenticationRestrictions" : [ ... ] // only if showAuthenticationRestrictions: true }, ], "ok" : 1 }
现在你已经对使用 usersInfo
命令可以完成的任务有了大致的了解,接下来可能会出现的明显的问题是,在查看特定的用户和多个用户时,什么命令会很方便?
这里有两个方便的例子来说明这一点。
要查看特定用户的具体权限和信息,而不是证书,对于 “office “数据库中定义的用户 “Anthony”,执行以下命令。
db.runCommand( { usersInfo: { user: "Anthony", db: "office" }, showPrivileges: true } )
如果你想查看当前数据库中的一个用户,你只能通过名字来提及该用户。例如,如果你在主数据库中,主数据库中存在一个名为 “Timothy “的用户,你可以运行以下命令。
db.getSiblingDB("home").runCommand( { usersInfo: "Timothy", showPrivileges: true } )
接下来,如果你想查看不同用户的信息,你可以使用一个数组。你可以包括可选的字段 showCredentials
和 showPrivileges
,或者你可以选择不包括它们。这就是该命令的样子。
db.runCommand({ usersInfo: [ { user: "Anthony", db: "office" }, { user: "Timothy", db: "home" } ], showPrivileges: true })
(5)revokeRolesFromUser
你可以利用 revokeRolesFromUser
命令,从存在角色的数据库中的用户身上删除一个或多个角色。 revokeRolesFromUser
命令的语法如下:
db.runCommand( { revokeRolesFromUser: "<user>", roles: [ { role: "<role>", db: "<database>" } | "<role>", ], writeConcern: { <write concern> }, comment: <any> } )
在上面提到的语法中,你可以在 roles
字段中指定用户定义的和内置的角色。与 grantRolesToUser
命令类似,你可以在文件中指定你要撤销的角色,或者使用它的名字。
为了成功执行 revokeRolesFromUser
命令,你需要在指定的数据库上有 revokeRole
动作。
这里有一个例子来说明这个问题。产品数据库中的 productUser00
实体有以下角色:
"roles" : [ { "role" : "assetsWriter", "db" : "assets" }, { "role" : "readWrite", "db" : "stock" }, { "role" : "read", "db" : "products" } ]
下面的 revokeRolesFromUser
命令将删除用户的两个角色: products
的 “read “角色和 “assets “数据库的 assetsWriter
角色:
use products db.runCommand( { revokeRolesFromUser: "productUser00", roles: [ { role: "AssetsWriter", db: "assets" }, "read" ], writeConcern: { w: "majority" } } )
产品数据库中的用户 “productUser00 “现在只有一个剩余角色:
"roles" : [ { "role" : "readWrite", "db" : "stock" } ]
角色管理命令
角色授予用户对资源的访问权。管理员可以使用几个内置角色来控制对MongoDB系统的访问。如果这些角色没有涵盖所需的权限,你甚至可以进一步在某个数据库中创建新的角色。
(1)DropRole
通过 dropRole
命令,你可以从运行该命令的数据库中删除一个用户定义的角色。要执行这个命令,请使用以下语法:
db.runCommand( { dropRole: "<role>", writeConcern: { <write concern> }, comment: <any> } )
为了成功执行,你必须在指定的数据库上有 dropRole
操作。以下操作将从 “产品 “数据库中删除 writeTags
角色:
use products db.runCommand( { dropRole: "writeTags", writeConcern: { w: "majority" } } )
(2)createRole
你可以利用 createRole
命令来创建一个角色并指定其权限。该角色将适用于你选择运行该命令的数据库。如果该角色已经存在于数据库中, createRole
命令将返回一个重复的角色错误。
要执行这个命令,请按照给定的语法进行。
db.adminCommand( { createRole: "<new role>", privileges: [ { resource: { <resource> }, actions: [ "<action>", ... ] }, ], roles: [ { role: "<role>", db: "<database>" } | "<role>", ], authenticationRestrictions: [ { clientSource: ["<IP>" | "<CIDR range>", ...], serverAddress: ["<IP>" | "<CIDR range>", ...] }, ], writeConcern: <write concern document>, comment: <any> } )
一个角色的权限将适用于创建该角色的数据库。角色可以从其数据库中的其他角色继承权限。例如,一个在 “admin “数据库上创建的角色可以包括适用于集群或所有数据库的权限。它也可以从其他数据库中的角色继承权限。
要在一个数据库中创建一个角色,你需要具备两点:
- 在该数据库上的
grantRole
动作,提及新角色的权限,以及提及要继承的角色。 - 在该数据库资源上进行
createRole
操作。
下面的 createRole
命令将在用户数据库上创建一个clusterAdmin
角色。
db.adminCommand({ createRole: "clusterAdmin", privileges: [ { resource: { cluster: true }, actions: [ "addShard" ] }, { resource: { db: "config", collection: "" }, actions: [ "find", "remove" ] }, { resource: { db: "users", collection: "usersCollection" }, actions: [ "update", "insert" ] }, { resource: { db: "", collection: "" }, actions: [ "find" ] } ], roles: [ { role: "read", db: "user" } ], writeConcern: { w: "majority" , wtimeout: 5000 } })
(3)grantRolesToRole
通过 grantRolesToRole
命令,你可以将角色授予一个用户定义的角色。 grantRolesToRole
命令将影响执行该命令的数据库中的角色。
这个 grantRolesToRole
命令的语法如下:
db.runCommand( { grantRolesToRole: "<role>", roles: [ { role: "<role>", db: "<database>" }, ], writeConcern: { <write concern> }, comment: <any> } )
访问权限与 grantRolesToUser
命令类似–你需要在数据库上有一个grantRole
动作才能正确执行该命令。
在下面的例子中,你可以使用 grantRolesToUser
命令来更新 “products “数据库中的productsReader
角色,使其继承 productsWriter
角色的权限:
use products db.runCommand( { grantRolesToRole: "productsReader", roles: [ "productsWriter" ], writeConcern: { w: "majority" , wtimeout: 5000 } } )
(4)revokePrivilegesFromRole
你可以使用 revokePrivilegesFromRole
来从执行命令的数据库上的用户定义的角色中移除指定的权限。为了正确执行,你需要记住以下语法:
db.runCommand( { revokePrivilegesFromRole: "<role>", privileges: [ { resource: { <resource> }, actions: [ "<action>", ... ] }, ], writeConcern: <write concern document>, comment: <any> } )
要撤销一个权限,”resource document”模式必须与该权限的 “resource”字段匹配。“actions”字段可以是完全匹配,也可以是一个子集。
例如,考虑产品数据库中的角色 manageRole
,它有以下特权,指定 “managers”数据库为资源:
{ "resource" : { "db" : "managers", "collection" : "" }, "actions" : [ "insert", "remove" ] }
你不能只从管理者数据库中的一个集合中撤销 “insert”或 “remove”操作。以下操作不会导致角色的改变。
use managers db.runCommand( { revokePrivilegesFromRole: "manageRole", privileges: [ { resource : { db : "managers", collection : "kiosks" }, actions : [ "insert", "remove" ] } ] } )
db.runCommand( { revokePrivilegesFromRole: "manageRole", privileges: [ { resource : { db : "managers", collection : "kiosks" }, actions : [ "insert" ] } ] } )
要撤销角色 manageRole
的 “insert”和/或 “remove”操作,你需要与资源文件完全匹配。例如,下面的操作只撤销了现有权限中的 “remove”操作。
use managers db.runCommand( { revokePrivilegesFromRole: "manageRole", privileges: [ { resource : { db : "managers", collection : "" }, actions : [ "remove" ] } ] } )
下面的操作将从管理人员数据库中的 “executive”角色中删除多个权限:
use managers db.runCommand( { revokePrivilegesFromRole: "executive", privileges: [ { resource: { db: "managers", collection: "" }, actions: [ "insert", "remove", "find" ] }, { resource: { db: "managers", collection: "partners" }, actions: [ "update" ] } ], writeConcern: { w: "majority" } } )
(5)rolesInfo
rolesInfo
命令将返回指定角色的权限和继承信息,包括内置和用户定义的角色。你也可以利用rolesInfo
命令来检索一个数据库的所有角色范围。
为了正确的执行,请遵循这个语法:
db.runCommand( { rolesInfo: { role: <name>, db: <db> }, showPrivileges: <Boolean>, showBuiltinRoles: <Boolean>, comment: <any> } )
要从当前数据库中返回一个角色的信息,你可以按以下方式指定其名称。
{ rolesInfo: "<rolename>" }
为了从另一个数据库返回一个角色的信息,你可以用一个提到该角色和数据库的文件来提到该角色:
{ rolesInfo: { role: "<rolename>", db: "<database>" } }
例如,下面的命令返回在管理人员数据库中定义的角色执行者的角色继承信息。
db.runCommand( { rolesInfo: { role: "executive", db: "managers" } } )
接下来这条命令将返回角色继承信息:在执行命令的数据库上的 accountManager
。
db.runCommand( { rolesInfo: "accountManager" } )
下面的命令将返回管理人员数据库中定义的角色 “executive “的权限和角色继承权。
db.runCommand( { rolesInfo: { role: "executive", db: "managers" }, showPrivileges: true } )
要提到多个角色,你可以使用一个数组。你也可以在数组中以字符串或文档的形式提到每个角色。
只有当角色存在于执行命令的数据库中时,你才应该使用字符串。
{ rolesInfo: [ "<rolename>", { role: "<rolename>", db: "<database>" }, ] }
例如,下面的命令将返回三个不同数据库中三个角色的信息。
db.runCommand( { rolesInfo: [ { role: "executive", db: "managers" }, { role: "accounts", db: "departments" }, { role: "administrator", db: "products" } ] } )
你可以通过以下方式获得权限和角色的继承权。
db.runCommand( { rolesInfo: [ { role: "executive", db: "managers" }, { role: "accounts", db: "departments" }, { role: "administrator", db: "products" } ], showPrivileges: true } )
嵌入MongoDB文档以提高性能
像MongoDB这样的文档数据库可以让你根据你的需要定义你的模式。为了在MongoDB中创建最佳模式,你可以对文档进行嵌套。因此,你可以建立一个与你的用例相匹配的数据模型,而不是将你的应用与一个数据模型相匹配。
嵌入文件让你存储相关的数据,你可以一起访问。在为MongoDB设计模式时,建议你默认嵌入文档。只有在值得的时候才使用数据库侧或应用程序侧的连接和引用。
确保工作负载可以根据需要经常检索文档。同时,文档也应该有它需要的所有数据。这对你的应用程序的卓越性能是至关重要的:
下面,你会发现一些不同的模式来嵌入文档。
嵌入文档模式
你可以用它来将甚至复杂的子结构嵌入到它们所使用的文档中。在单个文档中嵌入连接的数据可以减少获取数据所需的读取操作的数量。一般来说,你应该构建你的模式,使你的应用程序在一次读取操作中收到所有需要的信息。因此,这里要记住的规则是,一起使用的东西应该一起存储。
嵌入子集模式
嵌入子集模式是一种混合情况。你会把它用在一长串相关项目的单独集合上,你可以把其中一些项目保留在手边进行显示。
这里有一个例子,列出了电影评论。
> db.movie.findOne() { _id: 321475, title: "The Dark Knight" } > db.review.find({movie_id: 321475}) { _id: 264579, movie_id: 321475, stars: 4 text: "Amazing" } { _id: 375684, movie_id: 321475, stars:5, text: "Mindblowing" }
现在,想象一下有一千个类似的评论,但你只打算在放映电影时显示最近的两个。在这种情况下,将这个子集作为一个列表存储在电影文档中是有意义的。
> db.movie.findOne({_id: 321475}) { _id: 321475, title: "The Dark Knight", recent_reviews: [ {_id: 264579, stars: 4, text: "Amazing"}, {_id: 375684, stars: 5, text: "Mindblowing"} ] }
简单地说,如果你经常访问相关项目的一个子集,确保你嵌入它。
独立访问
你可能想把子文件存储在它们的集合中,把它们与它们的父集合分开。
例如,以一个公司的产品系列为例。如果公司销售的是一小部分产品,你可能想把它们存储在公司文件中。但如果你想在不同的公司之间重复使用它们,或者直接通过它们的库存单位(SKU)来访问它们,你也会想把它们存储在它们的集合中。
如果你独立地操作或访问一个实体,为了最好的实践,做一个集合来单独存储它。
无界列表
在它们的文档中存储相关信息的短列表有一个缺点。如果你的列表继续无限制地增长,你就不应该把它放在一个文档中。这是因为你不会有能力支持很长时间。
这有两个原因。首先,MongoDB对单个文档的大小有限制。第二,如果你访问文档的频率太高,你会看到内存使用不受控制的负面结果。
简单地说,如果一个列表开始无限制地增长,就做一个集合来单独存储它。
扩展参考模式
扩展参考模式与子集模式一样。它也优化了你经常访问存储在文档上的信息。
在这里,当一个文档引用另一个存在于同一集合中的文档时,而不是一个列表,它被利用。同时,它也存储了其他文档的一些字段,以便随时访问。
比如说:
> db.movie.findOne({_id: 245434}) { _id: 245434, title: "Mission Impossible 4 - Ghost Protocol", studio_id: 924935, studio_name: "Paramount Pictures" }
正如你所看到的,”the studio_id “被储存起来,以便你可以查询更多关于创作该影片的工作室的信息。但为了简单起见,工作室的名字也被复制到了这个文档中。
要想定期从修改过的文档中嵌入信息,记得在修改时更新你复制了该信息的文档。换句话说,如果你经常从一个被引用的文件中访问一些字段,就把它们嵌入。
如何监控MongoDB
你可以使用一些监控工具来调试长的API调用,缓慢的数据库查询,长的外部URL请求,等等。你甚至可以利用命令来提高数据库性能。你也可以用它们来检查你的数据库实例的健康状况。
为什么要监控MongoDB数据库?
数据库管理规划的一个关键方面是监控你的集群的性能和健康。MongoDB Atlas通过其容错/扩展能力处理了大部分的管理工作。
尽管如此,用户需要知道如何跟踪集群。他们还应该知道如何在遇到危机之前扩展或调整他们需要的东西。
通过监控MongoDB数据库,你可以。
- 观察资源的利用情况。
- 了解你的数据库的当前容量。
- 反应和检测实时问题,以增强你的应用堆栈。
- 观察是否存在性能问题和异常行为。
- 与你的治理/数据保护和服务级别协议(SLA)要求保持一致。
需要监测的关键指标
在监控MongoDB时,有四个关键方面你需要记住。
1. MongoDB硬件指标
以下是监控硬件的主要指标。
(1)归一化进程CPU
它被定义为CPU用于维护MongoDB进程的应用软件的时间百分比。
你可以通过将其除以CPU核的数量,将其扩展到0-100%的范围。它包括内核和用户等模块所利用的CPU。
高的内核CPU可能表明通过操作系统操作耗尽了CPU。但与MongoDB操作相关的用户可能是CPU耗尽的根本原因。
(2)正常化的系统CPU
这是CPU花在服务于这个MongoDB进程的系统调用上的时间百分比。你可以通过将它除以CPU核的数量,将其扩展到0-100%的范围。它还涵盖了iowait、用户、内核、steal等模块使用的CPU。
用户CPU或高内核可能显示通过MongoDB操作(软件)的CPU耗尽。高iowait可能与存储耗尽导致CPU耗尽有关。
(3)磁盘IOPS
磁盘IOPS是MongoDB的磁盘分区上每秒平均消耗的IO操作。
(4)磁盘延时
这是MongoDB中磁盘分区的读写磁盘延迟,单位是毫秒。高值(>500ms)表明存储层可能影响MongoDB的性能。
(5)系统内存
使用系统内存来描述使用的物理内存字节与可用的自由空间。
可用指标近似于系统内存的可用字节数。你可以用它来执行新的应用程序,不需要交换。
(6)可用磁盘空间
这被定义为MongoDB的磁盘分区上的可用磁盘空间的总字节数。MongoDB Atlas提供了基于此指标的自动扩展功能。
(7)交换使用情况
你可以利用交换使用图来描述有多少内存被放在交换设备上。在这个图表中,一个高使用量的指标表明,交换正在被利用。这表明内存对于当前的工作负载来说是配置不足的。
2. MongoDB集群的连接和操作指标
下面是操作和连接度量的主要指标。
(1)操作执行时间
在选定的样本期间执行的平均操作时间(写和读操作)。
(2)操作次数
这是在选定的样本期间,每秒执行的平均操作率。操作数图/指标显示操作类型的细分和实例的速度。
(3)连接数
这个指标指的是实例的开放连接数。高的峰值或数字可能指向一个次优的连接策略,无论是从无响应的服务器还是客户端。
(4)查询目标和查询执行器
这是在选定的扫描文件的样本期间,每秒的平均速率。对于查询执行者,这是在查询计划评估和查询期间。查询目标显示扫描的文件数量和返回的文件数量之间的比率。
一个高的数字比率指向次优操作。这些操作扫描了大量的文档以返回较小的部分。
(5)扫描和顺序
它描述了在选择的查询样本期中每秒的平均速率。它返回排序的结果,不能使用索引执行排序操作。
(6)队列
队列可以描述等待锁的操作的数量,无论是写还是读。高队列可能描绘了存在不理想的模式设计。它也可能表明有冲突的写入路径,推动了对数据库资源的高度竞争。
3. MongoDB副本集指标
以下是副本集监控的主要指标。
(1)副本集Oplog窗口
这个指标列出了主站的副本集Oplog中可用的大约小时数。如果一个辅助系统的滞后时间超过这个数量,它就不能跟上,将需要完全重新同步。
(2)副本集滞后
副本集滞后被定义为一个辅助节点在写操作中落后于主节点的大约秒数。高的副本集滞后将指向一个在副本集中面临困难的辅助节点。考虑到连接的读/写问题,它可能会影响你的操作的延迟。
(3)副本集余量
这个指标指的是主副本集的oplog窗口和辅助副本集的滞后之间的差异。如果这个值为零,可能会导致辅助系统进入RECOVERING模式。
(4)Opcounters -repl
Opcounters -repl 被定义为在选择的样本期每秒执行的副本集操作的平均速率。通过 opcounters -graph/metric,你可以看一下指定实例的操作速度和操作类型的细分。
(5)Oplog GB/小时
这被定义为主系统每小时产生的OPLOG的平均速率。oplog的高意外量可能指向一个高度不充分的写入工作负载或模式设计问题。
MongoDB性能监控工具
MongoDB在Cloud Manager、Atlas和Ops Manager中拥有内置的用户界面工具,用于性能跟踪。它还提供了一些独立的命令和工具来查看更多基于原始的数据。我们将谈论一些你可以从有权限和适当角色的主机上运行的工具,以检查你的环境。
mongotop
你可以利用这个命令来跟踪MongoDB实例在每个集合中花费的写入和读取数据的时间。使用下面的语法:
mongotop <options> <connection-string> <polling-interval in seconds>
rs.status()
该命令返回复制集的状态。它是从执行该方法的成员的角度来执行的。
mongostat
你可以使用 mongostat
命令来快速了解你的MongoDB服务器实例的状态。为了获得最佳输出,你可以用它来观察单个实例的特定事件,因为它提供了一个实时视图。
利用这个命令来监控基本的服务器统计数据,如锁队列、操作分解、MongoDB内存统计和连接/网络。
mongostat <options> <connection-string> <polling interval in seconds>
dbStats
该命令返回特定数据库的存储统计数据,例如索引的数量及其大小,总的集合数据与存储大小,以及与集合相关的统计数据(集合和文件的数量)。
db.serverStatus()
你可以利用 db.serverStatus()
命令来了解数据库的状态。它给你一个代表当前实例指标计数器的文件。每隔一段时间执行这个命令来整理关于实例的统计数据。
collStats
collStats
命令收集类似于 dbStats
在集合级别提供的统计数据。它的输出包括集合中对象的数量、集合所消耗的磁盘空间、集合的大小以及关于特定集合的索引的信息。
你可以使用所有这些命令来提供数据库服务器的实时报告和监控,让你监控数据库的性能和错误,并协助做出明智的决策来完善数据库。
如何删除MongoDB数据库
要放弃一个你在MongoDB中创建的数据库,你需要通过use关键字连接到它。
假设你创建了一个名为 “Engineers”的数据库。为了连接到该数据库,你将使用以下命令:
use Engineers
接下来,输入 db.dropDatabase()
来摆脱这个数据库。执行后,这是你可以期待的结果。
{ "dropped" : "Engineers", "ok" : 1 }
你可以运行 showdbs
命令来验证数据库是否仍然存在。
小结
为了从MongoDB中榨取每一滴价值,你必须对基础知识有深刻的理解。因此,对MongoDB数据库了如指掌是非常关键的。这就要求你首先熟悉创建数据库的方法。
在这篇文章中,我们阐明了在MongoDB中创建数据库的不同方法,然后详细介绍了一些灵巧的MongoDB命令,让你在数据库中保持领先地位。最后,我们通过讨论如何利用MongoDB中的嵌入式文档和性能监控工具来确保你的工作流程以最高效率运作来结束讨论。
评论留言