读构建可扩展分布式系统:方法与实践09可扩展数据库基础
1. 可扩展数据库基础
1.1. 绝大多数应用程序都是基于关系数据库技术构建的
1.2. 数据库必须存储大量数据,为分布在全球的客户端提供快速的查询响应,并且全天候可用
1.3. NoSQL数据库采用简单的数据模型,可以复制和分区以支持海量数据集和请求量
1.4. Facebook以使用MySQL管理PB级社交相关活动(例如用户评论和点赞)的数据而闻名
-
1.4.1. 基本架构基于副本集,一个主节点处理所有写入请求
-
1.4.2. 数据更新被异步复制到按地理分布的只读副本
-
1.4.3. 构建自己的存储技术MyRocks
-
1.4.3.1. MyRocks提高了写入性能并节省了50%的存储空间
1.5. 百度公司从2012年开始使用MongoDB
-
1.5.1. 使用MongoDB来管理包括地图、消息和共享照片在内的多项服务的数据
-
1.5.2. 大概有2000亿份文件和超过1 PB的数据,由600个节点管理,并分布在多个位置以确保可用性
2. 分布式数据库
2.1. 互联网大规模的应用程序让数据集在规模和复杂性上有了长足的增长
-
2.1.1. 为数千万用户创建和管理大量异构数据,包括诸如用户配置文件、用户偏好、行为数据、图像和视频、销售数据、广告、传感器读数、监控数据等
-
2.1.2. 许多数据集实在是太大了,无法放在一台机器上
2.2. 需要演化过的数据库引擎来管理大量的分布式数据集合
-
2.2.1. 低成本、功能强大的硬件的发展,使得在数百甚至数千个节点和磁盘上以低成本高效地分发数据成为可能
-
2.2.2. 在节点间和磁盘上复制数据增强了可扩展性和可用性
2.3. 当今互联网应用程序需求的不断变化是数据库引擎创新的另一个主要驱动力
-
2.3.1. 关系数据库的内在优势(即事务和一致性)是以性能成本为代价的,在Twitter和Facebook等网站中并不总是合适的
-
2.3.1.1. 这类网站并不要求每个用户总是看到相同版本的信息
2.4. 当系统拥有数万乃至数百万的用户时,可以放宽关系数据库的各种数据约束,从而获得更好的性能和可扩展性
-
2.4.1. 促使了非关系数据模型和原生分布式数据库引擎诞生,以支持当今应用程序的各种用例
-
2.4.2. 鱼和熊掌不可兼得,其中的取舍表现在数据库支持的功能范围及其编程模型的复杂性上
3. 扩展关系数据库
3.1. 支持关系模型和SQL查询语言的数据库代表了如今最成熟、稳定和强大的数据库软件平台
3.2. 关系数据库是非常复杂且非常成功的技术
- 3.2.1. 关系数据库技术是在数据集相对较小(与今天的标准相比)的时候设计和发展成熟的,数据库可以在一台机器上运行
3.3. 规范化
-
3.3.1. 关系数据库的设计鼓励规范化
-
3.3.2. 通过规范化构建业务域数据来消除数据冗余,并支持数据完整性
-
3.3.3. 3NF数据模型旨在简化数据管理
-
3.3.4. 域数据在多个关系之间拆分,使得每个数据项都有一个条目,在需要时引用唯一标识符
-
3.3.5. 3NF数据模型中的数据可以机械地转换为关系模式并由关系数据库引擎实例化
3.4. 垂直扩展
-
3.4.1. 关系数据库被设计为在一台机器上运行,这样就可以利用共享内存和磁盘来存储数据及处理查询
-
3.4.2. 用户可以定制数据库引擎使其运行在具有多个CPU、磁盘和大容量共享内存的机器上
-
3.4.3. 数据库引擎可以利用更多的资源并行执行数千个查询来提供极高的吞吐量
-
3.4.4. 数据库迁移到新的、更强大的(虚拟)硬件
-
3.4.4.1. 我们有数据库管理手段来执行迁移和调整数据库配置,使其有效利用新资源,而无须改动应用程序代码
-
3.4.5. 主要缺点
-
3.4.5.1. 成本
> 3.4.5.1.1. 随着计算资源的增长,硬件成本往往呈指数级增长
- 3.4.5.2. 可用性
> 3.4.5.2.1. 尽管你拥有很强大的数据库,但只有一个节点
> 3.4.5.2.1.1. 如果它不可用,你的系统就要崩溃
- 3.4.5.3. 扩展
> 3.4.5.3.1. 如果你的数据库继续增长,则不可避免地要再次迁移到更强大的硬件
> 3.4.5.3.2. 数据库增长到超出单个节点的处理能力
> 3.4.5.3.3. 需要低延迟数据库访问来服务遍布全球的客户
> 3.4.5.3.3.1. 穿越洲际网络并不能解决问题
3.5. 水平扩展:只读副本
-
3.5.1. 增加数据库处理能力的一个常见策略是使用只读副本进行水平扩展
-
3.5.2. 可以将一个或多个节点配置为主数据库的只读副本
-
3.5.2.1. 主数据库节点称为主节点,只读副本称为辅助节点
-
3.5.2.2. 辅助数据库维护主数据库的副本
-
3.5.2.3. 应用程序只能对主节点进行写入操作,然后所有更改都会异步复制到辅助节点
-
3.5.3. 只读副本方法将所有读取请求定向到只读副本来增强可扩展性
-
3.5.3.1. 对于读取密集型工作负载的应用程序来说,非常有效
-
3.5.3.2. 可以通过添加更多辅助节点来扩展读取能力,从而减少主节点的负载,使它能够更有效地处理写入请求
-
3.5.3.3. 如果主节点由于暂时性故障而变得不可用,不会中断面向辅助节点的读取请求
-
3.5.3.4. 由于数据写入主数据库和成功复制到辅助数据库之间存在延迟,客户端有可能从辅助数据库读取陈旧数据
3.6. 水平扩展:数据分区
-
3.6.1. 在关系数据库中拆分或分区存储数据是一种将数据库分布在多个独立磁盘分区和数据库引擎上的技术
-
3.6.2. 水平分区将一个逻辑表拆分为多个物理分区
-
3.6.2.1. 根据某种分区策略将数据行分配给一个分区
-
3.6.2.2. 常见的分区策略是根据行中的某个值将行分配给分区,或者对行的主键进行哈希计算
-
3.6.3. 垂直分区,也称为行拆分,按行中的列对表进行分区
-
3.6.3.1. 出于物理而非概念优化的原因,垂直分区将一行拆分为一个或多个部分
-
3.6.3.2. 一种常见的策略是对行中的静态数据、只读数据和动态数据进行分区
3.7. 分布式SQL连接
-
3.7.1. SQL连接在分布式关系数据库中实现起来很复杂
-
3.7.2. SQL引擎之所以长时间受到青睐,是因为它们针对单个数据库上的连接进行了高度优化
-
3.7.3. 当关系表被分区并分布在大型计算机集群中时,需要精心设计分布式连接,以尽量减少数据移动,从而降低延迟
-
3.7.4. 分布式SQL连接的常见策略
-
3.7.4.1. 定义相对较小、更改频率低且需要经常连接的引用表
-
3.7.4.2. 在连接中使用分区键或二级索引,允许连接操作使用索引字段在每个分区本地并行执行
-
3.7.4.3. 确保连接的一侧存在具有高度选择性的过滤器,可将行集减少为一个小集合
> 3.7.4.3.1. 然后将小集合发送到每个分区节点,像在引用表连接中一样进行连接操作
> 3.7.4.3.2. 最大限度地减少了数据移动
- 3.7.4.4. 高吞吐量的查询需要仔细设计并选择合适的连接算法
3.8. Oracle RAC
-
3.8.1. Oracle的RAC(Real Application Cluster,实时应用集群)数据库
-
3.8.2. Oracle的RAC数据库于2001年发布,为大容量、高可用系统提供分布式版本的Oracle数据库引擎
-
3.8.3. Oracle公司让部署一个由多达100个Oracle数据库引擎组成的集群成为可能,这些引擎都访问同一个物理数据库
-
3.8.4. 为避免数据分区问题,Oracle RAC是一个共享一切数据库的示例
-
3.8.5. 所有节点都通过SAN(存储区域网络)来访问物理存储
-
3.8.5.1. SAN提供对Oracle数据库的高速网络访问
-
3.8.6. 两个专有软件组件
-
3.8.6.1. Clusterware
> 3.8.6.1.1. 支持集群中数据库引擎之间的通信和协调
> 3.8.6.1.2. 支持集群中数据库引擎之间的通信和协调
- 3.8.6.2. Cache Fusion(缓存融合)
> 3.8.6.2.1. 使每个集群数据库节点中的缓存能够有效共享,从而最大限度地减少对持久存储的访问
-
3.8.7. Oracle RAC展示了一种用于扩展关系数据库的架构方法,即共享一切
-
3.8.7.1. 增强了Oracle部署的处理能力和高可用性,同时(在理论上)无须更改应用程序代码
-
3.8.7.2. 数据库需要多个专有的Oracle软件组件以及昂贵的冗余存储和互连硬件
-
3.8.7.3. 加上Oracle许可成本,无论如何你都没有低成本的解决方案
-
3.8.7.4. 是一种以高成本换取有限的按需扩展能力的架构
4. 向NoSQL转变
4.1. 广泛可用的低成本商业计算节点和存储的非共享架构
-
4.1.1. 功能强大、低成本的商业硬件的发展,包括多核CPU,速度更快、容量更大的磁盘,以及更高速的网络
-
4.1.2. 处理非结构化数据类型的应用程序的出现,及其业务和数据模型的快速发展
-
4.1.3. 面向互联网的应用程序在可扩展性和可用性方面的需求激增
-
4.1.4. 收集原始数据并将其用于新业务洞察和分析的新机会
4.2. 向NoSQL转变的核心特征
-
4.2.1. 简化的数据模型,可以轻松扩展
-
4.2.2. 专有的查询语言,限制或不支持连接
-
4.2.3. 原生支持低成本商业硬件的水平扩展
4.3. NoSQL连接查询
-
4.3.1. 数据模型的规范化(normalization)
-
4.3.1.1. 由于SQL和连接的强大功能,你不必过多考虑访问数据时所使用的怪异而奇妙的方式,无论是立即访问还是将来访问
-
4.3.2. 使用NoSQL,建模重点从问题域建模转变为方法域(solution domain)建模
-
4.3.3. 方法域建模的另一种方法是为每个用例创建一个表
-
4.3.4. 数据分区和数据分发变得更加容易,并且这些优势在大规模使用中累积
4.4. NoSQL数据模型
-
4.4.1. 键值数据库
-
4.4.1.1. 键值(Key-Value,KV)数据库基本上是一个哈希映射表
-
4.4.1.2. Redis
-
4.4.1.3. Oracle NoSQL
-
4.4.2. 面向文档数据库
-
4.4.2.1. 面向文档数据库建立在键值模型之上,同理,数据库中的每个文档都需要一个唯一的键
-
4.4.2.2. 与键关联的值对数据库来说是透明的
-
4.4.2.3. 它通常以JSON格式进行编码,从而可以在查询中引用文档中的各个元素,并让数据库在文档字段上建立索引
-
4.4.2.4. MongoDB
-
4.4.2.5. Couchbase
-
4.4.3. 列存储数据库
-
4.4.3.1. 列存储数据库是对键值模型的扩展,管理与命名列中的键相关联的数据
-
4.4.3.2. 本质上是一个二维哈希映射,使行中的列能够使用列名进行唯一标识和排序
-
4.4.3.3. Apache Cassandra
-
4.4.3.4. Google Bigtable
-
4.4.4. 图数据库
-
4.4.4.1. 图是一种很好理解的数据结构,用于存储和查询高度连接的数据
-
4.4.4.2. 图数据库在概念上最接近关系数据库
-
4.4.4.3. Neo4j
-
4.4.4.4. Amazon Neptune
4.5. NoSQL数据库通常被称为无模式数据库
-
4.5.1. 不可避免的代价是应用程序有责任解析它读取的数据的结构,需要将数据对象与元数据(用于解析结构,基本上是字段名称)一起存储在数据库中
-
4.5.2. 写时模式(schema-on-write,定义模式)
-
4.5.3. 读时模式(schema-on-read,无模式)
4.6. 查询语言
-
4.6.1. NoSQL数据库查询语言几乎总是特定数据库专有的,并且与基于显式API和类似SQL的声明性语言有所不同
-
4.6.2. 由供应商和第三方实现的支持不同语言的客户端库可供应用程序使用
-
4.6.3. 键值数据库可能只提供支持基于单个键值的CRUD操作的API
-
4.6.4. 文档数据库通常支持单个文档字段的索引
-
4.6.4.1. 使得检索结果集和更新文档等查询能够高效实现,并满足不同的文档搜索标准
-
4.6.5. 列存储数据库具有多种查询功能
-
4.6.6. 图数据库支持更丰富的查询功能
-
4.6.6.1. Cypher,最初是为Neo4j图数据库设计的,并通过openCypher项目
4.7. 分布式数据存储
-
4.7.1. NoSQL数据库通常为方便分布式计算节点水平扩展而设计,节点配备了本地存储
-
4.7.2. 由于没有共享状态,性能瓶颈和单点故障被消除,性能、可扩展性和可用性均得到增强
-
4.7.3. 分布式图数据库
-
4.7.3.1. 由图数据库实现的图数据结构明确表示图中节点之间的关系
-
4.7.3.2. 水平扩展图数据库以提高性能并非易事
-
4.7.4. 分区,通常称为分片,需要一种算法来跨多个服务器节点实现分布式存储逻辑数据库集合中的数据对象
-
4.7.4.1. 分片需要一个分片或分区键,将给定数据对象分配到特定分区
-
4.7.4.2. 散列键
> 4.7.4.2.1. 任何给定数据对象的分区都是将分片键散列(即应用哈希函数)后,再将结果映射到分区
> 4.7.4.2.2. 使用模数方法或名为一致性哈希的算法
- 4.7.4.3. 基于分片键值
> 4.7.4.3.1. 分区是根据分片键的值进行选择的
- 4.7.4.4. 基于分片键值范围
> 4.7.4.4.1. 分区托管特定范围内分片键值所在的数据对象
> 4.7.4.4.2. 分区可以通过增加处理资源和磁盘容量,并在额外的资源中分布数据来扩展数据库
4.8. 引入副本来解决可用性问题
-
4.8.1. 每个分区中的数据对象通常被复制到两个或更多节点
-
4.8.2. 如果其中一个节点变得不可用,应用程序可以通过访问其他副本继续执行
-
4.8.3. 副本增强了可用性和可扩展性
-
4.8.3.1. 存储副本的额外资源也可用于处理来自应用程序的读取和写入请求
-
4.8.3.2. 当发生数据更新请求时,数据库需要更新所有的副本
-
4.8.4. 领导者-追随者模式(leader-follower)
-
4.8.4.1. 其中一个副本被指定为领导者,它始终持有任何数据对象的最新值
-
4.8.5. 无领导模式(leaderless)
-
4.8.5.1. 所有副本都可以处理读取请求和更新请求
-
4.8.6. 如果一个数据库可以确保所有副本始终具有相同的值,那么它可以对外提供强一致性(strong consistency)
-
4.8.6.1. 所有客户端访问都会针对每个数据对象返回相同的值
-
4.8.7. 将数据库允许副本不一致的行为称为最终一致性(eventually consistent)
5. CAP定理
5.1. Eric Brewer著名的CAP定理优雅地阐述了使用分布式数据库时副本一致性和可用性的选择
5.2. 描述了数据库系统在存在网络分区时的选择,即当数据库节点之间发送的消息存在网络延迟和丢失时
5.3. 如果网络运行正常,系统就可以同时保持一致性和可用性
5.4. 如果发生网络分区,系统可以是一致的(CP)或可用的(AP)
-
5.4.1. 返回错误,因为它无法确保副本一致性(CP)
-
5.4.2. 将更新应用于可见的副本子集(AP)
-
5.4.2.1. 意味着在数据库通过分区修复使所有副本一致之前,副本是不一致的
-
5.4.2.2. 在解决不一致问题之前,客户端可能会看到同一数据对象的不同值