mongdb实战分享

mongoDB介绍

MongoDB 是一个文档数据库,一条记录就是一个文档,它是由字段和值对组成的数据结构。MongoDB 文档在内部以bson格式存储(bson二进制格式,内容上类似于 json文本格式,但是前者是不可直接阅读的 ,需借助特定的解析器、编码器)。字段的值可以包括其他文档、数组和文档数组。

PS:mongoDB是最接近关系型数据的NoSql数据库类型。有如下主要特征:

1.1 高性能

提供高性能的数据持久性。尤其:

  • 通过对嵌入式数据模型的支持,减少了数据库系统上的 I/O 活动。
  • 通过索引支持更快的查询,并且可以包含嵌入文档和数组中的键。

1.2 高可用性

MongoDB 的复制工具称为副本集,是一组维护相同数据集的 MongoDB 服务器,提供冗余并提高数据可用性,可做到:

  • 自动故障转移
  • 数据备份

1.3 水平扩展性

具备水平可扩展性,作为其核心功能的一部分:

  • 分片将数据分布在机器集群上。
  • 从 3.4 开始,支持基于分片键创建数据区域。在平衡集群中,MongoDB 将区域覆盖的读写操作仅定向到该区域内的分片。

1.4 支持多个存储引擎

MongoDB 支持多种存储引擎:

此外,MongoDB提供了可插拔的存储引擎API,允许第三方为MongoDB开发存储引擎。

数据库对比

mongoDB

mysql

es

redis

NoSql

Y

N

Y

Y

事务

4.0版本后支持

支持

不支持

借助lua脚本支持

索引

B+树

B+树

词典索引、倒排索引

依赖内置的数据结构

存储方式

使用BSON

以结构化表形式组织

文本数据

键值对

优点

支持嵌套结构和灵活的数据模型,文档处理友好

支持结构化数据的复杂查询和事务支持

快速且近实时的搜索和分析功能

高性能数据查询

适用场景

数据结构灵活、数据量较大的场景,如日志、社交网络

需要强数据一致性和复杂事务的应用,如金融服务

海量文本搜索,如网站搜索

需要高速读写操作的场景,如缓存

常用场景

一般来说,适用于具备如下数据特征的场景:

  • 数据量大,如TB甚至 PB 级别数据存储
  • 操作频繁(读写都很频繁),如2000-3000以上的读写QPS
  • 价值较低的数据,对事务性要求不高

除了上述的三个特点外,可以考虑以下的一些问题:

  • 不需要复杂事务及 join 支持
  • 数据模型暂无法确定(新应用,需求会变),想快速迭代开发
  • 需要高可用、大量的地理位置查询、文本查询

举例如下:

(1) 社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能。

(2) 物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。

(3) 物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析

CRUD 操作

MongoDB 查询 API 支持读写操作 (CRUD)以及:

对比SQL,从概念到聚合操作两个层面做个映射。

SQL 到 MongoDB 映射表

SQL Terms/Concepts

MongoDB Terms/Concepts

database

database

table

collection

row

documentorBSONdocument

column

field

index

index

table joins

$lookup, embedded documents

primary key

Specify any unique column or column combination as primary key.

primary key

In MongoDB, the primary key is automatically set to the_idfield.

aggregation (e.g. group by)

aggregation pipeline

See theSQL to Aggregation Mapping Chart.

SELECT INTO NEW_TABLE

$out

See theSQL to Aggregation Mapping Chart.

MERGE INTO TABLE

$merge(Available starting in MongoDB 4.2)

See theSQL to Aggregation Mapping Chart.

UNION ALL

$unionWith(Available starting in MongoDB 4.4)

transactions

transactions

SQL 到聚合映射图

SQL Terms, Functions, and Concepts

MongoDB Aggregation Operators

WHERE

$match

GROUP BY

$group

HAVING

$match

SELECT

$project

ORDER BY

$sort

LIMIT

$limit

SUM()

$sum

COUNT()

$sum

$sortByCount

join

$lookup

SELECT INTO NEW_TABLE

$out

MERGE INTO TABLE

$merge(Available starting in MongoDB 4.2)

UNION ALL

$unionWith(Available starting in MongoDB 4.4)

CRUD 操作创建、读取、更新和删除文档。

创建

