在一个苛刻的数据存储环境中,可能会出现很多情况:
数据库软件硬件随时失效
应用程序崩溃
节点之间相互失联
由于边界条件竞争引入各种奇怪的问题
事务将应用程序的多个读、写操作捆绑在一起成为一个逻辑操作单元,整个事务要么全部成功、要么失败,从而简化错误处理。
传统关系型数据库(如MySQL、PostgreSQL、Oracle、SQL Server)的事务处理机制是成熟且稳定的。几乎所有的关系数据库和一些非关系数据库都支持事务处理,且大多数的设计理念来源于1975年IBM推出第一个SQL数据库System R。
21世纪末随着非关系(NoSQL)数据库的兴起,这类数据库通过提供新的 数据模型、复制 和 分区 等功能来改进传统关系模型。但在这一变革中,很多新一代的数据库完全放弃事务,或重新定义为比以前弱得多的保证。
为了更深入的理解事务,让我们考虑在正常运行和各种极端情况下,事务所能提供的保证
ACID的含义事务所提供的安全保证即大家所熟知的ACID,分别代表 原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
...
当面对一些海量数据集或非常高的查询压力时,我们可以通过 数据复制复制,即在不同节点上保存相同数据的多个副本,然而有时还是不够,我们还需要将数据拆分为分区,也成为分片。
采用数据分区的主要目的是提高可扩展性。不同的分区可以放在一个无共享集群的不同节点上。这样一个大数据集可以分散在更多的磁盘上,查询负载也随之分布到更多的处理器上。
对单个分区进行查询时,每个节点对自己所在分区可以独立执行查询操作,因此添加更多的节点可以提高查询吞吐量。超大而复杂的查询尽管比较困难,但也可能做到跨节点的并行处理。
数据分区与数据复制分区通常与复制结合使用,即每个分区在多个节点都存有副本。这意味着某条记录属于特定的分区,而同样的内容会保存在不同的节点上以提高系统的容错性。
既然是切分,那么首要的目的是如何切分数据,即如何决定那些记录应该放在哪些节点上?
分区的首要目标是将数据和查询负载均匀的分布在所有节点上,因为如果分区不均匀,出现某些分区节点比其他分区承担更多的数据量与负载。这意味着10个节点9个空闲,那么最繁忙的那个将会成为系统的瓶颈。
如果随机平均分配在所有节点上,那么当读取特定数据时,便没法知道数 ...
就DDIA-分布式数据系统一文所提到的,出于高可用的目的,我们往往需要将数据复制到其他副本节点来提高容错。这里的复制主要指的是通过互联网在多台机器上保存相同的副本,并且可以带来如下好处:
使数据在地理位置上更接近用户,从而降低访问延迟。
当部分组件出现故障,系统依然可以继续工作,从而提高可用性。
扩展至多台机器以同时提供数据访问服务,从而提高读吞吐量。
在分布式系统中,复制并不像单机系统中几字节的移动那么简单,由于网络的不稳定,单机节点的故障,很有可能带来整个分布式系统的不一致或者不可用。
接下来我们将探讨三种流行的复制数据方式:主从复制、多主复制和无主复制,对于每一种复制方式,可能也需要考虑是同步复制还是异步复制,如何处理失败副本等。
主从复制每个保存数据库完整数据集的节点称之为副本。那么在分布式系统中如何保证数据的一致性呢?
对于每一笔数据写入,所有副本都需要随之更新;否则,某些副本将出现不一致。最常见的解决方案是基于主节点的复制(也称为主动/被动,或主从复制),主从复制的工作原理如下:
指定某一个副本为主副本 (或称为主节点) 。当客户写数据库时,必须将写请 ...
前面几篇文章主要讨论了单台机器存储系统设计的主要技术。后面我们将继续向前迈进,当需要多台机器提供数据存储和检索服务时,又会有哪些挑战和方案呢?
主要出于以下目的,我们需要在多台机器上分布数据:
扩展性当数据量或者读写负载巨大,严重超出了单台机器的处理上限,需要将负载分散到多台机器上。
容错与高可用性 当单台机器 (或者多台,以及网络甚至整个数据中心) 出现故障,还希望应用系统可以继续工作,这时需要采用多台机器提供冗余。这样某些组件失效之后,其他组件可以迅速接管。
延迟考虑如果客户遍布世界各地,通常需要考虑在全球范围内部署服务,以方便用户就近访问最近数据中心所提供的服务,从而避免数据请求跨越了半个地球才能到达目标。
系统扩展能力当负载增加需要更强的处理能力时,最简单的办法就是购买更强大的机器 (有时称为垂直扩展) 。由一个操作系统管理更多的CPU,内存和磁盘,通过高速内部总线使每个CPU都可以访问所有的存储器或磁盘。
这种共享内存架构的问题在于,成本增长过快甚至超过了线性: 即如果把一台机器内的CPU数量增加一倍,内存扩容一倍,磁盘容量加大一倍,则最终总成本增加不止一倍。并 ...
应用程序不可避免地需要随时间而变化,在演化过程中,需要不断地添加或修改功能。在大多数情况下,更改应用程序功能时,也需要更改其存储的数据: 可能需要捕获新的字段或记录类型,或者需要以新的方式呈现已有数据。
当数据格式发生变化时,经常需要对应用程序代码进行相应的调整(例如,向记录中添加新字段,然后应用程序代码开始读取和写入该字段) 。然而,对于一个大型应用系统,代码更迭往往并非易事。
服务器端程序,可能需要滚动升级,每次将新版本部署到少数几个节点,之后再逐步推广到所有节点。
对于客户端应用程序,用户可能不会在一段时间内马上更新软件
这意为着新旧版本的代码,以及新旧数据格式,可能同时在系统中共存,所以保证数据的双向兼容性至关重要
数据兼容性
向后兼容性较新的代码可以读取由旧代码编写的数据
向前兼容性较旧的代码可以读取新代码编写的数据
向后兼容通常不难实现:只需要清楚旧代码所编写的数据格式,就可以比较明确的处理这些数据,对于向前兼容性,旧的代码需要忽略新版本代码中所做的添加
在本文中,将介绍多种编码数据的格式,包括JSON、XML、Protocol Buffers、Thrift。特别 ...
对于数据库来说,向它插入数据,它就保存数据,向它查询,它就返回那些数据,根据上文,当确定数据库的数据格式(文档模型,关系模型,图状模型)以及对应的查询语言(SQL、命令式查询)之后,本文将从数据库的角度来探索如何存入数据以及在受到查询时,如何找到数据。
数据库核心123456789#!/bin/bashdb_set () { echo "$1,$2" >> database}db_get () { grep "^$1," database | sed -e "s/^$1,//" | tail -n 1}
这两个函数实现了一个 KV 存储,当调用 db_set key value,将在 database 中保存 key value, 调用 db_get key ,会返回 key 对应的 value
它底层的存储格式其实非常简单 : 一个纯文本文件。其中每行包含一个key-value对,用逗号分隔 (大致像一个CSV文件,忽略转义问题) 。每次调用db_set即 ...
背景数据模型可能是开发软件最重要的部分,它们不仅对软件的编写方式,而且还对如何解决问题都有深远的影响。
大多数应用程序是通过一层一层叠加数据模型来构建的。每一层都面临的关键问题是: 如何将其用下一层来表示? 例如:
作为一名应用程序开发人员,通过实际要求,创建对应的对象以及数据结构。
当需要存储这些数据结构时,可以采用通用数据模型 (例如JSON或XML文档、关系数据库中的表或图模型) 来表示。
数据库工程师接着决定用何种内存、磁盘或网络的字节格式来表示上述JSON/XML/关系/图形数据。数据表示需要支持多种方式的查询、搜索、操作和处理数据。
在更下一层,硬件工程师则需要考虑用电流、光脉冲、磁场等来表示字节。
复杂的应用程序可能会有更多的中间层,基于底层API来构建上层API,但是基本思想相同: 每层都通过提供一个简洁的数据模型来隐藏下层的复杂性。
下文将介绍一系列用于数据存储和查询的通用数据模型,我们将比较关系异型、文档模型和一些基于图的数据模型。我们还将讨论多种查询语言并比较它们的使用场景。
关系模型与文档模型SQL现在最著名的数据模型可能是 ...
背景当今许多新型应用都属于数据密集型 (data-intensive) ,而不是计算密集型(compute-intensive) 。对于这些类型应用,CPU的处理能力往往不是第一限制性因素,关键在于数据量、数据的复杂度及数据的快速多变性。
数据密集型应用通常也是基于标准模块构建而成,每个模块负责单一的常用功能。例如,许多应用系统都包含以下模块:
数据库: 用以存储数据,这样之后应用可以再次访问。
高速缓存: 缓存那些复杂或操作代价昂贵的结果,以加快下一次访问。
索引: 用户可以按关键字搜索数据并支持各种过着。
流式处理: 持续发送消息至另一个进程,处理采用异步方式。
批处理: 定期处理大量的累积数据。
越来越多的应用系统需求广泛,单个组件往往无法满足所有数据处理与存储需求。因而需要将任务分解,每个组件负责高效完成其中一部分,多个组件依靠应用层代码驱动有机衔接起来。
设计原则设计数据系统或数据服务时,一定会碰到很多环手的问题。例如,当统内出现了局部失效时,如何确保数据的正确性与完整性?当发生系统降级 (degrade) 时,该如何为客户提供一致的良好表现?负载增加时,系统如何扩 ...