Sybase在2004年左右就推出了列存储的Sybase IQ数据库系统,主要用于在线分析、数据挖掘等查询密集型应用。列存储,缩写为DSM,相对于NSM(N-ary storage model),其主要区别在于:
DSM将所有记录中相同字段的数据聚合存储;
NSM将每条记录的所有字段的数据聚合存储;
其实列存储并不是什么新概念,早在1985年SIGMOD会议上就有文章” A decomposition storage model”对DSM(decomposition storage model)做了比较详细的介绍,而Sybase更在2004年左右就推出了列存储的Sybase IQ数据库系统(见200年VLDB文章” Sybase iq multiplex - designed for analytics”),主要用于在线分析、数据挖掘等查询密集型应用。
列存储,缩写为DSM,相对于NSM(N-ary storage model),其主要区别在于,DSM将所有记录中相同字段的数据聚合存储,而NSM将每条记录的所有字段的数据聚合存储,如下图所示:
列存储有什么优点?
就我目前比较肤浅的理解,列存储的主要优点有两个:
1) 每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量,据C-Store, MonetDB的作者调查和分析,查询密集型应用的特点之一就是查询一般只关心少数几个字段,而相对应的,NSM中每次必须读取整条记录;
2) 既然是一个字段的数据聚集存储,那就更容易为这种聚集存储设计更好的压缩/解压算法。
列存储适合用在什么场合?
OLAP,数据仓库,数据挖掘等查询密集型应用。当然,列存储数据库并不是说完全不能进行更新操作,其实它们的更新操作性能并不是很差,一般也够用,但是一方面不如自己的查询性能,另外一方面也不如Oracle这种专门搞OLTP的数据库,所以一般就不提这个。
列存储不适合用在什么场合?
相对来说,不适合用在OLTP,或者更新操作,尤其是插入、删除操作频繁的场合。
为啥上世纪80年代就出现的概念现在又重新炒起来了呢?
2005年VLDB有篇文章(“One Size Fits All - An Idea Whose Time Has Come and Gone”),就是那个老牛M. Stonebraker写的,明确指出,时代变了,指望一个数据库产品就统一天下的日子已经一去不复还了。于是,这个老牛在2005年左右做了C-Store,一个列存储的数据库原型系统,在VLDB, SIGMOD等顶级国际会议上灌了几桶水后,
列存储的主要优点有两个:
1) 每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量,据C-Store, MonetDB的作者调查和分析,查询密集型应用的特点之一就是查询一般只关心少数几个字段,而相对应的,NSM中每次必须读取整条记录;
2) 既然是一个字段的数据聚集存储,那就更容易为这种聚集存储设计更好的压缩/解压算法。
列存储适合用在什么场合?
OLAP,数据仓库,数据挖掘等查询密集型应用。当然,列存储数据库并不是说完全不能进行更新操作,其实它们的更新操作性能并不是很差,一般也够用,但是一方面不如自己的查询性能,另外一方面也不如Oracle这种专门搞OLTP的数据库,所以一般就不提这个。
列存储不适合用在什么场合?
相对来说,不适合用在OLTP,或者更新操作,尤其是插入、删除操作频繁的场合。
有篇文章(“One Size Fits All - An Idea Whose Time Has Come and Gone”),就是那个老牛M. Stonebraker写的,明确指出,时代变了,指望一个数据库产品就统一天下的日子已经一去不复还了。于是,这个老牛在2005年左右做了C-Store,一个列存储的数据库原型系统,在VLDB, SIGMOD等顶级国际会议上灌了几桶水后,拉了一伙人出去开了个公司叫Vertica,将其商业化,专注于数据仓库、在线分析等市场,最近貌似还挺红火的;顺便说一下,为了贯彻上面的思想,这个老牛在同一时期又做了H-Store,一个主内存数据库原型系统,没怎么灌水就又招呼了一帮人开了个公司叫VoltDB,将其商业化,专注于联机事务处理。
M. Stonebraker在上世纪70年代带头开展关系数据库管理系统的实现工作,做出来了Ingres,其中灌水无数,从这个原型系统基础上产生了很多商业数据库软件,包括 Sybase、Microsoft SQL Server、NonStop SQL、Informix 等,而所谓的最先进的开源数据库系统PostgreSQL也是Ingres的一个后继分支。
Sybase IQ —— 革命性的产品。Sybase的数据仓库方法从根本上不同于其他的关系型数据库提供商。Sybase认为,传统的关系型方法以及ROLAP方法效率很低,要想获得足够的性能,必须通过高额的成本,在额外的硬件、软件、资源、钱、时间上进行大量投资,否则不可能达到。因此Sybase已经开发了一个新的关系型数据库——逆向关系型数据库可能是对此最好的解释,它使用一个传统的关系型结构以及类似的非常熟悉的术语,但是却是基于列的,而非基于行的。
我们开始审视Sybase IQ时,我们正是从此点开始,Sybase对其使用列方法的好处所作的论述“相当令人信服”。然而,通过对数据仓库不断增加需求、迅猛增长的来自Web的数据与用户所带来的分析和报表(更不必提即将到来的RFID应用),以及客户的经验等等,我们现在可以证明,Sybase IQ提供了一个远比那些传统的关系型供应商更优越的方法。
不同于传统的关系型数据库,其数据在表中是按行存储的,Sybase IQ是通过表中的列来存储与访问数据的。尽管这种方式很明显的不太适合于交易环境,在交易环境中,一个事务与一行数据有效对应,而在查询进程环境中,很显然,查询是基于特定的列来选择的。
列方式所带来的重要好处之一就是,由于查询中的选择规则是通过列来定义的,因此整个数据库是自动索引化的。事实上,情况并不象上述的这样简单,Sybase IQ有各种方法支持基于列的索引,我们将在下面就此讨论。
使用列方法的另一个结果就是,Sybase IQ在压缩方面比传统的关系型数据库更加有效(根据Sybase所称,效果可达5倍之好)。这个原因,无疑说,是由于同一列中的所有数据域有相同的数据类型。因此,每一列都可以为优化的效率与检索进行压缩。相比来讲,基于行的存储,各个不同的域拥有各不相同的数据类型,这非常适合于交易进程。在这样的环境中,不断变换理想的压缩算法是不可行的,这意味着任何压缩都将可能是一种最低通用的规则。
基于列的方法的另一个重要优势完全基于所有读出的数据量。无论何时你从传统的数据库中访问数据,你需要读出完整的每一行,而不管你实际所感兴趣的是哪些域。实际上,这可能意味着读300个字节的数据仅仅检索20个字符的数据。但是,基于列来读取数据,你仅仅需要读出你想要了解的数据。当然,读取一条单独的记录时,性能上的不同可以忽略,但是许多查询需要进行全表扫描。当读取数百万行数据时,性能的不同就会非常显著。
Sybase认为,Sybase IQ的列存储天然的比普通的ROLAP方法提供更佳的性能,IQ不需要象多数竞争对手或者Sybase Adaptive Server Enterprise(ASE)一样支持硬件的并行处理。尤其是,Sybase指出,与数据分区相关的问题就是需要支持硬件的并行机制。显而易见,不论如何进行分区,分区都会带来很多问题(更不必说额外的维护了),不过,它打开了性能改进的实质性途径。然而,Sybase进一步阐述道,这仅仅是对基于行的方法所与生俱来的糟糕性能的一种补偿机制。
Sybase有很多事实支持它的论断,但这并不意味着Sybase避免任何形式的数据分区。然而,不同于水平分区,Sybase IQ实施的是垂直分区,也就是说分区是按照列而不是按照行进行的。该方法的优势之一是分区从来不会变得不均衡,这是由于每个表中的每列都有相同数量的域。这大大降低了管理分区的维护需求,同时消除了数据库的重新组织,而在分区变得不均衡从而开始影响性能的时候,数据库重新组织是必需进行的。
最后,需要注意的是,Sybase IQ并没有避免使用OLAP。对于那些希望在聚合层次下以一个相对预先定义的方式进行查询的用户来讲,OLAP具有明显的优势。基于此,Sybase 支持OLAP功能属性如排列、百分比、平均。
传统的数据库引擎不能以一种通用的方式进行数据压缩,主要是由于存在以下三个问题:
1. 按行存储的数据存储方式不利于压缩。这是因为数据(大多为二进制数据)在以这种方式存储时重复并不多。我们发现,按行存储的数据,最多能有5-10%的压缩比例;
2. 对于许多2K 和4K 的二进制数据页来说,为压缩和解压缩而增加的开销太大;
3. 在OLTP 环境中,大量读取和更新混杂在一起。每一次更新需要进行压缩操作,而读取只需解压缩操作,大多数的数据压缩算法在压缩时比解压缩时慢4 倍。这一开销将明显降低OLTP 数据库引擎的事务处理效率而使得数据压缩的代价昂贵到几乎不能忍受。
在数据仓库应用中,数据压缩可以用小得多的代价换取更大好处。其中包括减少对于存储量的要求;增大数据吞吐量,这相当于减少查询响应时间。
Sybase IQ 使用了数据压缩。这是由于数据按列存储,相邻接的字段值具有相同的数据类型,其二进制值的范围通常也要小得多,所以压缩更容易,压缩比更高。Sybase IQ 对按列存储的数据通常能得到大于50%的压缩。更大的压缩比例,加上大页面I/O,使得Sybase IQ在获得查询的优良性能的同时,减少了对于存储空间的需求。
在传统的数据库中,为提高查询性能所建立的索引占用的磁盘空间往往需要比数据本身需要的磁盘空间多出3-10倍。而Sybase IQ 存储数据所占用的磁盘空间通常只是原数据文件的40%-60%,是传统数据库所占用空间的几分之一。
Sybase IQ与传统数据库在数据压缩方面的典型对比
智能压缩技术,与精巧的索引结构和列存储结合,给了IQ 比其他数据库引擎高的多的存储效果。这将获得更低的存储成本与更高的性能(因为系统仅需很少的磁盘I/O读取或写入任何给定的数据库块)。
Sybase IQ的秘密在于其索引。随着Sybase 客户发现了新的分析需求,Sybase可以简捷地建立新的索引以满足这些需求。这种方法的奇妙之处在于为数据仓库增加新的索引几乎不会(即使有也是微乎其微)影响数据仓库的架构或使用仓库的分析型应用。在实时企业与闭环应用领域,Sybase将索引视为在TB数量级(将来)甚至PB数量级数据仓库中获得更高查询性能的关键。今天,Sybase实际上已使用了如下几种种索引机制:
对于高基数的域,那些取值个数超过1500个(如金额值),Sybase使用其专利的被称之为Bit-Wise索引.这在你希望在范围搜索的时候同时进行计算的情况下,尤为有用,例如,查找销售价格低于50欧元的货品数量及总收入。如同位图的其他变量,该方法的优势之一就是计数(count)查询可以直接通过读取索引获得答案,而无需读取数据。
实际上,它是B-树索引。然而,此处的原则是,用户仅仅在几个列有可能作为一个组来使用的情况下,尤其是高基数与低基数的联合搜索时,才定义这些索引。比如可能有这样的例子,按照商店(低基数)查询产品销售清单与价格(高基数)。
2.3.4 Fast Projection 索引
该索引类型(缺省的)就是列存储本身。如果用户总是打算检索整个列的数据,则列存储事实上意味着列可以直接映射到表或查询中,而无需显式的定义任何索引。这非常有用,例如在“Where”从句中。
这是一个文本索引。它基于关键词或短语字符串搜索。这种类型的索引,历史上一直没有用于数据仓库中。然而,它有着大量重要的市场,在这些市场上,能够联合定量与定性的分析非常重要。例如,在医疗横业,医生的诊断通常就是:笔记。为了获取信息,例如发病率,因此可能必须访问这种非结构化的数据。
这个索引技术允许数据列的比较,从效果上讲,类似于“if…then…else”表达式。例如,“if支出大于收入,then…”。该类型的索引对于在Web应用中实时比较尤其有用。
正如索引的名称所示,它是为消除表连接的需要而设计的。正象大多支持索引的情况,它可能在预先已知的查询需求下更为有用。
这为基于日期、时间、日期与时间建立索引提供了选项。需要注意的是,对于传统的关系型数据库,处理基于时间的查询尤为困难。
大量扩展工具用以支持在各种情况下使用这些索引。这包括为减少硬盘(或内存——位图可能存在缓冲中)需求的索引压缩,联合使用不同类型索引的能力,以及使用布尔操作如AND与OR过滤比特队列等。这些特性表明,Sybase IQ克服了传统的位图的缺陷,即不适合于表连接或数据聚合。Sybase IQ在最近发布的版本中增加了一个索引顾问(Index Advisor),这一点尤其令人欢欣:这将建议管理员何时应该增加新的索引以及增加那种类型的索引。
将表放入存储系统中有两种方法,而我们绝大部分是采用行存储的。行存储法是将各行放入连续的物理位置,这很像传统的记录和文件系统。然后由数据库引擎根据每个查询提取需要的列。列存储法是将数据按照列存储到数据库中,与行存储类似;
基于行的存储是将数据组织成多个行,这样就能在一个操作中找到所有的列。这种做法的缺点是必须每次处理一整行,而不是只处理自己需要的列。不过,这样在处理相同实体的两个或多个列的查询时能够取得更快的速度,而且可以提高更新、插入和删除操作的速度。
基于行的存储系统可以进行并行处理,并且不需要模仿顺序文件系统,尽管有许多产品仍然在这样做。这种做法的缺点是,一旦确定了这种体系结构,那么使用的代码就不仅仅是“老式代码”,甚至比像“家族的诅咒”那样的代码还要难懂。
Teradata是一种非常流行的数据仓库产品,它使用了散列处理,并且从一开始就具有并行处理机制。最开始的时候它是一种数据库机,不过当前版本采用的是在标准硬件上建立虚拟机的方式。它从设计上就总是采用并行处理方式。各结点根据实际需要彼此交谈,而不是由一个中央点来控制。万维网就是采用了这种工作方式,因此对程序员而言,这种模型应该不会太陌生。(结点)数量将数据值尽可能均匀地分散到硬件存储设备中。如果结点数改变了,那么系统会重新分布这些数据。由于采用逻辑地址代替了人们在传统索引模型中使用的物理地址,因此用户根本不会看到这些过程。故障结点会对其数据进行重新定位,并将自身从系统中删除。新结点则会从现有结点将数据传送到其本地存储区,刀片服务器上使用了一种建立在内存中的模型。这里没有索引;数据都是尽可能多地保存在主存储器中,并在这里进行扫描。
基于列的访问存在的缺点是载入速度通常比较慢,因为源数据在外部来源中是以行或者记录的形式表示的。这样做的优点是针对某个列中的值进行简单查询的速度非常快,需要的内部存储资源最少。这表示对某个列中特定值的搜索可以直接进入该列的存储区,而不需要扫描整行的数据。这样也使得数据压缩变得更容易,因为一个列中的数据通常具有相同的数据类型。这种体系结构在处理数据仓库使用的海量数据时没有问题,但不适合需要进行大量以行的方式进行访问和更新操作的联机事物处理。就是这种数据库之一。在由一万亿行组成的测试数据集中,输入数据共很明显,这是一种适合数据仓库的技术。这种技术虽然在压缩和快速访问方面有优势,但也存在插入操作复杂的缺点。
引擎也采用了一种基于列的处理方式,但是它还对值进行标记,以获得更高的速度和更好的数据压缩效果。它们使用一种专用的位向量方案,可以在压缩的状态下进行搜索。这种技术非常适合档案处理,但是必须将标记恢复成其原始数据值才能显示,以及在表达式内使用。不过,在压缩方面鼓励将一个数据列分解成更多更详细的列。
随着计算机技术的快速发展以及数据库系统的深入研究和广泛应用, 人们在期望获得巨大
数据存储容量的同时, 对数据的检索效率, 尤其是即席查询和决策分析提出了更高的要求。列存储系统将同一列数据连续存储, 能避免在查询中访问无关列带来的性能损失, 使查询操作更有效率, 迅速成为数据库领域的研究热点。不少列存储系统如C-Store[1]、Sybase[2]、MonetDB[3]等, 都证实了列存储技术在读优先系统上的优越性。同时研究也发现, 列存储查询虽然可以避免操作无关列, 但还需连接相关列并将其组织成记录返回给用户。查询相关的列越多, 列之间的连接操作就越复杂。面对海量的复杂查询, 如何使列存储技术扬长避短, 充分利用其查询优势, 成为了当今列存储领域的研究重点。查询优化在数据库领域一直占有重要的地位。现有的列存储系统通过在存储上做改进来减少查询中的连接开销, 如C-Store 的“ 投影(projection)”技术[4−5]将属于同表的几列存储在一起; MonetDB 的“饼干图(cracker map)”[6]技术在查询时建立相关列的映射关系; PAX[7−8]将同一元组的属性存储在一个磁盘页上, 以此来加速同表之间的列连接。可以看出, 列存储技术虽然在存储方面已有很多研究成果, 在查询处理层的优化研究还相当少。这是由于列存储的处理对象缩小到“列”, 使得候选查询计划集合规模增大, 从而增加了查询优化本身的代价, 影响查询的性能。然而对连接用单一的处理方式, 也无法达到查询执行的最优效果。针对上述问题, 本文提出一种连接策略选择方法, 首先通过简单规则重写查询, 排除代价过大的计划, 生成候选计划树。进而提出动态优化树算法, 修改候选计划树中节点的执行顺序, 得到可被转化为最优计划的查询树。根据列存储的特点, 查询树中连接节点的连接策略可归纳为两种:串行连接与并行连接。在此基础上构建代价估计模型进行代价估计和策略选择, 不仅有效减少了代价估计的开销, 也增加了列存储查询处理的灵活性。经分析, 本文提出的连接策略选择方法能有效优化查询, 使得查询效率显著提高。
列存储的概念可以追溯到20 世纪70 年代,1976 年加拿大统计局开发实现了列存储数据库
管理系统RAPID[9], 并在80 年代广泛应用。随着企业对分析型查询需求的快速增长, 对列存储的研究在近十年得到了提升。早期的列存储模型有分解存储模型(DSM)[10]、PAX 等。新的列存储系统包括MonetDB/X100[11]、C-Store 等。研究表明,列存储数据库系统在分析型业务中的性能比行存储数据库系统性能超出多个数量级[5]。查询优化在数据库领域占有重要地位。目前较为成熟的优化方法有两类:基于规则的优化方法(rule based optimization, RBO)以及基于代价的优化方法(cost based optimization, CBO)[12−14]。基于规则的优化方法根据指定的规则或定义路径的优先级对逻辑计划进行优化。基于代价的优化方法通过统计信息和存取路径评估所有候选计划的代价, 选择代价最小的一个。通常优化器选择结合这两种方法来进行优化[14]。然而, 在列存储系统中的优化却相当少, 目前的列存储查询处理, 都将重点放在物理存储的改变上, 没有统一的规则或代价的权衡。C-Store 是开源列存储系统, 它将每列单独存储, 多列保存在一个投影(projection)中, 按照其中一列排序[1,4−5]。因此C-Store 查询经常基于一个投影, 或者含有公共排序列的不同投影, 以此减少列的连接代价。连接操作首先根据对排序列的筛选, 得到position 列表并用它过滤其他列。因此需要通过索引检索position 的第一个值来定位其他列的起始查找位置[4]。position 是基于排序列的, 若执行的谓词列在任何投影中都没有排序,则需对其进行全列扫描, 这样代价很大。MonetDB 以(key, value)形式存储数据, 利用“饼干图(cracker map)”来连接列。在多选择列之间, 选择某一列作为基列(左列), 跟其他相关列两两绑定在一起。根据左列的筛选条件进行分区, 并建立该分区的索引, 重新存储为M(crackermap)。由于基列一样, 使用位图向量之间的位与来连接列[6]。此方法在第一次查询的时候需要消耗大量的内存, 对列进行范围的划分以及索引的建立。在以后的查询中, 如果基于该列的谓词发生变化, 需重新划分范围并修改索引。该方法内存消耗巨大, 仅适用于内存数据库MonetDB , 却无法在列存储系统上通用。上述两个系统, C-Store 的查询执行器相当完善, 但是查询优化器却未实现[4]。MonetDB 的查询处理中, 执行计划并不通过代价模型评估, 仅是通过启发式规则来重写计划[15]。可见现有的列存储系统连接策略单一且局限, 在查询优化方向的研究非常少。本文结合简单规则和动态Huffman算法, 建立基于代价的连接策略选择模型, 针对不同情况处理列之间的连接。
定义 1 (空间) 列存储数据的查询处理对象为列, 属于一张表的列属于同一个空间。定义 2 (rowid) 为了重组一行数据, 每一列都附加一个伪列rowid, 形如, 如图
1. 每一列在rowid 上都存在B 树索引。
定义 3 (连接) 同空间内由and 连接的两个操作、两个列的比较操作称为同空间列的连接; 不同空间两列间的操作称为不同空间列的连接。定义 4 (串行连接) 一个连接操作的对象有两个孩子操作, 用一个操作的结果通过连接条件去过滤另一个操作的方法称为串行连接, 如图2。定义 5 (并行连接) 先分别执行两个相关操作,再通过连接条件得到结果的方法称为并行连接,如图3。
定义 6 (驱动列) 连接操作中, 两个处理对象中较少行数的列为驱动列。它可以是原始数据,
李静 等:列存储数据查询中的连接策略选择方法 853也可以是中间结果中的列。定义 7 (被探测列) 当从驱动列得到了一项数据以后, 在该探测列中查找符合条件的数据。
对于 SQL:select m from A, B where A.m=B.n其中m 为驱动列, 则连接策略如图2 和图3。
本文重点研究如下形式的查询:select A from T where Λ(P1,P2 ,...,Pm) (1)
T 是查询引用的关系集合; A 是引用关系T 的属性集; (P1,P2 ,...,Pm)是由and 连接的谓词。对于列存储而言, 该查询可转化为如下形式:
Ci 是查询相关的列, 如果Ck 上不存在选择谓词,设σ(Ck)为true。Fij 是连接条件, 如果Ci、Cj 上不存在连接谓词, 设Fij 为true。对于n 个节点的查询树来说, 列之间连接方法有种。基于代价的优化连接策略就是从中选择一个I/O 最小的计划, 然而数据按列存储后, n 往往很大, 这使得从中选择一个理想计划十分困难, 因此需要首先根据简单的规则削减计划的选择范围。
根据启发式原理:由于关系越小, 所需读写的I/O 也就越少, 因此中间结果之和较小的计划
很可能是较好的计划[12]。本文根据关系代数表达式的下推规则来执行不同空间的谓词下推; 并使用列的级联规则合并同列选择谓词[12]; 最后处理列间的连接谓词。通过以上规则得出式(2)中where 子句的形式:
定义选择空间 T, 连接空间J:
查询计划如图4, 用于连接的左深树能和连接算法很好地交互, 有利于形成有效的计划[12]。
对左深连接树而言, 应该选用估计数值较小的节点作为左变元[12]。本文结合动态Huffman
树[16]思想提出动态优化树算法, 改进查询执行顺序, 保证执行该树的代价尽可能最小, 如图5。(1) 利用动态Huffman 树原理修改空间之间的连接顺序。设 Tout 为每个空间根节点的输出元组数, 查询计划中的选择空间根节点Ti∈{T1,T2,…,Tn}的权值为Tiout。若存在某空间与多空间连接, 将该空间作为最左空间, 根据动态Huffman 树原理修改该空间与其他空间的连接顺序。若不存在这样的空间, 则修改所有空间的连接顺序。(2) 利用动态Huffman 树原理, 修改空间内的连接顺序。
优先处理选择性较好的谓词可以尽早减少需处理的元组数目, 从而避免I/O 的浪费[12]。设
C.ff 是C 列上满足筛选条件的选择率[17], 选择率越小, 选择性越好。若T 空间有n 个叶子节点, 设其权值为:C1.ff,C2.ff, …, Cn.ff. 。T 内Ci、Cj 列的连接结果权值为Ci.ff×Cj.ff, 因此动态Huffman树算法相当于从左至右结合最小ff 的节点。由于处理最左节点的I/O 过大会影响整体计划的I/O,优先处理带有索引的节点能够有效减少整体I/O。如图 4 所示, 当查找到C3 列的选择节点f3 是拥有索引的最小ff 节点, 且F2 仅是rowid 相等的连接条件时, 新建N_node 节点, 并通过1~4 步修改树的执行顺序, 删除虚线节点。对于每个连接节点, 其连接方式可归纳为两种:
串行连接和并行连接。若对每个节点都采用并行连接, 需考虑读取每列数据的I/O, 开销相当大。若都采用串行连接, 需考虑重复查找索引块和数据块带来的额外开销, 以及没有索引的情况下带来的巨大I/O 浪费。因此, 有必要定义代价估计模型进行策略选择。
查询计划的连接节点有两类:T 空间的中间节点和J 空间的叶子节点。针对这些节点的特点,本文提出一种代价估计模型:M={I, O, A}, 为每个节点决定连接策略。其中 I 是M 模型的输入信息, 是查询树节点的统计信息。它包括:
T 空间中间节点的统计信息node_info={Tout (A),B_info A∈T,B∈T} (7)Tout(A)是驱动列A 的筛选结果行数[17]; B_info 是被探测列的统计信息。
其中, FF(B)为B 列上满足条件的选择率[17]; B(B)为容纳一列B的数据块个数; T(B)为B的行数; V(B)为B 列的基数, 即B 列上不同值的个数; Htr(B)为B 列上rowid 索引的层数; Htv(B)为B 列上value索引的层数。J 空间的叶子节点的相关统计信息
node_info={J_info,T1_info, T2_info,Tmid_info} (9)
J_info={A_info, B_info A∈T1, B∈T2} (10)
{ } T1_info= T1out ,Columnlist (11)
{ } T2_info= T2out ,Columnlist (12)
{ , } out Tmid_info= T Columnlist (13)
其中J_info 是连接节点的信息; T1_info 是J 的左孩子所在空间的根节点信息, 它可以是T 空间的根节点, 也可以是J 空间的根节点; T2_info 是J的右孩子所在空间的根节点信息; Tmid_info 是先前节点处理的T1与T2的连接结果, 由于最左叶子节点还未处理连接, 因此该项统计信息为空;Columnlist 是相应空间内处理过的列的列表。O 是M 模型的输出集合, 它为每个连接节点选择代价最小的连接策略;A 是M 模型的算法, 分别针对T 空间的中间
节点和J 空间的叶子节点给出相应的策略选择。其中涉及的量值计算如下:
列存储系统的每一列数据的B树索引都是聚
簇索引。当A 为驱动列时, B 列与A 列的连接在B上所产生的磁盘I/O 为:
(1) 串行连接
① 根据 B 的索引:
对于驱动列A 中的每一条数据, 都约有B(B)/V(B)个符合条件的数据块。
② 根据 B 的rowid:
连接代价为查找索引块与数据块之和, 其中数据块可估计为:驱动列筛选的结果最稀疏和最
密集情况的折中。
(2) 并行连接
① 全列扫描:
FS(B)= B(B) (16)
② 索引扫描:
IS(B)=Htv(B)+B(B)× FF(B)
使用后序遍历查询树, 用M 模型算法A:
Cost_Model (node_info)为节点选择连接策略。
图 6 算法中2~5 行代码处理T 空间的中间节点, 为每个连接节点评估串行连接和并行连接的 I/O, 选取产生较小I/O 的连接方式。算法的 6~23 行处理J 空间的最左叶子节点:
(1) T1 处理的结果包含A 列, 可确定J 与T1空间的串行连接方式。T2 包含B 列时, 可确定A、B 并行, J 与T2 串行。T2 不包含B 列时, 根据代价的权衡来决定连接方式。(2) T1 处理的结果不包含A 列, 可根据代价估计A 列与T1 列的串并行I/O, 选择产生较小I/O的策略。接下来处理同(1)。算法的 24~29 行针对J 空间的右叶子节点:(1) T1、T2 筛选结果不包括A、B 列, 且节点本身独立执行连接的代价不大于过滤其兄弟节点的代价, 选择并行连接。
(2) 其他情况下, 用右节点直接过滤其左兄弟节点的结果。
至此, 本文给出列存储数据查询中的连接策略选择方法的总算法:Opt_Plan (tree), 如图7。
该算法对前文如(2)形式的SQL语句生成的语法树进行优化处理, 得到优化的查询计划。列存储数据查询可生成大量的候选查询计划, 中间关系的估计能够帮助选择较优的逻辑计划。本文用式(18)形式的SQL语句, 分析Opt_Plan算法。相关统计信息如表1 所示。
select , , , from ,
where and
and and
A.a A.b B.a B.c A B
A.b= 9 B.c= 8
A.c=6 A.a= B.a
同空间内 rowid 唯一, 每一个选择条件都能减少先前操作所产生的中间结果。执行A.b=9 and B.c=8 意味着两个空间的笛卡尔积操作, 中间元组约为7 500; 执行A.b=9 and A.c=6 产生的中间元组约为2。可见优先执行同空间内的选择, 能尽早减少需要处理的元组。因此优化的首要步骤是利用简单下推规则,排除代价过大的计划。驱动列的元组数是决定连接操作I/O 的主要因素。单独考虑A.a=B.a 节点, 将B.a 作为驱动列,串行连接需要约12 000 次I/O。将A.a 作为驱动列, 串行连接需要约7 000 次I/O。可见利用动态优化树算法修改执行顺序, 确定左变元为驱动列是非常重要的。简单规则和动态优化树算法都能有效地缩小中间结果之和, 具有最小中间结果之和的计划可能是较好的计划[12]。当涉及到具体执行时, 较小I/O 的计划被认为是较优计划, 因此对连接策略的选择非常重要。对优化过的式(18)产生的计划仅使用串行连接策略, 需要约245 次I/O(公式(19)); 仅使用并行连接策略, 需要约576 次I/O(公式(20)); 而M 模型评估的策略, 需要约95 次I/O(公式(21))。由于B.a 列不存在索引, 串行连接和并行连接都需要全列扫描B.a, I/O 开销较大。
可见 M 模型能够评估选择最优的连接策略,本算法能够有效地优化查询计划。不仅如此, 本文算法具有较好的可扩展性, 算法思想能在开源列存储系统上通用。以 C-Store 为例, C-Store 仅对排序列建立索引, 或者对所有列建立position 索引[4]。对于式(18)语句的A 表筛选条件来说, 若存在projection 包含A.b、A.c、A.a, 且根据A.c 排序, 则C-Store 系统上的计划为:全列扫描A.b 列, 得到长度约为100的position; 定位A.c 列的第一个position, 抽取A.c列相应position 上符合条件的值, 得到长度约为2的position, 最后得到A.a。优化后的计划为:索引扫描A.c, 忽略对索引块的访问, 可能只需要一个数据块的I/O, 得到长度为20 的position, 过滤A.b 列, 得到长度为2 的position, 最后得到A.a。显然, 经过优化的计划能以更小的I/O 得到较优的查询计划, 可见本文提出的优化算法思想可通用于列存储系统。
上述提出的方法根据简单规则过滤了代价过大的计划, 进而提出动态生成树算法, 根据动
态Huffman 树原理对候选查询计划树中的查询执行顺序进行修改并最终得到可生成较优计划的候选树。经分析, 该步骤能减少列连接过程中的I/O 开销。同时, 提出了基于代价的优化连接策略选择方法, 它针对数据按列存储后并行连接和串行连接两种策略进行代价估计和策略的选择, 充分利用了串行连接和并行连接各自的优势, 为列存储的查询优化提出了新的策略。经分析, 本模型能以较小的时空开销完成查询计划的优化。今后的工作重心将转向对并行连接策略的进一步研究, 对适用于列存储的哈希连接和排序合并连接等策略进行分析优化, 使列存储查询计划得到进一步的优化处理。
MonetDB是一个开源的高性能列存储数据库系统,比基于行存储的MySQL性能最多可提高10倍 , 参见 MonetDB的TPC-H 性能测试。 MonetDB目前仍然是一个学术机构的开源项目,差不多每6个月出一个Release,由于Bug众多并不推荐在生产环境中使用。
关于列存储数据库(Column-oriented DBMS) 近年来,从商业产品(Sybase IQ)到开源的BigTable类项目(HyperTable, Hbase, Cassandra等),列存储数据库在OLAP应用中的地位越来越突出。
安装步骤(CentOS 5.2 x86_64),安装过程同时编译了MonetDB的Java和PHP客户端库:
#wget http://apache.mirror.phpchina.com/ant/binaries/apache-ant-1.7.1-bin.tar.gz
将它们加入执行路径,例如/etc/profile中加入
export JAVA_HOME=/usr/local/jdk1.6.0_06
export ANT_HOME=/usr/local/apache-ant-1.7.0
export PATH=$PATH:$JAVA_HOME/bin:$ANT_HOME/bin:/usr/local/php-fcgi/bin
配置完后,重新登录,确认相关可执行文件的路径:
# java -version
java version "1.6.0_06"
Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
Java HotSpot(TM) 64-Bit Server VM (build 10.0-b22, mixed mode)
# javac -version
javac 1.6.0_06
# php-config
Usage: /usr/local/php-fcgi/bin/php-config [OPTION]
Options:
--prefix [/usr/local/php-fcgi]
--includes [-I/usr/local/php-fcgi/include/php -I/usr/local/php-fcgi/include/php/main -I/usr/local/php-fcgi/include/php/TSRM -I/usr/local/php-fcgi/include/php/Zend -I/usr/local/php-fcgi/include/php/ext -I/usr/local/php-fcgi/include/php/ext/date/lib]
--ldflags [ -L/usr/kerberos/lib64]
--libs [-lcrypt -lcrypt -lrt -lldap -llber -lpng -lz -ljpeg -lcurl -lz -lresolv -lm -ldl -lnsl -lxml2 -lz -lm -lssl -lcrypto -ldl -lz -lcurl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lresolv -ldl -lidn -lssl -lcrypto -lz -lxml2 -lz -lm -lxml2 -lz -lm -lxml2 -lz -lm -lcrypt -lxml2 -lz -lm -lxml2 -lz -lm -lxml2 -lz -lm -lxml2 -lz -lm -lcrypt ]
--extension-dir [/usr/local/php-fcgi/lib/php/extensions/no-debug-non-zts-20060613]
--include-dir [/usr/local/php-fcgi/include/php]
--php-binary [/usr/local/php-fcgi/bin/php]
--php-sapis [cli cgi]
--configure-options [--prefix=/usr/local/php-fcgi --with-config-file-path=/usr/local/php-fcgi/etc --enable-sockets --enable-pcntl --enable-mbstring --with-iconv --with-gd --with-curl --with-zlib --with-jpeg-dir --with-ttf --with-ldap --with-gettext --enable-soap --with-xmlrpc --with-openssl --enable-fastcgi --enable-discard-path --enable-force-cgi-redirect]
--version [5.2.6]
--vernum [50206]
#tar xvjf MonetDB-Jun2008-SuperBall.tar.bz2
#cd MonetDB-Jun2008-SuperBall
执行
#./monetdb-install.sh --prefix=/opt/MonetDB --enable-sql --enable-optimize
则安装编译MonetDB到/opt/MonetDB下。
PHP扩展等会自动编译,生成于 /opt/MonetDB/lib/php/extensions/下。
#/opt/MonetDB/bin/mserver5 --dbinit="include sql;"
#/opt/MonetDB/bin/mclient -lsql --time