1. 程式人生 > >oracle 12c 列式存儲 ( In Memory 理論)

oracle 12c 列式存儲 ( In Memory 理論)

向量 啟動 pop 結果 olt 機制 接下來 記錄 節點

隨著Oracle 12c推出了in memory組件,使得Oracle數據庫具有了雙模式數據存放方式,從而能夠實現對混合類型應用的支持:傳統的以行形式保存的數據滿足OLTP應用;列形式保存的數據滿足以查詢為主的OLAP應用。in memory組件可以和其他數據庫組件功能使用,並不需要用戶單獨開發或者修改應用程序,就可以非常方便的實現基於實時數據庫分析的轉變。本文會介紹in memory組件的一些相關知識,包含了以下的內容:

-列式存儲的基本知識
-訪問in memory area中的數據
-In memory和RAC的融合

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位圖等。

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

oracle 12c 列式存儲 ( In Memory 理論)