了解MongoDB——你想知道的都在这
数据库的学习总算是走出 MySQL 了,开源项目涉及的知识确实非常广,还有几个关系型数据库还在学习,今天就先来学习一下另外一个被广泛应用的 NoSQL——MongoDB。
本文仅作为笔者在了解 MongoDB 时学到的一些东西,并不会包含很深入的原理解析,当个科普文就可以了。
什么是 NoSQL?
在学习 MongoDB 之前,先来了解一下什么是 NoSQL。之前在面试时被面试官问道,除了 MySQL 以外,还了解什么数据库。我回答,不知道。结束后,我机智的舍友问我为什么不回答 NoSQL,我以为这是不知道其他数据库的英文表达(离谱)。
NoSQL 也被称为 “not only SQL” 或 “non-SQL”,它是一种数据库设计方法,可以在关系数据库中的传统结构之外存储和查询数据。
虽然 NoSQL 仍然可以存储关系数据库管理系统 (RDBMS) 中的数据,但与 RDBMS 相比,数据存储方式有所不同。决定使用关系数据库还是非关系数据库在很大程度上取决于上下文,并且因用例而异。
NoSQL 数据库并非采用关系数据库的典型表结构,而是将数据存储在一个数据结构中,例如 JSON 文档。由于这种非关系数据库设计不需要使用架构,因此,它提供快速可扩展性以管理通常为非结构化的大型数据集。
NoSQL 也是一种分布式数据库类型,这意味着将信息复制并存储在不同的服务器上,这些服务器可能是远程服务器或本地服务器。这确保了数据的可用性和可靠性。如果一些数据脱机,数据库的其他部分可以继续运行。
特点
- 灵活的数据模型:NoSQL数据库支持多种数据模型,如键值对、文档、列族和图形模型,适应各种应用场景。
- 高可扩展性:通过分布式架构实现水平扩展,可以轻松添加更多的服务器来处理增加的数据量和流量。
- 高性能:NoSQL数据库设计优化了读写操作,能提供高性能的查询速度。
- 可用性和可靠性:通常具备内置的故障转移和数据复制机制,保证数据的高可用性和可靠性。
类型
键值存储(Key-Value Store)
:数据以键值对的形式存储,适合简单查询和高速读写操作。
- 示例:Redis, DynamoDB
文档存储(Document Store)
:数据以文档的形式存储,每个文档可以有不同的结构,适合存储复杂的嵌套数据。
- 示例:MongoDB, CouchDB
列族存储(Column-Family Store)
:数据以列的形式存储,每一列可以独立查询,适合大规模数据分析。
- 示例:Cassandra, HBase
图形数据库(Graph Database)
:数据以节点和边的形式存储,适合处理复杂关系和网络图。
- 示例:Neo4j, ArangoDB
常见的 NoSQL
以下是几个常用的NoSQL数据库及其主要特点:
- MongoDB
- 类型:文档存储(Document Store)
- 特点:
- 使用JSON格式(BSON)存储数据,文档具有灵活的结构。
- 强大的查询和索引功能,支持复杂的查询操作。
- 支持水平扩展,通过分片实现高可扩展性。
- 提供复制集功能,保证数据的高可用性和自动故障转移。
- Cassandra
- 类型:列族存储(Column-Family Store)
- 特点:
- 采用分布式架构,无单点故障,提供高可用性和高容错性。
- 高写入和读取性能,适用于大规模数据处理。
- 支持多数据中心的复制,保证数据的高可用性和一致性。
- 灵活的Schema设计,适合时间序列数据和大数据分析。
- Redis
- 类型:键值存储(Key-Value Store)
- 特点:
- 数据存储在内存中,提供极高的读写性能,适用于实时应用。
- 支持多种数据结构,如字符串、哈希、列表、集合和有序集合。
- 提供丰富的操作和事务功能,支持发布/订阅、Lua脚本等。
- 具有持久化机制,数据可以持久化到磁盘。
- Neo4j
- 类型:图形数据库(Graph Database)
- 特点:
- 以节点和关系(边)的形式存储数据,适合处理复杂关系和网络图。
- 提供强大的图形查询语言Cypher,支持高效的图遍历和查询操作。
- 广泛应用于社交网络、推荐系统和网络分析等场景。
- 提供ACID事务保证,确保数据一致性和可靠性。
- CouchDB
- 类型:文档存储(Document Store)
- 特点:
- 使用JSON格式存储数据,每个文档可以有不同的结构。
- 提供RESTful API接口,便于集成和访问数据。
- 支持多主复制,允许在不同节点之间同步数据。
- 具备内置的冲突解决机制,适用于分布式环境。
- HBase
- 类型:列族存储(Column-Family Store)
- 特点:
- 基于Hadoop HDFS构建,适用于处理大规模结构化数据。
- 提供强一致性的读写操作,适合需要高可靠性的应用。
- 支持随机读写和实时查询,适合大数据分析和实时应用。
- 与Hadoop生态系统紧密集成,支持MapReduce等大数据处理框架。
这些NoSQL数据库在不同的应用场景中发挥着重要作用,根据具体需求选择合适的数据库可以显著提升系统的性能和可扩展性。
MongoDB 出现的原因
传统的关系型数据库(如MySQL),在数据操作的“三高”需求以及应对Web2.0的网站需求面前,显得力不从心。而MongoDB可应对“三高”需求。“三高”需求:
(1)High performance - 对数据库高并发读写的需求。
(2)Huge Storage - 对海量数据的高效率存储和访问的需求。
(3)High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求。 而MongoDB可应对“三高”需求。
简介
从它的命名也可以看出来,设计者是有一定野心的。MongoDB 的原名一开始来自于 英文单词”Humongous”, 中文含义是指”庞大”,即命名者的意图是可以处理大规模的数据。
在使用 MongoDB 我会有两种感觉:
- 第一层感受是”爽”,使用这个文档数据库的特点是几乎不受什么限制,一方面Json文档式的结构更容易理解,而无Schema约束也让DDL管理更加简单,一切都可以很快速的进行。
- 第二层感受是”酸爽”,这点相信干运维或是支撑性工作的兄弟感受会比较深刻,MongoDB 由于入门体验”太过于友好”,导致一些团队认为用好这个数据库是个很简单的事情,所以开发兄弟在存量系统上埋一些坑也是正常的事情。
所谓交付一时爽,维护火葬场.. 当然了,这句话可能有些过。 但这里的潜台词是:与传统的RDBMS数据库一样,MongoDB 在使用上也需要认真的考量和看护,不然的话,会遇到更多的坑。
尽管文档数据库在选型上会让一些团队望而却步,仍然不阻碍该数据库所获得的一些支持。
在全部的排名中,MongoDB 长期排在第5位(文档数据库排名第1位),同时也是最受欢迎的 NoSQL 数据库。
MongoDB 数据库的一些特性:
- 面向文档存储,基于JSON/BSON 可表示灵活的数据结构
- 动态 DDL能力,没有强Schema约束,支持快速迭代
- 高性能计算,提供基于内存的快速数据查询
- 容易扩展,利用数据分片可以支持海量数据存储
- 丰富的功能集,支持二级索引、强大的聚合管道功能,为开发者量身定做的功能,如数据自动老化、固定集合等等。
- 跨平台版本、支持多语言SDK..
基本模型
数据结构对于一个软件来说是至关重要的,MongoDB 在概念模型上参考了 SQL数据库,但并非完全相同。
关于这点,也有人说,MongoDB 是 NoSQL中最像SQL的数据库..
我们来看与MySQL的对比,通过对比来学习其基本模型。
- database 数据库,与SQL的数据库(database)概念相同,一个数据库包含多个集合(表)
- collection 集合,相当于SQL中的表(table),一个集合可以存放多个文档(行)。 不同之处就在于集合的结构(schema)是动态的,不需要预先声明一个严格的表结构。更重要的是,默认情况下 MongoDB 并不会对写入的数据做任何schema的校验。
- document 文档,相当于SQL中的行(row),一个文档由多个字段(列)组成,并采用
bson(json)
格式表示。 - field 字段,相当于SQL中的列(column),相比普通column的差别在于field的类型可以更加灵活,比如支持嵌套的文档、数组。
此外,MongoDB中字段的类型是固定的、区分大小写、并且文档中的字段也是有序的。
另外,SQL 还有一些其他的概念,对应关系如下:
- _id 主键,MongoDB 默认使用一个 _id 字段来保证文档的唯一性。
- reference 引用,勉强可以对应于 外键(foreign key) 的概念,之所以是勉强是因为 reference 并没有实现任何外键的约束,而只是由客户端(driver)自动进行关联查询、转换的一个特殊类型。
- view 视图,MongoDB 3.4 开始支持视图,和 SQL 的视图没有什么差异,视图是基于表/集合之上进行动态查询的一层对象,可以是虚拟的,也可以是物理的(物化视图)。
- index 索引,与SQL 的索引相同。
- $lookup,这是一个聚合操作符,可以用于实现类似 SQL-join 连接的功能
- transaction 事务,从 MongoDB 4.0 版本开始,提供了对于事务的支持
- aggregation 聚合,MongoDB 提供了强大的聚合计算框架,group by 是其中的一类聚合操作。
BSON
数据类型
MongoDB的最小存储单位就是文档(document)对象。文档(document)对象对应于关系型数据库的行。数据在MongoDB中以 BSON(Binary-JSON)
文档的格式存储在磁盘上。
BSON(Binary Serialized Document Format)
是一种类 json
的一种二进制形式的存储格式,简称Binary JSON
。BSON
和JSON
一样,支持 内嵌的文档对象和数组对象,但是BSON
有JSON
没有的一些数据类型,如Date和BinData类型。
BSON
采用了类似于 C 语言结构体的名称、对表示方法,支持内嵌的文档对象和数组对象,具有轻量性、可遍历性、高效性的三个特点,可以有效描述非结构化数据和结构化数据。这种格式的优点是灵活性高,但它的缺点是空间利用率不是很理想。
BSON
中,除了基本的JSON类型:string, integer, boolean, double, null, array 和 object,mongo还使用了特殊的数据类型。这些类型包括 date,object id,binary data,regular expression 和code。每一个驱动都以特定语言的方式实现了这些类型,查看你的驱动的文档来获取详 细信息。
BSON数据类型参考列表:
提示: shell默认使用64位浮点型数值。{“x”:3.14}或{“x”:3}。对于整型值,可以使用NumberInt(4字节符号整数)或NumberLong(8字节符 号整数),{“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)}
分布式 ID
面试问题:在进行数据库分表或者将同一个数据库部署到不同的服务器上,如何保证主键不重复。
当然,方法有很多,不同公司会有自己的处理方式,具体的后续会写文章来分析。我们先看在 MongoDB 中是如何保证主键不重复的。
在单机时代,大多数应用可以使用数据可自增式 ID 来作为主键。传统的 RDBMS 也都支持这种方式,比如 mysql 可以通过声明 auto_increment来实现自增的主键。 但一旦数据实现了分布式存储,这种方式就不再适用了,原因就在于无法保证多个节点上的主键不出现重复。
为了实现分布式数据ID的唯一性保证,应用开发者提出了自己的方案,而大多数方案中都会将ID分段生成,如著名的 snowflake 算法中就同时使用了时间戳、机器号、进程号以及随机数来保证唯一性。
MongoDB 采用 ObjectId 来表示主键的类型,数据库中每个文档都拥有一个_id 字段表示主键。
_id 的生成规则如下:
其中包括:
- 4-byte Unix 时间戳
- 3-byte 机器 ID
- 2-byte 进程 ID
- 3-byte 计数器(初始化随机)
值得一提的是 _id 的生成实质上是由客户端(Driver)生成的,这样可以获得更好的随机性,同时降低服务端的负载。
当然服务端也会检测写入的文档是否包含 _id 字段,如果没有就生成一个。
基础语法
除了文档模型本身,对于数据的操作命令也是基于JSON/BSON 格式的语法。
详细的语法操作,可以去看官方的文档,下面简单介绍一下基础使用语法。
比如插入文档的操作:
1 | db.book.insert( |
执行文档查找:
1 | db.book.find({author : "James"}) |
更新文档的命令:
1 | db.book.update( |
删除文档的命令:
1 | db.book.remove({"_id": |
在传统的SQL语法中,可以限定返回的字段,MongoDB可以使用Projection来表示:
1 | db.book.find({"author": "James"}, |
实现简单的分页查询:
1 | db.book.find({}) |
这种基于BSON/JSON 的语法格式并不复杂,它的表达能力或许要比SQL更加强大。
与 MongoDB 做法类似的还有 ElasticSearch,后者是搜索数据库的佼佼者。
那么,一个有趣的问题是 MongoDB 能不能用 SQL进行查询?
当然是可以!
但需要注意这些功能并不是 MongoDB 原生自带的,而需要借由第三方工具平台实现:
- 客户端使用SQL,可以使用 mongobooster、studio3t 这样的工具
- 服务端的话,可以看看 presto 之类的一些平台..
索引
无疑,索引是一个数据库的关键能力,MongoDB 支持非常丰富的索引类型。利用这些索引,可以实现快速的数据查找,而索引的类型和特性则是针对不同的应用场景设计的。
索引的技术实现依赖于底层的存储引擎,在当前的版本中 MongoDB 使用 wiredTiger 作为默认的引擎。在索引的实现上使用了 B 树,MySQL 则是采用 B+ 树来实现索引的。
使用 ensureIndexes
可以为集合声明一个普通的索引:
1 | db.book.ensureIndex({author: 1}) |
author后面的数字 1 代表升序,如果是降序则是 -1
实现复合式(compound)的索引,如下:
1 | db.book.ensureIndex({type: 1, published: 1}) |
只有对于复合式索引时,索引键的顺序才变得有意义
如果索引的字段是数组类型,该索引就自动成为数组(multikey)索引:
1 | db.book.ensureIndex({tags: 1}) |
MongoDB 可以在复合索引上包含数组的字段,但最多只能包含一个
索引特性
在声明索引时,还可以通过一些参数化选项来为索引赋予一定的特性,包括:
- unique=true,表示一个唯一性索引
- expireAfterSeconds=3600,表示这是一个TTL索引,并且数据将在1小时后老化
- sparse=true,表示稀疏的索引,仅索引非空(non-null)字段的文档
- partialFilterExpression: { rating: { $gt: 5 },条件式索引,即满足计算条件的文档才进行索引
索引分类
除了普通索引之外,MongoDB 支持的类型还包括:
- 哈希(HASH)索引,哈希是另一种快速检索的数据结构,MongoDB 的 HASH 类型分片键会使用哈希索引。
- 地理空间索引,用于支持快速的地理空间查询,如寻找附近1公里的商家。
- 文本索引,用于支持快速的全文检索
- 模糊索引(Wildcard Index),一种基于匹配规则的灵活式索引,在4.2版本开始引入。
索引评估、调优
使用 explain() 命令可以用于查询计划分析,进一步评估索引的效果。
如下:
1 | > db.test.explain().find( { a : 5 } ) |
从结果 winningPlan 中可以看出执行计划是否高效,比如:
- 未能命中索引的结果,会显示COLLSCAN
- 命中索引的结果,使用IXSCAN
- 出现了内存排序,显示为 SORT
更加详细的内容,请移步到官方文档。
集群
在大数据领域常常提到的4V特征中,Volume(数据量大)是首当其冲被提及的。
由于单机垂直扩展能力的局限,水平扩展的方式则显得更加的靠谱。 MongoDB 自带了这种能力,可以将数据存储到多个机器上以提供更大的容量和负载能力。
此外,同时为了保证数据的高可用,MongoDB 采用副本集的方式来实现数据复制。
一个典型的MongoDB集群架构会同时采用分片+副本集的方式,如下图:
架构说明
- 数据分片(Shards)
分片用于存储真正的集群数据,可以是一个单独的 Mongod实例,也可以是一个副本集。 生产环境下Shard一般是一个 Replica Set,以防止该数据片的单点故障。
对于分片集合(sharded collection)来说,每个分片上都存储了集合的一部分数据(按照分片键切分),如果集合没有分片,那么该集合的数据都存储在数据库的 Primary Shard中。 - 配置服务器(Config Servers)
保存集群的元数据(metadata),包含各个Shard的路由规则,配置服务器由一个副本集(ReplicaSet)组成。 - 查询路由(Query Routers)
Mongos是 Sharded Cluster 的访问入口,其本身并不持久化数据 。Mongos启动后,会从 Config Server 加载元数据,开始提供服务,并将用户的请求正确路由到对应的Shard。
Sharding 集群可以部署多个 Mongos 以分担客户端请求的压力。
分片机制
基于分片切分后的数据块称为 chunk,一个分片后的集合会包含多个 chunk,每个 chunk 位于哪个分片(Shard) 则记录在 Config Server(配置服务器)上。
Mongos 在操作分片集合时,会自动根据分片键找到对应的 chunk,并向该 chunk 所在的分片发起操作请求。
数据是根据分片策略来进行切分的,而分片策略则由 分片键(ShardKey)+分片算法(ShardStrategy)组成。
MongoDB 支持两种分片算法:
范围分片
假设集合根据x字段来分片,x的取值范围为[minKey, maxKey](x为整型,这里的minKey、maxKey为整型的最小值和最大值),将整个取值范围划分为多个chunk,每个chunk(默认配置为64MB)包含其中一小段的数据:
如Chunk1包含x的取值在[minKey, -75)的所有文档,而Chunk2包含x取值在[-75, 25)之间的所有文档…
范围分片能很好的满足范围查询的需求,比如想查询x的值在[-30, 10]之间的所有文档,这时 Mongos 直接能将请求路由到 Chunk2,就能查询出所有符合条件的文档。 范围分片的缺点在于,如果 ShardKey 有明显递增(或者递减)趋势,则新插入的文档多会分布到同一个chunk,无法扩展写的能力,比如使用_id作为 ShardKey,而MongoDB自动生成的id高位是时间戳,是持续递增的。
哈希分片
Hash分片是根据用户的 ShardKey 先计算出hash值(64bit整型),再根据hash值按照范围分片的策略将文档分布到不同的 chunk。
由于 hash值的计算是随机的,因此 Hash 分片具有很好的离散性,可以将数据随机分发到不同的 chunk 上。 Hash 分片可以充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要查询多个 chunk 才能找出满足条件的文档。
如何保证均衡
既然数据是分布在不同的 chunk 上的,而不同的 chunk 会被分配到不同的分片上,那么就会出现一个问题,如何保证分片上的数据(chunk)是均衡的。
一般会有以下两种情况:
- 全预分配,chunk 的数量和 shard 都是预先定义好的,比如 10个shard,存储1000个chunk,那么每个shard 分别拥有100个chunk。
- 非预分配,这种情况则比较复杂,一般当一个 chunk 太大时会产生分裂(split),不断分裂的结果会导致不均衡;或者动态扩容增加分片时,也会出现不均衡的状态。 这种不均衡的状态由集群均衡器进行检测,一旦发现了不均衡则执行 chunk数据的搬迁达到均衡。
MongoDB 的数据均衡器运行于 Primary Config Server(配置服务器的主节点)上,而该节点也同时会控制 Chunk 数据的搬迁流程。
对于数据的不均衡是根据两个分片上的 Chunk 个数差异来判定的,阈值对应表如下:
MongoDB 的数据迁移对集群性能存在一定影响,这点无法避免,目前的规避手段只能是将均衡窗口对齐到业务闲时段。
应用高可用
应用节点可以通过同时连接多个 Mongos 来实现高可用,如下:
当然,连接高可用的功能是由 Driver 实现的。
副本集
副本集又是另一个话题,实质上除了前面架构图所体现的,副本集可以作为 Shard Cluster 中的一个Shard(片)之外,对于规模较小的业务来说,也可以使用一个单副本集的方式进行部署。
MongoDB 的副本集采取了一主多从的结构,即一个Primary Node + N* Secondary Node的方式,数据从主节点写入,并复制到多个备节点。
典型的架构如下:
利用副本集,我们可以实现::
- 数据库高可用,主节点宕机后,由备节点自动选举成为新的主节点;
- 读写分离,读请求可以分流到备节点,减轻主节点的单点压力。
请注意,读写分离只能增加集群”读”的能力,对于写负载非常高的情况却无能为力。
对此需求,使用分片集群并增加分片,或者提升数据库节点的磁盘IO、CPU能力可以取得一定效果。
选举
MongoDB 副本集通过 Raft 算法来完成主节点的选举,这个环节在初始化的时候会自动完成,如下面的命令:
1 | config = { |
initiate 命令用于实现副本集的初始化,在选举完成后,通过 isMaster()命令就可以看到选举的结果:
1 | > db.isMaster() |
受 Raft算法的影响,主节点的选举需要满足”大多数”原则,可以参考下表:
因此,为了避免出现平票的情况,副本集的部署一般采用是基数个节点,比如3个,正所谓三人行必有我师..
心跳
在高可用的实现机制中,心跳(heartbeat)是非常关键的,判断一个节点是否宕机就取决于这个节点的心跳是否还是正常的。
副本集中的每个节点上都会定时向其他节点发送心跳,以此来感知其他节点的变化,比如是否失效、或者角色发生了变化。
利用心跳,MongoDB 副本集实现了自动故障转移的功能,如下图:
默认情况下,节点会每2秒向其他节点发出心跳,这其中包括了主节点。 如果备节点在10秒内没有收到主节点的响应就会主动发起选举。
此时新一轮选举开始,新的主节点会产生并接管原来主节点的业务。 整个过程对于上层是透明的,应用并不需要感知,因为 Mongos 会自动发现这些变化。
如果应用仅仅使用了单个副本集,那么就会由 Driver 层来自动完成处理。
复制
主节点和备节点的数据是通过日志(oplog)复制来实现的,这很类似于 mysql 的 binlog。
在每一个副本集的节点中,都会存在一个名为local.oplog.rs的特殊集合。 当 Primary 上的写操作完成后,会向该集合中写入一条oplog,
而 Secondary 则持续从 Primary 拉取新的 oplog 并在本地进行回放以达到同步的目的。
下面,看看一条 oplog 的具体形式:
1 | { |
其中的一些关键字段有:
- ts 操作的 optime,该字段不仅仅包含了操作的时间戳(timestamp),还包含一个自增的计数器值。
- h 操作的全局唯一表示
- v oplog 的版本信息
- op 操作类型,比如 i=insert,u=update..
- ns 操作集合,形式为 database.collection
- o 指具体的操作内容,对于一个 insert 操作,则包含了整个文档的内容
MongoDB 对于 oplog 的设计是比较仔细的,比如:
- oplog 必须保证有序,通过 optime 来保证。
- oplog 必须包含能够进行数据回放的完整信息。
- oplog 必须是幂等的,即多次回放同一条日志产生的结果相同。
- oplog 集合是固定大小的,为了避免对空间占用太大,旧的 oplog 记录会被滚动式的清理。
事务一致性
一直以来,”不支持事务” 是 MongoDB 一直被诟病的问题,当然也可以说这是 NoSQL 数据库的一种权衡(放弃事务,追求高性能、高可扩展)
但实质上,MongoDB 很早就有事务的概念,但是这个事务只能是针对单文档的,即单个文档的操作是有原子性保证的。
在4.0 版本之后,MongoDB 开始支持多文档的事务:
- 4.0 版本支持副本集范围的多文档事务。
- 4.2 版本支持跨分片的多文档事务(基于两阶段提交)。
在事务的隔离性上,MongoDB 支持快照(snapshot)的隔离级别,可以避免脏读、不可重复读和幻读。
尽管有了真正意义上的事务功能,但多文档事务对于性能有一定的影响,应用应该在充分评估后再做选用。
一致性
一致性是一个复杂的话题,而一致性更多从应用角度上提出的,比如:
1 | 向系统写入一条数据,应该能够马上读到写入的这个数据。 |
在分布式架构的CAP理论以及许多延续的观点中提到,由于网络分区的存在,要求系统在一致性和可用性之间做出选择,而不能两者兼得。
在 MongoDB 中,这个选择是可以由开发者来定的。 MongoDB 允许客户端为其操作设定一定的级别或者偏好,包括:
- read preference
读取偏好,可指定读主节点、读备节点,或者是优先读主、优先读备、取最近的节点 - write concern
写关注,指定写入结果达到什么状态时才返回,可以为无应答(none)、应答(ack),或者是大多数节点完成了数据复制等等 - read concern
读关注,指定读取的数据版本处于怎样的状态,可以为读本地、读大多数节点写入,或者是线性读(linearizable)等等。
使用不同的设定将会产生对于C(一致性)、A(可用性)的不同的抉择,比如:
- 将读偏好设置为 primary,此时读写都在主节点上。 这保证了数据的一致性,但一旦主节点宕机会导致失败(可用性降低)
- 将读偏好设置为 secondaryPrefered,此时写主,优先读备,可用性提高了,但数据存在延迟(出现不一致)
- 将读写关注都设置为 majority(大多数),一致性提升了,但可用性也同时降低了(节点失效会导致大多数写失败)
关于这种权衡的讨论会一直存在,而 MongoDB 除了提供多样化的选择之外,其主要是通过复制、基于心跳的自动failover等机制来降低系统发生故障时产生的影响,从而提升整体的可用性。
应用场景
行文至此,笔者对 MongoDB 也有了基本的了解,那么在什么样的情况下才需要使用 MongoDB 呢?换句话说就是 MongoDB 的应用场景是什么。
以下是几个实际的应用案例。
- 游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新
- 物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
- 社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
- 物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
- 视频直播,使用 MongoDB 存储用户信息、礼物信息等。
这些应用场景中,数据操作方面的共同特点是:
(1)数据量大
(2)写入操作频繁(读写都很频繁)
(3)价值较低的数据,对事务性要求不高 对于这样的数据,我们更适合使用MongoDB来实现数据的存储。
辅助决策
如果你还在为是否应该使用 MongoDB,不如来做几个选择题来辅助决策
如果上述有1个 Yes,可以考虑 MongoDB,2个及以上的 Yes,选择MongoDB绝不会后悔。
优势和局限性
MongoDB 为需要可靠数据库的公司提供了众多优势。以下是使用 MongoDB 的一些好处:
- 速度:MongoDB比一般的关系数据库快 100 倍。作为一个面向文档的数据库,MongoDB 通过索引使访问文档变得容易。这有助于快速查询响应,从而节省用户宝贵的时间。
- 分片:MongoDB 提供的另一个好处是它允许用户存储大量数据。这是通过将数据分发到连接到应用程序的多个服务器来完成的。如果一台服务器无法处理数据的大小,则通过称为自动分片的过程将其共享给另一台服务器。
- 灵活的数据库:MongoDB 允许用户在单独的文档中拥有不同类型的数据,因为它是一个无模式的数据库。这为其用户提供了高度的灵活性和存储任何类型数据的自由。
- 水平可扩展性:MongoDB 是一个可水平扩展的数据库,这是它的基本优势之一。它允许用户有效地将大量数据分发到多台机器。
- 事务可行性:一些 MongoDB 功能包括gridFS 和 Replication。这些功能支持在使用 MongoDB 时提高数据可用性。这确保了应用程序的性能非常高。
- 缺少模式:MongoDB 是 NoSQL,这意味着它不需要模式。因此,在使用 MongoDB 时无需概述数据库模式。这减少了数据库处理信息所需的时间。因此,您的架构将由您编写的算法定义。
尽管 MongoDB 提供了优于其他数据库的显着优势,但它并不完美。它有一些需要改进的限制。以下是 MongoDB 的一些缺点。
- 不支持连接:与支持连接的理性数据库不同,MongoDB 不支持。虽然可以通过手动编码添加连接功能,但执行速度可能会很慢并影响性能。
- 高内存使用率:MongoDB 旨在为每个值对存储键名称。它还受到数据冗余的影响,因为它缺少连接功能。这会导致高内存使用率。
- 有限数据大小:MongoDB 允许的最大文档大小为16MB。这是一个需要纠正的严峻挑战。
- 有限嵌套:MongoDB 提供有限的嵌套访问,因为用户不能执行超过100 级的文档嵌套。
ChatGPT 怎么看
问:怎么理解MongoDB 允许用户在单独的文档中拥有不同类型的数据,因为它是一个无模式的数据库。这为其用户提供了高度的灵活性和存储任何类型数据的自由?
答:MongoDB 作为无模式(schema-less)的文档存储数据库,其设计允许在同一个集合(相当于关系数据库中的表)中的不同文档(相当于关系数据库中的行)具有不同的结构和类型。以下是对这一特性的详细解释:
1. 无模式(Schema-less)特性
在传统的关系数据库中,所有行必须符合预定义的表结构(schema),即每一列的数据类型和属性在创建表时就已经固定下来。这意味着每一行的数据必须遵循相同的格式。
相比之下,MongoDB 不强制要求文档具有相同的结构。每个文档可以有不同的字段和数据类型。这种灵活性源于 MongoDB 使用 BSON(一种二进制的 JSON 格式)来存储数据。
2. 灵活的数据模型
- 不同文档的结构可以不同:在同一个集合中,一个文档可以包含某些字段,而另一个文档可以不包含这些字段,或者包含不同的字段。例如,在一个用户集合中,一个文档可以包含
name
和email
字段,而另一个文档可以包含name
、email
和phone
字段。 - 字段的数据类型可以不同:即使是相同字段,在不同文档中也可以具有不同的数据类型。例如,一个文档中的
age
字段可以是整数类型,而在另一个文档中,age
字段可以是字符串类型。
3. 优点
- 灵活性:开发者可以根据需要随时添加或修改文档中的字段,而无需修改整个集合的结构。这使得开发迭代更快,特别是在需求频繁变化的项目中。
- 适应性:能够轻松存储和处理复杂和多变的数据结构,如嵌套文档和数组。适用于需要处理异构数据(heterogeneous data)的应用场景。
- 快速开发和原型设计:无需提前定义和维护模式,能够快速启动项目和进行原型设计。
4. 示例
考虑一个用户集合,其中的文档结构如下:
1 | // 文档1 |
在上述例子中:
- 文档1 和 文档2 具有不同的字段,文档2 比文档1 多了
phone
字段。 - 文档3 没有
email
字段,但有age
字段,并且age
字段的数据类型为整数。
5. 应用场景
- 内容管理系统(CMS):需要存储不同类型的内容,如文章、评论、用户信息等,每种内容的结构可能不同。
- 物联网(IoT):不同的传感器设备产生的数据格式可能不同,需要灵活存储和处理。
- 快速原型设计:在产品开发初期,需求可能频繁变化,无模式数据库可以快速适应这些变化。
6. 注意事项
- 数据一致性和验证:由于没有固定模式,可能会引入不一致的数据,需要在应用层进行数据验证和清理。
- 查询优化:灵活的结构可能会影响查询性能,需要通过索引和查询优化来提高效率。
总之,MongoDB 的无模式特性为开发者提供了极大的灵活性,允许存储和处理多种类型和结构的数据,适应快速变化的需求和复杂的应用场景。
总结
通读全文,好像感觉这篇文章在哪看过。对,没错,这就是笔者对几篇文章的缝合。边学习边记录吗,不错的学习模式。
总之,MongoDB 以其高并发、高扩展性、高可用性和无模式特性取得了不错的成绩。也逐渐变成了找工作必须要会的一项技能。情到深处,不得不搞个一首,“都是内卷惹的祸……”。
关于 MongoDB 的学习绝不仅于此,后续会记录深入的内容。
一位同志在第三次考ccf通过后突然顿悟,决定不考研了,乐。猜一下他学 Java 能学多久,刚好我也要学。