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中. 这种分片模式是默认的.
另一种是基于哈希的分片:
显然, 哈希所以不存在排序, 但是在这种模式下, 我们可以使得数据写操作更分散.