第一部分 数据系统的基石
第一章 可靠性、可扩展性、可维护性
1、关于数据系统的思考
-
本书主要讲 数据系统,是通过数据库、队列、缓存、搜索引擎等等组合而成的应用的称呼,又可称为数据密集系统。
-
多个组件(数据库、缓存、队列等)配合使用的数据系统架构
-
数据软件系统中三个很重要的问题
2、可靠性(Reliability)
-
简单概念:即使出现偏离标准的问题(故障-fault),也能继续正确工作(容错-fault-tolerant)。容错机制是为了防止因故障而导致失效(停止为用户服务)。
-
故障类型
硬件故障(随着科技进步和硬件冗余该故障已变得相当罕见)
在遇到硬盘奔溃、内存出错、机房断电等原因时,一般是增加单个硬件冗余,然后是引入软件容错。只有极少量要求非常高的应用会要求多套硬件冗余。
软件错误
软件错误主要是指应用依赖的环境错误,如linux内核错误,cpu、内存等占用过高,依赖服务变慢等等。这些错误很难被发现,一般会潜伏很久才触发,可以做如下措施来预防和报警。
- 仔细考虑系统中的假设和交互
- 彻底的测试
- 进程隔离
- 允许进程奔溃并重启
- 测量、监控并分析生产环境中的系统行为
人为错误 (主要故障来源)
从开发到测试到运维都会出现没有考虑全面,或者人为操作失误等问题,这类问题不可避免,但是可以利用以下方法提高系统可靠性。
- 以最小化犯错机会的方式设计系统
- 将容易犯错的地方和可能导致服务不可用的地方解藕
- 彻底的测试,单元测试、系统集成测试、手动测试、自动化测试等等
- 允许人为错误快速的恢复,最大限度减少失效情况带来的影响
- 配置详细和明确的监控,如性能指标和错误率等
3、可扩展性(Scalability)
-
可扩展性意味着即使在负载增加的情况下也有保持性能的策略。
-
讨论可扩展步骤如下:
描述负载(数据库读写比例、活跃用户数、缓存命中率等)
负载可用一些称为负载参数的数字来描述,参数取决于系统架构。
描述性能(吞吐量、响应时间等)
一旦系统的负载被描述好,就可以研究当负载增加会发生什么,从两方面来说:
-
增加负载参数并保持系统资源(cpu、内存、网络带宽等)不变时,系统性能的影响?
-
增加负载参数并希望保持性能不变时,需要增加多少系统资源?
确定性能指标最好使用 百分位点 ,比如:要求99%的响应小于200ms,而不是平均响应时间要求200ms,这会使有一半的用户感受到系统延迟,显然是不允许的。
应对负载的方法(可扩展架构)
一个良好的可扩展架构是围绕着假设建立的:哪些操作常见?哪些操作罕见?这就是所谓的扩展参数。
- 纵向扩展 :转向更强大的机器,相对简单。
- 横向扩展 :将负载分散到多台小机器,跨多台机器分配负载也称为无共享架构。
- 弹性系统 :根据负载增加或减少自动增加或释放计算资源。
-
4、可维护性
-
软件大部分开销并不在初期开发阶段,而是在持续的维护阶段:修复漏洞、调查服务不可用、适配新技术、添加新功能、偿还技术债等。
-
可维护性方面的三个设计原则
第二章 数据模型与查询语言
数据模型 影响着我们的解题思路
1、关系模型与文档模型
-
NoSQL诞生的驱动因素:
- 需要比关系数据库更好的扩展性,包括非常强大的数据集或非常高的写入吞吐量。
- 相比商业数据库产品,免费和开源软件更受偏爱。
- 关系模型不能很好的支持一些特殊查询操作。
- 受挫于关系模型的限制性,渴望一种更具多动态性与表现力的数据模型。
-
这一小节主要讲了一些sql和noSql的历史和规范,对我没有太大帮助,简单过了一遍,我这里主要提一些名词,有兴趣的可以自行去了解
混合持久化(polyglot persistence)、阻抗不匹配(impedance mismatch)、ORM、数据库规范化(normalization)、层次模型(hierarchical model)、关系模型(network model)、网络模型(network model)、访问路径(access path)、
-
文档数据库和关系型数据库对比
- 文档数据库通常数据是自我包含,而且文档之间的关系非常稀少,最好不要有多对多。
- 文档数据库使用读时模式(数据结构是隐含的,只有在数据库读取时才被解释)
- 关系数据库使用写时模式(模式明确,数据库保存所有的数据字段都固定不变,要变需要单独更改表结构或迁移数据)
2、数据查询语言
- MapReduce
- 批量处理大规模数据的编程模型
- 介于声明式查询(sql)和命令式查询之间的模型
- 大多用于集群上的分布式计算
3、图数据模型
-
图的组成对象:顶点(也叫节点或实体)和 边(叫关系或弧),典型案例:
社交图谱:顶点是人,边指哪些彼此认识的人。
网络图谱 :顶点是网页,边表示指向其他页面的HTML链接。
公路或铁路网络 :顶点是交叉路口,边代表他们之间的道路或铁路线。
第三章 存储与检索
-
世界上最简单的数据库
#!/bin/bash db_set () { echo "$1,$2" >> database } db_get () { grep "^$1," database | sed -e "s/^$1,//" | tail -n 1 }
1、驱动数据库的数据结构
- 这小节主要是用实例简单介绍了下几种数据结构:
哈希索引
、SSTables索引
、LSM树
、B树
、R树
(空间索引数据结构),并多一些对比,并没有探究具体的原理和实现方式。如果本身对这些数据结构原理不怎么了解的话,看这小节可能会比较吃力。
2、事务处理还是分析
-
事务处理OLTP 和 分析系统OLAP 的特点
-
数据仓库
- 数据仓库是一个独立的数据库,包含一个公司所有各种OLTP系统中的只读数据副本。
- ETL(抽取-转换-加载) :从OLTP数据库中提取数据,转换成适合分析的模式,清理并加载到数据仓库中。
-
聚合:物化视图
数据仓库查询通常涉及一个聚合函数,如sql的count、sum、avg等,如果相同的聚合被许多不同的查询使用,那么每次都可以通过原始数据来处理。为什么不缓存一些查询使用频繁的计数或总和?创建这种缓存的一种方式就是物化视图。
第四章、编码与演化
1、编码数据的格式
-
程序通常(至少)使用两种形式的数据:
在内存中,数据保存在对象,结构体,列表,数据,哈希表,树等中。
如果要将数据写入文件,或通过网络发送,则必须将其序列化为某种字包含的字节序列(如json文档)。
-
语言的特定格式
各语言提供将内存对象编码为字节序列的问题:
这类编码通常与特定的编程语⾔言深度绑定,其他语⾔言很难读取这种数据;
为了了恢复相同对象类型的数据,解码过程需要实例例化任意类的能力,这通常是安全问题的一个来源;
因为它们旨在快速简便地对数据进行编码,所以往往忽略了前向后向兼容性带来的麻烦问题;
效率(编码或解码所花费的CPU时间,以及编码结构的⼤小);
因此最好不要使用语言内置编码。
-
JSON、XML和二进制变体
文本格式存在的问题(json、xml、csv):
数字的编码多有歧义之处。如:XML和CSV不能区分数字和字符串;JSON不区分整数和浮点数;
当处理⼤量数据时存在诸多限制。如:大于的整数不能在IEEE754双精度浮点数中精确表示等;
不支持二进制数据。
-
接下来讲了二进制编码格式(messagePack,BSON等)以及对应使用场景(Thrift和Protocol Buffers),但实际上二进制编码对于大多数开发人员来说接触会很少,有兴趣的可以读读,了解第三方组建采用的底层数据编码。
-
Avro(有兴趣单独了解,这里制作基本概述)
-
Apache Avro是另外一种二进制编码格式。有两种语言模式:一种(Avro IDL)是用于人工编辑,一种(基于JSON)更易于机器读取。
-
使⽤用Avro,向前兼容性意味着您可以将新版本的架构作为编写器,并将旧版本的架构作为读者。相反, 向后兼容意味着你可以有一个作为读者的新版本的模式和作为作者的旧版本。
-
2、数据流的类型
-
数据库中的数据流
在不同的时候写入不同的值:数据库字段的值写入,新字段的增加,我们往往都需要考虑废弃字段,或者字段不为空等对之前代码逻辑和之后代码逻辑的影响,要做到向前向后兼容。
归档存储:利用数据归档,可以把老数据转换为适配新数据结构的数据。
-
服务中的数据流:REST与RPC
web服务:当服务使用http作为底层协议通信时,可称之为web服务。
- 两种流行的web服务方法:REST和SOAP;
- REST不是一个协议,而是一个基于HTTP原则的设计哲学,强调简单的数据格式,使用URL来标识资源,使用HTTP功能进行缓存控制,身份验证和内容类型协商。
- SOAP时用于制作网络API请求的基于XML的协议,SOAP web服务的API使用称为web服务描述语言(WSDL)的基于XML的语言描述。
远程过程调用(RPC)的问题
- 本地函数调用可预测,网络请求是不可预知的,包含返回结果、调用时间、错误原因等等区别
-
消息传递中的数据流
异步消息系统:通过称为消息代理(消息队列或面向消息的中间件)的中介来临时存储消息,与RPC相比,差异在于消息传递通信通常是单向的:发送者不期望收到消息的回复。
- 如果收件人不可用或过载,可以充当缓冲区,从而提高系统可靠性。
- 它可以自动将消息重新发送到已经崩溃的进程,从⽽防止消息丢失。
- 避免发件人需要知道收件人的IP地址和端⼝号。
- 它允许将⼀条消息发送给多个收件⼈。
- 将发件人与收件人逻辑分离(发件人只是发布邮件,不关⼼使用者)。