【分布式数据库学习】由C-Store论文整理的而关于数据存储的相关知识

 一.数据处理类型

  • 联机事务处理 OLTP(on-line transaction processing)
  • 联机分析处理 OLAP(On-Line Analytical Processing)

区别:

  • OLTP 是传统关系型数据库的主要应用,用来执行一些基本的、日常的事务处理,比如数据库记录的增、删、改、查等等
  • OLAP 则是分布式数据库的主要应用,它对实时性要求不高,但处理的数据量大,通常应用于复杂的动态报表系统上

二.行式存储和列式存储

  • Row-based storage storesatable in a sequence of rows.
  • Column-based storage storesatable in a sequence of columns.

传统的关系型数据库,如 Oracle、DB2、MySQL、SQL SERVER 等采用行式存储法(Row-based),在基于行式存储的数据库中, 数据是按照行数据为基础逻辑存储单元进行存储的, 一行中的数据在存储介质中以连续存储形式存在

列式存储(Column-based)是相对于行式存储来说的,新兴的 Hbase、HP Vertica、EMC Greenplum 等分布式数据库均采用列式存储。在基于列式存储的数据库中, 数据是按照列为基础的逻辑存储单元进行存储的,一列中的数据在存储介质中以连续存储形式存在。

从上图可以很清楚地看到,行式存储下一张表的数据都是放在一起的,但列式存储下都被分开保存了。所以它们就有了如下这些优缺点对比:

1.在数据写入上的对比

1)行存储的写入是一次完成。如果这种写入建立在操作系统的文件系统上,可以保证写入过程的成功或者失败,数据的完整性因此可以确定。

2)列存储由于需要把一行记录拆分成单列保存,写入次数明显比行存储多(意味着磁头调度次数多,而磁头调度是需要时间的,一般在1ms~10ms),再加上磁头需要在盘片上移动和定位花费的时间,实际时间消耗会更大。所以,行存储在写入上占有很大的优势。

3)还有数据修改,这实际也是一次写入过程。不同的是,数据修改是对磁盘上的记录做删除标记。行存储是在指定位置写入一次,列存储是将磁盘定位到多个列上分别写入,这个过程仍是行存储的列数倍。所以,数据修改也是以行存储占优。

2.在数据读取上的对比

1)数据读取时,行存储通常将一行数据完全读出,如果只需要其中几列数据的情况,就会存在冗余列,出于缩短处理时间的考量,消除冗余列的过程通常是在内存中进行的。

2)列存储每次读取的数据是集合的一段或者全部,不存在冗余性问题。

3) 两种存储的数据分布。由于列存储的每一列数据类型是同质的,不存在二义性问题。比如说某列数据类型为整型(int),那么它的数据集合一定是整型数据。这种情况使数据解析变得十分容易。相比之下,行存储则要复杂得多,因为在一行记录中保存了多种类型的数据,数据解析需要在多种数据类型之间频繁转换,这个操作很消耗CPU,增加了解析的时间。所以,列存储的解析过程更有利于分析大数据。

4)从数据的压缩以及更性能的读取来对比

 

图列分析:首先将Customes Name列及Material列做逻辑化索引标识,查询时分别匹配Materia=Refrigerator及Customes Name=Miller的数据,然后做交叉匹配

3.优缺点

1)行存储的写入是一次性完成,消耗的时间比列存储少,并且能够保证数据的完整性,缺点是数据读取过程中会产生冗余数据,如果只有少量数据,此影响可以忽略;数量大可能会影响到数据的处理效率。

2)列存储在写入效率、保证数据完整性上都不如行存储,它的优势是在读取过程,不会产生冗余数据,这对数据完整性要求不高的大数据处理领域,比如互联网,犹为重要。查询过程中,可针对各列的运算并发执行(SMP),***在内存中聚合完整记录集,***可能降低查询响应时间;可在数据列中高效查找数据,无需维护索引(任何列都能作为索引),查询过程中能够尽量减少无关IO,避免全表扫描;因为各列独立存储,且数据类型已知,可以针对该列的数据类型、数据量大小等因素动态选择压缩算法,以提高物理存储利用率;如果某一行的某一列没有数据,那在列存储时,就可以不存储该列的值,这将比行式存储更节省空间。

