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

相關文章
相關標籤/搜索