C-Store: A Column-oriented DBMS(1)

  1.介绍

大部分的DBMS实现都采用面向记录(record-oriented)的存储方式,即把一条记录的所有属性(列)存储在一起。在这种行存储(row store)体系里,单次磁盘写操作就能把一条记录的所有列刷到磁盘里。这样能优化写操作,我们称这种存储体系为写优化(write-optimized)系统。

与之相反的是,面向ad-hoc大数据量查询的系统应该是读优化(read-optimized)的。例如数据仓库,常常是短时间内写入大批新数据,然后长时间的进行ad-hoc查询操作。其他以读操作为主的应用系统包括customer relationship management (CRM)系统,electronic library card catalogs和其他ad-hoc查询系统。在这些系统里,采用列存储体系,把同一列(或属性)的数据连续存储在一起,会更有效率。现有的数据仓库产品如Sybase IQ [FREN95, SYBA04]Addamark[ADDA04]KDB [KDB04]都能证明这个观点。在本文里,我们讨论一种叫做C-Store的列存储系统的设计,以及它的一些新颖特性。

在列存储体系里,DBMS只需要读取指定列的数据,避免了把不相干的数据带到内存里。在数据仓库里,查询一般是针对大量数据元素的集合型操作,列存储就有很大的性能优势。不过,还是需要仔细看看读优化系统和写优化系统之间的一些本质区别。

当前的关系型DBMS都把属性(列)填充到字节或双字节的边界,以本地格式存储数据。原因是他们认为在处理时进行数据的移位操作太耗时了。但是,CPU越来越快,而磁盘速度却没怎么增长。所以,用CPU时间交换磁盘带宽是有意义的,特别是在以读为主的环境里。

采用列存储,一般有2种途径用CPU时间交换磁盘带宽。第一种方法是采用高效的编码技术。比如存储一个客户的住址,可以用6bits来编码所有的US州名,如果采用州名缩写,则需要16bits;如果用全称则更多。第二种方法是浓缩存储的数据。比如把N个数据,每个K bits,直接打包成N*K bits。列存储里,编码和浓缩技术比在行存储里更有效,这一点[FREN95]已经指出来了。当然DBMS还是需要一些未压缩的数据副本(即cache),尽量避免解压操作。

商用关系型DBMS一般单独存储一行数据的所有列,用B树来索引。索引可以是主的(primary),记录会尽量按这个索引的顺序、接近的存储;或副的(secondary),记录不需要任何特殊的顺序。这些索引在OLTP的写优化系统里是高效的,但是在读优化系统里则不是。后者使用其他的数据结构会更好,比如bit map indexes [ONEI97]cross table indexes [ORAC04],和and materialized views[CERI91]。在读优化的DBMS里,只能使用读优化的数据结构,而完全不进行写优化。

所以,C-Store在物理上存储列的集合,每个集合都按照一些属性来排序。以同一个属性进行排序的列,组成“projections”(不知道翻译成什么,就用“列族”吧)。同一个列可能在多个列族里,按照不同的属性进行了排序。我们用强力的压缩技术,在得到多种排序方式的同时,避免存储空间的激增。多种排序方式还为我们打开了优化的大门。

显然,大计算量和大存储量应用系统最廉价的硬件和体系结构,就是普通PC机集群。所有,任何新的DBMS体系都要支持网格环境,支持拥有自己磁盘和内存的G nodes。我们建议在“shared nothing”体系下对磁盘数据进行水平分区。未来的网格计算机集群可能有成千上万个节点,所以任何新系统都需要支持这样的规模。当然,网格中的一个节点可能重合或由另一个计算机集群组成。优化网格环境已经给数据库管理员造成了很大压力,所以为网格节点分配资源必须要自动进行。在水平分区基础上,我们按照Gamma[DEWI90]的方法实现了内部的并行查询。

许多数据仓库系统(比如Walmart [WEST00])维护2份数据拷贝,因为通过DBMS的日志来恢复TB级的数据代价太高了。这个方案随着磁盘效率的提高而越来越有吸引力。在网格环境里,可以在多个节点里放置数据副本,所以也支持Tandem风格的高可靠系统[TAND89]。但也不是非要这样存储多个备份。C-Store允许多余对象按照不同的排序形式存储,以得到好的读取效率和高的可靠性。基本上,在列族里存储多余的列能很好的改善性能,即使某个G site挂了也能从多余的数据里寻找。一个系统允许K个失败,我们称之为K-safeC-Store通过可配参数调整K的范围。

即使在读频繁的系统里,也需要更新数据。数据仓库需要在线更新来更正错误数据,同样也不断有数据需要加入实际的数据仓库里,而且数据更新的反应时间要尽量接近0。最终的需求就是数据仓库的在线更新。显然,读频繁的世界,比如CRM,需要通用的在线更新方案。

在“提供更新”和“优化读取”之间,有一个矛盾。比如,在KDBAddamark里,数据列按照插入的顺序排列,这样插入新元素就非常高效。但是代价就是读取效率的下降,因为更有效的读取只有在另一种顺序下才能达到。不过,如果不按照插入顺序排列数据列,又会导致插入操作非常耗时。

C-Store用一种全新的视角解决了这个问题。我们结合了读优化的列存储系统和写优化的存储系统,用一个tuple mover来联系它们,如下图所示。在顶层,有一个小的可写存储组件(WS),支持高性能的插入和更新。还有一个大些的读优化组件(RS),支持大数据量读取。RS对数据读取进行了优化,只能进行特殊的插入,比如从WSRS的批量记录转移。这个转移工作就是由tuple mover来完成的。

 

 

 

 