4.使用场景

  如果你大部分时间都是关注整张表的内容,而不是单独某几列,并且所关注的内容是不需要通过任何聚集运算的,那么推荐使用行式存储。原因是重构每一行数据(即解压缩过程)对于HANA来说,是一个不小的负担。列式存储的话,比如你比较关注的都是某几列的内容,或者有频繁聚集需要的,通过聚集之后进行数据分析的表。

行式存储的适用场景:

  1、适合随机的增删改查操作;

  2、需要在行中选取所有属性的查询操作;

  3、需要频繁插入或更新的操作,其操作与索引和行的大小更为相关。

列式存储的适用场景:

  一般来说,一个OLAP类型的查询可能需要访问几百万甚至几十亿个数据行,且该查询往往只关心少数几个数据列。例如,查询今年销量最高的前20个商品,这个查询只关心三个数据列:时间(date)、商品(item)以及销售量(sales amount)。商品的其他数据列,例如商品URL、商品描述、商品所属店铺,等等,对这个查询都是没有意义的。而列式数据库只需要读取存储着“时间、商品、销量”的数据列,而行式数据库需要读取所有的数据列。因此,列式数据库大大地提高了OLAP大数据量查询的效率。

  很多列式数据库还支持列族(column group,Bigtable系统中称为locality group),即将多个经常一起访问的数据列的各个值存放在一起。如果读取的数据列属于相同的列族,列式数据库可以从相同的地方一次性读取多个数据列的值,避免了多个数据列的合并。列族是一种行列混合存储模式这种模式能够同时满足OLTP和OLAP的查询需求

  实操中我们会发现,行式数据库在读取数据的时候,会存在一个固有的“缺陷”。比如,所选择查询的目标即使只涉及少数几项属性,但由于这些目标数据埋藏在各行数据单元中,而行单元往往又特别大,应用程序必须读取每一条完整的行记录,从而使得读取效率大大降低,对此,行式数据库给出的优化方案是加“索引”。

  在OLTP类型的应用中,通过索引机制或给表分区等手段,可以简化查询操作步骤,并提升查询效率。但针对海量数据背景的OLAP应用(例如分布式数据库、数据仓库等等),行式存储的数据库就有些“力不从心”了,行式数据库建立索引物化视图,需要花费大量时间和资源,因此还是得不偿失,无法从根本上解决查询性能和维护成本等问题,也不适用于数据仓库等应用场景,所以后来出现了基于列式存储的数据库。对于数据仓库和分布式数据库来说,大部分情况下它会从各个数据源汇总数据,然后进行分析和反馈,其操作大多是围绕同一列属性的数据进行的,而当查询某属性的数据记录时,列式数据库只需返回与列属性相关的值,在大数据量查询场景中,列式数据库可在内存中高效组装各列的值,最终形成关系记录集,因此可以显著减少IO消耗,并降低查询响应时间,非常适合数据仓库和分布式的应用。

       

5.总结

1.传统行式数据库的特性如下:

  ①数据是按行存储的。

  ②没有索引的查询使用大量I/O。比如一般的数据库表都会建立索引,通过索引加快查询效率。

  ③建立索引和物化视图需要花费大量的时间和资源。

  ④面对查询需求,数据库必须被大量膨胀才能满足需求。

2.列式数据库的特性如下:

  ①数据按列存储,即每一列单独存放。

  ②数据即索引

  ③只访问查询涉及的列,可以大量降低系统I/O。

  ④每一列由一个线程来处理,即查询的并发处理性能高。

  ⑤数据类型一致,数据特征相似,可以高效压缩。比如有增量压缩、前缀压缩算法都是基于列存储的类型定制的,所以可以大幅度提高压缩比,有利于存储和网络输出数据带宽的消耗。

