Wetts's blog

Stay Hungry, Stay Foolish.

0%

MongoDB-0-知识点汇总.md

基础

  • MongoDB 是一个文档数据库,提供好的性能,领先的非关系型数据库。采用 BSON 存储文档数据。
  • MongoDB的优势有哪些
    • 面向文档的存储:以 JSON 格式的文档保存数据。
    • 任何属性都可以建立索引。
    • 复制以及高可扩展性。
    • 自动分片。
    • 丰富的查询功能。
    • 快速的即时更新。
    • 来自 MongoDB 的专业支持。
  • 基础概念
    • 模型层面
      • database 数据库,与 SQL 的数据库(database)概念相同,一个数据库包含多个集合(表)
      • collection 集合,相当于 SQL 中的表(table),一个集合可以存放多个文档(行)。不同之处就在于集合的结构(schema)是动态的,不需要预先声明一个严格的表结构。更重要的是,默认情况下 MongoDB 并不会对写入的数据做任何 schema 的校验。
      • document 文档,相当于 SQL 中的行(row),一个文档由多个字段(列)组成,并采用 bson(json)格式表示。
      • field 字段,相当于 SQL 中的列(column),相比普通 column 的差别在于 field 的类型可以更加灵活,比如支持嵌套的文档、数组。
      • 此外,MongoDB 中字段的类型是固定的、区分大小写、并且文档中的字段也是有序的。
      • MongoDB 和关系型数据库术语对比
        • 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 是其中的一类聚合操作。
      • MongoDB_SQL概念映射对比图
  • 分布式 ID
    • MongoDB 采用 ObjectId 来表示主键的类型,数据库中每个文档都拥有一个_id 字段表示主键。
    • _id 的生成规则如下:
      • MongoDB_ID生成规则
      • 4-byte Unix 时间戳
      • 3-byte 机器 ID
      • 2-byte 进程 ID
      • 3-byte 计数器(初始化随机)
    • 值得一提的是 _id 的生成实质上是由客户端(Driver)生成的,这样可以获得更好的随机性,同时降低服务端的负载。
    • 当然服务端也会检测写入的文档是否包含 _id 字段,如果没有就生成一个。

