Derived Data
存储与处理数据的系统大致上可以分为两个大类:
Systems of record
记录系统(system of record),也被认为是事实的来源(source of truth),保存着权威版本的数据。每一个事实(fact)都被准确地表达了一次(这个表达representation的过程被称为normalized)。Derived data systems
衍生系统(derived system)中的数据是另外一个系统中已有的数据以某种方式转换或处理得出的结果。从技术角度来说,衍生数据是冗余的,因为它重复了已有的信息。如果你丢失掉了衍生数据,你可以通过源数据再重新得到这份数据。从一个单一的数据源衍生出不同的数据集,可以从不同角度来理解这份数据,这个过程被称为denormalized。
我们现在来区分三种不同类型的系统:
Services(online systems)
一个服务等待着客户端的一个请求或者指令的到达,之后尽可能快地处理然后发送响应。响应时间(response time)通常是服务性能的衡量标准,可用性也非常重要。Batch processing systems(offline systems)
一个批处理系统一次接收大量的输入数据,运行一个任务(job),处理数据并生成一些数据。这种任务通常会花费一段时间,因此用户一般不会等待任务的完成。衡量批处理的性能指标是吞吐量(throughput)。Stream processing systems(near-real-time systems)
流处理介于在线(online)处理与离线(offline/batch)处理之间,因此有时被叫做近实时(near-real-time)处理。类似批处理系统,流处理消费输入数据并生成输出数据,不同之处在于,流处理任务在事件发生后短暂时间内就开始处理,而批处理一次操作一个固定的输入数据集。
Batch Processing
Unix的设计哲学
在Unix中,类似sort
,awk
,uniq
等等这样的unix工具命令都可以为我们完成一些处理数据的工作,而bash shell
可以将这些命令组合起来,构建出一个强大数据处理任务。不同的人写出的这些不同的程序都可以用一种灵活的方式工作在一起,以下几条就是Unix满足这项能力的原因。
统一的接口
如果想要任何程序的输出都可以连接到任何程序的输入,那么就需要一套输入输出的接口,而这个接口就是文件描述符(file descriptor)。一个文件仅仅是一些字节(bytes),而很多东西都可以用这个接口来表示:文件系统上的文件,进程间通信通道(unix socket,stdin,stdout),设备驱动,TCP套接字等等…由于统一的接口,这些东西都能更加容易地工作在一起。分离逻辑与装配
将程序逻辑与输入输出的装配(wiring)过程解耦,这使得用户能够以任意方式去装配输入输出,从而更容易把小的工具组装成更大的系统。透明度
Unix工具使得很多事情都可见了:
- 输入通常是不可变的(immutable),也就是说可以用不同选项运行任意次数的命令,这都不会影响原有的输入数据。
- 可以在任意时刻终止这个管道,调试更加容易
- 可以将一个阶段管道的输出存到一个文件,作为下个阶段的输入,这样可以运行阶段的任务,而不用重新运行整个管道任务。
Unix工具十分有趣,但是最大的限制就是Unix只能在单机上运行,因此Hadoop这样的工具出现了。
MapReduce与分布式文件系统
MapReduce是一种底层的编程模型,它使得在廉价的机器上实现大规模的处理成为可能。类似Unix工具将stdin与stdout作为输入和输出,MapReduce 任务从分布式文件系统(distributed system)中读与写文件。在MapReduce的一种实现Hadoop中,这个文件系统即HDFS。HDFS在每个机器上都有一个daemon进程,这个进程暴露出一个网络服务使得其它节点可以访问该机器上的文件。概括的说,HDFS创建了一个大的文件系统,这个文件系统可以使用所有运行该daemon的机器的磁盘空间。
分布式批处理框架需要解决两个主要的问题:
分区Partitioning
在MapReduce中,mappers会根据输入的文件块被分区,mappers的输出会被重新分区、排序并且合并到可配置数量的reducer分区中。这个目的是为了将所有关联的数据(比如有着相同key的所有记录)放在同样的位置。容错Fault tolerance
MapReduce会频繁地往磁盘中写入信息,使得出现一个单独失败的任务时更加容易恢复,不必重新运行整个任务,这样会降低拖慢执行时间。
MapReduce中有多种join算法,它们也说明了分区算法是如何工作的。
- Sort-merge joins
- Broadcast hash joins
- Partitioned hash joins
分布式批处理引擎使用了一种严格的编程模型:回调函数(比如mappers与reducers)被认为是无状态的,并且除了指定输出以外没有外部可见的副作用。这个限制使得框架隐藏了抽象背后一些复杂的分布式系统的问题:当出现异常或者网络故障时,任务可以安全地重试,任何失败任务的输出都可以被丢弃。框架使得实现批处理作业并不需要关心实现容错机制,框架能够保证任务最终的输出和没有错误发生时任务的输出是一样的。
Stream Processing
在批处理系统中,一个很重要的前提是输入是有界的,但是在现实中,很多数据是无界的,因为数据是随着时间推移而逐渐到达的。用户可能会在昨天和今天产生数据,明天他们可能依然会产生更多数据,因此除非你的业务结束,这个处理过程不会停止下来,数据集永远都不会达到完成状态。而批处理的过程产生输出的时间太久,对于一些用户来说太慢,需要将这个时间缩短到每秒甚至到每个事件就产生一个输出,这就是流处理的思想。
传递事件流
在批处理系统中,一个文件被一次写入,然后被多个任务(jobs)读取。类似的,在流处理中,一个事件被一个producer(或者publisher,sender)生成,然后可能被多个consumer(subscriber,recipient)处理。消费者consumer可能可以通过poll的方式来检查新的事件,但是在低延时持续处理时,polling这种方式开销太大,最好有一种机制能够让consumer在新的事件到达时被通知(notified)。所以一些特定的工具被用来传递事件通知。
message broker有以下两种类型:
AMQP/JMS-style message broker
broker将单独的消息分配到consumer,当consumer成功处理后会进行acknowledge操作,acknowledge过后的消息会从broker中删掉。这种方式适合于消息处理顺序不重要并且被处理过得消息不会再被读取的情形。Log-based message broker
broker将所有消息分配到相同consumer节点的一个分区中,并且总是以相同顺序去传递消息。并行化通过分区实现,consumers检查最后处理消息的偏移量offset来定位它们目前的进度。消息会被broker保留在磁盘中,因此如果有必要的话可以对被处理过的消息再次读取。
处理周期的窗口类型
窗口可以用于聚合操作,比如给事件计数,计算一个窗口内的均值等等操作,有几种常用的窗口:
- Tumbling window
- Hopping window
- Sliding window
- Session window
流的合并
流处理中三种类型的join操作
- Stream-stream join
- Stream-table join
- Table-table join
容错机制
- Microbatching and checkpointing
- Atomic commit revisited
- Idempotence
- Rebuilding state after a failure
The Future of Data Systems
在最后一章作者提出了自己对于未来数据系统的主观看法。包括下列几个方面
- 数据系统的集成
- 将数据库松耦合
- 容错
- 道德问题