创建或插入操作将新文档添加到集合中。如果该集合当前不存在,则插入操作将创建该集合

MongoDB 提供了以下方法将文档插入到集合中:

读取

读取操作从集合中检索文档;即查询文档集合。MongoDB 提供了以下方法从集合中读取文档:

可以指定标识要返回的文档的查询过滤器或条件。

示例请参见:

更新

更新操作修改集合中的现有文档。MongoDB 提供了以下方法来更新集合的文档:

在 MongoDB 中,更新操作针对单个集合。MongoDB 中的所有写入操作在单个文档级别上都是原子的。

可以指定用于标识要更新的文档的条件或过滤器。这些过滤器使用与读取操作相同的语法。

有关示例,请参阅更新文档。

删除

删除操作从集合中删除文档。MongoDB 提供了以下方法来删除集合中的文档:

在 MongoDB 中,删除操作针对单个集合。MongoDB 中的所有写入操作在单个文档级别上都是原子的。

可以指定标准或过滤器来标识要删除的文档。这些过滤器使用与读取操作相同的语法。

有关示例,请参阅删除文档。

批量写入

MongoDB 提供了批量执行写入操作的能力。详细信息请参见批量写操作。

db.collection.bulkWrite()。方法提供了执行批量插入、更新和删除操作的能力。

读写特性说明

通过有效利用写关注点和读关注点,可以适当调整一致性和可用性的级别,例如等待更强的一致性保证,或者放宽一致性要求以提供更高的可用性。可用的参数包括readConcern、writeConcern和Read Preference。

5.1 读关注

readConcern用于控制从MongoDB副本集或分片集群读取数据时的一致性和隔离级别。它确保读取的数据在特定级别上的一致性。

常见枚举值及其影响

  • "linearizable":提供最强的一致性保证,确保读取操作在因果一致性边界内的数据上执行,这可能会降低性能,因为需要等待所有副本集成员的确认。
  • "majority":读取操作在大多数已提交的数据上执行,确保读取到已写入多数节点的数据,但不是最新数据。
  • "available":读取数据时不考虑数据的写入确认(write concern),可能会导致读取到尚未完全复制到所有副本集成员的数据。
  • "local":读取操作在本地分片或副本集节点上执行,不考虑网络延迟带来的数据复制差异。

5.2 写关注

writeConcern用于控制MongoDB写入操作的确认级别。它定义了在操作被认为是成功之前必须有多少个服务器节点确认写入操作。较高的writeConcern值(如w: majority或j: true)提供了更强的数据一致性和持久性保证,但可能增加写入操作的延迟。

常见枚举值及其影响

  • "acknowledged"(默认):至少有一个节点确认写入操作,通常是主节点。
  • "unacknowledged":不等待任何确认,写入操作可能不会持久化。
  • { w: 0 }:不等待任何节点的确认,适用于性能敏感的应用。
  • { w: 1 }:等待主节点确认,适用于大多数场景。
  • { w: >1 }:等待指定数量的节点确认,提供更高的数据安全性。
  • { w: "majority" }:等待多数节点确认,确保数据的高可用性和持久性。
  • { j: true }:确保写入操作写入日志,即使节点故障也能恢复数据。

5.3 读偏好

Read Preference用于控制MongoDB客户端从副本集读取数据时的偏好设置。它定义了客户端如何选择副本集的哪个节点来服务读取操作,允许在性能和数据新鲜度之间做出选择。

常见枚举值及其影响

  • "primary":只从主节点读取数据。
  • "primaryPreferred":首选从主节点读取,但如果主节点不可用,则从次要节点读取。
  • "secondary":只从次要节点读取,适用于读取密集型的应用。
  • "secondaryPreferred":首选从次要节点读取,但如果次要节点不可用,则从主节点读取。
  • "nearest":从最接近的节点读取,根据网络延迟选择节点。

索引介绍

索引是一种特殊的数据结构,它以易于遍历的形式存储集合数据集,支持 MongoDB 中查询的高效执行。MongoDB 索引使用B树,具体一点来说是B+树,但是区别于mysql的B+树,叶子节点间没有双向链表的指针。

索引存储特定字段或字段集的值,按字段值排序。索引条目的排序支持高效的相等匹配和基于范围的查询操作。此外,还可以用索引排序以返回排序结果。

