一、Why NoSQL?
- 关注NoSQL的两个原因
- 应用程序的开发效率(NoSQL简化了数据交互)
- 大规模的数据(NoSQL为集群环境而设计)
- NoSQL不是独立存在的,以后也不会取代关系型数据库,以后数据库领域将步入混合持久化(Polyglot Persistence)时代。
- 关系型数据库的优点:
- 标准化的建模
- 较为容易的处理关系
- 通过事务来处理并发
- 可以持久化
- 关系型数据库的缺点:
- 阻抗失谐:关系型数据库中的存储结构(模式、表、元组)与应用程序中的数据结构需要转换。ORM框架可以解决这个问题,但是会引起性能的下降。
- 应用程序数据库(摘绿帽)与集成数据库(戴绿帽):SOA的兴起(内部数据库与外部通信服务间的解耦)
- 集群化问题:扩展性问题(纵向与横向)与许可费问题,大数据是一个巨大推动
- NoSQL的定义:开源分布式的非关系型数据库
- 开源
- 分布式
- 非关系型(无模式)
- 使用NoSQL的主要原因:(其他情况下,请依然使用关系型数据库)
- 数据量大,访问效率要求高
- 要解决“阻抗失谐”的问题
二、聚合数据模型
- NoSQL的主要分类
- 面向聚合(aggregate oriented):来自于DDD
- 键值模型(key-value):聚合对数据库不透明,只能整个的读取
- 文档模型(document):透明的聚合,可以看到数据结构,可以读取其部分(以结构的限制换取更好的访问性)
- 列族模型(column family):“两级映射”——两级聚合结构。因此其既“面向行”也“面向列”
- 非面相聚合:图数据库(graph)
- 面向聚合(aggregate oriented):来自于DDD
- 聚合的优点:
- 在集群中,使用聚合操作数据比较简单
- 聚合也方便应用程序操作数据结构(对程序员更为友好)
- 以聚合为单位的原子操作
- 降低了数据采集时的需要节点数
- 聚合的缺点:
- 多个聚合的操作原子性需要应用代码来维护,而这常常比较复杂
三、数据模型详解
- 关系型:如果待处理的数据中存在大量关系,那么这就意味着需要选用关系型数据库(但其实图数据库在这方面更强)
- 聚合型:操作单个聚合很方便,但是操作多个聚合时就很笨拙
- 图数据库:
- 以包含节点和边的图构成;
- 节点简单,互连结构丰富
- 图数据库中遍历关系非常迅速,但是关系型数据库较差
- 通常运行在单一的服务器上
- 无模式的数据库:
- 其实总包含“隐含模式”
- 本质上说,无模式数据库把模式交由访问其数据的应用程序代码来处理
- 如果你发现存储的数据类型不统一,那么应优选无模式数据库
- 无模式的灵活性仅限于聚合内部
- 数据迁移对有无模式的数据库而言都是难点
- 物化视图
- 作用:使基本数据和派生数据对客户端透明
- 计算生成物化视图比较复杂耗时
- 方式:
- 一旦有数据变化,立即更新
- 定期通过批处理操作更新(一般通过Map-Reduce)
- 可以将物化视图在聚合内使用,以便在原子操作内更新
四、分布式模型
- 横向扩展易于纵向扩展
- 数据分布:
- 复制(replication)
- 主从式(master-salve)
- 对等式(peer-to-peer)
- 分片(sharding)
- 这两种方式正交,可以同时使用
- 复制(replication)
- 单一服务器
- 单一服务器即可以SQL也可以NoSQL
- 在不需分布数据时,应总是选择“单一服务器”方案
- 分片
- 将不同的数据放在不同的数据库服务器上
- 读、写性能都可以提升
- 会降低数据库的错误恢复能力(因为需要维护更多的数据库服务器了)
- 优化方式:
- 地理空间上应将数据库靠近访问者
- 负载均衡
- 自动分片技术(大部分NoSQL提供,其将应用代码与数据库分片功能解耦)
- 主从复制
- 可以将系统视为带有“即时备份”功能的“单服务器存储方案”
- 优点:
- 通过新增从节点可以方便的进行水平扩展,可以处理更多的读请求,并保证读请求都被引导至从节点
- 可以增强读操作的故障恢复能力
- 主节点出错了,从节点依然可以提供读服务
- 拥有与主节点相同内容的从节点可以很快的被指派为新的主节点,代替故障的原主节点
- 减少了写操作的冲突概率
- 缺点:
- 数据的不一致性
- 主节点是性能瓶颈
- 对等复制
- 优点
- 所有的节点都可以读写
- 易于扩展
- 不存在性能瓶颈节点
- 缺点:
- 数据一致性问题
- 优点
- 解决写入操作冲突的两种极端解决思路:
- 总是去协调节点间的关系,确保不发生冲突,只需保证各大部分副本的一致性,
- 允许节点间的冲突,但尝试合并这些冲突的写入操作
- 分片+复制
- 对等复制+分片
- 将分片存放在一定数量(复制因子)的对等节点中
五、一致性
- 需要理解并权衡
- 强一致性(strong consistency):时时一致
- 最终一致性(eventual consistency):有不一致的时间窗,但最后一致
- 更新一致性
- 问题:写冲突(write-write conflict)
- 常用解决方式:
- 先决条件:处理更新操作的顺序必须一致
- 顺序一致性(sequential consistency)
- 悲观方式:避免发生冲突
- 写入锁
- 会大幅降低系统的响应能力
- 容易产生死锁
- 乐观方式:发生冲突,再解决冲突
- 条件更新
- 保存所有的更新,标注出冲突,并合并冲突
- 先决条件:处理更新操作的顺序必须一致
- 读取一致性
- 问题:读写冲突(read-write conflict)/读取不一致(logical consistency)
- NoSQL对事物的支持:
- 面向聚合:支持“原子更新”,但不支持多个聚合构成的事物
- 图数据库:支持
- 不一致窗口:数据逻辑不一致的时间长度
- 复制一致性:不同副本中数据的一致性
- 通常可以指定单个请求所需的一致性级别,合理地降低部分请求的一致性级别可以提高性能
- 照原样读出所写内容的一致性(read-your-writes consistency)
- 会话一致性
- 黏性会话
- 将读写绑定至某一个节点
- 会降低负载均衡器的效能
- 版本戳
- 黏性会话
- 会话一致性
- 放宽“一致性”约束
- 制定合理的隔离级别,合理地放宽一致性要求
- CAP理论
- 一致性(Consistency)
- 可用性(Availability)
- 分区耐受性(Partition tolerance)
- 常见错误理解:
- 我们只能同时满足其中两个方面
- 实际上:当系统可能会遇到“分区”状况时,我们需要在“一致性”和“可用性”之间进行权衡;这并不是一个二选一问题。
- 有时可以适当放宽一致性,允许冲突的发生,并通过领域知识指导下的应用程序代码来解决冲突,以换取更好的并发能力。
- NoSQL倡导的BASE理论:
- 基本可用(Basically Available)
- 柔性状态(Soft State)
- 最终一致性(Eventual consistency)
- 本质上大多是一致性与时延的取舍
- 放宽“持久性”约束
- 非持久性写入操作
- 例如,Redis先写入内存,然后再定期的写入硬盘
- 复制持久性
- 在复制过程中,写节点失效,就会造成数据的丢失
- 需要权衡对复制质量的保证还是数据库的响应速度
- 非持久性写入操作
- 仲裁(避免冲突的方式)
- 写入仲裁
- 在对等式分布模型下:
- W>N/2
- W:写节点数
- N:复制因子
- 在主从式分布模型下:
- 从主节点
- 在对等式分布模型下:
- 读取仲裁
- 在对等式分布模型下:
- R+W>N
- R:读节点数
- 在主从式分布模型下:
- 从主节点
- 在对等式分布模型下:
- 还是需要根据实际情况来确定具体的仲裁方式
- 写入仲裁
六、版本戳
- 商业事务vs系统事务
- 商业事务:针对用户而言从,整个交互流程
- 系统事务:用户提交后,对系统而言的事务
- 问题:商业事务和系统事务之间,存在较大的时间窗
- 解决方式:离线并发技术
- 乐观离线锁(条件更新的一种):compare-and-set(CAS操作)
- 通过比较商业事务开始执行时的版本戳与系统事务开始执行时的版本戳,来检验信息是否发生了变化,以确定是否需要进行更新操作
- 常见的版本戳类型:
- 计数器
- 易于比较
- 但是需要一个主服务器用来生成并保证不同版本的计数器值不会重复
- GUID(Globally Unique Identifier),全局唯一标识符:
- 任何人都可以生成
- 但是数值较大,而且难以直接比较
- 根据资源内容生成Hash码
- 足够大时,可以唯一标识
- 任何人都可以生成
- Hash值是确定的
- 但是冗长且无法直接比较
- 上一次更新的时间戳
- 短小,易于生成
- 可以直接比较
- 但是不同服务器之间的时钟必须同步,否则很容易造成数据损毁
- 且时间戳的精度很难确定:过低,无法区分;过高,需要频繁的更新
- 复合版本戳(composite stamp)
- 运用多种手法
- 计数器
- 在多节点环境中生成版本戳
- 单服务器或主从式复制模型:
- 基本的版本戳方案就足够了:计数器
- 时间戳也可以,但不如计数器好
- 多个主节点时:
- 每个节点维护一份版本戳记录(version stamp history)
- 通过判断“祖先”记录来确定新旧关系;如互不为祖先,则检测为冲突
- 对等式复制模型
- 数组式版本戳(vector stamp)
- 维护一个数组记录所有节点的版本,例如[server01:1, server02:4, server03:5]
- 如缺失某些值,则视为0,例如视server04:0
- 这样便于新增节点
- 单服务器或主从式复制模型:
- 版本戳仅能检测冲突,而不能解决冲突,冲突的解决依赖于领域知识
七、Map-Reduce
- 分散-聚集(Scatter-Gather)模式的一种形式
- 将部分计算逻辑放于数据库服务器上
- 输入值是某个集合,输出值是键值对的集合
- 主要包括以下函数
- Mapper
- Combiner
- Reducer
- 通常会要求Mapper Combinable,既Mapper同时作为Combiner使用
- 通常会使用管道及过滤器(Pipes-and-filters)来组合处理
- 增量式Map-Reduce: 通常需要保存部分中间结果,以备下次使用
- 经典实现:Hadoop
- Pig:专用语言
- Hive:类SQL语言
八、常见NoSQL实现
- 键值
- Memcached
- Redis
- Riak
- 文档
- CouchDB
- MongoDB
- 列族
- HBase
- Cassandra
- Amazon SimpleDB
- 图
- Neo4J
- HyperGraphDB