因为个人兴趣,想学学分布式方面的知识,然后找到了这本《数据密集型应用系统设计》,确实非常的不错,无论对于以前的工程还是现在的科研都有启迪和感悟,所以就写份读书笔记记录一下,里面提到的知识非常之多,有些如需详细研究需要花大量时间,仅记录下自己感兴趣或者不太熟悉的部分。
数据密集型与计算密集型是当今两大典型的负载类型,前者以大数据为代表,后者以深度学习为代表,其中大数据是开展深度学习的重要前提,本书主要介绍的是数据密集型系统。
第一部分 数据系统基础
第一章
作者将数据库、队列、高速缓存等等用于数据处理的系统均归为数据系统(data system),书中主要介绍影响数据系统设计的三大要素:可靠性,可扩展性和可维护性。
可靠性(reliability):
可靠性意味着及时发生故障,系统也可以正常工作。其中故障包括硬件、软件以及人为方面。
可扩展性(scalability):
可扩展性是指负载增加时,有效保持系统性能的相关技术策略。
可维护性(maintainability):
可维护性是指,让维护变得更简单、轻松。可维护性需要特别关注软件系统的三个设计原则:可运维性,简单性和可演化性。
第二章
第二章主要讨论了三种数据存储模型以及它们的查询语言,三种数据模型分别是关系模型,文档模型以及图模型。
关系模型
现在网络上大部分用的还是关系型数据库,在存储数据时需要定义所有数据的格式,关系(表)只是元组(行)的集合而已,在关系数据库中,由查询优化器自动决定以何种顺序执行查询。
文档模型
文档数据库是某种方式的层次模型,即在其父记录中保存了嵌套记录,而不是存储在单独的表中。如果应用数据具有类似文档的结构(一对多关系树),那么使用文档模型更为合适。
图模型
图数据库更适用于表示多对多关系的数据,例如社交网络,web图,公路铁路网等。图数据库有neo4j等。
数据查询语言
最知名的就是sql了,还有类似于cypher这种用于图数据库的查询语言
其实这三种模型都可以相互模拟,例如,关系型也可以表示多对多的关系,不过在进行查询时处理起来需要写更多的sql语句,所以不同的系统应用于不同的目的,所使用的数据存储方案也是不一样的。
第三章
有两大类存储引擎家族,分别是日志结构的存储引擎和面向页的存储引擎(例如B-tree)
数据库索引
哈希索引
哈希索引通常使用hash map这一结构作为数据存储结构,数据存储全部采用追加式文件组成,内存中保存hash map结构,把每个键映射到数据文件中特定的字节偏移量,这样就可以找到每个键的值。存储实例如下表:
键 | 字节量偏移 |
---|---|
123 | 0 |
124 | 32 |
写入记录:
每次写入时,直接在文件中追加,不过可能会使文件超出大小,一个好的解决方案是将日志分成一定大小的段,当文件达到一定大小时就关闭,然后将后续写入到新的段文件中。一定时间可以执行压缩操作,将重复的键进行合并。
段合并操作实例:
数据文件段1
key | value |
---|---|
url1 | 1003 |
url2 | 514 |
url1 | 1004 |
url1 | 1005 |
url2 | 515 |
数据文件段2
key | value |
---|---|
url1 | 1006 |
url1 | 1007 |
url1 | 1008 |
url2 | 515 |
url1 | 1009 |
压缩合并后的段
key | value |
---|---|
url1 | 1009 |
url2 | 515 |
删除记录:
如果要删除记录,必须在数据文件中追加一个特殊的删除记录。当合并日志段时,一旦发现标记,就会丢弃该值。
并发控制:
只能有一个写进程,不过可以被多个线程同时读取
优点:
- 非常适用于每个键的值更新频繁的场景。例如key是某个视频的url,而值是它的播放次数,每次播放就会增加播放量,对于类似这种工作,有很多的写操作,但是没有太多的key。
- 追加和分段合并主要是顺序写,比随机写入快得多
- 如果段文件是追加或者不可变的,并发和崩溃恢复要简单得多
局限性:
- 哈希表必须全部放入内存,如果存在大量的键,性能表现不是那么好
- 区间查询效率不高,例如不能简单扫描1-100内所有的键,只能使用逐个查找的方式
LSM-Tree
在介绍LSM-Tree之前先介绍下SSTable
SSTables(Sorted String Tables)
从名字就可以看出它的特点,sorted,在前面哈希索引的基础上,要求key-value对的顺序按照key排序。
工作流程:
- 写入时,将数据添加到内存中的平衡树(例如红黑树)中。这个树也叫内存表
- 当内存表大于某个阈值时,将树作为SSTable文件写入磁盘。
- 读请求:首先在内存表中查找键,然后在最新的磁盘段文件中查找,接下去是次新的磁盘段文件,直到找到或为空
- 后台进程周期性执行合并与压缩
优点:
- 合并段更加简单高效,合并的方法类似于归并排序
- 因为键有了顺序,不再需要在内存中保存所有键的索引。将一个范围内的多个键值对保存到一个块中并压缩,然后稀疏内存索引的每个条目指向压缩块的开头。这样可以节省磁盘空间并减少I/O带宽占用。
这种基于合并和压缩排序文件原理的存储引擎通常被称为LSM存储引擎。(LSM-Tree,Log-Structured Merge-Tree)
B-tree
B-tree是一种数据库中广泛使用的索引结构。
B-tree的特点是将数据库分解为固定大小的块或者页,页是内部读/写的最小单元。(之前的数据存储形式相当于每个节点拥有一个数据,而B-tree中每个节点存储一个页面,页中拥有一定数量的数据)
B-tree的构建方式不在这里详细介绍,可以查看wiki B-tree
比较
根据经验,LSM-tree通常对于写入更快,而B-tree被认为对于读取更快。
OLTP与OLAP
OLTP,在线事务处理(online transaction processing),其基本访问模式于处理业务交易类似,应用程序通常使用索引中的某些键查找少量记录,或根据用户的输入插入或更新记录。例如博客的评论、游戏的动作、通讯录中的联系人等等。
OLAP,在线事务分析(online analytic processing),用于数据分析的数据库具有不同的访问模式,分析数据时需要扫描大量数据,每个记录只读取其中的几列,并计算汇总中的统计信息。例如,分析销售记录表。
属性 | OLTP | OLAP |
---|---|---|
主要读特征 | 基于键,每次查询返回少量的记录 | 对大量记录进行汇总 |
主要写特征 | 随机访问,低延迟写入用户的输入 | 批量导入(ETL)或事件流 |
典型使用场景 | 终端用户,通过网络应用程序 | 内部分析师,为决策提供支持 |
数据表征 | 最新的数据状态(当前时间点) | 随着时间而变化的所有事件历史 |
数据规模 | GB到TB | TB到PB |
列式存储
如果字段数量很多,数据量非常大,但每次查询只访问4或5个字段用于分析,如何能高效执行这一查询?
面向列存储的想法就是:不是将一行中的所有值存放在一起,而是将每列中所有值存储在一起。每个列存放在一个单独的文件中,在查询时只要读取和解析对应的列即可。