当然,数据访问操作需要2个存储系统的合作。插入操作交给WS,删除操作则在RS里做标记,然后由tuple mover来清理。更新操作分离为插入和删除2个操作。为了提高tuple mover的速度,我们应用了LSM树理论[ONEI96]的一个变体,采用merge out操作来从WSRS移动大量数据。merge out用有效的方式把排好序的WS数据对象合并到大的RS块中,生成新的RS拷贝,在操作完成的时候使用。

上图的体系必须支持多ad-hoc复杂查询事务,小的更新事务,和可能的连续插入。显然盲目的支持动态锁定会导致阻塞和死锁,进而导致读写冲突和性能下降。

所以我们希望只读的查询运行在historical模式下。在这种模式下,查询操作选择一个时间戳T,它应该比最后提交的事务时间戳小,然后查询操作能保证产生的结果在那个时间点是正确的。为了提供这种snapshot isolation [BERE95]特性,C-Store需要在元素插入时标记时间戳,并在程序里小心的处理时间戳在T以后的元素。

最后,大部分商用的优化器和执行器(executors)都是建立在行存储体系之上的,而WSRS都是面向列的,于是有必要实现面向列的优化器和执行器。我们将看到,这样的软件与当前的传统设计完全不同。

本文描述一个可更新的列存储系统,C-Store。它对数据仓库式的查询达到高性能的同时,还能在快速处理OLTP式的事务。C-Store是一个面向列的DBMS,旨在减少查询时的磁盘访问次数。C-Store的革新点包括:

1.  一种由WSRS混合的架构,前者优化频繁的插入和更新,后者优化查询;

2.  同一列以不同的顺序在多个列族里存在,这样查询能选择最有效的顺序;

3.  强力的列压缩技术;

4.  面向列的优化器和执行器,和独有的特性;

5.  通过列族的重叠调整K-safe级别,达到高可靠性和高性能;

6.  使用snapshot isolation避免2PC和查询阻塞;

这里需要强调的是,虽然这些特点在过去可能被独立的研究过,但是现在我们把它们合在了一起,于是让C-Store变得有趣和与众不同。

本文第2部分展示C-Store的数据模型,第3部分介绍RS组件,第4部分介绍WS组件,第5部分考虑对网格节点的数据结构分配,第6部分是C-Store的更新和事务处理,第7部分介绍tuple mover组件,第8部分介绍查询优化器和执行器。第9部分给出C-Store和流行的商用行存储系统和列存储系统之间的性能比较。对于TPC-H式的查询,C-Store比其他系统明显要快。不过需要指出的是性能比较并不完整,我们没有整合WStuple mover,他们的耗时不可忽略。最后,第1011部分讨论未来的改进工作和结论。

2.数据模型

C-Store支持标准的关系型逻辑数据模型(logical data model),即一个由表格组成的数据库,每个表格都有一些属性(列)。和其他多数关系型系统一样,C-Store里的属性(或属性集合)能作为唯一的主键,或作为外键引用其他表的主键。C-Store的查询语言是标准的SQL语言。但是C-Store里的数据在物理上不是按逻辑数据模型存储的。大多数行存储系统以直观的方式实现表格,然后加入许多索引来提高访问速度,但是C-Store只实现列族。比如,一个C-Store列族由一个逻辑表格T锚定,且包含一个或多个T的属性(列)。一个列族甚至可以包含任意个表格里的任意个属性,只要从锚定表格到包含其他属性的表格之间有一系列的n:1(即外键)关系。

生成列族时,我们从T里选出感兴趣的属性,保留重复的行,然后通过外键从非锚定表格里得到相应的属性。这样,一个列族和它的锚定表格有相同数目的行。当然,可以有多个精心设计的列族。我们相信这个简单的方案能满足高性能的要求。同时要说明的是,我们对锚定表格并不进行存储。

 

 

 

我们把对表格t的第i个列族记为ti,后面跟列族里的属性名称。其他表的属性名称前还要加上逻辑表名。我们使用一个经典的表格组合:EMPnameagesalarydept)和DEPTdnamefloor)。EMP的示例数据在Table 1里。下面是一种可能的列族分布:

EMP1 (name, age)

EMP2 (dept, age, DEPT.floor)

EMP3 (name, salary)

DEPT1(dname, floor)

列族里的元素按列存储,所以如果列族里有K个属性,则有K个数据结构,每个结构存储一列,并按照同样关键字排序。关键字可以是列族里的任意列或列的组合。列族元素按照关键字排序,并从左到右排列。

我们在列族后面用竖线加属性名的方式表示排序关键字。例如下面的表示方式:

EMP1(name, age| age)

EMP2(dept, age, DEPT.floor| DEPT.floor)

EMP3(name, salary| salary)

DEPT1(dname, floor| floor)

最后,每个列族都水平分区成一个或多个组,每组都有标示符SidSid > 0C-Store只支持基于关键字的分区,所以一个列族的每个组都对应了一个关键字范围,而关键字范围的集合则把整个关键字空间进行了“分区”。

显然,为了完成一个SQL查询,C-Store需要一个覆盖集合(covering set),比如某个表的某个列在哪些列族里。同时,C-Store也必须能从不同的存储组合里重现表格的整个行。所以它需要利用storage keysjoin indexes来联合不同列族里的不同组。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值