oracle 12c 列式存储 ( In Memory 理论)

随着Oracle 12c推出了in memory组件,使得Oracle数据库具备了双模式数据存放方式,从而可以实现对混合类型应用的支持:传统的以行形式保存的数据知足OLTP应用;列形式保存的数据知足以查询为主的OLAP应用。in memory组件能够和其余数据库组件功能使用,并不须要用户单独开发或者修改应用程序,就能够很是方便的实现基于实时数据库分析的转变。本文会介绍in memory组件的一些相关知识,包含了如下的内容:html

-列式存储的基本知识 
-访问in memory area中的数据 
-In memory和RAC的融合sql

1.列式存储的基本知识。数据库

1.1内存结构设计模式

传统的数据库采用的是行式存储,当一个事务发生时,oracle会对一行(或多行)数据进行操做,也就是说数据的操做单位是一行数据,即便可能须要被访问的数据只是其中的几个列,这种数据保存方式对以DML为主的OLTP应用是很是适合,也是很是高效的。可是在OLAP系统当中,针对大量数据的查询操做是占绝对地位的,而这些查询每每只针对表中一些特定的列。另外,数据的改变都是以数据装载的方式发生的,也就是说数据被装载到数据库后是极少发生改变的,毫无疑问以列的方式组织数据无疑是更好的选择。正是由于这两种存放数据的方式各有利弊,不管以哪种方式来保存数据都没法很好的知足混合式应用的数据库系统的要求,Oracle推出了所谓的双模式数据存放方式:在磁盘(也就是数据文件)和database buffer cache中以行的形式存放数据;单独开辟一块内存空间(in memory area),其中以列的方式保存数据,知足OLAP类型的查询需求。而Oracle之因此选择单独开辟一块内存来保存列模式数据的主要缘由之一就是OLAP的应用是以查询为主的,并且数据改变的发生方式绝大部分都是以数据加载的方式发生的,这意味着oracle彻底也经过批量数据加载的方式来完成in memory area空间中的数据加载从而保证数据的实时性。接下来,从in memory area内存结构,数据加载过程两个方面来介绍in memory组件的一些基本知识。数组

首先,in memory area是独立于传统的SGA和PGA的单独的内存空间,由1Mpool和64Kpool两部分构成。其中1M pool用于保存列格式的数据,IMCU(in memoryCompressionUnit)是基本的存储单位;64Kpool用于保存和IMCU相对的元数据信息,SMU(SnapshotMetadataUnit)是这部份内存的基本单位。读者能够经过下面的查询了解相关的信息。 
图片描述网络

IMCU是用于在内存中保存列格式数据的基本存储单位,oracle会尽可能保证每一个IMCU的大小为1M,每一个IMCU由图1所示的两部分构成并发

图片描述

图1

 

 

SMU部分主要用于保存IMCU的原数据信息,例如:IMCU对应的指针,IMCU包含的extent范围,DBA范围,Journaltable的指针,Rowid位图等。oracle

1.2数据加载(populate)函数

在了解了in memory如何在内存中保存数据以后,再来看一下数据是如何被加载到内存中的。根据以前内容的介绍,数据在数据文件中是以行格式来保存的,那么就须要一种机制来把数据加载到in memory area当中,而且在加载过程中完成从行模式到列模式的转变。性能

首先,oracle支持对表,分区或表空间指定in memory属性,也就是说in memory属性是针对物理数据库对象的,而不是逻辑数据库对象的。例如:咱们可使用下面的语句来为数据库对象指定in memory属性:

SQL>alter table sales inmemory no memcompress priority critical; 
SQL>ALTER TABLESPACE ts_data INMEMORY; 
SQL>ALTER TABLE sales MODIFY PARTITION SALES_201501 INMEMORY;

须要说明的是,因为in memory组件主要是针对OLAP应用的,而这种应用绝大部分的操做都是查询,并且不少时候只关心表中特定的一个或多个列,因此in memory特性还能够指定只把表中的特定的一个或多个列加载到in memory area当中。