三:C-Store论文整理

     0.列式存储的发展历程

本文是列式存储系列的第一篇。在这个系列中,我们将介绍几个典型的列式存储系统。这些列式系统的出现都有各自的时代背景。在介绍这些系统的同时,我们也尽量介绍一下它们的背景,以便大家有一个更宏观的认识,理解这个系统为什么会出现,它要解决的问题,以及它如何影响后来类似系统的发展。

列式存储不是一个新概念,它最早可以追溯到上个世纪 70 年代。在上个世纪六七十年代,数据库学者们的主题是如何定义一个数据模型,以便更好的存储、管理和使用数据。被提出的数据模型多种多样,其核心就是构造什么样的逻辑关系,设计什么样的存储结构和计算方法,满足人们存储、管理和使用数据的需求。在这种背景下,1970 年就诞生了我们后来熟知的关系型数据模型[1]。关系型数据模型是一种逻辑模型,只关心数据的组织方式,不关心底层的存储,也就是说,它只是定义了一个数据该如何被组织、被表现的接口,至于底层,它是不关心的。由于一个关系被组织成一张表,所以天然的想法就是按照多维数组的方式存储数据,这种存储模型称为 n-array 存储模型(也叫 NSM)。传统的 n-array 模型按照数据写入的方式,将数据一行一行的存储下来,形成一个行优先的表。这种存储模型对数据的插入、删除、更新友好,是 OLTP 工作负载的理想选择。1985 年,Copeland 和 Khoshafian 提出了一种称为 “Decomposition Storage Model” DSM 存储模型[2],在这种模型中,数据以属性(也就是列)的方式进行存储,每一行数据有一个代理主键,每个列存代理主键和该列的值。当用户查询时,系统从最底层的存储层取出相应的列,并通过一定的算法还原出用户想要的 row,并返回给用户。DSM 的数据模型大概是下面这个样子

DSM 模型的特点是数据按列存储,每列数据定长,易于压缩。由于数据是排序的,因此可以按照列偏移量进行读取。另外,读取时可以只读取涉及到的列,而不涉及的列则不参与计算。这些特点使得 DSM 对分析任务友好。引起了业界的关注,启发并产生了一些基于列式存储的商用分析系统,如 SyBase IQ[7]。不过在 90 年代,数据量还不是很大,所以更多的数据仓库的设计都是基于关系型数据库的,数据库提供商想用一套方案覆盖所有场景,称之为“One Size Fits All”[3]。这种“One Size Fits All”的尝试在 2000 年后变得不再适应时代,因为互联网的爆发使得数据量激增,用户分析数据的时间花费变得非常漫长。在这种背景下,业界不得不探索新的解决方案。现实需要直接结果就是推动了整个分析型时长的繁荣。也就是大约 2005 年以后,我们看到了各种针对大数据量的分析型引擎雨后春笋般出现了(Google 的 Bigtable[6] 论文发表于 2006 年)。

下图是 column store 的一个发展历史[8]。(columns store 是基于列式的数据库系统,和目前流行的 NoSql 概念上还不太一样,注意图中 “Re-birth” 一词的含义)

     1.摘要

今天要介绍的第一个列式存储系统为 C-Store。C-Store 不像其他大数据系统那样出名,但在分析型数据库历史中的地位还是非常高的。C-Store 的提出者是 Michael Stonebraker,是数据库领域四位图领奖获得者之一,也是著名的商用分析系统 Vertica、开源数据库 Postgresql 的发明者。Vertica 正式基于 C-Store 的原型开发的,由此也可见 C-Store 的地位。

