目录
大数据
大数据的3个 V(数量,多样性,速率)
NoSql
ACID 对比 BASE
CAP 原理
一致性:意味着在修改数据的任何操作被执行之后,数据仍旧保持一致,并且所有访问该应用程序的用户或客户端都要得到相同的更新后的数据。 可用性:意味着系统总是保持可用。 分区容错性:意味着即使系统被划分为无法彼此通信的几组服务器,也要持续地正常运行。
CAP 定理宣称,在任何时间点,一个分布式系统都只能满足上面三个保障中的两个。
BASE方法
base 可被解释为基本可用、软状态、最终一致性
-
基本可用(Basically Available):意味着根据 CAP 定理,系统将是可用的。
-
软状态(Soft state):表明即便没有为系统提供任何输入,其状态也将随时间而变化。这是复合最终一致性的。
-
最终一致性(Eventual consistency):意味着从长远来看,系统将达到一致性,即使这段时间内没有任何数据被发送到系统也会如此。
NRW 是用于描述最终一致性模型是如何在所有 Nosql 数据库中实现的表示法,其中: N是数据库已经保留的数据副本数量。 R是在返回一个读取请求的输出之前,一个应用程序需要涉及的副本数量。 W是在一个写操作被标记为成功完成之前需要对其进行写入的数据副本数量。 使用这些表示法配置,数据库就实现了最终一致性的模型。可以同时在读和写操作层面实现一致性。 大多数Nosql写操作实现都使用 N>W>1 ,这表明需要成功更新多个节点;不过并非所有节点都需要同时更新。 读操作使用 N<W+R ,总是会确保一个读操作检索最新的值。这是由于写副本和读副本的数量总是大于实际的副本数量,从而确保至少有一个读副本具有最新的版本,这是约定俗成的组合
Nosql 数据库种类
类别 | 简要描述 | 产品示例 |
---|---|---|
基于文档的 | 以文档形式存储数据 | MongoDb |
XML 数据库 | 将 XML 用于存储数据 | MarkLogic |
图形数据库 | 数据被存储为节点集合。这些节点通过边界被连接起来。一个节点相当于编程语言中的一个对象 | GraphDB |
键值存储 | 将数据存储为键值对 | Cassandra、Redis、memcached |
功能对比
功能 | 面向列 | 文档存储 | 键值存储 | 图形 |
---|---|---|---|---|
类似表模式的支持(列) | 是 | 否 | 否 | 是 |
完全更新/抓取 | 是 | 是 | 是 | 是 |
部分更新/抓取 | 是 | 是 | 是 | 否 |
对值进行查询/过滤 | 是 | 是 | 否 | 是 |
跨行聚合 | 是 | 否 | 否 | 否 |
实体之间的关系 | 否 | 否 | 否 | 是 |
跨实体视图支持 | 否 | 是 | 否 | 否 |
批量抓取 | 是 | 是 | 是 | 是 |
批量更新 | 是 | 是 | 是 | 否 |
MongoDB 介绍
数据库模型
概念
MongoDB 会以二进制编码格式存储 Json 文档,这被定义为 BSON。
MongoDB 的 BSOn 文档实现是快速、高度可遍历并且轻量级的。它支持将数组和对象嵌入到其他数组中,并且还使得 MongoDb 可以到达对象内部以便针对查询表达式构建索引和匹配对象,在顶层和嵌套的 BSON 键上都可以这样做。
MongoDB 安装与配置
目录结构说明
-
Mongod:核心数据哭服务器。
-
Mongo:数据库 shell
-
Mongos:自动分片程序,被用于 mongodb 分片。它充当了一种路由服务,会处理来自应用程序的查询,并且判定所请求的数据位于分片集群的哪个为止。
-
Mongoexport:导出实用程序
-
Mongoimport:导入使用程序
使用 MongoDB 云管理器进行配置
MongoDB 云管理器是由数据库开发人员内置的一个监控解决方案。其中包括备份、时点恢复以及一种让操作 Mongodb 的任务比之前更简单的自动化功能为管理员提供了几次单机就能快速创建、升级、扩展或关闭 Mongodb 实例的强大能力。它会要求在每一台服务器上安装一个自动化操作代理,然后该代理会被 MongoDB 云管理器用来与服务器通信。
MongoDB Shell 使用
mongoimport
该命令用来将数据导入到 mongodb 一个新的集合
# 帮助命令 > mongoimport.exe --help # 执行命令(hostname为主机名;dbname 为数据库名;collection_name 为要导入的结合;file_path 为要导入的文件路径) > mongoimport.exe --host hostname --db dbname --collection collection_name < file_path
创建和插入
要使用哪个数据库需先切换到对应数据库 db
# 查看所有数据库 show dbs # 切换数据库 use db_name
数据插入
-
所有文档都必须有一个唯一的_id 字段。如果没有显式指定,MongoDB 会自动分配一个作为唯一对象
# users为集合名称 # 方式一 > user1 = {"name":"abc","age":"1"} > user2 = {"name":"abc1","age":"1"} > db.users.insert(user1) > db.users.insert(user2) # 方式二 > db.users.insert({"name":"abc3","age":"1"})
使用循环插入
# 循环插入20条数据 > for(var i = 0;i<20;i++) db.users.insert({"name":"abc3","age":"1"})
显式指定_id 插入
db.users.insert({"_id":10,"name":"abc3","age":"1"})
显式创建集合
db.createCollection("collection_name")
数据更新
update()方法默认会更新单个文档,如果需要更新所有符合选择条件的文件,那么可以通过设置 multi 选项为 true 来实现这一目的。
# 更新所有女性用户的国家为 UK(单条) > db.users.update({"Gender":"F"}, {>set:{"Country":"UK"}}) # 更新所有女性用户的国家为 UK(批量) > db.users.update({"Gender":"F"}, {>set:{"Country":"UK"}}, {multi:true}) # 更新 Company 字段为 China,如果文档中不存在该字段,则新加该字段 > db.users.update({}, {>set:{"Company":"China"}}, {multi:true}) # 移除文档中 Company 字段 > db.users.update({}, {>unset:{"Company":""}}, {multi:true}) > db.users.find({"Gender":"F"})
-
首先更新已有的列的值。>set 操作符将被用于更新记录
-
如果对已经存在于文档中的字段执行 update()命令,那么将会更新该字段的值;如果该字段并不存在文档中,那么该字段将会被添加到文档。
-
unset 操作符可用来从文档中移除字段
删除
要删除一个集合中的文档,可以使用 remove()方法。
# 删除 Gender='M'的文档 > db.users.remove({"Gender":"M"}) # 删除集合里的所有文档 > db.users.remove() # 删除集合 > db.users.drop() # 查看所有集合 > show collections
读取
查询文档
一个查询文档可以包含选择器和投影器。
-
选择器就想 SQL 中的 where 条件或者一个用于过滤出结果的过滤器
-
投影器就想选择条件或者用于显示数据字段的选择列表
# 命令 > db.users.find()
在使用 find()方法时,MongoDB 会将查询结果作为一个游标对象来返回。为了显示该结果,mongo shell 会便利所有返回的游标
# 使用 while循环遍历和打印输出结果,c 是变量,该结果是游标 > var c = db.users.find() > while(c.hasNext()) printjson(c.next())
next()函数会返回下一个文档。hasNext()函数会在一个文档存在时返回 true,而 printjson()会以 json 格式呈现该结果
选择器
# 查询所有女性用户 > db.users.find({"Gender":"F"}) # 查询来自印度的女性 > db.users.find({"Gender":"F"}, >or: [{"Country":"India"}]) # 查询来自印度或美国的女性 > db.users.find({"Gender":"F"}, >or: [{"Country":"India"}, {"Country":"US"}])
聚合 count()
# 查询总数 > db.users.find().count()
投影器
# 获取女性用户的姓名,年龄 > db.users.find({"Gender":"F"}, {"Name":"1", "Age":"1"})
排序 sort()
在 MongoDB 中,排序顺序是按如下来指定的:1用于升序排序,-1用于降序排序
# 获取女性用户,按照年龄升序排列记录 > db.users.find({"Gender":"F"}, {"Name":1, "Age":1}).sort({"Age":1})
limit()
limit()用来限制结果集中的记录
# 获取女性用户,返回2条数据 > db.users.find({"Gender":"F"}, {"Name":1, "Age":1}).limit(2)
skip()
skip()用来跳过前N 个记录,并返回n 多个记录
# 获取女性用户,跳过前2条记录并返回2条数据 > db.users.find({"Gender":"F"}, {"Name":1, "Age":1}).limit(2).skip(2)
findOne()
findOne()命令类似于 find()命令。findOne()方法可以使用与 find()一样的参数,但不同于返回一个游标,它会返回单个文档。
# 获取一个性别女的用户 > db.users.findOne({"Gender":"F"}, {"Name":1, "Age":1})
explain()
explain() 函数可用于查看在执行一个查询时 MongoDb 数据库当前运行的步骤。
详细模式有几种:allPlansExecution、executionStats、queryPlanner。默认模式是queryPlanner。
> db.users.find().explain("allPlansExecution")
索引
索引被用于为频繁使用的查询提供高性能读取操作。默认情况下,当一个集合被创建并且文档被添加到其中时,就会在改文档_id 字段上创建一个索引。
单键索引
使用 ensureIndex()创建索引
# 给 name 字段创建索引 > db.users.ensureIndex({"Name":1})
复合索引
复合索引有助于 MongoDB 更有效地执行带有多个子句的查询。
# 给 name,age 字段创建复合索引 > db.users.ensureIndex({"Name":1, "Age":1}) # 以下两种会走索引 > db.users.find({"Name":"abc"}) > db.users.find({"Name":"abc", "Age":{"$gt":25}}) # 以下不走索引 > db.users.find({"Age":{"$gt":25}})
唯一索引
在一个字段上创建索引并不会确保唯一性,如果唯一性需要被启用,那么在创建字段时,需要加上该属性为 true
# 创建 name 字段唯一索引 > db.users.ensureInde({"Name":1}, {"unique":true})
system.indexes
无论何时创建一个数据库,默认情况下都回创建一个 system.indexes 集合。数据库所有的索引信息都存在该集合中
# 获取某个集合的索引 collectionName 为集合名称 > db.collectionName.getIndexes() # 移除某个集合索引 > db.collectionName.dropIndex({"Name":1})
reIndex
reIndex命令用于重构索引。它会首先删除索引,包括_id 字段上的默认索引,然后重构这些索引。
> db.collectionName.reIndex()
索引如何工作
-
Mongodb 会在一个二叉树(BTREE)结构中存储索引,因此它自动支持范围查询。
-
如果一个查询中使用了多个选择条件,那么 MongoDb 就会尝试找到最佳的单个索引来选择一个候选集。在那之后,它会一次遍历该集合来估算其他条件。
-
在首次执行该查询时,MongoDb 会为每一个可用于该查询的索引创建多个执行计划。它会让这些执行计划在一定时间间隔中轮流执行,直到执行最快的计划完成。然后其结果会被返回到系统,该结果会记住最快执行计划所使用的索引
-
对于后续查询,将会使用此被记住的索引,指导该集合中发生了一定数量的更新。在超过更新限制时,系统将再次执行该处理过程以找出此时使用的最佳索引。
当发生以下任意事件时,就会再次对查询计划进行评估:
-
集合接受到1000个写操作
-
添加或删除了一个索引
-
mongod 程序重启了
-
发生了用于重构索引的再次索引
-
条件操作符
$lt、$lte、$gt、$gte、$in、$nin 以及$ not
# 年龄大于等于23的用户 > db.users.find("Age":{"gtl:"23})
正则表达式
i 表明该正则表达式不区分大小写;
(st|te)*意味着 Name 字符串必须以 st 或 te 开头
结尾的*意味着匹配其后面所有内容
# 找出姓名以 st,te开头并且班级以 che 开头的学生 > db.students.find({"Name":/(st|te)*/i, "Class":/(che)/i})
MapReduce
MapReduce 框架使得任务分配成为可能,它由映射(Map)和缩小(Reduce)构成。
MapReduce 是一个框架,用于处理跨大量数据集的高可分配问题以及使用多个节点来运行的问题。
MapReduce 是为了批量方法使用的,而非用于实时分析。
-
Map:在这个步骤中,该节点会充当接收输入参数的主节点并且将大问题划分为多个小的子问题。然后这些子问题会被跨工作节点进行分发。工作节点可能会进一步将问题划分为子问题。这就产生了多层树结构。然后工作节点将处理其中的子问题并将答案返回给主节点。
-
Reduce:在这个步骤中,所有的子问题答案都被提供给了主节点,然后主节点会合并所有这些答案,并且生成最终的输出结果,这就是你尝试解决的大问题答案。
# 编写一个 mapreduce 按性别统计 > var map = function() {emit(this.Gender, 1);}; > var reduce = function(key, value){return Array.sum(value);}; > db.users.mapReduce(map,reduce, {out:"newConnetionName"}) { "result":"newConnetionName", "timeMillis":29, "counts":{ "input":15, "emit":15, "reduce":2, "output":2 }, "OK":1, } > db.newConnetionName.find()
newConnetionName是一个新的集合名称,MapReduce 会将结果输出到一个新的集合,该集合名可自己命名。
聚合框架 agggregate()
# 按性别分类统计人数 > db.users.aggregate($group:{_id:"$Gender", totalSum:{$sum:1}}) # 计算班级平均分 > db.students.aggregate($group:{_id:"$Class", avgScore:{$avg:"$score"}})
数据模型
引用、内嵌
Mongodb 架构
核心程序
mongoDB 工具
-
mongodump:此工具被用作一种有效备份策略的一部分。它会创建数据库内容的一份二进制导出。
-
mongorestore:有 mongodump 工具创建的二进制数据库转储是使用 mongorestore 工具导入到一个新的或者现有的数据库中的。
-
bsondump:这一工具会将 Bson 文件转换成可供人们阅读的格式,比如 json 或 csv。例如,这个工具可被用于读取 mongodump 生成的输出文件。
-
mongoimport、mongoexport:提供以一种 json、csv 或 tsv 格式的导入导出文件。
-
mongostat、mongotop、mongosniff:这些工具提供了与 mongod 实例的当前操作有关的诊断信息。
主从复制
-
mongodb 使用了分片和复制功能通过分发和复制数据来提供高可用系统。
-
首选的复制方法是副本集
-
实际场景中,最好不要超过十几个从节点
-
主节点会存留一个固定集合(oplog),该集合存储了对数据库进行的逻辑写入的顺序历史,从节点使用这个 oplog 集合来复制数据
读取偏好模式
MongoDB 支持以下读取偏好模式:
-
Primary:这是默认模式。所有的读取请求都会被路由到主节点。
-
primaryPreferred:在常规环境中,会从主节点读取,但在像主节点不可用这样的情况下,会从辅助节点读取。
-
secondary:从辅助成员读取。
-
secondaryPreferred:从辅助成员读取。如果辅助节点不可用,这从主节点读取。
-
nearest:从最近的副本集成员读取
副本集
副本集实质上是主/从复制的一种类型,但它们提供了自动化故障转移。在副本集上下文中,副本集有一个主节点,它被称为主副本,还有多个从节点,他们被称为辅助副本;不过,不同于主/从复制的是,副本集中没有一个节点是固定的主副本。
备注:副本集复制在成员数量上有一个限制。在3.0版本前,该限制为12,在3.0中这个限制改成了50.因此现在副本集复制只能拥有最多50个成员。并且在50个成员的副本集中,在任意指定时点,只有7个成员可以参与一次投票。
主副本和辅助副本成员
副本集拥有2种成员类型:主副本成员、辅助副本成员
辅助副本成员类型:
-
0优先级成员
-
隐藏成员
-
延迟成员
-
仲裁者
-
非投票成员
设置一个副本集
# -replSet选项会指定实例联接到的副本集的名称以及该副本集的一个成员名称,如下命令标识当前服务器指定了另外2台服务其的副本集。 mongod --dbpath 数据文件路径 -port 27021 -replset 集合名称/[hostname]:27022,[hostname]:27023 --rest # 其余服务器都连到主机节点 mongod --dbpath 数据文件路径 -port 27022 -replset 集合名称/[hostname]:27021
副本集状态
MYSTATE | 描述 |
---|---|
0 | 阶段1,启动 |
1 | 主节点成员 |
2 | 辅助节点成员 |
3 | 恢复状态 |
4 | 致命错误状态 |
5 | 阶段2,启动 |
6 | 未知状态 |
7 | 仲裁者成员 |
8 | 关闭或无法连接 |
9 | 当一个写操作从主节点迁移成辅助节点之后被该节点回滚时,就会变成这个状态 |
10 | 从副本集移除成员时,该成员就会进入这个状态 |
分片
分片使用场景
-
数据集的大小很大并且它已经开始挑战单一系统的容量。
-
由于 Mongodb 使用内存来快速抓取数据,因此在预计将达到活跃工作集限制时,扩展就变得很重要拉
-
如果应用程序以写入为主,这可以使用分片来跨多台服务器分散写操作。
分片组件
一个分片集群的组件包括分片、mongos、配置服务器
-
分片就是存储实际数据的组件
-
对于分片集群来说,它会持有数据的一个子集并且它可以是一个 mongod 或者一个副本集。所有分片的数据合并起来就会形成分片集群的完整数据集。
-
分片是以每个集合为基础来启用的,因此可能会出现集合未被分片的情况。在每一个分片集群中,有一个主分片,其中会放置除了分片集合数据之外的所有未分片集合。
-
在部署一个分片群集时,默认情况下,第一个分片会编程主分片,尽管这个主分片是可配置的。
-
-
配置服务器是特殊的 mongod,它们会持有分片群集的元数据。这些元数据描述了分片系统的状态和组织。
-
配置服务器会存储用于单个分片的群集数据。生产环境,建议至少部署3台配置服务器。
-
配置服务器会将数据存储在配置数据库中,这使得可以将不通客户端请求路由到各自的数据。这个数据库不应该被更新
-
只有在处于平衡群集的原因对数据分布变更时,mongodb 才会将数据写到配置服务器。
-
总得来说,配置服务器就是大脑。无论何时,有任何数据在分片中移动是,配置服务器就会让 mongos 知道最终的配置,这样一来mongos 就可以继续进行正确的路由。
-
-
mongos 充当路由器。负责将读取和写入请求从应用程序路由到分片。
数据分发的过程
分片的集合中的分片之间数据分发:在 mongodb 中,会在集合级对数据分片或者分发。集合是由分片键来划分的。
分片键
存在于集合的所有文档中的任何索引的单一/复合字段都可以称为一个分片键。你要指定分片键,这是集合文档需要用来分发字段基础。在内部,mongodb 会根据该字段的值将文档划分成多块并且跨分片分发他们。
mongodb 有两种方式可以启用数据分发:基于范围的划分以及基于哈希值的划分。
数据块
数据会以数据块的形式在分片之间移动。分片键范围会被进一步划分成子范围,这也被称为数据块。
对于一个分片集群来说,64MB 是默认的数据块大小
数据平衡过程
mongodb 会使用以下后台程序确保平衡:
-
数据块划分
-
平衡器
数据块划分是其中一个程序,它会确保数据块都是指定大小。
平衡器是后台程序,它被用来确保所有分片的负荷都均等或者处于一种平衡状态。这个程序会管理数据库迁移。
MongoDB 阐释
mongodb 使用 MMAP 作为其默认的存储引擎,这个引擎适用于内存映射文件。mmap 是 os 的一个功能,它可以将硬盘上的文件映射到虚拟内存中。实现了集合层面的并发控制。这个存储引擎擅长应对大量读取、插入以及就地更新的工作负荷。
mongodb 存储引擎包括 MMAPv1和 WiredTiger
WiredTiger 会以压缩格式将数据存储在硬盘上。压缩可以将数据大小最多减少70%以及将索引大小最多减少50%,这取决于压缩算法。可选择的压缩算法:
-
默认的是 Snappy,它被用于文档和日志。它提供了很好的压缩率,而这只需要很少的 CPU 开销。根据数据类型的不同,其压缩率大约在70%上下。
-
zlib 提供了非常棒的压缩,但需要额外的 CPU 开销。
-
前缀压缩被默认用于索引,以便将索引存储的内存占用空间降低约50%(取决于工作负荷)以及释放更多的工作集用于频繁的访问的文档。
GridFS——MongoDB 文件系统
GridFS 的基本原理
GridFs 使用了两个集合用于存储文件。一个集合维护文件的元数据,而另一个集合通过讲文件数据分解成称为数据块的小块来存储文件的数据。这意味着该文件会被划分成较小的数据块,且每一个数据块会被存储为一个独立的文档。默认情况下,数据块大小被限制为255KB。
GridFs 的底层机制
GridFs 是用于存储文件的一个轻量级规范。
GridFs 可以通过将大型文件划分成较小的数据块,并且将每个数据块存储为独立文档来存储大型文件。除了这些数据块之外,还有一个文档包含了关于文件的元数据。使用这些元数据信息,数据块就会被组合到一起,形成完整的文档。
-
用于数据块的存储开销可以保持到最小化,因为 mongodb 支持在文档中存储二进制数据。
-
GridFS 用于存储大型文件的两个集合,其默认名称为 fs.files 和 fs.chunks,不过相较于 fs,也可以选择不同的存储桶名称。
-
默认情况下,数据块会被存储到 fs.chunks 集合中
管理 Mongodb
MongoDB 用例
MongoDB 使用限制
MongoDB 的最佳实践
MongoDB 总结
-
mongodb 文档具有16MB 这一固定的大小限制
-
每个索引至少需要8kb 的数据空间,其中包括_id 索引
-
数据库的元数据都被存储在<database>.ns 文件中
-
数据文件默认在目录/data/db 下,端口默认27017;mongod 还有一个 http 服务器,它会监听比默认端口大1000的端口
-
对于一个分片集群来说,64M 是默认的数据块大小
-
用于存储元数据的.ns 文件的大小是16M。这个文件可以被看做一个大的哈希表,它被划分成小的存储桶,其大小约为1KB。每个存储桶会存储特定于一个命名空间的元数据。
-
集合中可能不允许使用多余64个索引。索引键不能大于1024字节。索引名称(包含命名空间)必须小于128个字符。