Skip to content

设计数据密集型应用

其实关于这种神书,是需要反复阅读多遍的

第一章 可靠性、可伸缩性和可维护性

关于推特设计的例子

参考链接,这就是很好的一个数据系统设计的取舍,对于普通用户,和大V用户的设计分开,毕竟这两个用户的扇出差距巨大。 对于普通用户,会去预生成,因为写入很快,其发表推特之后,可以预先写入其关注者的数据列表中 对于大V用户,这种写入是有问题的,不可能并发去写百万或者千万的关注者的列表, 直接按照关系型数据库的设计去查表即可,但对于这个一定要做好缓存,尽量让查询都命中缓存
突然想到,其实微信朋友圈的设计就会比较简单了,都是预先生成列表,并且本身也可以针对用户id做数据的分片

第三章 存储和检索

两大类存储引擎,日志结构(log-structured)的存储引擎,面向页面(page-oriented)的存储引擎(例如B树) 日志结构学派:只允许追加到文件和删除过时的文件,但不会更新已经写入的文件。Bitcask、SSTables、LSM 树、LevelDB、Cassandra、HBase、Lucene 等都属于这个类别。 就地更新学派:将硬盘视为一组可以覆写的固定大小的页面。 B 树是这种理念的典范,用在所有主要的关系数据库和许多非关系型数据库中。

一些值得反复品味的话

消除额外复杂度的最好工具之一是抽象 高级编程语言是一种抽象,隐藏了机器码、CPU 寄存器和系统调用.SQL也是一种抽象,隐藏了复杂的磁盘/内存数据结构、来自其他客户端的并发请求、崩溃后的不一致性 确实整个计算机世界中无处不存在着抽象

关于B树与LSM树的一些比较

根据经验,通常LSM树的写入更快,而B树的读取速度更快,LSM树上的读取速度通常比较慢,因为他们必须检查几种不同的数据结构和不同压缩层级的 SSTables

一些优化的细节

要让存储引擎再实践中表现良好涉及到大量的技术细节。

  1. 查找数据库中不存在的键时,LSM树算法可能会很慢;你必须先检查内存表,然后查看最近的到最旧的所有的段(可能还必须从硬盘去读取每一个段文件),才能确认这个键是否存在。为了访问这种优化,存储引擎通常使用额外的布隆过滤器

关于分布式数据

需要考虑的三个点:

  1. 可伸缩性 如果你的数据量、读取负载、写入负载超出单台机器的处理能力,可以将负载分散到多台计算机上
  2. 容错/高可用性 需要在单台机器(或多台机器,网络或整个数据中心)出现故障的情况下仍然能继续工作
  3. 延迟 如果世界各地都有用户,可以考虑多地域多机房

第五章 复制

多主复制

自定义冲突解决逻辑:

  1. 写时执行
  2. 读时执行

无主复制

单主复制与多主复制

第六章

关于分区的问题,其实最容易遇到的就是热点的问题,不论是单key热点,还是单分片热点,或者说单key热点本身其实也就是单分片热点 可以在业务上去更好地将热点打散

再平衡策略

第七章 事务

两阶段锁定 2PL 和 两阶段提交 2PC的区别

一些疑惑

  1. 压缩的时候,如果有数据写入或者读取怎么办 我的想法,如果是有数据写入,就写到新的段里面,有读取的话,旧的段也是可以读的

  2. 关于Lucene中用的类似于SSTable的结构

  3. 关于bigtable的分区策略

  4. 遇到的单点key热点的问题

  1. 单分片一般有多副本,可以让读分流到其他副本上
  2. 本地的二级缓存
  1. 关于ES的分区问题 ES如何对数据进行分区,其实涉及到倒排索引与正排数据 倒排是否都是全扇出