需要注意的是,虽然索引可以提高查询性能,但添加索引会对写入操作的性能产生负面影响。对于具有高写入读取比率的集合,索引的成本很高,因为每次插入还必须更新索引。

6.1 索引种类

mongodb支持多种类型的索引,包括单字段索引、复合索引、多键索引、哈希索引、文本索引、 地理位置索引等,每种类型的索引有不同的使用场合。

  • 单字段索引:建立在单个字段上的索引,MongoDB 可以头/尾开始遍历。
  • 复合索引:复合索引从集合中每个文档的两个或多个字段收集数据并对其进行排序。数据先按索引中的第一个字段排序,然后再按每个后续字段排序,遵循最左前缀原则。

例如,下图显示了一个复合索引,其中文档首先按userid升序(按字母顺序)分组。然后,对每个userid,scores降序排序:

要了解更多信息,请参阅复合索引。

  • 多键索引:字段可能是数组,在对这种字段创建索引时,就是多键索引。mongodb会为数组的每个值创建索引。按照数组里面的值做条件来查询,这个时候依然会走索引。

  • 哈希索引:按数据的哈希值索引,用在哈希分片集群上。
  • 文本索引:支持对字符串内容的文本搜索查询。文本索引可以包含任何值为字符串或字符串元素数组的字段。

一个集合只能有一个文本搜索索引,但该索引可以覆盖多个字段。MongoDB 虽然支持全文索引,但是性能低下,暂时不建议使用参阅文本索引。

  • 地理空间索引:地理空间索引可提高地理空间坐标数据查询的性能。参阅地理空间索引。

使用平面几何返回结果的二维索引。

使用球面几何形状返回结果的2dsphere 索引。

6.2 索引属性

索引属性影响查询计划程序如何使用索引以及索引文档的存储方式。

创建索引时,可以将索引属性指定为可选参数。如下介绍了在构建索引时可以指定的索引属性。

PS:并非所有索引类型都与所有索引属性兼容。

唯一索引

唯一索引拒绝索引字段的重复值。当文档需要包含唯一标识符(例如userId)时适用。

TTL索引

TTL 索引会在一段时间后自动从集合中删除文档。将这些索引用于仅需要保留有限时间的数据,例如机器生成的事件数据、日志和会话信息。

不区分大小写的索引

不区分大小写的索引支持对字符串的查询,而不考虑字母大小写。

隐藏索引

隐藏索引对查询计划程序不可见,并且不用于支持查询。

可以使用隐藏索引来评估删除索引的潜在影响,而无需实际删除它。如果影响是负面的,可以取消隐藏索引,而不必重新创建已删除的索引。隐藏索引得到完全维护,一旦取消隐藏即可立即使用。

部分索引

部分索引仅对集合中满足指定过滤表达式的文档进行索引。部分索引的存储要求较低,并降低了索引创建和维护的性能成本。

部分索引提供了稀疏索引功能的超集,并且应该优于稀疏索引。

稀疏索引

稀疏索引仅对包含索引字段的文档进行索引。这些索引会跳过没有索引字段的文档。

6.3 索引构建

6.3.1 构建方法

可以使用驱动程序或 MongoDB Shell 创建和管理索引。

名称

描述

db.collection.createIndex()

在集合上构建索引。

db.collection.dropIndex()

删除集合上的指定索引。

db.collection.dropIndexes()

删除集合上的所有索引。

db.collection.getIndexes()

返回描述集合上现有索引的文档数组。

db.collection.reIndex()

重建集合上的所有现有索引。

db.collection.totalIndexSize()

报告集合上索引使用的总大小。totalIndexSize提供输出字段周围的包装器collStats

cursor.explain()

报告游标的查询执行计划。

cursor.hint()

强制 MongoDB 使用特定索引进行查询。

cursor.max()

指定游标的独占索引上限。配合使用cursor.hint()

cursor.min()

指定游标的包含索引下限。配合使用cursor.hint()

要了解更多信息,请参阅以下资源:

6.3.2 构建过程

从 MongoDB 4.2 开始,索引构建使用优化的构建过程,该过程在索引构建的开始和结束时在集合上持有独占锁。构建过程的其余部分由交错的读和写操作产生。有关索引构建过程和锁定行为的详细说明,请参阅索引构建过程。

