《数据密集型应用系统设计》读书笔记

发布时间:2022-06-27 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了《数据密集型应用系统设计》读书笔记脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

个人读书笔记,有些地方用词不够严谨(欢迎评论指正),见谅。书籍链接

笔记是个人理解,与书籍可能有偏差,建议看书。

问题:

    @H_777_7@

    什么样的数据适合图数据库

    社交关系?网页数据?地图数据?

  • MySQL有没有事务重试?

  • 更新数据时,是按行更新还是按页更新?

  • 更新数据时,是采用copy on wrITe还是直接修改?

第一部分 数据系统的基石

第一章:可靠性、可扩展性、可维护性

可靠性

  • 硬件故障:机房断、硬盘崩溃
  • 软件错误:数据库异常、缓存异常等
  • 认为错误:业务bug、运维失误等

允许部分服务可不用不影响整体系统),最后同一个服务有多个提供者(集群)

可伸缩性

  • 吞吐量:每秒可以处理的记录数量
  • 延迟:包括网络延迟、排队延迟(多个请求需要排队)
  • 响应时间:包含服务器的处理事件以及延迟,一般取中位数而不是平均数

可维护性

  • 代码尽量抽象封装
  • 运维方后续讨论

第二章:数据模型与查询语言

关系模型与文档模型

相对应的就是关系型数据库以及NoSQL(文档型MongoDB,图数据库等)

对象关系不匹配

拿简历举例:

简历包含有工作经历、教育经历

  • 在关系型数据库中,会分基本信息表、工作经历表、教育经历表等
  • 在文档数据库中,只有一张表(教育经历等用数组表示)

如果场景都是要加载全部数据,那么适合使用NoSQL

多对一和多对多的关系

  • NoSQL支持关联查询,但是性能不好
  • 多对多关系更使用关系型数据库

关系型数据库与文档数据库在今日的对比

灵活性

  • 读时模式:文档型数据库,数据的结构是隐含的(就是数据也是有结构的),只有在数据被读取时才被解释
  • 写时模式:关系型数据库,数据结构由数据库保证,写入时校验

mysql5.7开始增加对json的支持,mysql8.0进一步优化的json支持

数据查询语言

  • SQL
  • Mapreduce

图数据模型

  • 顶点(vertex):表示一组属性(比如人、地址、职业等)
  • 边(Edge):表示关系,有起点和终点(xxx出生在xxx,xxx的职业是xxx)

和文档型数据库一样,不会在保存时强制格式。

查询时无法确定级联层数???

第三章:存储与检索

驱动数据库的数据结构

  • 哈希索引

    优点:速度快

    缺点:全部放入内存。无序,所以对顺序范围等查询不友好

  • SSTable和LSM树

    因数据结构以及顺序写,保证的写入的性能

    Memtable:数据首先放在内存中,按key排序

    Immutable MemTable:MemTable达到一定大小后,会转化成Immutable MemTable。写操作由新的MemTable处理,Immutable转成SSTable

    SSTable:已持久化。不同SSTable可能存在相同的key,以最新的为准。SSTable达到一定层数,会触发合并,此时冗余的旧key会被去除

  • B树

    mysql使用的B+树是B树的增强版

  • 比较B树和LSM树

    B树写入慢,读取快。LSM树写入快,读取慢。

    LSM写入同等数据需要磁盘IO比B树少

    LSM压缩比B树好,因为B树的空字段、页结构等,会存在少量不被使用的空间。

    LSM需考虑压缩和写入的比

事务处理还是分析

  • 在线事务处理(OLTP)
  • 在线分析处理(OLAP)
  1. 星型模型:一个事实表(存主要数据,比如人的基本信息),多个纬度表(关联表,存关联数据,比如人的浏览历史)
  2. 雪花模型:星型模型的变体,某些纬度被进一步分解为子纬度(3层表联查)

列存储

数仓的事实表基本都是大表,字段较多(100列)

  • 不会采取select * 的方式查询数据
  • 可以考虑列存储,列式存储很适合压缩(数据重复率高,比如男、男、女、男、女、女)