在 C-Store 的论文中,作者明确了写优化读优化两种系统的工作场景,并把前者行优先的存储方式称为 row-store,而列的方式称为 column-store(不知道 column store 一词是否由此而来)。作者指出了row-store 类型的系统在进行分析任务时运行低效的问题,并给出了 C-Store 如何解决这些问题的。同时,针对 column-store 不擅长的数据更新,作者也给出了自己的解决方案。这些 argue point 简要列在下面:

  • 需要额外精力处理内存对齐。CPU 运算速率以摩尔定律增长,而相应的主存存取速率却增长有限。一次性的主存访问大约需要几百个 CPU 周期。在这种情况下,数据库实现者需要仔细考虑内存对齐,以避免数据跨越边界,造成处理一次数据需要两次访问内存的情况。C-Store 不存在这个情况,因为列的长度都是固定的,一次可以处理 N 条数据,使得 N 条数据恰好填满一个数据块,将数据元素编码成最有效的形式,将解码的压力交给CPU,将数据集尽可能密集包装
  • 一般的数据块使用 B-tree 进行索引。索引键一般是主键。对于其他列,往往通过创建二次索引来进行快速检索。二次索引在进行点查时需要读取完数据才知道该条数据符不符合要求。因此为了增加查询性能,数据库系统需要引入对读友好的其他存储结构,比如 bit-map 索引,交叉表索引以及物化视图等等。C-Store 的处理方式有些不同。它将一个表的一个到多个列组成一个 projection,多个 projections 的并集为该表,且每个列可能属于多个 projections。Projection 可以按照某个列进行排序,这样一个列可能有多个排序方式。在查询时选择合适的 projection 就能达到一个比较好的效果。
  • C-Store 的 projection 带来了额外的好处。在集群环境下,存储成本很低,数据往往存在多份拷贝。但是没有必要为所有数据进行一模一样的拷贝备份。C-Store 将列分开存储,涉及查询频繁的列可以存放多份拷贝,并且存放在不同的 projection 里,使用不同的 sort order。这样 C-Store 在高性能和 HA 两方面取得了很好的平衡
  • 插入删除更新。虽然数据仓库更多的使用方式是批量的导入,很少的更新,但是更新也是有必要的。而且,发展趋势则是使数据流式地进入系统,甚至在线更新数据仓库中的数据。针对这样的需求,C-Store 设计了一个两层的架构,上层是一个面向写的 Writeable Store(WS),下层有一个面向读的 Read-Optimized Store(RS),中间运行一个 Tuple Mover 不断地将数据从 WS 移至 RS:  (这个结构有点类似与LSM树)
                                                     

在支持事务方面,C-Store 使用基于快照隔离的事务方案,用于避免额外的锁开销。

      2.数据模型

C-Store 的核心是 projection。一个 projection 就是一个或多个列的“部分表”,多个 projections 可以合成一张表,同时,一个表的一个列也可以出现在多个 projection 当中。一个 projection 中也可以包含其他表的列,前提是该 projection 所属(原文称 anchor,即锚表)表含有该其他表列的外键。一个 projection 有一个 sort key,用于确定该 projection 数据的排列方式。下面是一张逻辑表,以及几个可能的 projections:

其中 projection 括号中竖线后边的键为 sort key。另外,一个 projection 可以被按列拆分成多个分区,称为 segments,存放在不同的节点上。

有了 projections,一个核心问题是如何通过 projections 构建原表(这是所有列式系统共同的问题)。C-Store 的工具有两个:

  • Storage Keys(SK): C-Store 为每个 segment 中的每个列赋予一个 SK,SK 对应原逻辑表中对应列的位置。
  • Join Indices: Join Indices 用于关联不同 projections,下图是一个例子:
               

查询时,C-Store会先选择一组相对合适的Projection,然后进行计算。 为了支持同一个表能够放在不同的节点上,C-Store中的一个Projection按照排序列的取值范围,划分为不同的Segment,Segment支持备份。