从 MongoDB 4.4 开始,索引构建在分片集群上,并在所有数据承载副本集成员上同时构建。默认情况下,服务器最多允许三个并发索引构建。要更改允许的并发索引构建数量,需修改该maxNumActiveUserIndexBuilds参数。如果并发索引构建的数量达到指定的限制maxNumActiveUserIndexBuilds,服务器将阻止其他索引构建,直到并发索引构建的数量降至限制以下。

6.3.3 索引构建的影响

在目标集合处于重写入负载的时间段内时,构建索引可能会导致写入性能降低和索引构建时间更长。

考虑指定一个维护时段,在此期间应用程序停止或减少针对集合的写入操作。在此维护时段内启动索引构建,以减轻构建过程的潜在负面影响。

createIndex操作支持在集合上构建一个或多个索引,使用内存和磁盘上临时文件的组合来完成索引构建。内存使用的默认限制为200 MB(对于版本 4.2.3 及更高版本)和 500 MB(对于版本 4.2.2 及更早版本),在使用单个createIndexes命令构建的所有索引之间共享。达到内存限制后, 可使用该目录createIndexes中指定的子目录中的临时磁盘文件来完成构建。

6.3.4 约束违规

对于对集合实施约束的索引(例如唯一索引),索引构建完成mongod会检查所有预先存在的和同时写入的文档是否违反这些约束。在索引构建期间可能存在违反索引约束的文档。如果任何文档在构建结束时违反索引约束,则会终止索引构建并引发错误。

对于分布在多个分片上的集合,一个或多个分片可能包含具有重复文档的块。因此,创建索引操作可能在某些分片上成功,但在其他分片(即有重复的分片)上失败。

为了降低由于违反约束而导致索引构建失败的风险,有以下建议:

  • 验证集合中没有文档违反索引约束。
  • 无法保证无违规写入操作的应用程序,索引期间停止对集合的所有写入。

6.3.5 构建失败和恢复

中断恢复

从 MongoDB 5.0 开始,如果mongod在索引构建期间完全关闭并且commitQuorum设置为 defaultvotingMembers,则索引构建进度将保存到磁盘。重新启动时mongod会自动恢复索引构建并从保存的检查点继续。

在早期版本中,如果索引构建中断,则必须从头开始。

关闭恢复

如果mongod在索引构建期间关闭,则索引构建作业和所有进度都会丢失。重新启动mongod不会重新启动索引构建。必须重新发出该createIndex()操作才能重新启动索引构建。

回滚处理

从 MongoDB 5.0 开始,如果在索引构建过程中节点回滚到之前的状态,索引构建进度将保存到磁盘。如果回滚结束时仍有工作要做,则mongod自动恢复索引构建并从保存的检查点继续。

从版本 4.4 开始,MongoDB 可以暂停正在进行的索引构建以执行回滚。

  • 如果回滚没有恢复索引构建,MongoDB会在完成回滚后重新启动索引构建。
  • 如果回滚恢复了索引构建,则必须在回滚完成后重新创建一个或多个索引。

在 MongoDB 4.4 之前,只有在所有正在进行的索引构建完成后才能开始回滚。

6.4 索引限制

某些限制适用于索引,例如索引键的长度或每个集合的索引数量。有关详细信息,请参阅索引限制。

  • 单个集合最多可以有64 个索引。
  • 复合索引中的字段不能超过 32 个。
  • 查询不能同时使用文本和地理空间索引

部署架构

7.1 主从模式

主节点读写,从节点读;主节点宕机后服务不可用。一般很少采用。

7.2 复制集模式

有3个角色:主节点(primary)、从节点(secondary)、仲裁者(Arbiter);主节点负责接收客户端的读写请求等操作;从节点复制主节点的数据,也可以提供读服务;仲裁者则是辅助投票修复集群。默认情况下读写均仅发生在主节点。

主节点宕机后从节点可以通过选举成为主节点,继续提供服务。从节点通过消费主节点的oplog(类似于mysql的binlog)完成数据同步。

从节点还有有些特殊配置,如下

  1. priority为0,则不会成为主节点,可参与读取数据,可用于延时高或者性能一般的服务器上。
  2. hidden设置为true,则对于集群不可见,并且必须设置为priority为0,可用于执行报告和备份等专用任务。
  3. 延迟的副本集成员,通过secondaryDelaySecs设置延迟时间,并且priority为0 hid扥为true,延迟成员是数据集的滚动备份或正在运行的历史快照。