第四章:编码与演化

  • 文本格式:JSON、XML、CSV

  • json不支持二进制字符串以及对数字类型不够友好,比如无法准确表达int和float,比如20不知道是int还是float

  • 二进制格式:Thrift、PRotocol Buffers、Avro

  • 数据流:

    数据库中的数据流

    服务中的数据流:REST和RPC

    消息传递中的数据库(消息队列)

  • 注意向前和向后兼容:

    对于RESTful API,常用的方法是在URL或HTTP Accept头中使用版本号

第二部分:分布式数据

第五章:复制

主从复制

  1. 同步复制:从库写入成功,主库才能继续写入
  2. 一般是一个主库,一个同步从库,多个异步从库,称为:同步
  3. 链式复制:更多低等级从库从高等级从库拉取数据,而不是从主库拉取
  4. 设置新从库
    1. 获取主库快照,并导入
    2. 导入从快照时刻开始的bingLOG
  5. 故障切换
    1. 异步复制时主库故障,从库提升为主库,
      1. 从库数据不是最新
      2. 主库从新加入时可能有冲突写入(处理:丢弃未写入从库的数据)
      3. 如果数据库有与其他外部存储(比如缓存)协调,错误参考上一条
  6. 复制日志的实现
    1. 基于sql的复制,会因为sum、now等语句产生不确定性
    2. 不确定性的语句使用基于行的复制

复制延迟的问题

保证最终一致性。一是性能方面的考虑,二是数据离用户越近,延迟越小(CDN?)

  1. 读己之写

    用户不想延迟看到自己提交的数据(写入主库,但是读取从库时还没更新)

    解决方案

    • 只有单一用户能修改的东西(例如个人资料),可以写入主库,本地放一份到缓存(设置超时),本地读取缓存的,没有再去读从库
    • 记住写入的时间戳,从库未到达改时间戳,不返回数据
  2. 单调读

    确保每个用户总是从同一个副本读取,否则会出现两次查询,副本a有数据,副本b没有

  3. 一致前缀读

    如果一系列写入按某个顺序发生,那么任何人读取这些写入时,也会看见它们以同样的顺序出现。

    比如顺序为ab,但是读取到ba,常见于分区、分片

    解决方案:保证因果相关的写入都写入相同的分区(分区划分策略?)

  4. 复制延迟的解决方案

    事务???

多主复制

  1. 场景

    1. 对不同表的复制划分到不同主库写入?
    2. 容忍延时或者离线的写入?
  2. 处理写入冲突

    1. 冲突时,使用版本最高的数据
    2. 自定义冲突解决逻辑
  3. 多主复制拓扑

    MySQL仅支持环形拓扑,即只接收一个节点的写入和自己的写入。也类似与星型拓扑。

无主复制

  1. 当节点故障时写入数据库

    同时写入多节点,过半成功则成功。同时读取多节点,取最新版本的数据。

    • 读修复和返熵

      1. 读修复:并行读取多节点,返回最新版本数据,有副本存在旧数据的,则更新
      2. 反熵过程:轮询比较差异,补齐差异
    • 读写的法定人数

      n个副本(不是节点),写入w个节点,读取r个节点,则 w + r > n

      这样才能保证每次读取至少有一个节点是最新的

      节点可能大于副本数,有可能两个节点组成一个副本(分区)

  2. 法定人数一致性的局限性

    1. 性能问题(并发写入、读取过多节点,取时间最长的)
    2. 部分节点宕机,导致写入失败
  3. 宽松的法定人数与提示移交

    1. 宽松的法定人数:写和读仍然需要w和r成功的响应,但这些响应可能来自不在指定的n个“主”节点中的其它节点。
    2. 提示移交:一旦网络中断得到解决,代表另一个节点临时接受的一个节点的任何写入都被发送到适当的“主”节点。
  4. 运维多个数据中心

    法定人数配置只限定在本地数据中心,并异步将数据同步到其余数据中心。

检测并发写入

  1. 最后写入胜利(LWW)

    保留最后版本数据,其他版本

  2. 捕获”此前发生“关系

    就是每个连接(客户端)记录并维持自己的版本号,只修改自己版本的东西

第六章:分区

在MongoDB,ElasticseArch和Solr Cloud中被称为分片(shard),但是分区(partitioning)** 是最约定俗成的叫法。

分区与复制

一个大型数据库可以拆分成多个分区,分区也可以复制(主从、无主等)