可以看到,EMP1 和 EMP3 通过一个 Join Indices 相联系,从而构造出 (Name, Salary, Age) 这个 tuple。值得注意的是,这个 join indices 是单向的。为了构造出整张表,C-Store 需要在多个 projections 的 join indices 中找到一条能够构造出整张表的path(在上面的例子中,至少需要两个合理的 join indices,比如从 EMP3 到 EMP1 再到 EMP2,才能构造出原表)。C-Store 的这套 join indices 也有不足,即维护成本很高所以作者建议一个列最好保存在多个 projections 中,也就是说,尽量避免一个 projection 仅包含一个列这样琐碎,以减少 join indices 的数量。如果 projection 包含的列很多,存储成本会相应加大,但是性能会提升,反之存储成本下降,性能也下降,这是一个权衡的因素

       3.WS 和 RS

C-Store的基本架构如上图所示,有两种类型的节点,一种是为了读优化的并且是只读的(RS),另一种是可以写的(WS)。 插入、更新、删除等操作都发动到WS,而查询会发到RS和WS,并合并二者的结果。 数据周期性的通过一个叫Tuple Mover的组建从WS移动到RS。

RS是为了读而进行优化的,主要是对不同的列采用了不同的编码方式。 针对某一列,C-Store根据它是否是Projection的排序列,以及该列的取值个数,来决定采取何种编码方式。 具体说来,有如下几种:

  • 是排序列,并且取值的个数比较少,例如1,1,2,2,2,2,2,2,2,2,3,3,3,3,3。 C-Store会将这中列表示成一个三元组(v,f,n)的集合,v表示值,f表示该值的起始位置,n是该值一共的个数。 这里的例子可以表示为:(1,1,2), (2,3,8),(3,11,5)。
  • 不是排序的列,取值的个数比较少,例如0,0,1,1,2,1,0,2。对于这种列,采用Bitmap编码方式:(0, 11000010), (1, 00110100), (2,00001001)。
  • 是排序列,取值个数比较多,例如1,4,7,7,8,12。这种情况下按照块进行编码,每块内记录第一个值,其他的值都记和前面的差值,(1,3,3,0,1,4)。
  • 不是排序列,取值的个数比较多,在这种情况下,直接记录值,不进行编码。

WS是为了支持写的,WS中的每一列都用一个从行号到取值的B+树表示,对排序列,还记录一个取值到行号的B+树,以方便查询。

Tuple Mover周期性的把WS中的数据移动到RS中。 C-Store中的更新是通过一个删除加一个插入实现的,所以Tuple Mover的主要工作是载入RS中的数据,删掉在WS中被删除的,然后添加WS中新的数据。
 

RS 的主要功能是保存 projections 以及相应的 join indices。为了和 RS 保持一致,减轻 Tuple Mover 的额外开销,WS 中也保存了同样的 projections 和 join indices,只不过,WS 中经过了优化,以适应大量的写

1.首先 WS 中的 projections 并没有被压缩,

2.WS 中使用 B-tree 而不是直接排序来保证存储的顺序。WS 中的列按照 (v, sk) 的方式进行存储,其中 v 是属性的值,sk 是该列的 storage key,并且按照 B-tree 索引。对于 sort key,(v, sk) 就简化为 (s, sk),其中 s 就是 sort key。在进行基于 sort key 的查询时,首先根据 (s, sk) 找到 sk,然后再根据 sk 和 (v, sk) 获得其他列的值。

WS 和 RS 通过 Tuple Mover 相联系。Tuple Mover 使用一个类似于 LSM 的结构将 WS 中的数据持久化,并发往 RS 进行存储。

       4.事务

C-Store期望大部分的事务都是只读的,只有商量的支持更新的事务。 为了避免只读的事务有任何锁操作,C-Store使用了Snapshot Isolation的策略。 C-Store周期性的对已经提交的事务进行Snapshot,而只读的Transaction只能查询这些Snapshot而不是最新提交的结果。 对于少量的可以更新的事务,C-Store采用了传统的2PC。
 