选举模式介绍

选举时机:初始化复制集;主节点故障;重新配置复制集;将一个新节点加入复制集

各个节点之间均有心跳机制;从所有从节点中选择数据最新的成为主节点;降级为从节点的主节点,在恢复后加入集群,如果自己数据比主节点还要新,则要回滚到跟当前主节点一致的状态。选举采用raft一致性算法(过半数的投票节点参与)

投票节点最多7个(非投票节点一般是用来做数据备份,可供客户端读取);节点的priority越大,选为主节点的概率越高(为0则永远不会被选中)

复制集要完成数据复制以及修复集群依赖于两个基础的机制: oplog (operation log,操作日志)和心跳 (heartbeat)。oplog 让数据的复制成为可能,而“心跳”则监控节点的健康情况并触发故障转移。

7.3 分片模式

mongodb原生支持了自动分片,支持水平扩展;分片类型包括范围分片、哈希分片和标签分片等策略。

该模式下,一般包括如下节点:

  • 路由节点(mongos):选择合适的数据节点进行读写,合并多个数据节点的读返回。无状态,不持久化任何信息,建议mongos节点以复制集部署、提供高可用性
  • 配置节点config:提供高可用、集群元数据(存储分片数据分布)的数据,存储的是片键与chunk以及chunk与数据节点的映射关系。以复制集的形式部署,实现元数据的冗余备份与高可用性
  • 数据节点:以复制集为单位,横向扩展最大1024分片,分片之间数据不重复,所有分片数据在一起才可以完整工作

启动顺序:配置节点->数据节点->路由节点;关闭集群时顺序反着来。

下图为生产中使用的常见分片集群架构

企搜的mongoDB采用的正是这种部署模式。

分片键:

分片键上一定有索引(先有数据则需要手工创建索引;无数据时则指定片键那刻会自动创建索引),分片键的要点:能够均分分布数据;能够确保chuck可以被进一步拆分。

支持的类型有范围分片(可能造成数据聚集)、哈希分片(范围查询时变得随机,影响性能)和标签分片。mongos根据分片键定位到chunk,然后chuck会关联到分片上。chunk默认大小为64M,满足特定条件时会进行拆分。此时数据会在平衡器的操作下,在分片之间迁移

平衡器:

  • 运行在配置服务器的primary节点上;
  • 在chuck迁移的整个过程中,读写操作还是会路由到原分片上;默认情况下,不需要等待目标分片的secondary节点同步数据,即可以迁移下一条(可以通过参数来控制);
  • 可以指定对特定集合关闭平衡

性能调优

利用索引:

合理创建索引以支持查询,尤其是对于排序、分组和过滤条件中的字段

如果可以,尽量通过覆盖索引查询

用$hint选择特定索引

避免使用复杂的查询条件和大量的$or查询,它们可能会导致全表扫描

避免在索引字段上使用函数或表达式,会导致索引失效

查询限制:

仅返回所需的字段来获得更好的性能,限制查询结果数量以减少网络需求limit()

对于大数据集,应进行分页查询,避免一次性返回过多数据,以减少网络传输和客户端处理压力

大对象(如超过16MB的文档)的读写操作可能会影响性能,应尽量避免

优化数据结构:

通过合理的数据结构设计,如使用分级文档,可以让MongoDB在偶尔没有索引时也能快速查询。同时,避免文档嵌套过深,以减少查询的复杂度。

合理利用MongoDB的文档结构,将经常一起查询的字段放在同一个文档中,减少跨文档查询的开销。

尽量减少数据冗余,避免不必要的数据转换和计算。

其他策略:

预加载热点数据:通过预加载热点数据到内存中,可以减少磁盘I/O,提高查询性能。需确保 MongoDB 有足够的内存来缓存热点数据。

读写分离,适当增加副本集以提高读取速度

PS:查看慢查询的操作

  1. 通过开启慢查询进行分析,db.setProfilingLevel()
  1. 使用explain()方法来分析查询计划,检查索引的使用情况和效率。

参考文档

Introduction to MongoDB - MongoDB Manual v8.0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值