深刻浅出计算机组成原理学习笔记:第五十三讲

1、上节总结回顾

上一讲里,根据DMP系统的各个应用场景,咱们从抽象的原理层面,选择了AeroSpike做为KV数据库,Kafka做为数据管道,Hadoop/Hive来做为数据仓库。算法

不过呢,确定有不信邪的工程师会问,为何MongoDB,甚至是MySQL这样的文档数据库或者传统的关系型数据库不适应呢?为何不能经过优化SQL、添加缓存这样的调优手段,解决这个问题呢?数据库

今天DMP的下半场,咱们就从数据库实现的原理,一块儿来看一看,这背后的缘由。若是你能弄明表今天的这些更深刻、更细节的原理,对于什么场景使用什么数据库,就会更加成竹在胸,而不是只有缓存

跑了大量的性能测试才知道。下次作数据库选型的时候,你就能够“以理服人”了。网络

2、关系型数据库:不得不作的随机读写

咱们先来想想,若是如今让你本身写一个最简单的关系型数据库,你的数据要怎么存放在硬盘上?最简单最直观的想法是,数据结构

一、你的数据要怎么存放在硬盘上?

一、用一个CSV文件格式。一个文件就是一个数据表。并发

二、文件里面的每一行就是这个表里面的一条记录。dom

三、若是要修改数据库里面的某一条记录,那么咱们要先找到这记录,分布式

四、而后直接去修改这一行的数据。读取数据也是同样的。高并发

要找到这样数据,最笨的办法固然是一行一行读,也就是遍历整个CSV文件。不过这样的话,至关于随便读取任何一条数据都要扫描全表oop

二、太浪费硬盘的吞吐量了。那怎么办呢?

咱们能够试试给这个CSV文件加一个索引。好比,给数据的行号加一个索引。若是你学过数据库原理或者算法和数据结构,那你应该知道,经过B+树多半是能够来创建这样一个索引的。

一、索引里面没有一整行的数据,只有一个映射关系,这个映射关系可让行号直接从硬盘的某个位置去读。

二、因此,索引比起数据小不少。咱们能够把索引加载到内存里面。

三、即便不在内存里面,要找数据的时候快速遍历一下整个索引,也不须要读太多的数据。

加了索引以后,咱们要读取特定的数据,就不用去扫描整个数据表文件了。直接从特定的硬盘位置,就能够读到想要的行。索引不只能够索引行号,还能够索引某个字段。
咱们能够建立不少个不一样的独立的索引。写SQL的时候,where语句后面的查询条件能够用到这些索引。

不过,这样的话,写入数据的时候就会麻烦一些。咱们不只要在数据表里面写入数据,对于全部的索引也都须要进行更新。这个时候,写一条条数据就要触发好几个随机写入的更新。

在这样一个数据模型下,查询操做很灵活。不管是根据哪一个字段查询,只要有索引,咱们就能够经过一次随机读,很快地读到对应的数据。可是,这个灵活性也带来了一个很大的问题,

三、加索引后查询操做很灵活、可是不管干点什么都有大量的随机读写请求

那就是不管干点什么,都有大量的随机读写请求。而随机读写请求,若是请求最终是要落到硬盘上,特别是HDD硬盘的话,咱们就很难作到高并发了。毕竟HDD硬盘只有100左右的QPS。

而这个随时添加索引,能够根据任意字段进行查询,这样表现出的灵活性,又是咱们的DMP系统里面不太须要的。DMP的KV数据库主要的应用场景,是根据主键的随机查询,
不须要根据其余字段进行筛选查询。数据管道的需求,则只须要不断追加写入和顺序读取就行了。即便进行数据分析的数据仓库,一般也不是根据字段进行数据筛选,而是全量扫描数据进行分析汇总。

后面的两个场景还好说,大不了咱们让程序去扫描全表或者追加写入。可是,在KV数据库这个需求上,刚才这个最简单的关系型数据库的设计,就会面临大量的随机写入和随机读取的挑战。

因此,在实际的大型系统中,你们都会使用专门的分布式KV数据库,来知足这个需求。那么下面,咱们就一块儿来看一看,Facebook开源的Cassandra的数据存储和读写是怎么作的,
这些设计是怎么解决高并发的随机读写问题的。

3、Cassandra:顺序写和随机读

一、Cassandra的数据模型

做为一个分布式的KV数据库,Cassandra的键通常被称为Row Key。其实就是一个16到36个字节的字符串。每个Row Key对应的值实际上是一个哈希表,里面能够用键值对,再存入不少你须要的数据。