C-Store 提供快照隔离的机制包保证事务。具体地说,当记录被插入时,其获得一个时间戳,如果查询某一个快照 S,那么时间戳大于 S 对于时间戳的那些记录将会被忽略。同时,update 操作也不是原地更新,而是被分成了 delete 和 insert 两个动作,delete 的 record 会被记录一个删除标记。这一点和 Hive ACID-2.0 是一样的(但是 C-Store 是 05 年的系统)。与 HIVE ACID-2.0 有点不一样的是,HIVE 使用了WriteId 来跟踪事务的顺序,WriteId 由中心化的 Metastore 产生。WriteId 跟 TransactionId 之间有些关联,当一个 Transaction 完成后,相应的 WriteId 就被持久化到 Metasotre,之后的读就可以读该 WriteId 及之前的记录了。C-Store 则使用类似的概念,称为“水位(Water Mark)”,水位有高水位和低水位,只有那些位于两者之间的记录才能被读取。C-Store 的水位用“时间戳”来标识,但这个时间戳不是真正的时间戳,而叫做 epoch。C-Store 引入了一个中心化的 Time Authority TA 节点,它在启动时将 epoch 设置为 0,此后周期性的将 epoch 加 1,并发送给其他节点。epoch 是时间的最小粒度。epoch 的工作原理如下图所示,当某个 epoch 结束开始一个新的 epoch 时,TA 发送 end of epoch 给各个节点。各个节点等待在此次 epoch 内的事务(包括之前未完成的事务)完成后发送 Epoch complete 到 TA。等 TA 收集到所有节点的 Epoch complete 事件后,TA 将水位更新。

               

对于事务的并发控制,C-Store 采用严格的两阶段封锁协议。在事务的提交方面,它使用省略了 PREPARE 阶段的两阶段提交协议来实现。

       5.查询

C-Store的查询执行和传统的Sql操作没有什么不同,主要是多了一个根据Bitmap来Filter数据的操作。 C-Store的查询优化最重要的一步是选出一组Projection

四:C-Store论文补充

背景知识

行式存储是当时数据库的主流,由于适用 OLTP 场景,于是叫做 write-optimized,而针对 OLAP 场景的系统叫做 read-optimized,如数据仓库。

CPU的增速比磁盘带宽快很多,于是可以牺牲一定的 CPU 来换取磁盘带宽。

有两种方式干这个事:(1)编码(2)densepack,紧凑存储,我理解就是压缩。

当时关系数据库不能很好的支持 OLAP 查询密集场景。于是作者提出了一个新的列存数据库 C-Store,这篇文章里包含很多内容,是个大杂烩,其中有几个新的特点:(1)write-optimized 和 read-optimized 混合架构 (2)存储模型,冗余的数据按不同顺序存储,来支持快速检索。(3)高效的压缩,直接处理压缩的数据(4)列式查询优化器(5)数据恢复(6)快照隔离避免 2PC

本文介绍其中的(1)(2)(5)

(1)混合架构

优化写入和优化查询是比较互斥的,比如直接按写入顺序存储数据,就像日志追加一样,但是这种方式对查询不友好,因为查询可能在另一种顺序下比较快。

一个模型适用两个场景是很难的,因此本文的架构是搞两个模块。一个模块负责处理快速写入,就是上边的 WS,一个模块负责提供高效的查询,就是下边的 RS,这样就需要一些连接器,就是 Tuple Mover,将 WS 中的数据同步到 RS 中。

作者的预期是 WS 相比 RS 而言是很小的一部分,可以全部放在内存中,其实这个架构就类似 LSM 了

为了实现简便,C-Store 用同一套列存引擎来管理 WS 和 RS,只不过在 WS 中多存一些索引信息用来快速定位数据。

(2)存储模型

projection:

每一个表可以绑定多个 projection,这是什么概念呢?每个 projection 是这张表的某些列的组合,是实际存储在磁盘上的,每个 projection 可以按不同顺序存储,一张表的每个列必须出现在至少一个 projection 中。一个表绑定的 projection 也可能包括其他表中的列(相当于重新划分表了)。

比如一张用户表(姓名,年龄,工资),可以绑定两个 projection,P1(姓名,年龄) order by 年龄,P2(姓名,工资),order by工资。