因为in memory area区域的大小是有限的,主机的内存资源也是有限的,而数据库的容量每每会超过已有的内存资源,因此Oracle建议将性能要求很高的表装载到in memory area当中,而将性能要求比较低的表保存到闪存或者磁盘上。固然,若是内存资源充足,并且数据库不大,大部分的应用是以查询为主,也能够考虑将全部的表都装载到in memory区域中。另外,也正是因为资源的限制,Oracle容许用户为不一样的表设置in memory加载优先级,基本的原则是优先级高的对象被首先加载到in memory区域当中,优先级低的对象须要等到高优先级的对象加载完毕以后才可以被加载。Oracle提供了5种in memory加载优先级,表1包含了每种优先级的详细信息。 
图片描述

表1

 

另外,因为in memory主要是面向查询为主的OLAP或者决策支持系统,也就是说绝大部分的数据再被装载(Load)到数据库以后就不会再改变了,那么在加载数据的同时对数据进行压缩无疑能够节省内存空间,并且还可以提升查询的效率(主要的缘由是不少被查询的列会包含大量的重复值)。因此in memory组件提供了丰富的压缩选项,容许用户在为对象指定in memory选项的同时指定压缩方法。表2列出了支持的压缩级别:

图片描述

上表中的压缩比率由上至下,愈来愈高。如下的sql语句说明在将表salse加载到in memory area时的优先级最高,并且须要使用“memcompress for query”方式进行压缩:SQL>alter table sales inmemory memcompress for query low priority critical; 
若是须要在指定压缩选项以前了解每种压缩选项可以得到的压缩比,可使用Oracle Compression Advisor(DBMS_COMPRESSION包)来进行估算。

最后,加载过程是经过后台进程IMCO和工做进程(W00)进程来协同实现的,在数据库启动后或者一些对象的in memory选项被启用后,IMCO进程会建立出一些加载任务,并根据须要分配给若干个工做进程,每一个工做进程负责一部分数据的加载工做,当全部工做进程完成了对应部分数据的加载以后,通知IMCO进程加载完成。

2.In memory的数据一致性

若是咱们的数据库是只读的,那么事情就变得简单多了,由于数据就不会存在一致性问题,可是事实并不是如此,对于大部分的数据库,事务处理是一直都会发生的,那么数据的一致性就须要获得保证。对于in memory组件也不例外,若是DML语句修改的数据并无被加载到in memory区域当中,那么DML语句的修改就仅限于SGA当中;反之若是修改的数据已经被加载到了in memory区域中,那么就须要一种机制来确保数据的一致性。例如:没有被提交的数据不能被看到,而执行改变的会话应该能看到最新的数据。

Oracle 是经过journal table 的方式来确保数据的一致性的。每一个IMCU都会对应一个本身的journal table, 若是DML语句修改的数据包含在IMCU当中,就在journal table 当中把修改后的数据记录下来,咱们称之为private journal;当事务提交以后,再把journal table当中对应的记录标识成为shared journal。这样就能够保证查询在访问IMCU时可以得到一致的数据,而若是查询须要的数据在journal table 中也没法找到时,oracle 会自动根据IMCU中记录的Rowid 位图中的信息映射到buffer cache 当中相应的位置来找到知足查询要求的数据。图2描述了journal table和IMCU的基本关系。

图片描述

图2

 

然而,若是DML 语句不断发生的话,就会使journal table 中的数据愈来愈多,甚至出现IMCU中大部分的数据都是旧数据,而新数据都保存在journal table中的状况,这对于in memory查询的性能伤害是很大的。因此,Oracle定义了一个阀值(threshold),当IMCU中旧数据的比例达到这个阀值时就会触发从新加载的过程,也就是说,IMCO后台进程会每隔一段时间(默认2分钟)检查一次是否有IMCU 知足从新加载的条件,若是发现了知足条件的IMCU,就会通知W00工做进程对相应的IMCU进行从新加载,可是因为从新加载的成本是比较高的,并且可能会影响一些正在运行的语句,因此Oracle 会采用渐进的方式来对IMCU进行从新加载的操做,也就是每次只选择一部分知足从新加载条件的IMCU进行处理,而具体的程度能够经过INMEMORY_TRICKLE_REPOPULATE_SERVERS_PERCENT参数来进行调整。