Cassandra自己不像关系型数据库那样,有严格的Schema,在数据库建立的一开始就定义好了有哪些列(Column)。可是,它设计了一个叫做列族(Column Family)的概念,咱们须要把常常放在一块儿使用的
字段,放在同一个列族列族。好比,DMP里面的人口属性信息,咱们能够把它当成是一个列族。用户的兴趣信息,能够是另一个列族。这样,既保持了不须要严格的Schema这样的灵活性,也保留了
能够把经常起使用的数据存放在一块儿的空间局部性。

往Cassandra的里面读写数据,其实特别简单,就好像是在一个巨大的分布式的哈希表里面写数据。咱们指定一个Row Key,而后插入或者更新这个Row Key的数据就行了。

二、Cassandra的写操做

Cassandra解决随机写入数据的解决方案,简单来讲,就叫做“不随机写,只顺序写”。对于Cassandra数据库的写操做,一般包含两个动做。第一个是往磁盘上写入一条提交日志(Commit Log)。另外一个操做

则是直接在内存的数据结构上去更新数据。后面这个往内存的数据结构里面的数据更新,只有在提交日志写成功以后才会进入。每台机器上,都有一个可靠的硬盘可让咱们去写入提交日志。写入提交日志都是顺序
写(Sequential Write),而不是随机写(Random Write),这使得咱们最大化了写入的吞吐量。

若是你不明白这是为何,能够回到第47讲,看看硬盘的性能评测。不管是HDD硬盘仍是SSD硬盘,顺序写入都比随机写入要快得多。

内存的空间比较有限,一旦内存里面的数据量或者条母超过必定的限额,Cassandra就会把内存里面的数据结构dump到硬盘上。这个Dump的操做,也是顺序写而不是随机写,因此性能也不会是一个问题。除了
Dump的数据结构文件,Cassandra还会根据row key来生成一个索引文件,方便后续基于索引来进行快速查询。

随着硬盘上的Dump出来的文件愈来愈多,Cassandra会在后台进行文件的对比合并。在不少别的KV数据库系统里面,也有相似这种的合并动做,好比AeroSpike或者Google的BigTable。这些操做咱们通常称之为
Compaction。合并动做一样是顺序读取多个文件,在内存里面合并完成,再Dump出来一个新的文件。整个操做过程当中,在硬盘层面仍然是顺序读写。

三、Cassandra的读操做

 

 

当咱们要从Cassandra读数据的时候,会从内存里面找数据,再从硬盘读数据,而后把两部分的数据合并成最终结果。这些硬盘上的文件,在内存里面会有对应的Cache,只有在Cache里面找不到,咱们才会去请求
硬盘里面的数据。

若是不得不访问硬盘,由于硬盘里面可能Dump了不少个不一样时间点的内存数据的快照。因此,找数据的时候,咱们也是按照时间重新的往旧的里面找。

这也就带来另一个问题,咱们可能要查询不少个Dump文件,才能找到咱们想要的数据。因此,Cassandra在这一点上又作了一个优化。那就是,它会为每个Dump的文件里面全部Row Key生成一个
BloomFilter,而后把这个BloomFilter放在内存里面。这样,若是想要查询的Row Key在数据文件里面不存在,那么99%以上的状况下,它会被BloomFilter过滤掉,而不须要访问硬盘。

这样,只有当数据在内存里面没有,而且在硬盘的某个特定文件上的时候,才会触发一次对于硬盘的读请求。

4、SSD:DBA们的大救星

Cassandra是Facebook在2008年开源的。那个时候,SSD硬盘尚未那么普及。能够看到,它的读写设计充分考虑了硬件自己的特性。在写入数据进行持久化上,Cassandra没有任何的随机写请求,不管是
Commit Log仍是Dump,所有都是顺序写。

一、Cassandra在数据读的请求上作的优化

在数据读的请求上,最新写入的数据都会更新到内存。若是要读取这些数据,会优先从内存读到。这至关因而一个使用了LRU的缓存机制。只有在万般无奈的状况下,才会有对于硬盘的随机读请求。即便在这样的情
况下,Cassandra也在文件以前加了一层BloomFilter,把原本由于Dump文件带来的须要屡次读硬盘的问题,简化成屡次内存读和一次硬盘读。

这些设计,使得Cassandra即便是在HDD硬盘上,也能有不错的访问性能。由于全部的写入都是顺序写或者写入到内存,因此,写入能够作到高并发。HDD硬盘的吞吐率仍是很不错的,每秒能够写入100MB以上的
数据,若是一条数据只有1KB,那么10万的WPS(Writes per seconds)也是可以作到的。这足够支撑咱们DMP指望的写入压力了。

而对于数据的读,就有一些挑战了。若是数据读请求有很强的局部性,那咱们的内存就能搞定DMP须要的访问量。

二、问题就出在这个局部性上

