NoSQL
NoSQL => NOT ONLY SQL
现在我们来了解一下NoSQL的概念. 为了引入NoSQL, 我们先从现在的大数据来说说. 互联网公司每分钟甚至每秒都产生巨大的数据量. 并且数据量的增长速度还在越来越快, 因此, 数据的存储, 处理分析都会出现各种各样问题.
因此, 衍生出的数据库系统, 我们有:
- 并行数据库: 水平切分, 分区查询
- NoSQL数据库管理系统: 非关系模型, 分布式, 可能不支持ACID数据库设计范式
- 数据模型简单
- 弱一致性 (分布式通用概念)
- 元数据和数据分离
- 高吞吐量
- 水平扩展能力和低端硬件集群
- NewSQL数据库管理系统 (Combination of ACID and scalability of NoSQL)
- Azure Cosmos DB
- 云数据管理系统
我们现在很流行的大数据分析处理方案就是MapReduce, 例如Hadoop. 而Hadoop的数据库软件, HBase也是一个NoSQL数据库系统.
典型NoSQL数据存储模型有这些:
- 列式存储: 以”列”为中心进行存储, 将同一列数据存储在一起
- 应用场景: 在分布式文件系统上提供支持随机读写的分布式数据存储
- 典型产品: HBase, Cassandra
- 优点: 快速查询, 高扩展性, 易于实现分布式扩展.
- 文档数据: 键值模型, 但是存储为文档
- 应用场景: 非强事务需求的Web应用.
- 典型产品: MongoDB, Elastic
- 优点: 数据模型无需事先定义.
- 键值数据: 基于哈希表实现的K-V
- 应用场景: 内容缓存, 用于大量并行数据访问的高负载场景
- 典型产品: Redis
- 优点: 查询迅速
- 图式数据: 图式结构
- 应用场景: 社交网络, 推荐系统, 关系图谱
- 典型数据: Neo4j
- 优点: 图式计算
MongoDB
接下来我们就来说说MongoDB, 今天的主角.
MongoDB, 刚刚也已经提到, 是一个文档数据模型的NoSQL数据库. 在我们的epel源中就有mongodb的包 但是版本是2.6, 如果需要新版本就还是使用mongodb官方提供的yum仓库就好.
官方的文档写的很详细了, 熟悉一下CRUD操作直接来一遍就行. 这里做一个简单总结
插入操作:db.coll.insert()
查找操作:
db.coll.find(cond, specified field)
-> cond: { field: {operator}, ... }
其中, 比较操作使用$gt $gte $lt $lte $ne $in $nin
逻辑操作使用: $or $and $not
存在性判断使用$exists
值类型判断$type
MongoDB的查询语法还是很有意思的, 都是使用{}
来描述一个操作, 通过operator: value
来指明.
接下来我们再来说说说谈到数据库的时候, 就一定绕不开的一个话题, 那就是索引.
对于特别大的数据集, 索引就十分重要了. 索引会对一个特定字段来实现, 这个字段一般都是查找的条件. 我们将这些查找条件抽取出来, 放在一个专门的数据结构, 然后进行排序. 索引也是需要增删查改的, 当我们的原始数据的数据发生变化, 索引也需要发生变化. 因此我们说索引加速的查询操作, 但是加重了IO操作. 不过对于大量数据而言, 索引还是利远远大于弊的.
我们知道索引可以降序也可以升序. 另外, 根据索引查找的结果不是直接结果, 而是一个指向结果的位置. 索引是个数据结构, 因此也有不同的类型, 其中对于MySQL我们之前说过是B+ Tree. MongoDB同样, 也是使用的B+ Tree.
另外一种索引就是哈希索引. 哈希索引把对应字段每一个值当做键, 构建哈希, 使用这个哈希值来查找. 一般来说, 哈希索引会构建哈希桶, 现检索哈希桶, 再在桶里面查找对应的值.
另外还有空间索引和全文索引.
对于MongoDB而言, 它支持多种索引形式.
- 单字段索引
- 组合索引(多字段索引)
- 多键索引 - 用于数组
- 基于位置的索引, 空间索引
- 对于文本的搜索, 全文索引
- 哈希索引
MongoDB创建索引很简单, 直接对应的collection中使用createIndex
就可以了. 这个过程我们也可以理解成是一个排序过程, MongoDB使用1
来表示升序, -1
表示降序. 创建各种索引的方式也很简单:
1 | db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } ) // 创建一个稀疏索引 |
接下来我们回到服务端程序, mongod. 来看一下它的一些常用选项吧.
fork={true|false}
mongod是否运行到后台
bind_ip=IP
监听地址
port=PORT
指定监听的端口
maxConns
最大连接数
logPath
/ syslog
可以指定是mongo自己来管理日志或者交给rsyslog来管理日志.
等等等…
MongoDB的复制功能
和MySQL一样, MongoDB也需要在某一个数据服务器离线之后, 仍然需要通过冗余继续提供数据服务. 因此需要复制功能. MongoDB有两种类型的复制功能, 其一就是主从复制master/slave
, 另一个就是replica set
复制集. 更常用的, 是复制集.
我们可以把replica set理解成是服务于同一个数据集的多个mongodb实例. 在MySQL中, 我们是使用的二进制日志来实现复制, 从节点去获取主节点的日志来进行重放. 而MongoDB的主节点将数据修改操作保存到了oplog中.
对于mongodb来讲, 当主节点不可用的时候, 两个从节点会对主节点进行心跳检测, 当一段时间内收不到心跳回复后, 这两个从节点就会触发选举操作, 谁的优先级高, 谁就会自动成为主节点. 这种模式下, 最好是存在一个第三节点, 用来参与仲裁过程. 这样当从节点联系不到主节点的时候, 就会去联系仲裁节点. 这个仲裁节点甚至可以是一个web服务, 只要安装mongodb服务即可, 并不需要参与到操作日志的保存和数据操作. 因此对于mongodb的复制过程, 应该至少三个节点, 且应该是奇数个节点.
在复制集中, 节点有这些分类:
第一种, 0优先级的节点, 冷备节点, 不会被选举成为主节点, 但可以参与选举.
基于这一种, 我们有被隐藏的从节点, 它也是一个0优先级的从节点, 且对客户端不可见.
第三种, 延迟复制的从节点, 同样, 也是一个0优先级的从节点, 且复制时间落后于主节点一个固定时长(数据一直是过期数据)
还有就是我们说过的仲裁节点.
说完这些节点, 我们就来说下MongoDB的复制架构, 一个就是oplog, 另一个是heartbeat.
我们先来说这个oplog, 他是一个大小固定的文件. 通常这个文件位置在local数据库中.
1 | > use local |
我们说过这个大小是固定的, 因此在一开始的时候, 这个空间(5%)就会预留出来了. (最小是990MB, 最大是50G, 当然这个大小是可以修改的) 另外, 这个日志文件是幂等性的. 不过当然, 既然大小是固定的, 我们就不能从一开始进行存储数据.
那么如何应用这个文件呢. 我们的从节点一般有这些方式来应用:
- 初始同步
- 回滚后追赶
- 切片块迁移
local数据库存放了副本集的所有元数据和oplog.
当从节点联系不到主节点的时候, 就会触发选举, 除此之外, 还有这些事件可以触发选举机制:
- 新副本集初始化时
- 主节点收到stepDown()命令时
- 某从节点有更高的优先级且已经满足成为主节点的其他所有条件
- 主节点无法联系到副本集的”多数方”
MongoDB的分片功能
随着我们业务发展, 数据量会越来越大. 渐渐地, 单机资源CPU. MEM…会不够, 为了扩展MongoDB, 我们就可以将数据进行切片. MongoDB可以自己做分片, 而MySQL就需要使用额外的分片框架来实现了.
在MongoDB内部, 有三个组件:
- mongos: 这是一个router, 接受用户的查询请求, 相当于代理
- config server: router需要知道查询的数据在哪一个分片上, 需要一个元数据服务器, 存放了索引的mapping
- shard: 数据分片, 也成为mongod实例
当我们的sharding有节点挂掉了的时候, 就会出现数据不完整的情况, 因此, 我们需要做replica, 同样的, 作为元数据服务器, 如果它挂掉了, 整个集群就完蛋了, 所以这个也需要做复制冗余.
接下来我们来看一下分片的策略, 显然每一个分片应该保证尽量平均才行, MongoDB使用shard key
来进行集合中文档的分片分布. 这个shard key
是一个被索引的域, 也就是说, MongoDB通过索引排序来进行分片的划分.
当我们进行分片操作的时候, 我们可以选择shard key
, 文档中的这个shard key
的值就会决定它在不同分片中的分布. 我们之前说过, 索引十分类型的对不, 所以同理, 不同类型的索引, 也会决定不同类型的分片.
首先第一种是基于范围的切片:
此时我们的分片是连续的, 在这种分片模式下, shard key越接近的文档就越有可能会被分在同样的chunk或者shard中. 这种分片模式是默认的.
另一种是基于哈希的分片:
显然, 哈希所以不存在排序, 但是在这种模式下, 我们可以使得数据写操作更分散.