对于事务所产生的journal table对系统产生的额外负载到底有多大,这个是很难进行量化的,由于有太多的因素会对它产生影响,例如加载时采用的压缩方法,改变的方式,应用程序访问数据的行为。可是,仍然有一些基本的原则是能够尽可能减小数据改变对in memory 产生的影响的。因为数据再被加载到in memory area时是以extent 为单位的,若是对数据的改变是随机分布到表的各个extent的话,从新加载的成本就会很高,由于这意味着大量的IMCU须要被从新构建;而若是数据的改变可以集中到特定范围的extent中,或者大部分的改变都是数据插入并且使用直接路径加载的话,那么从新加载的成本就会被大大下降。另外的建议就是对尽可能使用分区表来保存数据,这样有利于将数据的改变限定到特定的分区当中,并且针对这些分区不使用或者尽可能使用 DML,MEMORYCOMPRESS FOR DML这些轻量级的压缩方式。

3.访问in memory area中的数据

3.1单表访问

在数据被加载到in memory区域以后就能够经过sql语句对它们进行访问了。分析型查询的一个很大的特色就是它只关心表当中特定的一些列而不是所有的列,并且这些列的值不少时候会有大量的重复值,而且做为条件的列不少时候都是常见的数据类型(例如:数值,字符串,日期),基于这些特色,Oracle的in memory组件也作了相应的设计来提升这些分析型查询语句的性能。

首先,在IMCU当中每个列都会包含对应的字典信息和存储索引信息。在加载过程中,工做进程会将对应的IMCU中每一个列所拥有的不一样值编写成一个字典,以后为该列的每一行数据指定一个keyvalue,用这个keyvalue来代替具体的值,这样作既能够节省空间也为未来查询时可以使用CPU的SIMD技术作准备。而存储索引(StorageIndex)其实是数据仓库中常见的一种技术,他经过记录某一个列的最大值和最小值的方式可以避免访问大量不知足条件的数据。在IMCU中每一个列的头信息当中都会保存这个列在对应的IMCU当中的最大值和最小值,以及他们所对应的偏移量。经过这种方法就能够在查询数据时经过对比最大和最小值的方式快速过滤掉不知足条件的数据,并且一旦数据改变影响到了存储索引中的信息,能够快速定位到对应的位置。可是须要指出的是,存储索引并不见得适用于全部的where条件(谓词)。

另外,因为数据已经被加载到了内存当中,因此绝大部分的操做都是须要经过cpu来实现的,I/O相关的操做基本不会出现了(除非被查询的表有一部分数据尚未被加载到in memory区域中来)。如何可以更加高效的利用CPU资源就成为了决定性能的一个重要因素,因此Oracle采用了SIMD技术(Single Instruction processing Multiple Data values)使CPU能在一个指令当中访问多个数据,可是因为SIMD所支持的指令是有限的,因此这也解释了为何Oracle在构建IMCU时会为每一个列都建立字典信息。图3描述了SIMD访问数据的基本概念.

图片描述

图3

 

在上图中,sales表被加载到了in memory area当中,并且IMCU中PROMO_ID列的头信息当中也包含了该列的字典信息,该列当中的每一行的值都已经被转换成为了keyvalue,当查询条件为PROMO_ID=9999是,就能够利用SIMD技术使CPU每次比较多行数据,从而极大地提高了查询的性能。

最后,咱们能够经过在执行计划中查找“TABLE ACCESS INMEMORY FULL TEST”信息来确认是否使用了in memory选项访问表。例如: 
图片描述

3.2多表链接

除了针对访问表的优化,in memory组件针对表链接也进行了改进,主要的特性有:布隆过滤器和in memory聚合。