键值数据的分区

  1. 根据键得范围分区

  2. 根据键的散列分区(hash)

    破坏了排序,使得范围查询效率低下,但可以更均匀地分配负载

  3. 负载偏斜与热点消除

    这属于设计阶段的问题,目前设计系统没有自动检测和补偿偏斜的工作负载。

分区与次级索引

  1. 基于文档的次级索引进行分区

    每个分区维护自己的索引,也叫本地索引。即分区0,分区1都可能存在color=red的索引数据

  2. 基于关键词的次级索引进行分区

    也叫全局索引。一定范围的索引数据汇聚到同一个分区,比如首字母从ar的颜色在分区0中,sz的在分区1。就是分区1的索引可能被存到分区1中。

    分区同时存数据以及索引数据。

    缺点:写入速度较慢且较为复杂

分区再平衡

就是分区的增减,节点故障等导致的数据、请求、负载转移的过程,叫再平衡。

  1. 再平衡策略
    1. 固定数量的分区

      初始创建好固定数量的分区,增加节点时,帮其他节点接管一部分分区。比如初始4个节点,20个分区,每个节点5个分区。当增加一个节点时,每个节点那管理4个分区。

      缺点:初始很难定好总分区数。分区数量不正确会影响性能。

      Riak、ES、Couchbase、Voldemort使用了此方法。

    2. 动态分区

      当分区增长到超过配置的大小时,会被分成两个分区。与之相反,分区缩小到某个阈值以下,则合并。

    3. 按节点比例分区

      每个节点具有固定数量的分区,新节点加入集群时,所以选择固定数量的现有分区拆分,一人一半。

请求路由

查询要转发到哪个节点,问题概括为服务发现

三种方案:

  1. 请求节点0,节点0说没有,知道节点1有,转发到节点1,节点1返回节点0,节点0返回客户端。
  2. 统一路由转发。客户端 → 路由 → 节点。
  3. 客户端知道分区和节点的分配,请求时直接到相应的。(Eureka?)

事务

事务的棘手概念

  1. ACID的含义
    • 原子性(atomicity)

    • 一致性(consistency)

      原子性,隔离性和持久性是数据库的属性,而一致性(在ACID意义上)是应用程序的属性。

    • 隔离性(isolation)

      同时执行的事务是相互隔离的。

      串行化级别:事务串行执行,上一个事务没结束,下一个事务不会开始执行。

    • 持久性(durability)

  2. 对象和多对象操作
    • 处理错误和中止

      发生错误时,事务可以中止并安全地重试。

      事务重试仅在临时性错误(例如,由于死锁,异常情况,临时性网络中断和故障切换)后,永久性错误(违反约束等)重试是无意义的。

弱隔离级别

即非串行化的级别

  1. 读已提交

    解决脏读和脏写。就是只有数据在commit的一刻才全部生效

  2. 快照隔离和可重复读

    1. 可重复读:事务a读取name=’a’ → 事务b提交name=’b’ →事务a只要未提交,读取多少次name都是a
    2. 实现快照隔离:多版本并发控制(MVVC)
  3. 止丢失更新

    丢失更新:事务a、b同时修改同一条数据,后提交的事务会覆盖前事务修改的数据。

    自动检测丢失的更新oracle可串行化隔离级别,可自动检测到丢失更新,并终止惹麻烦的事务。(mysql没有)

    解决方案:

    1. 读取时就锁定,不允许别的事务读(这样mvcc就没意义了)。比如select xxx From table for update;
    2. cas,写入时再比较一次
  4. 写入偏差与幻读

    写偏差:两个事务正在更新两个不同的对象。丢失更新是两个事务更新同一个对象

    举例:医院规定最少有一人值班,医生a、b同时请假,事务A、B各自修改了a、b的值班状态。最后0人值班。

    解决方案:

    1. 锁住整个表
    2. 真正的串行化

可串行化