这样,按年龄查找姓名和按工资查找姓名这两种查询就可以分别分配到 P1 和 P2 里,每个都很快。

由于把各个列分散开了,就需要重组一行数据。这里涉及三个概念

  • SID:Segment id,每个 projection 水平分成多个 segment 分区,SID 就是分区号。
  • SK:Storage keys,每个分区中,给每行数据分配的一个自增的主键,用来将不同的 projection 对齐,其实就是行号、下标。

下图就是一个示例:

  • join index:为了重建一行完整的数据,需要将这些按不同顺序的记录映射到同一个顺序上,也就是 join index 的作用。比如将 projection2 映射到 projection1 上,这是个一对一映射。

这个 join index 可以是一条路径,比如还有一个 projection3 到 projection2 的映射,有传递性。这样,就能根据 join index 重组数据了。

在对数据的遍历过程中,将传统的按点返回的 Iterator 接口改成了批量返回的 iterator ,每个批次 64KB,避免了方法的过多调用。

  • 数据恢复

当节点挂掉但是数据没丢时,可以直接重启机器,把其他机器的执行队列中的操作拿过来执行。

当一个节点的 RS 和 WS 都丢了,就需要从其他节点的 projections 和 join indexes 重建这个节点的数据。

当仅仅 WS 丢了,可以快速从 RS 恢复出来,这个涉及快照隔离,不详细说了。

  • 局限

projection 是如何生成的没有具体说明,没有讲如何做负载均衡。

join index 的维护比较麻烦,尤其是加入update,在恢复数据时候也需要 join index,没有做错误恢复的性能。

在完成这篇论时系统还没开发完,功能不全,还是个单机系统。

(3)总结

C-Store 应该是第一个将各种列存技术在实际系统中实现出来的,并且对查询进行了优化,通过数据冗余和按需排序优化了查询性能。在 BigTable 的论文里轻怼了一下 C-Store,C-Store 更像一个关系型数据库,而 BigTable 的 API 比较底层,支持高吞吐率。C-Store 只对读做了优化,而 BigTable 即对读优化又对写优化。

五:C-Store论文所延伸的一些概念

1.InnoDB一颗B+树上能存放多少数据?

原文链接如下https://www.cnblogs.com/leefreeman/p/8315844.htm

InnoDB中存储单位是一页16k,一个普通表一行数据大约是1k,一页大概能存储16行数据

先将数据表按主键排序存储在相应的页面上(页面一般为连续),如果顺序遍历页面则时间实在太慢,所以采用B+树进行存储加快索引速度。

不仅由存放数据的页面,还有存放指向数据页面的指针加键值的页面,这样的页面称为索引页面

如select * from user where id=5;

这里id是主键,我们通过这棵B+树来查找,首先找到根页,你怎么知道user表的根页在哪呢?其实每张表的根页位置在表空间文件中是固定的,即page number=3的页(这点我们下文还会进一步证明),找到根页后通过二分查找法,定位到id=5的数据应该在指针P5指向的页中,那么进一步去page number=5的页中查找,同样通过二分查询法即可找到id=5的记录。

现在我们要计算出b+树非叶子节点上能存储多少指针键值,我们假设主键ID为bigint类型,为8字节,InnoDB中指针设置为6字节,则一组指针键值占14字节。16k/14=1170个指针键值,则2层B+树可存储1170*16=18724行数据,则三层B+树科存储1170*1170*16=21907748行数据。

2.LSM-tree 基本原理及应用

B+树与LSM树的比较:https://blog.csdn.net/u013928917/article/details/75912045

LSM树原理详解:https://blog.bcmeng.com/post/lsm-tree-vs-b-tree.html

B树的由来以及应用场景https://www.cnblogs.com/yanghuahui/p/3483047.html

3.快照隔离

https://www.jianshu.m/p/44a22ff62c2d

发布了13 篇原创文章 · 获赞 7 · 访问量 382

猜你喜欢

转载自blog.csdn.net/weixin_39966701/article/details/102647195