对于布隆过滤器(Bloom Filters),相信你们并不陌生。它的主要做用就是判断某一个数据是否出如今另外一个集合当中,或者用于比较大数据集合之间的共同元素。Oracle从10g开始就在处理一些SQL语句中的表链接时使用布隆过滤器。若是表链接中涉及到的表都已经指定了in memory属性,而且已经加载到了in memory area当中,那么优化器会首先选择链接中的一个表(一般是较小的表),对做为连接条件的列进行一系列的hash函数,并产生一个结果位图(bitmap),以后再对另外一个表的数据分批进行一样的hash函数,并和以前的结果位图进行比较,在整个过程当中并不会产生I/O并且SIMD技术在比较过程当中也能够被使用,因此布隆过滤器的引入使in memory在处理表链接时变得更加高效。

CBO会在制定执行计划时自动判断是否使用布隆过滤器,用户不须要手动指定。若是在执行计划中看到了如下的信息,说明布隆过滤器被使用了。

图片描述

在上面的执行计划说明:

1.首先在in memory area中访问了表“TEST_SMALL”,就是执行计划中的第5步,以后构建了连接使用的过滤器(BF0000),也就是执行计划中的第4步。 
2.以后在in memory area中访问了表“TEST_BIG”,就是执行计划中的第7步,以后使用了以前构建的过滤器。

3.3多表链接

在以分析型的查询语句为主的数据仓库应用当中,除了简单的表链接,还常常出现多表的连接,并且常常会包含一些聚合和分组操做,例如数据仓库应用当中的星型查询。针对这种查询,oracle提出了向量分组(VectorGroupBY)特性来提升select语句的性能。向量分组是一个两阶段的过程:

阶段1:CBO会找到查询中数据量较小的维度表(Dimension table),将知足条件的做为和庞大的事实表(Fact table)进行链接的列找出来并生成向量组(Vector Group)。以后将向量组和须要进行分组或者聚合的事实表中的列组合,造成一个多维数组和若干个临时表。

阶段2:在事实表上应用上一阶段产生的向量分组,以后向临时表当中添加须要计算分组或聚合结果的列的值。最后将这些临时表的数据应用到多维数组中,计算出最后的分组或者聚合结果。

在整个过程当中向量分组的构建和向量于事实表的比较都是在内存中完成的,并且SIMD也会被使用,因此能够极大的提高这种查询的性能。固然,因为这种操做都是在内存中完成的,因此对系统的内存资源要求也是比较大的,要求运行这种查询的进程拥有足够的PGA空间。下面的执行计划说明了分组向量在查询中的应用:

图片描述

根据上面的执行计划:

-首先,表”TEST_SMALL_1”和”TEST_SMALL_2”被访问,固然它们都已经被加载到了in memory area 
当中。以后分组向量被构建,他们是”KV0000”和”KV0001”,并且在和须要分组的表进行结合后,临时表也被建立了出来,它们是“SYS_TEMP_0FD9D6604_116B7C6”和“SYS_TEMP_0FD9D6604_116B7C6”。 
-表“TEST_BIG”被访问,以后向量分组被应用到了这个表上。而后开始向临时表当中添加分组结果。 
-多维数组中的结果被生成,它是“VW_VT_F486F43F”。最后经过“HASH GROUPBY”的方式完成最后的分组。

4.in memory和RAC的融合

延续Oracle新特性的一向特色,in memory特性也能够和已经存在的其它数据库组件兼容,例如RAC,从而实现系统的高可用性和可扩展性。因为RAC属于典型的share everything结构,它能够同时在多个节点打开相同的数据库,因此对于同一个数据库对象,它能够被加载(populate)到多个节点上去。固然,前提条件是这些节点的数据库实例都设置了in memory area(参数in memory_size不等于0)。既然数据能够被加载到多个节点,那么就意味着咱们须要思考两个问题:

-问题1:如何将数据分布到多个节点。 
-问题2:数据是否有必要在in memory area当中保存冗余来确保高可用性。

