玩转MongoDB 从入门到实战 (it-ebooks) (Z-Library)

Author: it-ebooks

教育

No Description

📄 File Format: PDF
💾 File Size: 1.5 MB
32
Views
0
Downloads
0.00
Total Donations

📄 Text Preview (First 20 pages)

ℹ️

Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

📄 Page 1
玩转 MongoDB 从入门到实战
📄 Page 2
1 2玩转 MongoDB 从入门到实战 序言 阿里云智能资深技术专家 阿里云 NoSQL 数据库负责人 杨成虎(叶翔) 2016 年开始大量的用户反馈希望阿里云能提供 MongoDB 云服务。同年,MongoDB 正式发 布了 3.2 版本,存储引擎重构到 WiredTiger 之上。其稳定性,功能性相比以往的版本都有了大幅度 的提升。我和同事们经过一番调研,确定时机成熟,随即敲定了将 MongoDB 放到阿里云上,作为 一方阿里云产品提供商业服务,并且成立了相应的 MongoDB 研发团队。 在整个产品的上线研发过程中,对 MongoDB 的学习愈加深入,就愈加的喜爱它。其灵活的 JSON 数据处理,弹性的 Sharding,高可靠的 ReplicaSet 等能力可以极大的简化业务系统复杂度, 提升研发效率,不愧于“程序员最喜爱的数据库”称号。而运行于云之上的 MongoDB,被赋予了生 命周期管理,自动备份管理等云原生的企业级能力。阿里云与 MongoDB,云原生与开源数据库的姻 缘就此开启。 2019 年,阿里云与 MongoDB 达成战略合作,作为云厂商独家支持 MongoDB 4.0 以及后续 最新版本的服务,与 MongoDB 的关系从开源社区到商业合作全面覆盖。同年,MongoDB 4.2 版本 发布,具备了分布式事务的处理能力,覆盖传统关系型数据库的事务能力。近期,阿里云 MongoDB 的云原生技术也从生命周期的托管逐步升级到资源池化与存储计算分离的云原生架构。利用云原生技 术架构,让传统数据库具备了系统资源灵活弹性升级的云化能力,帮助用户便捷的实现数字化转型升 级,降低企业 TCO 成本。 但是我们也发现,中文的 MongoDB 学习资料并不多。国内很多的用户对 MongoDB 的使用也 常有一些误解,例如错误的认为数据库会丢失,且不可靠。也并不清楚 MongoDB 现已具备了强大的 事务处理能力。在市场中急需这样的一本书帮助大家更深入的学习 MongoDB,在生产系统中更好的 使用它,为业务赋予更高的价值。本次书籍的编写者,有来自阿里云的研发工程师,也有 MongoDB 公司的原厂架构师,还有来自社区的支持者。合众人之力完成了本次书籍编写,也感谢阿里云的开发 者社区,使本书得以顺利出版。欢迎广大读者能加入到开源社区,源于开源、回馈开源。
📄 Page 3
3 4玩转 MongoDB 从入门到实战 走进 MongoDB 05 目录 MongoDB 聚合框架 11 复制集使用及原理介绍 16 分片集群使用及原理介绍 25 ChangeStreams 使用及原理 34 事务功能使用及原理介绍 47 MongoDB 最佳实践一 56 MongoDB 最佳实践二 63 微信关注公众号:阿里云数据库 第一时间,获取更多技术干货 阿里云开发者“藏经阁” 海量免费电子书下载
📄 Page 4
5 6玩转 MongoDB 从入门到实战 走进 MongoDB 定位上,MongoDB 介于 Memcached 和关系型数据库之间,扩展性和性能上,MongoDB 更接近于 Memcached,功能上,MongoDB 更接近于关系型数据库。 在生产环境中,MongoDB 经常会部署成一个三节点的复制集,或者一个分片集群。我们先来看左边。当 MongoDB 部署 为一个复制集时,应用程序通过驱动,直接请求复制集中的主节点,完成读写操作。另外两个从节点,会自动和主节点同 步,保持数据的更新。在集群运行的过程中,万一主节点遇到故障,两个从节点会在几秒的时间内选举出新的主节点,继 续支持应用的读写操作。我们再来看右边,当 MongoDB 被部署为一个分片集群时,应用程序通过驱动,访问路由节点, 也就是 Mongos 节点 Mongos 节点会根据读写操作中的片键值,把读写操作分发的特定的分片执行,然后把分片的执行 结果合并,返回给应用程序。那集群中的数据是如何分布的呢?这些元数据记录在 Config Server 中,这也是一个高可用 的复制集。每个分片管理集群中整体数据的一部分,也是一个高可用复制集。此外,路由节点,也就是 Mongos 节点在生 产环境通常部署多个。这样,整个分片集群没有任何单点故障。 (二)MongoDB 基本概念与关系数据的对应关系 关系型数据通常有数据库和表的概念,对应在 MongoDB 里有数据库和集合关系;数据库有主表和子表,对应 MongoDB 通常使用内嵌的子文档或内嵌数组;关系型数据里有 Index 索引,MongoDB 也有类似概念;关系数据库里一条数据称为一 行,MongoDB 称为一个文档 Document;关系型数据库里面的列,对应到 MongoDB 成为一个字段 Field;关系数据库里 面经常使用 Join 连接操作,对应 MongoDB 通常使用内嵌方式解决,如果使用Linking 方式,对应使用$Lookup 操作符 也可支持左外链接;关系型数据库里面还有视图,对应 MongoDB 也有只读视图和按需物化视图;关系型数据库里面会有 ACID 的多记录事务,对应的 MongoDB 里面会有 ACID 的多文档事务。 (三)MongoDB 的数据层次结构 MongoDB 里面数据主要分为三层,文档(Documents),集合(Collections), 和数据库(Databases),多个文档是存 储在一个集合中,多个集合存放在一个数据库里,每个集群里面可能会有多个数据库。 例如: ·数据库(Database): Products ·集合(Collections): Books, Movies, Music 一、MongoDB 的相关概念 作者 | 张春立 Depth of Functionality S ca la b ili ty & P e rf o rm a n ce Memcached MongoDB RDBMS MongoDB 的功能定位 Replica Set Read Write Application Application MongoD MongoD MongoD Shard Cluster Read Write Application Driver MongoS Config Servers Shard (replica set) MongoDB 部署模型 (一)MongoDB 部署模型 RDBMS MongDB MongoDB 基本概念与关系数据的对应关系 Database Database Table Collection Parent- Child Tables Nested Sub- Document or Array Index Index Row Document Column Fieid Join Embedding Linking, Slookup View Read-Only View On-Demand Materialized View Multi- Record ACID Transaction Multi- Document ACID Transaction
📄 Page 5
7 8玩转 MongoDB 从入门到实战 运行 MongoDB /bin/mongod --dbpath /data/db MongoDB 安装的 bin 目录 MongoDB 数据文件的位置 数据库和集合的组合,构成 MongoDB 的命名空 间: ·Products.Books ·Products.Movies ·Products.Music *数据库名最长不能超过 64 个字节,命名空间最 长不能超过 120 字节 *FCV>=4.4,命名空间长度限制为 255 字节 (四)MongoDB 的数据结构 MongoDB 采用 JSON 文档结构: ·JSON 的全称:JavaScript Object Notation ·JSON 的格式:支持如下数据格式 ·字符串 :e.g., “Thomas” ·数字 :e.g., 29, 3.7) ·布尔:True / false ·空值:Null ·数组:e.g., [88.5, 91.3, 67.1] ·对象:Object 安装你的第一个 MongoDB 系统 第一个命令:下载 第二个命令:解压 第三个命令:改个目录名 访问 MongoDB 创建复制集练习 1. 创建数据目录: 3. 连接 MongoDB 服务: 4. 指定复制集配置 2. 启动三个 MongoDB 服务 创建分片集群练习 有以下 5 个步骤: 1. 创建 Config Server 复制集 2. 创建一个或多个分片(每个分片为一个复制集) 3. 启动 Mongos(一个或多个) 4. 访问 Mongos,把分片添加到集群 5. 选择片键,启用分片 以上就完成整个分片集群的部署。 生产环境部署建议 生产环境当中,大家应该遵循生产环境部署的一些最 佳实践。比如: 1. 容量规划:计算资源,存储容量,IOPS,Oplog, 网络带宽 2. 高可用:部署复制集或分片集群 3. 节点个数:复制集部署奇数个节点,避免脑裂 4. 应用生产环境的最佳实践,如: ·使用主机名而不是 IP ·文件系统(Linux 环境推荐 XFS) ·禁用 NUMA ·禁用 THP ·调高资源限制 ·Swappiness ·Readahead ·Tcp_Keepalive_Time ·时钟同步 ·安全配置 ·… 上图是 MongoDB 数据类型的一些列 表,常见类型 MongoDB 几乎都支持。 (五)MongoDB 采用 BSON 格式保存数据 { "firstName": "Thomas", "lastName": "Smith", "age": 29 } 二、集群部署 MongoDB 数据类型 Type Number Alias Double 1 “double” String 2 “string” Object 3 “object” Array 4 “array” Binary data 5 “binData” Undefined 6 “undefined” Objectld 7 “objectld” Boolean 8 “bool” Date 9 “date” Null 10 “null” DBPointer 12 “dbPointer” JavaScript 13 “javascript” Regular Expression 11 “regex” Symbol 14 “symbol” JavaScript (with scope) 15 “javascriptWithScope” 32-bit integer 16 “int” Timestamp 17 “timestamp” 64-bit integer 18 “long” Decimal128 19 “deciml” Min key -1 “minKey” Max key 127 “maxKey” MongoDB 数据类型 Type Number Alias Double 1 “double” String 2 “string” Object 3 “object” Array 4 “array” Binary data 5 “binData” Undefined 6 “undefined” Objectld 7 “objectld” Boolean 8 “bool” Date 9 “date” Null 10 “null” DBPointer 12 “dbPointer” JavaScript 13 “javascript” Regular Expression 11 “regex” Symbol 14 “symbol” JavaScript (with scope) 15 “javascriptWithScope” 32-bit integer 16 “int” Timestamp 17 “timestamp” 64-bit integer 18 “long” Decimal128 19 “deciml” Min key -1 “minKey” Max key 127 “maxKey” 插入新文档 insertOne db.products.insertOne( { item: "card", qty: 15 } ); insertMany db.products.insertMany( [ { _id: 10, item: "large box", qty: 20 }, { _id: 11, item: "small 三、基本操作 curl -O https://fastdl.mongodb.org/linux/mongodb- linux-x86_64-rhel70-4. 4.2.tgz $ ./bin/mongo MongoDB 安装的 bin 目 录 MongoDB shell version: 4.4.2 ... Server has startup warnings: 2020-12-15T04:23:25.268+0000 I CONTROL [initandlisten] 2020-12-15T04:23:25.268+0000 I CONTROL [initandlisten] ** WARNIN G: Access control is not enabled for the database. ... mkdir rs1 rs2 rs3 mongo //connect to the default port 27017 rs.initiate() // 初始复制集 rs.add ('<HOSTNAME>:27018') // 增加一个节点配置 rs.add('<HOSTNAME>:27019') // 增加一个节点配置 rs.status() mongod --replSet rs --dbpath ./rs1 --port 27017 --fork --logpath ./rs 1/mongod.log mongod --replSet rs --dbpath ./rs2 --port 27018 --fork --logpath ./rs 2/mongod.log mongod --replSet rs --dbpath ./rs3 --port 27019 --fork --logpath ./rs 3/mongod.log tar xzvf mongodb-linux-x86_64-rhel70-4.4.2.tgz mv mongodb-linux-x86_64-rhel70-4.4.2 mongodb 第四个命令:没了!
📄 Page 6
9 10玩转 MongoDB 从入门到实战 删除文档 删除集合和数据库 使用 Drop 删除集合 ·使用 DB.<COLLECTION>.Drop()来删除一个集合 ·集合中的全部文档都会被删除. ·集合相关的索引也会被删除 更新操作 Update 操作需要执行参数 参数包括 ·查询参数 ·更新参数 Update 更新数组操作 $Push: 增加一个对象到数组底部 $PushAll: 增加多个对象到数组底部. $Pop: 从数组底部删除一个对象 $Pull: 如果匹配指定的值或条件,从数组中删除相应的 对象. $PullAll: 如果匹配列表中的任意值,从数组中删除相应 的对象. $PullAll: 如果匹配列表中的任意值,从数组中删除相应 的对象 $AddToSet: 如果不存在则增加一个值到数组 使用 {Upsert: True} 更新或插入 如果希望没有就添加,指定 Upsert: True 参数 默认情况下如果没有匹配的对象,就不会执行更新 使用 Find 查询数据文档 使用 Find 查询数据文档 Find 是 MongoDB 的基础查询命令 Find 返回数据的游标(Cursor) Find 是 MongoDB 的基础查询命令 Find 返回数据的游标(Cursor) SQL 查询条件对照 查询操作符 使用 DropDatabase 删除数据库 ·使用 DB.dropDatabase()来删除数据库 ·数据库相应文件也会被删除,磁盘空间将被释放 deleteOne db.orders.deleteOne( { "_id" : ObjectId("563237a41a 4d68582c2509da") } ); db.orders.deleteOne( { "expirationTime" : { $lt: ISODate("2015-11-01T12:40:15Z") } } ); deleteMany db.orders.deleteMany( { "client" : "Crude Traders Inc." } ); remove db.collection.remove( <query>, <justOne> ) db.colToBeDropped.drop() use tempDB db.dropDatabase() show collections // No collections show dbs // The db is gone db.movies.find( { "year" : 1975 } ) //单条件查询 db.movies.find( { "year" : 1989, "title" : "Batman" } ) // 多条件 and 查询 db.movies.find( { $or: [{"year" : 1989}, {"title" : "Batman"}] } ) //多条件 or 查询 db.movies.find( { $and : [ {"title" : "Batman"}, { "category" : "action" }] } ) // and 查询 db.movies.find( { "title" : /^B/} ) //按正则表达式查找 db.movies.find( { "year" : 1975 } ) //单条件查询 db.movies.find( { "year" : 1989, "title" : "Batman" } ) // 多条件 and 查询 db.movies.find( { $or: [{"year" : 1989}, {"title" : "Batman"}] } ) //多条件 or 查询 db.movies.find( { $and : [ {"title" : "Batman"}, { "category" : "action" }] } ) // and 查询 db.movies.find( { "title" : /^B/} ) //按正则表达式查找 a = 1 -> {a: 1} a <> 1 -> {a: {$ne: 1}} a > 1 -> {a: {$gt: 1}} a >= 1 -> {a: {$gte: 1}} a < 1 -> {a: {$lt: 1}} a <= 1 -> {a: {$lte: 1}} a = 1 AND b = 1 -> {a: 1, b: 1}或{$and: [{a: 1}, {b: 1}]} a = 1 OR b = 1 -> {$or: [{a: 1}, {b: 1}]} a IS NULL -> {a: {$exists: false}} a IN (1, 2, 3) -> {a: {$in: [1, 2, 3]}} $lt: 存在并小于 $lte: 存在并小于等于 $gt: 存在并大于 $gte: 存在并大于等于 $ne: 不存在或存在但不等于 $in: 存在并在指定数组中 $nin: 不存在或不在指定数组中 $or: 匹配两个或多个条件中的一个 $and: 匹配全部条件 db.movies.update( { "title" : "Jaws" }, { $inc: { "budget" : 5 } }, { upsert: true } ) {“_id” : ObjectId("5847f65f83432667e51e5ea8"), "title" : "Jaws", "budget" : 5 }// insert data db.movies.insert( [ { "title" : "Batman", "category" : [ "action", "adventure" ], "imdb_rating" : 7.6, "budget" : 35 }, { "title" : "Godzilla", "category" : [ "action", "adventure", "sci-fi" ], "imdb_rating" : 6.6 }, { "title" : "Home Alone", "category" : [ "family", "comedy" ], "imdb_rating" : 7.4 } ] ) db.movies.update( { "title" : "Batman" }, { $set : { "imdb_rating" : 7.7 } } ) 查询蝙蝠侠 更新 IMDB 评分字段 如果找不到“大白鲨”就增加一个“大白鲨”电影 box", qty: 55 }, { _id: 12, item: "medium box", qty: 30 } ] ); Insert db.collection.insert( <document or array of documents>, { writeConcern: <document>, ordered: <boolean> } )
📄 Page 7
11 12玩转 MongoDB 从入门到实战 Type $project $group $eq/$gt/$gte/$lt/$lte $and$or$not$in 选择需要的或排除 不需要的字段 $map/$reduce/ $filter $multiply/$divide/ $substract/$add $year/$month/ $dayOfMonth/ $hour/$minute/ $second ...... $range $sum/$avg $push/ $addToSet $geoWithin/ $intersect $first/$last/ $max/$min ...... ...... MongoDB 聚合框架 一、基本概念 二、基本使用 作者 | 张春立 (一)什么是聚合框架 聚合框架(Aggregation Framework)是用于在一 个或几个集合上进行的一系列运算从而得到期望的数 据集的运算框架。 从效果而言,聚合框架相当于 SQL 查询中的: ·GROUP BY ·LEFT OUTER JOIN ·AS ·… ·但聚合框架的作用不限于此 (二)管道(Pipeline)和步骤(Stage) 管道(Pipeline): ·聚合框架对数据进行处理的过程; ·与 Linux 管道有类似之处; 步骤(Stage): ·步骤(Stage)是指管道中的一步操作。每个步 骤: ·接受一系列文档(原始数据); ·在这些文档上进行一系列运算; ·生成下一个步骤所需的文档; (一)使用形式 (二)常用步骤(stage)运算符 1 (三)子运算符 聚合框架的基本使用,在 MongoDB 聚合框架里面 最重要的一个参数就是 Pipeline,Pipeline 是一个数 组的形式,在数组里面每一个元素表示一个对应的步 骤,然后把整个Pipeline 传给数据库进行执行,然后 就按照这些步骤进行执行,最后把结果返回给客户端。 聚合框架的基本使用,在 MongoDB 聚合框架里面 最重要的一个参数就是 Pipeline,Pipeline 是一个数 组的形式,在数组里面每一个元素表示一个对应的步 骤,然后把整个Pipeline 传给数据库进行执行,然后 就按照这些步骤进行执行,最后把结果返回给客户端。 在每一个步骤里面还包含很多的子运算符,在$Match 里 面经常会用到了 EQ,表示某个条件相等,$GT 大于 $GTE 大于等于等; 还有$And/$All 这些逻辑运算符, 还有表示地理位置的,例如$Geowithin/$Intersect, 表示地理位置运算的运算符,$Project 阶段步骤,可 能会用到$Map,$Reduce 来对数组的每个元素进行 处理。 还有进行加减乘除的,例如$Multiply、$Divide、$Su btract、$Add 等;进行日期运算的像$Year、$Month 等子的运算符,在$Group 里面经常进行聚合操作,例 如子运算符经常会用到$Sum 来进行汇总、$Avg 求平 均值、$Push、$AddToSet 来对数组进行操作。 (三)常用步骤(stage)运算符 2 MongoDB 里面连接 Join 操作只有左外连接,因为 Join 这种操作上是违反 MongoDB设计的初衷的,这样操作 经常要对两个表的不同数据进行连接操作,这些数据在 物理存储的时候,通常不是在相邻的区域里面,读取的 效率比较低。 此外 MongoDB 是一个分布式的环境,校验操作的左 右两边如果都是一个分片的表,当进行 Join 操作的时 候,左边有一个又有一条数据,它可能在分片一上要连 接的一个数据可能在分片二上,下一条数据可能又是另 外一种情况,这种情况下数据库很难保证整个操作的性 能。 基于这些原因,MongoDB 只提供左外连接,并且要求 From 表不能是分片表,左边的表主表可以是分片表, 在 SQL 里面会使用类似于左边这样的一个查询语句。 ·原因二:读取效率低下; ·原因三:分布式环境; 在 SQL 里面还会使用 Group BY 类似于左边这样的一 个运算符,对应的在 MongoDB 里面是使用聚合运算 Aggregate。 还有其他的常用步骤运算符$Unwind 可以展开数组, 例如在数组中有三个元素,到了$Unwind 后就会展开 成三个文档,形成三条记录。$GraphLookUp 可以进 行图搜索,$Facet 和$Bucket,进行分面搜索。 (五)为什么只有左外连接? ·原因一:反范式设计; 聚合框架管道和步骤的示意,一个原始的数据集,有 很多的数据,中间有对应的结果,1 和 2 是每一个阶 段执行的结果,最后处理作为最终的一个结果集返回 中 间 结 果 1 原始 数据集 处理步骤1 管道 中 间 结 果 2 处理步骤2 结果集 处理步骤3 聚合框架 给应用程序。 每一次的处理过程对应的一个处理的步骤,这些步骤 按照顺序整合起来,就是聚合框架的管道,从原始的 数据集经过每一个步骤的处理,整个处理的方式和流 程,就是 MongoDB 聚合框架。 pipeline = [$stage1, $stage2, ...$stageN]; db.<COLLECTION>.aggregate( pipeline, { options } ); SELECT FIRST_NAME AS '名' LAST_NAME AS '姓' FROM Users WHERE GENDER = '男' SKIP 100 LIMIT 20 db.users.aggregate([ {$match: {gender: '女'}}, {group: { _id: 'DEPARTMENT' emp_qty: {$sum: 1} }, {$match: {emp_qty: {It: 10}}} ]}; Type Number Alias $match 过滤 WHERE $project 投影 AS $sort 排序 ORDER BY $group 分组 GROUP BY $skip/$limit 结果限制 SKIP/LIMIT $lookup 左外连接 LEFT OUTER JOIN 云算符 作用 SQL等价运算符 $unwind 展开数组 N/A $graphLookup 图搜索 N/A $facet/$bucket 分面搜索 N/A 右表 左表 左表 右表 单机JOIN 分 片 2 分 片 1 分 片 3
📄 Page 8
13 14玩转 MongoDB 从入门到实战 [0,10) 120条 [10,20) 20条 Other 10条 [1890, 1901) 80条 [1901, 1920) 30条 Unknown 40条 价格 数据 年份 db.products.aggregate( [ { $facet: { price: { $bucket: {...} }, year: { $bucket: {...} } } } ]) } db.students.aggregate([{$unwind: 'score'}]) {name: '张三', score: {subject: '语文', score: 84}} {name: '张三', score: {subject: '数学', score: 90}} {name: '张三', score: {subject: '外语', score: 69}} db.students.aggregate([ {$unwind: '$score'}, {$sort: {name:1, "score.score": -1}} ]} {name: '张三', score: {subject: '数学', score: 90}} {name: '张三', score: {subject: '语文', score: 84}} {name: '张三', score: {subject: '外语', score: 69}} $Unwind 的操作符,这里有示例一条数据,Name 等 于张三,张三的成绩放在 Score 这个数组里面,分别 对应有三个元素,第一个是语文成绩,第二个是数学 成绩,第三个是外语成绩。 如果只使用$Unwind 的操作符,就会把这一个数据一 条文档展开成三个文档,分别是语文、数学和外语的 成绩,进一步在$Unwind 的下面加一个$Sort 的操作 符,按照姓名 Name 进行排序,在 Name 相同的情况 下,按照 Score 进行降序。可以看到返回的第一条结 果就是张三并且是他科目当中成绩最高的,数学的得 分在三科目当中最高,所以数学的成绩会排在最前面。 (二)分面搜索 分面搜索(faceted search)用于对结果文档进行分 类以缩小搜索结果。例如: 还有常用的像分面搜索,例如逛一些论坛,或者是博 客网站的时候,经常会看到帖子或者是文章,会按照 不同的地区,不同的内容板块,不同的类别,不同的 标签来进行统计。 每一个内容板块对应到 MongoDB 里,可以把它认为 一个是 Bucket。 例一:有很多的商品,我们希望按照商品的价格来 进行分类统计,想按照价格在 0~10 之间,10~20 之间,还有 20~30,30~40 以及大于 40 的,希 望能够统计每一个区间里面商品的个数,对应可以用 Aggregation,然后使用$Bucket 这样的阶段操作 符。 $Bucket 主要有这几个参数,第一个是 GroupBy,也就是要进行分组,使用$Price,基于价格字段进行分组。 第二个参数是 Boundaries,在进行分组的时候,每个分割的区间有 00:00、2:00、3:00、4:00 这几个分区的点,其他的 放在 Default,它的值是 Other. 针对每一个分组的操作,要执行$Sum,求和的操作,返回的结果放在 Count 里面。 例二:商品按照不同的价格区间来进行分组的同时还希望商品按照年份来进行分组。 在 MongoDB 里面,可以通过$Facet 查询操作来完成这样的统计,可先按照 Price 也就是价格进行一个$Bucket 的操作,此外除了 Price 之外,还按照年份进行另外一个的运算。如果有其他的一个统计的需要,可以继续加在后面, 通过一个$Facet 的操作,就可以把商品按照不同的维度进行分类统计。 (三)图搜素 可以使用$GraphLookUp 操作符来进行图搜索。 [0,10) 120条 [10,20) 20条 Other 10条 [1890, 1901) 80条 [1901, 1920) 30条 Unknown 40条 价格 数据 年份 db.products.aggregate( [ { $facet: { price: { $bucket: {...} }, year: { $bucket: {...} } } } ]) 三、进阶使用 (一)$unwind 查找个人最好成绩科目; { name: '张三', score: [{subject: '语文', score: 84}, {subject: '数学', score: 90}, {subject: '外语', score: 69}] 右表 Top: World: Chinese Simplified (30,507) 相关类别: 地区(13,604) 地区(610) 桶(bucket)体育(270) 健康(798) 儿童与青少年@(259) 参考(2,316) 商业(5,940) 家庭(125) Regional: Asia China(1,830) World: Chinese Traditional(10,498) World: Taiwanese(54) 新闻(464) 游戏(566) 社会(1,647) 科学(983) 艺术(1,028) 计算机(1,727) 购物(429) [0,10) 120条 [10,20) 20条 [20,30) 300条 [30,40) 500条 [40,) 10条 价格 db.products.aggregate( [ { $bucket: { groupBy: "$price", boundaries: [0, 10, 20, 30, 40], default: "Other" output: { count: { $sum: 1} } } } ]) $graphLookup(from表暂不支持分片) A认识a Double Double Double Double Double Double Double Double A与a互相熟识 游泳 围棋 音乐 电脑
📄 Page 9
15 16玩转 MongoDB 从入门到实战 对于敏感数据,把数据的敏感字段放到视图之外,查 询的结果里面就不会包含视图之外的字段,视图还 是只读的。结合上 MongoDB 里基于角色的权限授 权,可以按角色去控制数据的访问,保护这些敏感的 数据,让没有获得授权的人通过访问视图不能够访问 到敏感的信息。 创建视图的命令对应的是 CreateView,其中包含 的参数视图的名称和对应的聚合表达式,这里使用的 是$Project,排除_ID,保留 First_Name、Last_ Name、Gender、Email、Phone。 (三)删除 删 除 视 图 的 话 和 删 除 一 个 集 合 是 类 似 的 , 我 们 用“DB.+视图的名称+.Dorp()”就可以删除视图。 //delete view db.contact_info.drop() 复制集使用及原理介绍 一、MongoDB 副本集概念及创建 作者 | 夏德军(夏周) (一)MongoDB 副本集的概念 官方概念:副本集是一组 MongoDB 的进程去维持 同样的一份数据集,通过 MongoDB 的复制协议保 证主备之间的数据一致性。 如上图所示,MongoDB 有两种部署方式,一个是 Standalone 部署模式,另外一个是副本级,有不同 角色的节点,像 Primary 节点和 Secondary 节点。 生产环境不建议部署 Standalone 模式。 (二)使用 MongoDB 副本集的原因 关键词:可用性、拓展性、维护性 ·可用性 (三)MongoDB 副本集成员角色 副本集里面有多个节点,每个节点拥有不同的职责。 在看成员角色之前,先了解两个重要属性: 属性一:Priority = 0 当 Priority 等于 0 时,它不可以被副本集选举为 主,Priority 的值越高,则被选举为主 的概率更大。 属性二:Vote = 0 不可以参与选举投票,此时该节点的 Priority 也必须 额外的副本结合高可用机制提升 MongoDB 实例可用 性。 ·扩展性 通过 Secondary 节点配合 Driver 扩展 MongoDB 实 例读能力。 ·维护性 通过滚动的方式 MongoDB 实例进行维护,尽量减少业 务所受到的影响,比如版本升级与可能会影响用户流量 的 Compact 操作。Write Reads ClientApplication Driver MongoD Not Recommend Recommend Write Reads ClientApplication Driver Primary Secondary Secondary Replication Replication 1. 主节点故障 高可用机制自动 选举新主 3. 滚动方式进行实例维护, 比如升级版本 2. 客户端读取 Secondary 节点数据扩展 读能力 Write Reads ClientApplication Driver Primary Secondary Secondary ReplicationReads Replication # db.createView(<name>, <source>, <pipeline>,<collation>) db.createView("contact_info", patients",[ {$project:{ _id: 0, first_name: 1, Iast_name: 1, gender: 1, email: 1, phone: 1 } } ]) # views are shown along with other collections show collections # views metadata is stored in the system. views collection db.system.views.find() 四、视图 (一)视图的概念: ·基于一个或多个其他集合创建 ·预定义聚合查询 ·类似于 SQL 中的视图 作用: ·数据抽象 ·保护敏感数据的一种方法 ·将敏感数据投影到视图之外 ·只读 ·结合基于角色的授权,可按角色访问信息 (二)创建视图
📄 Page 10
17 18玩转 MongoDB 从入门到实战 为 0,即它也不能被选举为主。 "members" : [ { "_id" : 0 "host" : "localhost: 27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "localhost : 27018", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, 成员角色: ·Primary:主节点,可以接受读写,整个副本集某 个时刻只有一个。 ·Secondary:只读节点,分为以下三个不同类型: ·Hidden = False:正常的只读节点,是否可选 为主,是否可投票,取决于 Priority,Votes 的值; ·Hidden = True:隐藏节点,对客户端不可见, 可以参与选举,但是 Priority 必须为 0,即不能被提 升为主; ·Delayed Secondary:延迟只读节点,会延迟 一定的时间(SlaveDelay 配置决定)从上游复制增 量,常用于快速回滚场景。 ·Arbiter:仲裁节点,只用于参与选举投票,本身不承 载任何数据,只作为投票角色。 (四)如何获取 MongoDB 副本集 创建云上实例 ·MongoDB 的运维门槛较高,需要对相关原理有比较深刻的理解,才能运维好 MongoDB 副本集实例; ·推荐更便捷、可靠的方式:使用 MongoDB 云服务,一站式的解决方案。阿里云 MongoDB 具有社区版不具备的 高级功能,如审计日志、按时间点恢复等,详情如下: ·阿里云 MongoDB :https:// www.aliyun.com/ product/mongodb 基于工具搭建测试实例 目的:业务要做线下测试,需要在本地环境搭建一个 副本级或去探索新版本,比如 4.4 新增的功能。 使用工具 Mtools: ·MongoDB 官方工程师个人作品,功能强大; ·包含:实例部署,日志解析/可视化,数据迁移等功 能; ·Github:ht tps: / /g i thub.com/rueckst iess / mtools 基于 mtools 部署副本集,一条命令: 执行 Mlaunch,Init 然后指定 Binarypath 还有一些 副本集相关参数,Mtools 会一键创建副本集,具体 命令执行演示如下: mlaunch init --binarypath '/usr/local/Cellar/mongodb- community/ 4.4.0/bin' --replicaset -- nodes 3 --name replset44 --dir ~/work/ mtools_data/data ~ mlaunch init --binarypath '/usr/local/Cellar/mongodb- community/4.4.0/bin' > --replicaset --nodes 3--name replset44 --dir ~/work/mtools_data/data2 launching: "/usr/local/Cellar/mongodb-community/4.4.0/bin/ mongod" on port 27017 launching: "/usr/local/Cellar/mongodb-community/4.4.0/bin/ mongod" on port 27018 launching: "/usr/local/Cellar/mongodb-community/4.4.0/bin/ mongod" on port 27019 replica set 'replset44' initialized. ~ mongo --eval "rs.status()" MongoDB shell version v4.2.0 connecting to: mongodb://127.0.0.1:27017/?compressors= disabled&gssapiServiceName=mongodb Implicit session : session { "id" : UUID("5104295a-30d4-4d 02-8836-5a76eb81c93c") } MongDB server version: 4.4.0 WARNING: shell and server versions do not match { "set" : "replset44", "date" : ISODate("2020-12-02T07:43:54.249Z"), "myState" : "replset44", "term" : NumberLong(1), "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "majorityVoteCount" : 2, "writeMajorityCount" : 2, "votingMembersCount" : 3, "writableVotingMembersCount" : 3, 二、MongoDB 副本集使用介绍 (一)MongoDB 副本集连接方式 方式一:直接连接 Primary 节点,正常情况下可读写 MongoDB,但主节点故障切换后,无法正常访问, 如下图所示。 方式二(强烈推荐):通过高可用 Uri 的方式连接 MongoDB,当 Primary 故障切换后,MongoDB Driver 可自动感知并把流量路由到新的 Primary 节点,如下图所示。 参 考 文 档 : h t t p s : / / h e l p . a l i y u n . c o m / document_deta i l /44623.html Primary Swcondary Hidden IP2:Port2 IP1:Port1 connect 连接方式一 IP1:Port1 Primary Swcondary Hidden IP2:Port2 IP1:Port1 connect 连接方式二 "mongo://user:pwd@\ IP1:Port1,IP2:Port2/admin? replicaSet=xxx"
📄 Page 11
19 20玩转 MongoDB 从入门到实战 (二)MongoDB 副本集状态查看 查看副本集整体状态:rs.status() 可查看各成员当前状态,包括是否健康,是否在全量同 步,心跳信息,增量同步信息, 选举信息,上一次的心 跳时间等。 查看当前节点角色:db.isMaster() 除了当前节点角色信息,是一个更精简化的信息,也返 回整个副本集的成员列表,真正 的 Primary 是谁,协议 相关的配置信息等,Driver 在首次连接副本集时会发送 该命令。 查看同步进度/oplog信息:rs.printSlaveReplicat ionInfo()/rs.printReplicationInf o() 共两个命令,第一个命令返回一个汇总的各 Secondary 同步延迟信息,第二个命令返 回 Oplog 大小、保留时 长、 起始时间等信息。 (三)副本集的基本读写 当用客户端,比如 Mongo Shell,通过 Mongodb Uri 连接副本集实例,来执行例如 Insert,Find,Delete 等 命令,和 Standalone 模式无差异,重点看副本集读写 和 Standalone 比较特异化的地方。 (四)扩展副本集的读能力 —— ReadPreference 如上图所示,左边为一个三节点的副本集,它部署在 两个数据中心,Primary 和其中一个 Secondary 部 署在 Data Center 1,另一个 Secondary 部署在 Data Center 2,当用默认的 ReadPreference 时, 直接读写 Primary 节点。如果在 DataCenter2 也有 业务进程存在时,也需要读取 MongoDB 时,则需要 用 ReadPreference 模式自动识别节点的远近,读取 Data Center 2 的 Secondary。 ReadPreference 共有以下五种模式: 模式一 Primary 默认模式,直接读取主节点,更好的一致性保证。 模式二 PrimaryPreferred 主节点不可用时,选择从从节点读取。 模式三 Secondary 只从从节点读取。 模式四 SecondaryPreferred 尽力从从节点读取,如果找不到可用的从节点,从主节点 读取。 模式五 Nearest 根据客户端对节点的 Ping 值判断节点的远近,选择从最 近的节点读取。 Read Preference(读偏好)决定了读请求会访问什角色 的节点,合理的 ReadPreference 可以极大地扩展副本 集的读性能,降低访问延迟。 (五)控制写操作的持久性级别 —— WriteConcern 上图为 WriteConcern 标准的配置格式,包括三个子参 数: ·w: 决定了写操作返回前需要等待多少个副本集节点的 确认。 · j: 决定写操作产生的日志是否已经落盘。 ·wtimeout:决定了写操作等待的超时时间,避免客户 端一直阻塞。 下面具体演示几个 WriteConcern 配置的值。 如上图,当 W:1 时,写操作在本地执行完成后,直接向 客户端返回成功,无需等待日志(Journal)刷盘。 如上图,当 W:1,J:true 时,区别于 W:1 的主要特点在 于写操作在本地执行完成后,需要等待日志(Journal) 刷盘,会增加额外延迟。 如上图,当 W: "Majority",当下发了这个写操作之 后,除了需要在 Primary 节点 Apply 完成,还需要 复制到其中一个 Secondary 节点去 Apply 完成,才能 向 Driver 反馈写操作成功。 在三节点副本集情况下,"Major i ty "相当于两个节 点,等同于 W:2。 (六)控制读操作的一致性级别 —— ReadConcern ReadConcern 有五个级别如下: ·"Local":读操作直接读取本地最新提交的数据,返 回的数据可能被回滚。 · " A v a i l a b l e " : 含 义 和 " L o c a l " 类 似 , 但 是 用 于 Sharding 场景可能会返回孤儿文档。 ·"Majority":读操作返回已经在多数节点确认应用 完成的数据,返回的数据不会被回滚,但可能会读到 历史数据。 ·"Linearizable":读取最新的数据,且能够保证数 据不会被回滚,是所谓的线性一致性,是最高的一致 性级别。 · "Snapshot":只用于多文档事务中,和"Majority" 语义类似,但额外提供真正的一致性快照语义。 Read with Read Preference (default) Data Center 1 Driver Primary Priority:1 Primary Priority:1 Read with Read Preference (nearest) Data Center 2 Driver Secondary Priority:0 { w: <value>, <boolean>, wtimeout: <number> } w 决定了写操作返回前需要 等待多少个副本集节点的确认 j 决定写操作产生的日志是否已经落盘 wtimeout 决定了写操作等待的超时时间, 避免客户端一直阻塞 Driver W rite R e sp o n se Apply W rite C o n ce rn : { w : 1 } mongod Driver W rite Apply R e sp o n se Write to journal W rite C o n ce rn : { w : 1 } mongod Driver W rite R e p lica te R e p lica te Apply R e sp o n se W rite C o n ce rn : { w : 1 } Primary Apply Secondary Secondary
📄 Page 12
21 22玩转 MongoDB 从入门到实战 上图为一个三节点副本集,每个 Secondary 节点的复 制进度不同,用 Oplog 来表示,比如 Primary 最新节点 写到 5,第一个 Secondary 节点复制到 x=4,第二个 Secondary 节点复制到 x=3,在不同的 ReadConcern 值下,Client 从不同节点读的时候,读到的是不同版本 的数据。 对于 Local 来说,总是读取最新的数据,Available 也是 读取最新数据,但在分片集群场景下两者不太一样。 在 Majority 情况下,只有 x=4 是复制到多数派节点, 也就是其中两个节点。当用 Majority 读的时候,在 Primary 上只能读到 x=4,在第一个 Secondary 上能 读到 x=4,但在第二个 Secondary 只能读到 x=3。 Linearizable 也比较特殊,只能在 Primary 节点上使 用,因此也能读取到 x=5,但大家可能有疑惑,x=5 并没有复制到多数派节点,MongoDB 的解决方法 是,当使用 Linearizable 时,在读到 x=5 之后,会 等 x=5 复制到多数派节点,才会向客户端返回成功。 不同 ReadConcem Level 的 Tradeoff LATEST:能读到多新的数据; FAST:能多快地返回数据; SAFE:读的数据是否会发生回滚; Majority:能够保证 FAST,也就是数据不会被回滚。 Linearizable:能够保证数据不回滚,同时读取最新 数据,但牺牲了延迟。 Local/avai lable:能够最快返回数据,读取最新数 据,但数据可能会回滚。 总结:ReadConcern Level 越高,一致性保证越 好,但访问延迟也更高。 (七)基于副本集的维护性操作举例 —— Rollover Compact 背景:集合频繁的插入和删除会导致“碎片率”上 升,浪费存储空间。 Tips:碎片率计算方式 Https://developer.aliyun. com/article/769536 MongoDB 提供 Compact 命令来回收碎片,但会阻 塞读写,对业务有影响,在副本集模式下,滚动的方 式来进行 Compact 操作,避免影响业务。 举个例子,上图为一个三节点副本集,在最左边的 副本集,用户可以在 Hidden 节点完成 Compact Collection 操作。因为 Hidden 对客户端不可见,因 而对业务没有影响。当 Hidden 节点操作后,可以把 Secondary 节点切换成 Hidden 节点,在新的节点上 做 Compact Collection 操作(如上图中间所示), 最后将 Primary 节点也切换成 Hidden 节点(如上图 右边所示),最终完成 Compact Collection 操作。 切换节点会对业务产生些许影响,但 Driver 能够自动 Handle,避免直接在 Primary 节点完成 Compact Collection 操作,导致业务对 DB 不可访问。 LATEST majority FASTSAFE linearizable local/ available Switch Secondary to Hidden Primary Secondary Hidden Compact collection Primary Hidden Secondary Compact collection Hidden Primary Secondary Compact collection Switch Primary to Hidden 三、MongoDB 副本集原理介绍 (一)什么是 MongoDB Oplog ·MongoDB Oplog 是 Local 库下的一个集合,用 来保存写操作所产生的增量日志(类似于 MySQL 中 的 Binlog); ·它是一个 Capped Collection,即超出配置的最大 值后,会自动删除最老的历史数据,MongoDB 针对 Oplog 的删除有特殊优化,以提升删除效率; ·主节点产生新的 Oplog Entry,从节点通过复制 Oplog 并应用来保持和主节点的状态一致; ·Oplog 中包含的有:O —— 插入或更新的内容;Op —— 操作类型;Ns —— 操作执行的 DB 和集合;Ts —— 操作发生的时间等。 { // The oplog entry timestamp. "ts": Timestamp(1518036537, 2), // The term of this entry. "t": NumberLong("i"), // The operation type. "op": "i", // The collection name. "ns": "test. collection identifier". // A unique collection identifier. "ui": UUID("c22f2fe6")), // The document to insert. "o": { "_id", Objectld( "5a7b6639176928f52231db8d"), "x", 1 } } MongoDB Oplog Entry 样例 secondary oplog getMore primary data {t: 2, ts: 8, op:"i"} {t: 2, ts: 7, op:"u"} {t: 1, ts: 8, op:"d"} {t: 1, ts: 2, op:"u"} ... oplog secondary data {t: 2, ts: 7, op:"u"} {t: 1, ts: 5, op:"d"} {t: 1, ts: 2, op:"u"} {t: 1, ts: 1, op:"i"} ... {t: 2, ts: 8, op:"i"} PrimaryNode oplog Driver local: x=5 available: x=5 majonrity: x=4 snapshot: (only in transaction) linearizable: x=5 (need wait) lmage source: ©zhangyoudong https://mongoing.com/zyd X=1 X=2 X=3 X=4 X=5 Client Secondray local: x=4 available: x=4 majonrity: x=4 snapshot: (only in transaction) X=1 X=2 X=3 X=4 Client Secondary local: x=3 available: x=3 majonrity: x=3 snapshot: (only in transaction) X=1 X=2 X=3 Client
📄 Page 13
23 24玩转 MongoDB 从入门到实战 4.4 增强 —— 全量同步断点续传 具体全量同步流程图如下: ·4.4 之前:全量同步期间,如果发生网络异常,导致 同步中断,需要重头开始,网络环境比较差时,大数 据量很难完成全量同步,可用节点数变少,实例可用 性存在隐患; ·4.4:基于「Resume Token」机制,记录全量 拉取的位点,网络异常导致同步中断后,重连时带上 Resume Token; ·Replication.initialSyncTransientErrorRetryPeri odSeconds 参数决定了同步中断 后重试的超时时间,默认 24h。 原理二:增量同步 ·发生时机:全量同步结束后,持续同步,保持和主 节点数据一致,可以从不同工作职责的角度分析增量 同步的具体流程; ·Oplog Fetcher 线程负责拉取 Oplog Find 命令创 建 Tailable Cursor,GetMore 命令批量从同步源拉 取 Oplog,单个 Batch 最大 16MB; ·拉取的 Oplog Batch 放到内存中的 Blocking Queue 中; ·ReplBatcher 线程负责从 Blocking Queue 中取出 Batch 生成新的可 Apply 的 Batch 放到 Deque 中,这里主要是因为需要控制并发,有些操作需要放 到一个单独的 Batch; ·OplogApplier 线程负责从 Deque 中取出 Batch 写 Oplog, 然后把 Batch 拆分,分发到 Worker 线 程进行并发 Apply; ·为了保持一致性,中间需要保存多个不同的 Oplog 应用位点信息。 (四)MongoDB 副本集高可用原理 ·主备切换时机: ·主节点不可用; ·新增节点(更高的 Priority); ·主动运维,rs.stepDown() or rs.reconfig() ·MongoDB 基于 Raft 协议实现了自己的高可用机 制; ·副本集之间保持心跳(默认 2 秒探测一次); ·如果超出 ElectionTimeoutMill is(默认 10 秒) 没有探测到主节点,Secondary 节点会发起选举, 发起前检查自身条件: ·Priority 是否大于 0 ·当前状态是否够新 ·在真正选举前,会先进行一轮空投(Dry-Run),避 免当前 Primary 无意义的降级(StepDown),因为 Primary 收到其他节点且 Term 更高的话则会降级; ·Dry-Run 成功后,会增加自身的 Term 发起真正 的选举,如果收到多数派的选票则选举成功,把新的 拓扑信息通过心跳广播到整个副本。 upstream return query result and resume token {find: {}, $_requestResumeToken: true } downstream upstream return query result from last break point and resume token {find: {}, $_resumeAfter: resumeToken } downstream 四、总结 (一)什么是 MongoDB Oplog ·副本集是可用性、扩展性、维护性的有效保证,中 等业务规模,生产环境建议优先选择部署该形态,如 果是大型业务规模,建议使用 Sharing 形态; ·副本集使用务必使用高可用连接串的方式,避免业 务访问单点; ·默认情况下的 Read/Write Concern 可以满足绝 大部分业务需求,特殊情况需要在一致性和性能之间 做出取舍; ·Oplog 是 MongoDB 的重要基础设施,除了用 于同步(全量 + 增量),还可用于构建数据生态 (ChangeStream); ·MongoDB 以 Raft 协议为指导实现了自己的高可 用机制,大部分情况下,主节点故障 15 秒内可选出 新主。 (二)Oplog 保留策略 —— 根据大小及根据时间范围 ·4.4 之前 ·根据 Replication.OplogSizeMB 的配置值决定 Oplog 集合的大小上限,默认为磁盘空间的 5%,如 果用户是单机多实例的部署形态,需要调整默认值; ·当 Oplog 集合大小超过上限时,会自动删除最 老的 Oplog Entry。 ·4.4 删除策略增强:MongoDB 提供了按时间段来保 留 Oplog,由参数 Storage. OplogMinRetentionHours 控制,方便用户更好地完成定期维护的操作。 ·删除时,即使 Oplog 集合大小超过了配置的最大值, 但最老的 Oplog 仍然在 Storage.OplogMinRetenti- onHours 范围内,那么 Oplog 不会删。 MongoDB 目前提供在线修改 OplogMinRetention- Hours 配置值的方式,用户无需重启实例,如下图所示。 (三)MongoDB 副本集同步原理 MongoDB 副本集同步原理分为两部分:全量同步与 增量同步。 原理一:全量同步 ·发生时机: ·新节点刚加入副本集时 ·老的节点因为同步滞后而进入 Recovering 状态时 ·全量同步包含两个阶段: ·数据克隆阶段:记录开始时间 T1,从源端拉取所有 的集合数据,不保证数据和源的一致性,记录结束时间 T2; ·增量应用阶段:应用从 T1 - T2 期间的 Oplog,达 到一致性状态,全量同步结束。 在线修改 opMinRetentionHours 1 // First, show current configured value\ 2 db.getSiblingDB("admin"). serverStatus(). oplogTruncation. oplogMinRetentionHours 3 4 // Modify 5 db.adminCommand({ 6 "replSetResizeOplog": 1, 7 "minRetentionHours": 2 7 }) tailablecursor ...default 16 threads ... sync source oplog sync source data initial sync oplogApplication writeOpsToOplog replWriter(applyOps) replWriter(applyOps) capped collection producer BlockQueue replBatcher std:deque Heartbeat Election for New Primary Secondary Primary Secondary Replication Election for New Primary Raft: server 状态转换 discovers current leader or new term discovers server with higher term starts up times out, starts election Heartbeat receives votes from majority of servers times out, new election Primary Follower Candidate Leader Secondary 数 据 克 隆 阶 段 失败,1s 后重新 选择同步源 初始化副本 集配置 进入 STARTUP2 状态 删除除 local 库 以外的所有 DB 选择同步源 依次克隆 DB 下所有集合 增量应用 阶段 从 oplog 临时集合 读取 oplog 检查同步源是否 发生了回滚 进入 Secondary 状态 (一致性状态) 克隆阶段结束,从同步源 获取 stopTimestamp 如果 发生 需要 重新 全量 同步 从同步源获取 beginFetchingTimestamp/ beginApplyingTimestamp 获取所有 DB 列表 Another bg thread 初始化 CollectionCloner, 克隆集合和索引 初始化 DatabaseCloner, 获取所有集合列表 从初始化 OplogFetcher,从 beginFetching- Timestamp 持续拉取 oplog 到临时集合 依次 克隆 所有 DB 从 beginApplying- Timestamp 开始批量并发应用 oplog, 到 stopTimestamp 为止
📄 Page 14
25 26玩转 MongoDB 从入门到实战 分片集群使用及原理介绍 一、分片集群的基本架构 作者 | 煮茶 为什么要使用分片集群? 副本集遇到的问题: 副本集(ReplicaSet) 帮助我们解决读请求扩展、 高可用等问题。随着业务场景进一步增长,可能会出 现以下问题: ·存储容量超出单机磁盘容量 ·活跃数据集超出单机内存容量:很多读请求需要从 磁盘读取 ·写入量超出单机 IOPS 上限 垂直扩容(Scale Up) VS 水平扩容(Scale Out): ·垂直扩容 : 用更好的服务器,提高 CPU 处理核 数、内存数、带宽等 ·水平扩容 : 将任务分配到多台计算机上 什么是 MongoDB 分片集群: ·MongoDB 分片集群(Sharded Cluster)是对数 据进行水平扩展的一种方式。 ·MongoDB 使用 分片集群 来支持大数据集和高吞 吐量的业务场景。 分片集群的基本架构 ·Mongos ·分片集群的访问入口 ·对请求进行路由、分发、合并 ·部署多个 Mongos 来保证高可用 ·ConfigServer ·存储元信息和集群配置 ·部署为副本集来保证高可用 ·Shard ·存储用户数据,不同 Shard 保存不同用户数据 ·部署为副本集来保证高可用 如何链接分片集群 有了一个分片集群以后,Drivers 需要通过连接 Mongos 来达到和整个集群交互的目的,而 Mongos 则会根 据客户端的请求来向后端不同的 Shard 进行请求的发 起。比如对集合一进行读写,Mongos 会和 Shard A 和 Shard B 进行请求交互,如果读写集合二,那么 Mongos 只会和 Shard A 进行数据交互。 如下图所示:在阿里云 Mongos 上申请的一个分片集群,列举了每个 Mongos 的链接地址,并且拼接好了 ConnectionStringURI,如果使用单个 Mongos 进行链接,可能会有单点的风险,所以推荐使用 ConnectionStringURI 来进行访问。 ConnectionStringURI 各个组成部分: ·mongodb://:前缀,代表这是一个 Connection String URI 连接地址。 ·username:password@:连接 MongoDB 实例 的用户名和密码,使用英文冒号(:)分隔。 ·hostX:portX:实例的连接地址和端口号。 ·/database:鉴权数据库名,即数据库账号所属的 数据库。 ·?options:指定额外的连接选项。 举个例子: 用户名为 user,密码为 password,然后来连接 mongos1 和 mongos2,它们的端口都是 3717,鉴权 数据库是 admin,这样的一个 connectionStringURI。 database 的主分片(primary shard) Primary Shard 的定义: 默认情况下,每个 database 的集合都是未分片的,存 储在一个固定的 shard 上,称为 primary shard。 primary shard 的选择: 当创建一个新的 database 时,系统会根据各个 shard 目前存储的数据量,选择一个数据量最小的 shard 作为 新 database 的 primary shard。 如何将集合进行分片 mongoDB 将数据进行分片支持集合级别,已经被分 片的集合被切分成多份保存在 shard 上。 App Server Router (mongos) App Server Router (mongos) Shard (replica set) Shard (replica set) I or more Routers 2 or more Shards Config Servers (mongos) Router (mongos) Shard A Collection 1 Collection 2 Applications/ Drivers Reads/Writes Reads/Writes Shard B Shard A Database 1 Database 3 Collection 1-1 Collection 3-1 Collection 1-2 Shard B Database 2 Collection 2-1 Collection 3-2 Collection 2-2 Cost Capability Scale Up Cost Capability Scale Out mongodb://[username:password@]host1[:port1] [,host2[:port2],...[,hostN[:portN]]][/[database] [?options]] sh.enableSharding("<database>") ·<database> // eg: "record" Example : sh.enableSharding("records") sh.shardCollection("<database>.<collection>", { <key> : <direction>, ... } ) ·<key> : 分片键字段的名字 ·<direction> : {1 | -1 |"hashed"} 。1 | -1 : 基于 范围分片键,"hashed" : 哈希分片键 m o n g o d b : / / u s e r : p a s s w o r d @ mongos1:3717,mongos2:3717/admin
📄 Page 15
27 28玩转 MongoDB 从入门到实战 Shard A Collection 1 Collection 2 Shard B 二、Shard Key(分片键) 范围分片 VS 哈希分片 ·范围分片:根据 ShardKey 的值进行数据分片。 优点:很好的满足范围查询的需求; 缺点:分片键单调写入,无法扩充写能力; 范围分片持多个字段的范围分片:{x : 1} {x : 1 , y : 1} 如上图所示: x:25 x:26 x:27,经过哈希计算后数据 被打散不同的 Chunk 上,基于哈希分片可以单调, 对于分片键单调写入的场景,可以充分的扩展写能 力,但是却不能高效的进行范围查询。 哈希分片仅支持单个字段的哈希分片: 4.4 以后的版本,可以将单个字段的哈希分片和一个到 多个的范围分片键字段来进行组 合,比如下指定 x:1,y 是哈希的方式。的范围分片键字段来进行组合,比如 上面展示 {x : 1 , y : "hashed"} 的方式。 如何选择合理的分片键 ·Cardinality(基数):越大越好 ·以性别作为分片键 :数据最多被拆分为 2 份 ·以月份作为分片键 :数据最多被拆分为 12 份 如上图所示:是一个基于 x 的范围分片,数据被分为 了 4 部分,切割点分别是 x:-75 ; x:25 ; x:175 值相近 的数据是相邻的,这种情况下,可以很好的满足范围 查询的需求。但是如果是基于分片键的单调写入,由 于数据都会由于所有的写入都会被最后一个 Chunk 来 承载,所以这样就无法很好的扩充写能力。 ·哈希分片:根据 ShardKey 计算 哈希值,基于哈 希值进行数据分片。 优点:分片单调写入,充分的扩展写能力; 缺点:不能高效的进行范围查询。 ·Frequency(频率,文档中出现某个值的频率): 越低越好记录全国人口的集合,以当前所在城市作为 分片键:大多数数据集中在一线城市所在的 Chunk。 ·Monotonically Changing(单调变化):使用哈希 分片记录日志集合,使用日志生成时间作为分片键: 如果使用范围分片,数据写入只会在最后一个 Shard 上完成。 RefineCollectionShardKey 4.4 版本新增命令,通过分片键增加后缀字段的方式 来修改分片键: 这个例子中: ·<existing key specification> : 当前的分片键, 即新的分片键必须以当前分片键为前缀; ·<suffix1> : 新增的分片键字段; ·<1|"hashed"> : <1> -- 范围分片键 ;<"hashed"> -- 哈希分片键。 RefineCollectionShardKey 的使用说明: ·新的 ShardKey 对应的索引在 RefineCollection- ShardKey 执行前须已经创建完成; ·RefineCollectionShardKey 只会修改 Config 节 点上的元数据,不会有任何数据迁移,数据的打散随 后续正常分裂&迁移而完成; ·4.4 版本中支持了 ShardKey 缺失的情况(当做 Nul l 处理),为了应对并不是所有文档都存在新的 ShardKey 的所有字段; ·4.4 版本中支持复合哈希分片键,而在之前的版本 中只能支持单字段的哈希分片键。 特定目标的操作(Targeted Operations)vs 广 播的操作(Broadcast Operations) Mongos 是如何基于请求当中的分片键信息来做请求 转发,有两种转发行为,一种叫做特定目标的操作, 一种叫做广播操作。 ·特定目标的操作(Targeted Operations):根据 分片键计算出目标 Shard(s),发起请求并返回结果。 ·包含分片键的查询操作、更新、删除操作、插入操作。 分片键(ShardKey)的约束 ShardKey 必须是一个索引。非空集合须在 ShardCollection 前创建索引;空集合 ShardCollection 自动创建索引 4.4 版本之前: ·ShardKey 大小不能超过 512 Bytes; ·仅支持单字段的哈希分片键; ·Document 中必须包含 ShardKey; ·ShardKey 包含的 Field 不可以修改。 4.4 版本之后: ·ShardKey 大小无限制; ·支持复合哈希分片键; ·Document 中可以不包含 ShardKey,插入时被当 做 Null 处理; ·为 ShardKey 添加后缀 refineCollectionShardKey 命令,可以修改 ShardKey 包含的 Field; 而在 4.2 版本之前,ShardKey 对应的值不可以修 改;4.2 版本之后,如果 ShardKey 为非_ID 字段, 那么可以修改 ShardKey 对应的值。 {x: minKey} Chunk 1 {x: -75} {x: 175}{x: 25} {x: maxKey} Key Space for Chunk 2 Chunk 3 Chunk 4 {x:"hashed"} Chunk 1 Hash Function Chunk 2 Chunk 3 Chunk 4 {x: 25} {x: 26} {x: 27} Shard A minKey ≤ x < 1 Shard B 1 ≤ x < 2 Shard C 2 ≤ x < maxKey { x : 1 } { x : 2 } { x : 3 } Chunk A minKey ≤ x < 10 Chunk B 10 ≤ x < 20 Chunk C 20 ≤ x < maxKey { x : 25 } { x : 26 } { x : 27 } Shard A minKey ≤ x < 10 Shard B 10 ≤ x < 20 Shard C 20 ≤ x < maxKey { x : 12 } { x : 12 }{ x : 12 } { x : 23 } db.adminCommand( { refineCollectionShardKey: "<database>.<collection>", key: { <existing key specification>, <suffix1>: <1|"hashed">, ... } } ) 举个例子: “Example : sh.shardCollection("records.people", { zipcode: 1 } )”对 records.people 集合进行分片, 这是一个基于 zipcode 的范围分片。 { x : "hashed" } {x : 1 , y : "hashed"} // 4.4 new
📄 Page 16
29 30玩转 MongoDB 从入门到实战 如上图所示:以 a 为 Shard Key 如果请求当中带 了 a 字段,那么 Mongos 就可以识别出来它的目标 Shard,如果是 Shard B,就可以直接跟 Shard B 进行交互,获取结果并返回给客户端。 ·广播的操作(Broadcast Operations):将请求 发送给所有 Shard,合并查询结果并返回给客户端。 ·不包含分片键的查询操作、_ID 字段的更新、删 除操作。 如图所示: Read { a: "zl"} Results Driver mongos Shard A Shard B Shard C a: "zl" Collection (shard key a) Read Results Driver mongos Shard A Shard B Shard C Collection l (shard key x) 三、Chunk & Balancer 什么是 Chunk? ·MongoDB 基于 ShardKey 将 Collection 拆分成 多个 数据子集,每个子集称为一个 Chunk; ·shardedCollection 的数据按照 ShardKey 划分为 MinKey ~ MaxKey 区间; ·每个 Chunk 有自己负责的一个区间(前闭后开); ·存储 ShardedCollection 的 Shard 上有该 Collection 的一个或多个 Chunk ; Chunk 分裂管理 Chunk 分裂管理包括:手动进行 Chunk 分裂与调整 ChunkSize。 ·手动进行Chunk 分裂 场景举例:业务需要向集合中插入量的数据,而这些 数据只分布在较少的 Chunk 中。 直接插入无法利用多 Shard 并发写入,并且插入后 触发 Chunk 分裂,进而触发 Chunk 迁移,产生很 多无效 IO。 sh.splitAt(namespace, query) : 指定 Chunk 分 裂点, 例如:x: [0, 100) , sh.splitAt(ns, {x: 70}) 分裂后 x: [0, 70) , [70, 100) sh.splitFind(namespace, query) : 从中间分裂目 标 Chunk, 例如:x: [0, 100) , sh.splitFind(ns, {x: 70})分裂后 x: [0, 50) , [50, 100) ·调整 ChunkSize 例如: use config; db.settings.save( { _id:"chunksize", value: <sizeInMB> } ); 这里调整 ChunkSize 的方式,是对 conf ig 库的 settings 集合,增加一条文档,这个文档的 ID 是 ChunkSize。 说明: ·只有在插入和更新操作才会触发对应 Chunk 分裂 -- 调ChunkSize会立即触发所有 Chunk 分裂为新 的大小; ·ChunkSize 取值范围 : 1 ~ 1024 MB; ·调小 ChunkSize 可以让 Chunk 更均衡的分布, 但是 Chunk 迁移次数会增加; ·调大 ChunkSize 会减少 Chunk 迁移,但会导致 Chunk 分布不均。 Chunk 迁移(Chunk Migration) ·Chunk 迁移的定义: 为了保证数据负载均衡,MongoDB 支持 Chunk 在 Shard 间迁移,称为 Chunk Migration。 ·Chunk 迁移的方式: ·自动触发:当 Chunk 在 Shard 之间分布不均 时,Balancer 进程会自动触发 Chunk 迁移; ·手动触发:sh.moveChunk(namespace, query, destination) ·Example : sh.moveChunk("records.people", { zipcode: "53187" }, "shard0019")。 ·Chunk 迁移的影响: ·影响 Shard 使用磁盘的大小; ·增加 网络带宽 及 系统负载,这些会对系统性能 造成影响。 ·Chunk 迁移的约束: ·每个 Shard 同一时间只能有一个 Chunk 在进 行迁移; ·不会迁移 Chunk 中文档数量是平均 Chunk 文档 数 1.3 倍的 Chunk // 4.4 提供选项支持。 如上图所示:分片的集合是基于 x 的范围分片,数据 被分成了 4 个 Chunk, Chunk 1 : [minKey, -75) ; Chunk2 : [-75, 25) ; Chunk3 : [25, 175) ; Chunk4 : [175, maxKey)是个前闭后开的区间。ShardA 是持 有 Chunk1 和 Chunk2,而 ShardB 和 ShardC 则 分别持有 Chunk3 和 Chunk4。 Chunk 分裂(Chunk Splits) ·Chunk 分裂的定义 伴随着数据的写入,当 Chunk 增长到指定大小( 默认为 64MB)时,MongoDB 会对 Chunk 进行分 裂,称为 Chunk Split。 ·Chunk 分裂的方式 手动触发: ·sh.splitAt(namespace, query) ·sh.splitFind(namespace, query) 自 动 触 发 : 只 有 插 入 和 更 新 操 作 才 会 触 发 自 动 Chunk Split。当 Chunk Size 被调小时,不会立即 发生Chunk Split。 ·JumboChunk 一个最小的 Chunk 可以只包含一个唯一的 ShardKey,这 样的 Chunk 不可以再进行分裂,称为 JumboChunk。 {x: minKey} Chunk 1 ShardA {x: -75} {x: 175}{x: 25} {x: maxKey} Key Space for Chunk 2 Chunk 3 Chunk 4 shardKey is {x: 1} shardedCollection ShardB ShardC {x:...} {x:...} {x:...} {x:...} 1. insert / update Chunk Split shardKey is {x: 1} shardedCollection ShardA ShardA Split Split Chunk Chunk 64MB 64.2 MB 32.1 MB 32.1 MB ShardB Chunk ShardC Chunk Shard A Shard B Shard C Migrate
📄 Page 17
31 32玩转 MongoDB 从入门到实战 Balancer ·Balancer 是 MongoDB 的一个后台进程,用保证 集合的 Chunk 在各个 Shard 上是均衡的。 ·Balancer 运行在 ConfigServer 的 Primary 节 点。 默认为 开启状态。 ·当分片集群中发生 Chunk 不均衡的情况时,Balancer 将触发 Chunk 从 Chunk 数量最多的 Shard 向 Chunk 数量最少的 Shard 上迁移。 JumboChunk JumboChunk 的定义:一个最小的 Chunk 可以只 包含一个唯一的 ShardKey,这样的 Chunk 不可以 再进行分裂。 JumboChunk 的产生:ShardKey 选择不合理才 会产生 JumboChunk。如果这些 JumboChunk 是高频访问的,就会引起单 Shard 性能瓶颈。另外 Chunk 无法迁移,如果再进行迁移,会引起 Shard 间数据不均。 随着 MongoDB 版本迭代,这些问题也在逐步的被 解决,比如 4.4 版本当中为我们提供了 Ref ine- CollectionShardKey 的命令,重新设置 ShardKey 同时 4.4 当中也给 Balancer 提供了一些设置,给 MoveChunk 提供了一些 Option,来支持 Chunk 迁移。 在 4.2 和 4.0 的较新的小版本当中,也提供了命令来 清理集群中的 JumboChunk 标识。 如图所示:Chunk 的数量小于 20,迁移阈值是 2, 随着 Chunk 数量增大,迁移阈值 分别增长为 4 和 8。 AutoSplit & Balancer 管理命令 ·开启 Chunk自动分裂:sh.enableAutoSplit()。 ·关闭 Chunk自动分裂:sh.disableAutoSplit()。 ·查看 Balancer 是否开启:sh.getBalancerState()。 ·查看 Balancer 是否正在运行:sh.isBalancer- Running()。 ·开启 Balancer:sh.startBalancer() / sh.set- BalancerState(true) ·4.2 版本开始,会同时开启 AutoSplit。 ·关闭 Balancer:sh.stopBalancer() / / sh.set- BalancerState(false) ·4.2 版本开始,会同时关闭 AutoSplit。 ·开启某个集合自动迁移:sh.enableBalancing (namespace)。 ·关闭某个集合自动迁移:sh.disableBalancing (namespace)。 ·修改 Balancer Window: Chunk数量 移动阈值 小于 20 2 20-79 4 大于等于80 8 四、集群管理 命令回顾 ·Balancer ·sh.setBalancerState(state) ·true : sh.startBalancer() ·false : sh.stopBalancer() ·sh.getBalancerState() ·sh.isBalancerRunning() ·sh.disableBalancing(namespace) ·sh.enableBalancing(namespace) ·Chunk ·sh.disableAutoSplit() ·sh.enableAutoSplit() ·sh.moveChunk( … ) ·sh.splitAt( … ) ·sh.splitFind( … ) ·Sharding ·sh.shardCollection() ·sh.enableSharding() 集群状态查看 - sh.status() 如图所示: Sharding Version:分片集群的版本信息。 Shards:分片集群中目前有 2 个 Shard,每个 Shard 的名称、链接信息以及当前状态。 Most Recently Active Mongoses:目前分片集 群中有 2 个 4.2.1 版本的 Mongos。 Autosplit&balancer ·当前开启 Auto-Split ·Balancer 状态为开启 ·Balancer 目前没有正在运行 ·过去一段时间 Balancer 执行的成功、失败信息。 Records 库 ·Primary Shard : xxxx746b04 ·开启 Sharding(EnableSharding) ·相关版本信息。 Records.people 集合 ·Shard Key 为 { "Zipcode" : "Hashed" },无须 Unique 约束 ·Balancer 可以针对该 集合 进行 Balance ·集合有 4 个 Chunk,平均分布在 2 个 Shard 上 ·各 Chunk 所负责的范围以及所属的 Shard。 use config; db.settings.update( { _id: "balancer" }, { $set: { activeWindow : { start : "<start-time>", stop : "<stop-time>" } } }, { upsert: true } ); 。 refineCollection- ShardKey //4.4 clearJumboFlag // 4.2.3+ or 4.015+ ShardKey选择不合理 产生JumboChunk Chunk无法迁移 高频访问 Shard间数据不均 单Shard性能瓶颈 balancer's setting : attemptToBalanceJum- boChunks // 4.4 moveChunk forceJumbo //4.4
📄 Page 18
33 34玩转 MongoDB 从入门到实战 LogicalSession 3.6 版本开始,MongoDB driver 将所有的操作与 LogicalSession 关联 3.4 版本及以前,如图所示: 3.4 版本及以后,如图所示: ·LogicalSession ID { // 唯一标识。可以由客户端生成,也可以由服务端生成 "id" : UUID("32415755-a156-4d1c-9b14- 3c372a15abaf"), // 目前登录用户标识 "uid" : BinData(0,"47DEQpj8HBSa+/TImW+5J CeuQeRkm5NMpJWZG3hSuFU=") } ·自清理机制 ·持久存储: Config.System.Sessions。TTL 索引: 默认 30 分钟 ·默认每 5 分钟一次同步,关闭已被清理的 Session, 同时关闭 Session 上的 Cursor ·使用方式 ·use config; db.system.sessions.aggregate( [ { $listSessions: { allUsers: true } } ] ) ·db.runCommand( { killSessions: [ { id : <UUID> }, ... ] } ) ·startSession / refreshSessions / endSessions ... find find find ShardA Client MongosA MongosB Secondary Secondary cursor ShardB Primary Secondary Secondary cursor cursor Primary logicalSession($listSessions, killSessions) ShardA Client MongosA MongosB Secondary Secondary cursor ShardB Primary Secondary Secondary cursor cursor Primary ChangeStreams 使用及原理 一、概述 作者 | 陈星(烛昭) (一)什么是 ChangStreams ChangeStreams 功能是基于 MongoDB Oplog 实现的,Oplog 为增量数据,ChangeStreams 在 Oplog 之上包裹一层应用,对外提供一个 API 接 口,将数据进行实时推送,推送的数据类型包括:In sert、Delete、Update、Invalidate、DDL。Inv al idate:主要适用于监控的表被删除时,监控此时 没有意义,会返回 Invalidate 的事件;DDL 事件 是数据库操作语言,如 Create Database、Drop Database 等。总的来说,ChangeStreams 是基 于 Oplog 实现的,提供推送实时增量推送功能。 (二)支持场景 版本的要求: ChangeStreams 支持副本集和分片集群的 MongoDB 形态,版本版本>=3.6。 支持粒度的三个维度: ·全部 DB ·单个 DB ·单个表 引擎的要求: (三)版本历史 MongoDB 3.6 以前 MongoDB3.6 以前,用户只能自己从 local.oplog.rs 表 拉取增量变更的 Oplog 数据。 困难点:1.手动设置过滤条件。2.分片集群处理非常复 杂。3.自己管理断点续传。 WiredTiger 引擎。 ReadConcern 的要求: ChangeStreams<=4.0 时,需要 major i ty 的 ReadConcern; 考虑到用户对于数据实时性的要求比较强,对数据一 致性的要求比较弱,所以 ChangeStreams>=4.2 时 放 开 了 这 个 要 求 。 我 们 建 议 用 户 还 是 配 置 一 个 Majority 的 ReadConcern 级别,不过用户可根据 自己场景的不同,适当放开这个一致性要求。 MongoDB 实时推送 Change Stream Event Insert Delete Update Invalidate DDL C h a n g e S te a m s A P I cursor 粒度 Change Streams 引擎 版本 粒度 全部db 单个db 单个表 <=4.0: majority <=4.2: 无要求 WiredTiger 副本集、分片集群 >=3.6 支持场景
📄 Page 19
35 36玩转 MongoDB 从入门到实战 MongoDB 3.6 MongoDB ChangeStreams 正式发布后,能够提供一个实时吐出增量变更信息,方便用户的运维处理。 支持 PostImage。数据发生变更,发生 Update 操作,那么数据发生变更之前它是 PreImage,数据发生变更之后 它是 PostImage。3.6 提供了 PostImage 的镜像,同时它还支持断点续传管理。 MongoDB 4.0-4.2 ChangeStreams 不断进行优化,更好的支持分片集群的场景。 4.0.7 支持 PostBatchResumeToken,使得位点能够更好推进,防止用户在发生 MongoDB 重启或客户端重启导 致数据位点回退,进而引发大批量无效的数据扫描。 4.0 的多文档事务,4.2 的分布式事务,ChangeStreams 都进行了很好地支持。 支持指定时间点启启动/恢复的功能。 用户可以指定任意时间点,ChangeStreams 都可以从此时间点进行启动 ChangeStreams 流以及恢复数据。 MongoDB 4.4+ MongoDB ChangeStreams 性能持续进行优化。 即将支持 PreImage,在性能上也得到了较大的提升。 随着 MongoDB ChangeStreams 版本不断进行迭代,功能和性能上做了很大的优化。 (四)使用场景 案例 1.监控 用户需要及时获取变更信息(例如账户相关的表),ChangeStreams 可以提供监控功能,一旦相关的表信息发生 变更,就会将变更的消息实时推送出去。 案例 2.分析平台 例如需要基于增量去分析用户的一些行为,可以基于 ChangeStreams 把数据拉出来,推到下游的计算平台, 比如 类似 Flink、Spark 等计算平台等等。 案例 3.数据同步 基于 ChangeStreams,用户可以搭建额外的 MongoDB 集群,这个集群是从原端的 MongoDB 拉取过来的, 那么这个集群可以做一个热备份,假如源端集群发生 网络不通等等之类的变故,备集群就可以接管服务。 还可以做一个冷备份,如用户基于 ChangeStreams 把数据同步到文件,万一源端数据库发生不可服务, 就可以从文件里恢复出完整的 MongoDB 数据库, 继续提供服务。(当然,此处还需要借助定期全量备 份来一同完成恢复) 另外数据同步它不仅仅局限于同一地域,可以跨地 域,从北京到上海甚至从中国到美国等等。 案例 4.消息推送 假如用户想实时了解公交车的信息,那么公交车的位 置每次变动,都实时推送变更的信息给想了解的用 户,用户能够实时收到公交车变更的数据,非常便捷 实用。 总的来说,用户可以于 MongoDB ChangeStreams 功能,进行平台化构建,满足用户的各项需求。当 然,用户的需求可以多样化,不仅仅局限这几个案例。 二、功能介绍 (一)特性 ChangeStreams 可归纳为 5 部分 持久性: Majority-CommittedChanges 数据能够保证持久化,不会被回滚。 断点续传: 通过 Resume Token 进行断点续传的功能。 顺序性: 对于副本集保证线性一致性,对于分片集群保证因果 一致性。 安全性: ChangeStreams 可以进行安全控制。 灵活性: 因为 ChangeStreams 本身是基于 MongoDB Changes Aggregate 框架来实现的,所以用户还可 以在 Aggregate 上添加一些步骤,实现过滤、计算 等需求。 (二)Majority-Committed Changes 的持久化 用户写请求写到了 Primary 上,那么这个时候 Primary 上产生一条 Oplog,此时 ChangeStreams 并不会 把 Oplog 吐出来,它还会等 Secondary 写成功后 才把这个数据吐出来,防止用户写 Primary 成功,之 后 Primary 发生宕机的情况,Secondary 成为新的 Primary,导致数据被回滚。 因此,ChangeStreams 吐出的数据都是持久化成功 的数据。 Majority- Committed Changes 持久性 通过 ResumeToken 保证断点续传 断点续传 因果一致性 顺序性 权限控制 安全性 基于Aggregate框架 根据特定需求进行 过滤计算 灵活性 update: {_id:1, x:2, y:3}=>{_id:1, x:10} Postlmage: {_id:1, x:10} Prelmage: {_id:1, x:2, y:3} 3.6以前 oplog拉取 用户自己从local.oplog.re 表中获取增量变更信息。 手动设置过滤条件 分片集群处理非常复杂 自己管理断点续传 3.6 发布Change Streams 用户自己从local.oplog.re 表中获取增量变更信息。 方便运维处理 支持Postlmage ($set, $unset) 支持断点续传管理 4.0-4.2 性能优化 更好的支持分片集群 4.0.7支持 PostBatchResumeToken 位点及时推进 事务支持 支持指定时间点启动/恢复 4.4+ 持续优化 性能&功能持续迭代 即将支持Prelmage 优化性能 支持场景 平台构建 数据同步 监控 消息推送 分析平台
📄 Page 20
37 38玩转 MongoDB 从入门到实战 Secondary Write Change Streams Secondary Primary Majority-Committed Changes (三)断点续传 举例:如用户从 MongoDB 去拉取一个实时的数据, 此数据是根据时间戳递增的,如 8:00、9:00、10:00, 那么如果 10:00 的时候 MongoDB 发生宕机,或者 用户本身的服务发生意外,导致连接链路断开,后来 MongoDB 或 Server 端恢复服务,希望数据继续接 着 10:00 拉取。 恢复后服务器发送一条消息“请给我 10:00 以后的 数据。”那么 MongoDB 收到这个消息后,继续把 10:00 以后的数据源源不断的通过 ChangeStreams 推送出来,10:00 后是 11:00、 12:00,如此这般服 务就能正常运行。 另一个例子,例如用户 Insert 了 1 条数据是 a=1,然后 它落在 Shard2 上,然后又 Insert 了 1 条数据 a=2,但 它落在 Shard3 上。 后来这 2 条数据它是同时落在 2 个不同的 Shard 上 的,那么 ChangeStreams 它吐出的顺序可能是先 a=2,再 a=1,也就是说,如果 2 条数据在不满足因 果序的条件下,吐出的changeStrearn event 不能 够保证先后顺序。 (四)顺序性—如何满足因果一致性 假如用户写请求,将 insert a=1,写在 Shard2 上 , 此时这条语句通过 ChangeStreams 吐出来了。 后来用户又 Update 了该条文档,把它从 a=1 改成 a=2,这条操作落在了 Shard2 上,此时 Change- Streams 会把第 2 条文档输出,也就是说这两条文档 是具有前后因果性的,不会先吐出第 2 条文档再吐出 第 1 条文档,顺序保持严格的因果性。 (五)ChangeStreams vs Oplog 拉取的对比 对接/使用成本: Oplog 对接成本是远远高于 ChangeStream 的, 因为用户需要自去监听一个表,然后 Find+getMore 拉取,需要做过滤,对事务还需要进行一些额外的处 理等等。 副本集支持: Oplog 拉取和 ChangeStream 都是支持的。 DML 支持: Oplog 拉取和 ChangeStream 都是支持的。 DDL 支持: Oplog 拉取是全部支持的,ChangeStream 目前支 持 dropCollection,dropDatabase,renameColle ction 这 3 个语句,但后续官方会持续完善 DDL 语 句,如增加 CreateIndex,DropIndex 等。 集群板支持: Oplog 拉取必须关闭 Balancer,否则拉取出来 Oplog 不 能保证因果一致性。另外 DDL 需要去重,在 Change- Stream 里不需要额外去处理,成本对接很低。 事务处理: Oplog 拉取的事务处理比较繁琐,例如事务中只同 步了部分语句后发生了断开,重启后事务处理会很繁 琐。ChangeStream 不需要额外的对接,只当做一 个普通语句处理即可。 断点续传: Oplog 拉取是根据时间戳的,ChangeStream 可以 根据时间戳,也可以根据 ResumeToken。 实时性/吞吐: Oplog 拉取的实时性和吞吐比 ChangeStream 更 高,ChangeStream 为了兼顾一致性,加上目前实现 方式是通过副本集上单线程拉取,因此 ChangeStream 性能上略低于 Oplog 拉取。 但是未来 MongoDB 官方将会持续进行优化,使 ChangeStream 的性能追上 Oplog,缩小差距。 权限控制 : Oplog 拉取只能是 2 种权限,All or Nothing,All 意 思是全部的权限,Nothing 的意思是没有权限,所以 要么能监听到所有表所有数据,要么 1 条都拉不到。 ChangeStream 权限控制会更细粒度,用户可以根 据目前账号的权限,有哪些表权限就提供哪些表权限 的拉取。 请给我从10:00以后的数据 好的,这是10:00开始的数据 时间递增 8:00 9:00 10:00 11:00 12:00 实时数据 MongoSMongoSngoS Change Streams Shard2 Shard3Shard1 顺序性-满足因果一致性 insert {_id: 1, a=1} Update {_id: 1}=>, {_id: 1, a=2} insert {_id: 1, a=1} Update {_id: 1}=>, {_id: 1, a=2} insert {_id: 1, a=1} Update {_id: 1}=>, {_id: 1, a=2} MongoSMongoSngoS Change Streams Shard2 Shard3Shard1 顺序性-满足因果一致性 insert {_id: 1, a=1} insert {_id: 2, a=2} insert {_id: 1, a=1} insert {_id: 2, a=2} insert {_id: 2, a=2} insert {_id: 1, a=1} 不同shard 不满足因果序 可能会不一致 三、使用介绍 (一)MongoShell 示例 如何根据 Mongoshell 去使用 ChangeStream (1)db 参数 db 这个部分参数可以有 3 个参数, ·单个 db:db.Watch(), ·全部 db:db.GetMongo() . Watch(), ·单个表:db.Collection.Watch(), (2)Aggregate 的框架 这个参数默认可以留空,用户如果有过滤、计算等需 求可以添加到 Stage 里。 Oplog拉取 高 必须关闭Balancer,DDL需要去重 处理繁琐 All or Nothing 不需要关闭Balancer,DDL不需要去重 不需要额外处理对接 根据Token/时间戳 目前支持dropCollection, dropDatabase,renameCollection 低 Yes Yes 全部DDL 根据时间戳 较好好 较高 细粒度控制 高 Yes Yes ChangeStreams VS Oplog拉取 对接/使用成本 副本集支持 DML支持 DDL支持 集群版支持 事务处理 断点续传 实时 对接/使用成本 对接/使用成本 Change Stream
The above is a preview of the first 20 pages. Register to read the complete e-book.

💝 Support Author

0.00
Total Amount (¥)
0
Donation Count

Login to support the author

Login Now
Back to List