索引

  • MongoDB 支持非常丰富的索引类型。
  • 索引的技术实现依赖于底层的存储引擎,在当前的版本中 MongoDB 使用 wiredTiger 作为默认的引擎。
  • 在索引的实现上使用了 B+树的结构。
  • 大部分基于SQL数据库的一些索引调优技巧在 MongoDB 上仍然是可行的。
  • 索引特性
    • unique=true,表示一个唯一性索引
    • expireAfterSeconds=3600,表示这是一个TTL索引,并且数据将在1小时后老化
    • sparse=true,表示稀疏的索引,仅索引非空(non-null)字段的文档
    • partialFilterExpression: { rating: { $gt: 5 },条件式索引,即满足计算条件的文档才进行索引
  • 索引分类
    • 哈希(HASH)索引,哈希是另一种快速检索的数据结构,MongoDB 的 HASH 类型分片键会使用哈希索引。
    • 地理空间索引,用于支持快速的地理空间查询,如寻找附近1公里的商家。
    • 文本索引,用于支持快速的全文检索
    • 模糊索引(Wildcard Index),一种基于匹配规则的灵活式索引,在4.2版本开始引入。
  • 索引评估、调优
    • 使用 explain() 命令可以用于查询计划分析,进一步评估索引的效果。

集群

  • 一个典型的 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 支持两种分片算法:
          • 范围分片
            • MongoDB_分片算法_范围分片
          • 哈希分片
            • MongoDB_分片算法_哈希分片
    • 如何保证均衡
      • 真实的场景中,会存在下面两种情况:
        • 全预分配,chunk 的数量和 shard 都是预先定义好的,比如 10 个 shard,存储 1000 个 chunk,那么每个 shard 分别拥有 100 个 chunk。
        • 非预分配,这种情况则比较复杂,一般当一个 chunk 太大时会产生分裂(split),不断分裂的结果会导致不均衡;或者动态扩容增加分片时,也会出现不均衡的状态。 这种不均衡的状态由集群均衡器进行检测,一旦发现了不均衡则执行 chunk 数据的搬迁达到均衡。
      • MongoDB 的数据均衡器运行于 Primary Config Server(配置服务器的主节点)上,而该节点也同时会控制 Chunk 数据的搬迁流程。
        • MongoDB_分片机制_数据自动均衡
        • 对于数据的不均衡是根据两个分片上的 Chunk 个数差异来判定的,阈值对应表如下:
          • MongoDB_分片机制_数据自动均衡规则
      • MongoDB 的数据迁移对集群性能存在一定影响,这点无法避免,目前的规避手段只能是将均衡窗口对齐到业务闲时段。
    • 应用高可用
      • 应用节点可以通过同时连接多个 Mongos 来实现高可用
        • MongoDB_Mongos_连接
  • 副本集
    • 副本集可以作为 Shard Cluster 中的一个Shard(片)之外,对于规模较小的业务来说,也可以使用一个单副本集的方式进行部署。
    • MongoDB 的副本集采取了一主多从的结构,即一个 Primary Node + N* Secondary Node 的方式,数据从主节点写入,并复制到多个备节点。
    • 架构
      • MongoDB_副本架构
    • 利用副本集,我们可以实现:
      • 数据库高可用,主节点宕机后,由备节点自动选举成为新的主节点;
      • 读写分离,读请求可以分流到备节点,减轻主节点的单点压力。
        • 请注意,读写分离只能增加集群”读”的能力,对于写负载非常高的情况却无能为力。
        • 对此需求,使用分片集群并增加分片,或者提升数据库节点的磁盘IO、CPU能力可以取得一定效果。
    • 选举
      • MongoDB 副本集通过 Raft 算法来完成主节点的选举,这个环节在初始化的时候会自动完成
    • 心跳
      • 副本集中的每个节点上都会定时向其他节点发送心跳,以此来感知其他节点的变化,比如是否失效、或者角色发生了变化。
      • 利用心跳,MongoDB 副本集实现了自动故障转移的功能
      • 流程
        • 默认情况下,节点会每 2 秒向其他节点发出心跳,这其中包括了主节点。如果备节点在 10 秒内没有收到主节点的响应就会主动发起选举。
        • 此时新一轮选举开始,新的主节点会产生并接管原来主节点的业务。整个过程对于上层是透明的,应用并不需要感知,因为 Mongos 会自动发现这些变化。
        • 如果应用仅仅使用了单个副本集,那么就会由 Driver 层来自动完成处理。
    • 复制
      • 主节点和备节点的数据是通过日志(oplog)复制来实现的,这很类似于 mysql 的 binlog。
      • 流程
        • 在每一个副本集的节点中,都会存在一个名为 local.oplog.rs 的特殊集合。当 Primary 上的写操作完成后,会向该集合中写入一条 oplog,而 Secondary 则持续从 Primary 拉取新的 oplog 并在本地进行回放以达到同步的目的。
      • MongoDB 对于 oplog 的设计是比较仔细的,比如:
        • oplog 必须保证有序,通过 optime 来保证。
        • oplog 必须包含能够进行数据回放的完整信息。
        • oplog 必须是幂等的,即多次回放同一条日志产生的结果相同。
        • oplog 集合是固定大小的,为了避免对空间占用太大,旧的 oplog 记录会被滚动式的清理。

事务与一致性

  • 实质上,MongoDB 很早就有事务的概念,但是这个事务只能是针对单文档的,即单个文档的操作是有原子性保证的。
  • 在 4.0 版本之后,MongoDB 开始支持多文档的事务:
    • 4.0 版本支持副本集范围的多文档事务。
    • 4.2 版本支持跨分片的多文档事务(基于两阶段提交)。
  • 在事务的隔离性上,MongoDB 支持快照(snapshot)的隔离级别,可以避免脏读、不可重复读和幻读。
  • 一致性
    • 在分布式架构的 CAP 理论以及许多延续的观点中提到,由于网络分区的存在,要求系统在一致性和可用性之间做出选择,而不能两者兼得。
      • CAP理论
    • 在 MongoDB 中,这个选择是可以由开发者来定的。MongoDB 允许客户端为其操作设定一定的级别或者偏好,包括:
      • read preference
        • 读取偏好,可指定读主节点、读备节点,或者是优先读主、优先读备、取最近的节点
      • write concern
        • 写关注,指定写入结果达到什么状态时才返回,可以为无应答(none)、应答(ack),或者是大多数节点完成了数据复制等等
      • read concern
        • 读关注,指定读取的数据版本处于怎样的状态,可以为读本地、读大多数节点写入,或者是线性读(linearizable)等等。
    • 使用不同的设定将会产生对于C(一致性)、A(可用性)的不同的抉择,比如:
      • 将读偏好设置为 primary,此时读写都在主节点上。这保证了数据的一致性,但一旦主节点宕机会导致失败(可用性降低)
      • 将读偏好设置为 secondaryPrefered,此时写主,优先读备,可用性提高了,但数据存在延迟(出现不一致)
      • 将读写关注都设置为 majority(大多数),一致性提升了,但可用性也同时降低了(节点失效会导致大多数写失败)