对于数据的分布方式,oracle提供了根据数据的ROWID范围或根据表的分区(或子分区)两种方式来将数据分布到多个节点上。第一种方法是指将表的数据按照rowid的范围划分红若干份,以后将每份数据均匀的加载到不一样的节点当中去,这种分布方式比较适用于数据分布不均匀的表,并且应用程序对表的访问在每一个实例上都是比较均匀的场景。例如: 
ALTER TABLE test INMEMORY DISTRIBUTE BY ROWID RANGE; 
第二种方式适用于分区表,oracle会根据分区的定义将每一个分区加载到不一样节点的in memory area当中去,这种分布方式比较适合数据分布均匀的表。若是应用程序对表的访问在每一个实例上都是比较均匀的,尤为适合hash分区表。例如:ALTER TABLE lineorder INMEMORY DISTRIBUTE BY PARTITION;

对于数据是否应该在in memory area中保存冗余,若是是普通的RAC数据库,那么数据并不会在in memory area中保存冗余;而对于Exadata一体机,in memory area中的数据是能够设置冗余的。之因此选择这样作的缘由在于,非Exadata一体机的RAC系统的私网配置千差万别,若是选择保存冗余的话,一旦当某一个实例down掉以后,意味着会有大量的数据须要在节点的私网之间进行传输,以便确保数据的冗余,若是私网的性能不能获得保证的时候,这种数据的传输可能消耗大量的时间和网络资源,并致使严重的后果。而Exadata一体机的私网采用光纤网络,并且使用了先进的RDS协议,数据传输能够达到几十G每秒,因此在处理因为节点故障致使的大量私网数据传输时,仍然能够保证集群的私网正常工做。

另外,因为目前硬件层面的高可用技术已经很是成熟,一个数据库实例或者节点down掉的事故大部分都是一次性的,很快就能恢复。因此Oracle在发现某一个实例或者节点fail以后并不会立刻触发数据的从新分布,而是会等待一段时间以便让问题节点或实例可以从新启动并加载本身的数据,只有当等待时间超时以后,其余节点才会触发数据从新分布的过程,将失败节点的in memory area中的数据从新分布到正常节点。基于这种设计模式,建议在使用RAC系统上的in memory选项时应该为每一个节点的in memory area预留出一部分空间,以便确保数据从新分配时仍然有足够的空间。 
下面的图4和图5描述了Exadata环境和非Exadata环境in memory area保存数据的区别

图片描述

图4-Exadata环境

 

图片描述

图5-非Exadata环境

 

根据上面的图形不难发现,在RAC环境下的,每一个节点都不会包含表当中的全部数据。因此在RAC环境下,须要启用oracle的自动并行查询(AutoDOP)才可以使用in memory的方式访问加载到in memory area中的表。另外还要说明的是在多实例的并发查询中实例之间传输的并非IMCU,而是每一个节点都会对本节点的数据运行相同的sql语句,以后把本身的结果集发送给发起sql语句的实例,组成最终的结果返回给用户。例如:一个4节点的RAC数据库,表sales已经被加载到了in memory area当中。运行下面的查询: 
select sum(stock) from sales where store_id in (100,200,300) and order_date=to_date(‘2016-01-01’, ‘yyyy-mm-dd’);

CBO首先会计算使用in memory scan的成本,若是成本最低,CBO就会选择使用在in memory area中访问sales表。接下来,oracle会访问数据字典中的信息,找到这个表被加载到了哪些实例,并在对应的节点启动相应的并发进程(parallelslave),把这个查询语句发送给并发进程运行。每一个实例的并发进程运行完对应的sql语句以后,把产生的汇总值发送给发起查询的实例,生成最终的汇总值并返回给客户。在整个过程当中,并非IMCU在实例之间传递,而是汇总值在传递,因此可以避免大量的私网数据通讯。

以上就是做者对oracle 12c in memory组件一些粗浅的介绍,但愿对各位使用Oracle数据库进行开发的人员有所帮助,可以在使用了in memoery组件的oracle数据库上开发应用程序时有些借鉴做用。

转:http://blog.sina.com.cn/s/blog_74a7d3390102wegl.html

相关文章
相关标签/搜索