为什么数据库默认都不使用串行化?

  • 真的串行执行

    在单个线程上按顺序依次只执行一个事务,正是可串行化的定义。

    Redis实现的就是串行执行事务(旧版redis是单线程)

    1. 在存储过程中封装事务

      传统多语句事务:

      1. select结果 → 应用程序
      2. 应用程序判断 → update

      以上经过了两次io,在不允许并发的可串行化中,吞吐量会很差。

      出于这个原因,具有单线程串行事务处理的系统不允许交互式的多语句事务。(还是redis)

    2. 分区

      将数据划分多个分区,一个事务查询一个或多个分区。只要多个事务查询的分区互不冲突,就可以并发执行事务。这种串行化比单分区事务慢。

  • 两阶段锁定

    两阶段锁定(2PL,two-phase locking),不是2PC

    • mysql的默认隔离级别为可重复读(mvcc是实现此隔离级别的技),在此级别下,如果两个事务同时尝试写入同一个对象,则锁可确保第二个写入必须等到第一个写入完成事务(中止或提交),然后才能继续(防止脏写)。

      事务A修改了id=3的数据,不会阻塞事务B的读,但是B读的是B开启事务时的旧数据。

    • 在2PL中,写入不仅会阻塞其他写入,也会阻塞读,反之亦然。快照隔离使得读不阻塞写,写也不阻塞读。

      事务A修改了id=3的数据,事务B会被阻塞,直到事务A中止、提交或者回滚。

    1. 实现两阶段锁

      2PL用于MySQL(InnoDB)的可串行化、SQL Server的可串行化、DB2的可重复读隔离级别。

    2. 2PL的性能

      因为2PL会导致阻塞,在事务中越早获取独占锁,就阻塞越久。且容易导致死锁。

    3. 谓词锁

      2PL无法解决幻读问题,因此需要引入谓词锁。

      在2PL级别下的幻读问题如下:

      // 1.事务A读取
      select name from student where id=2;// 结果 name='jack'
      // 2.事务B修改
      update student set name='rose' where id=2;
      // 3.事务A第二次读取,id=2的数据,会被阻塞
      // 4.事务B提交,释放锁
      // 5.事务A第二次读取的结果返回 name='rose',此时出现了幻读
      

      谓词锁即共享读,排斥写

      例如:

      // 1.事务A读取(创建谓词锁)
      select name from student where id=2;// 结果 name='jack'
      // 2.事务B读取
      select name from student where id=2;// 结果 name='jack'
      // 3.事务B修改
      update student set name='rose' where id=2;// 此时会被阻塞直到事务A提交
      

      关键思想是,谓词锁甚至适用于数据库中尚不存在,但将来可能会添加的对象(幻象)。如果两阶段锁定包含谓词锁,则数据库将阻止所有形式的写入偏差和其他竞争条件,因此其隔离实现了可串行化。

    4. 索引范围锁

      谓词锁性能不佳:如果活跃事务持有很多锁,检查匹配的锁会非常耗时。因此引入索引范围锁(index-range locking,也称为next-key locking)

      通过使谓词匹配到一个更大的集合来简化谓词锁是安全的。

      例如:如果有在12:00 ~ 13:00预定123号的房间,则锁定123号房间的所有时间段或者锁定12:00 ~ 13:00的所有房间。

      这是匹配开销和阻塞开销的平衡。

    5. 可串行化快照隔离

      可串行化快照隔离(SSI, serializable snapshot isolation)

      和mvcc类似,但是在事务提交时(写入操作),会判断事务期间操作的数据时候已被其他操作修改,如被修改,则事务提交失败

      // 1.事务A查询
      select name from student where id=2;// name='jack'
      // 2.事务B修改
      update student set name='rose' where id=2;
      // 3.事务A再次查询
      select name from student where id=2;// name='jack'
      // 4.事务A修改
      update student set name='张三' where id=2;
      // 5.事务B提交
      // 6.事务A提交,报错。因为事务B修改了数据。
      

      Q:为什么不在第4步的时候就报错,而是等待提交时才检测?

      A:如果事务A是只读事务,即没有第4步的update,则没有写入偏差的风险

      Q:如果事务B的提交放到第三步之前呢,事务A的再次查询会异常吗???

      A:我猜测会,否则就不是串行化了。实际结果如何,求大神解答

      可串行化快照隔离的性能

      与2PL相比,写不阻塞读,且读取运行在一致性快照上。

      与串行执行相比,事务能在保证可串行化隔离的同时读写多个分区的数据,很好利用了多核cpu的性能。

脚本宝典总结

以上是脚本宝典为你收集整理的《数据密集型应用系统设计》读书笔记全部内容,希望文章能够帮你解决《数据密集型应用系统设计》读书笔记所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。