可是,问题就出在这个局部性上。DMP的数据访问分布,实际上是缺乏局部性的。你仔细想想DMP的应用场景就明白了。DMP里面的Row Key都是用户的惟一标识符。普通用户的上网时长怎么会有局部性呢?每一个
人上网的时间和访问网页的次数就那么多。上网多的人,一天最多也就24小时。大部分用户一天也要上网2〜3小时。咱们没办法说,把这些用户的数据放在内存里面,那些用户不放。

那么,咱们可不可能有⼀定的时间局部性呢?若是是Facebook那样的全球社交⽹络,那可能还有⼀定的时间局部性。毕竟不一样国家的用的时区不同。咱们能够说,在印度人民的一天,把印度人民的数据加载到内
存里面,美国人民的数据就放在硬盘上。到了印度人民的晚上,再把美国人民的数据换到内存里面来。若是你的主要业务是在国内,那这个时间局部性就没有了。你们的上网高峰时段,都是在早上上班路上、中
午休息的时候以及晚上下班以后的时间,没有什么区分度。

面临这个状况,若是大家的CEO或者CTO问你,是否是能够经过优化程序来解决这个问题?若是你没有仔细从数据分布和原理的层面思考这个问题,而直接一下答应下来,那你可能以后要头疼了,由于这个问题颇有
多是搞不定的。

由于缺乏了时间局部性,咱们内存的缓存可以起到的做用就很小了,大部分请求最终仍是要落到HDD硬盘的随机读上。可是,HDD硬盘的随机读的性能太差了,咱们在第45讲看过,也就是100QPS左右。而若是全都
放内存,那就太贵了,成本在HDD硬盘100倍以上。

三、2010年SSD硬盘的大规模商用解决了局部性问题

不过,幸运的是,从2010年开始,SSD硬盘的大规模商用帮助咱们解决了这个问题。它的价格在HDD硬盘的10倍,可是随机读的访问能力在HDD硬盘的百倍以上。也就是说,用上了SSD硬盘,咱们能够用1/10的成
本得到和HDD硬盘一样的QPS。一样的价格的SSD硬盘,容量则是内存的10倍,也可以知足咱们的需求,比较低的成本存下整个互联网络信息。

不夸张地说,过去几年的“大数据”“高并发”“千人千面”,有一半的功劳应该归在让SSD容量不断上升、价格不断降低的硬盘产业上。回到咱们看到的Cassandra的读写设计,你会发现,Cassandra的写入机制完美匹配了咱们在第46和47讲所说的SSD硬盘的优缺点。

在数据写入层面,Cassandra的数据写入都是Commit Log的顺序写入,也就是不断地在硬盘上日后追加内容,而不是去修改现有的文件内容。一旦内存里面的数据超过必定的阈值,Cassandra就会完整地Dump一
个新文件到文件系统上。这一样是一个追加写入。

数据的对等和紧凑化(Compaction),一样是读取现有的多个文件,而后写一个新的文件出来。写入操做只追加不修改的特性,正好自然地符合SSD硬盘只能按块进行擦除写入的操做。在这样的写入模式下,
Cassandra用到的SSD硬盘,不须要频繁地进行后台的Compaction,可以最大化SSD硬盘的使用寿命。这也是为何,Cassandra在SSD硬盘普及以后,可以得到进一步快速发展。

5、延伸总结

好了,关于DMP和存储器的内容,讲到这里就差很少了。但愿今天的这一讲,可以让你从Cassandra的数据库实现的细节层面,完全理解怎么运用好存储器的性能特性和原理。

传统的关系型数据库,咱们把一条条数据存放在一个地方,同时再把索引存放在另一个地放。这样的存储方式,其实很方便咱们进行单次的随机读和随机写,数据的存储也能够很紧凑。可是问题也在于此,大部分的SQL请求,都会带来大量的随机读写的请求。这使得传统的关系型数据库,其实并不适合用在真的高并发的场景下。

咱们的DMP须要的访问场景,其实没有复杂的索引需求,可是会有比较高的并发性。我带你一看了Facebook开源的Cassandra这个分布式KV数据库的读写设计。经过在追加写入Commit Log和更新内存,
Cassandra避开了随机写的问题。内存数据的Dump和后台的对比合并,一样也都避开了随机写的问题,使得Cassandra的并发写入性能极高。

在数据读取层面,经过内存缓存和BloomFilter,Cassandra已经尽量地减小了须要随机读取硬盘里面数据的状况。不过挑战在于,DMP系统的局部性不强,使得咱们最终的随机读的请求仍是要到硬盘上。幸运的是,SSD硬盘在数据海量增加的那几年里价格不断降低,使得咱们最终经过SSD硬盘解决了这个问题。而SSD硬盘自己的擦除后才能写入的机制,正好很是适合Cassandra的数据读写模式,最终使得Cassandra在SSD硬盘普及以后获得了更大的发展。

相关文章
相关标签/搜索