用户关系表 存储_列式存储系列(一)CStore
作者:辛庸,阿里巴巴計算平臺事業部 EMR 技術專家。Apache Hadoop,Apache Spark commiter。對 Hadoop、Spark、Hive、Druid 等大數據組件有深入研究。目前從事大數據云化相關工作,專注于計算引擎、存儲結構、數據庫事務等內容。
序
本文是列式存儲系列的第一篇。在這個系列中,我們將介紹幾個典型的列式存儲系統。這些列式系統的出現都有各自的時代背景。在介紹這些系統的同時,我們也盡量介紹一下它們的背景,以便大家有一個更宏觀的認識,理解這個系統為什么會出現,它要解決的問題,以及它如何影響后來類似系統的發展。
列式存儲不是一個新概念,它最早可以追溯到上個世紀 70 年代。在上個世紀六七十年代,數據庫學者們的主題是如何定義一個數據模型,以便更好的存儲、管理和使用數據。被提出的數據模型多種多樣,其核心就是構造什么樣的邏輯關系,設計什么樣的存儲結構和計算方法,滿足人們存儲、管理和使用數據的需求。在這種背景下,1970 年就誕生了我們后來熟知的關系型數據模型[1]。關系型數據模型是一種邏輯模型,只關心數據的組織方式,不關心底層的存儲,也就是說,它只是定義了一個數據該如何被組織、被表現的接口,至于底層,它是不關心的。由于一個關系被組織成一張表,所以天然的想法就是按照多維數組的方式存儲數據,這種存儲模型稱為 n-array 存儲模型(也叫 NSM)。傳統的 n-array 模型按照數據寫入的方式,將數據一行一行的存儲下來,形成一個行優先的表。這種存儲模型對數據的插入、刪除、更新友好,是 OLTP 工作負載的理想選擇。1985 年,Copeland 和 Khoshafian 提出了一種稱為 “Decomposition Storage Model” DSM 存儲模型[2],在這種模型中,數據以屬性(也就是列)的方式進行存儲,每一行數據有一個代理主鍵,每個列存代理主鍵和該列的值。當用戶查詢時,系統從最底層的存儲層取出相應的列,并通過一定的算法還原出用戶想要的 row,并返回給用戶。DSM 的數據模型大概是下面這個樣子
DSM 模型的特點是數據按列存儲,每列數據定長,易于壓縮。由于數據是排序的,因此可以按照列偏移量進行讀取。另外,讀取時可以只讀取涉及到的列,而不涉及的列則不參與計算。這些特點使得 DSM 對分析任務友好。引起了業界的關注,啟發并產生了一些基于列式存儲的商用分析系統,如 SyBase IQ[7]。不過在 90 年代,數據量還不是很大,所以更多的數據倉庫的設計都是基于關系型數據庫的,數據庫提供商想用一套方案覆蓋所有場景,稱之為“One Size Fits All”[3]。這種“One Size Fits All”的嘗試在 2000 年后變得不再適應時代,因為互聯網的爆發使得數據量激增,用戶分析數據的時間花費變得非常漫長。在這種背景下,業界不得不探索新的解決方案?,F實需要直接結果就是推動了整個分析型時長的繁榮。也就是大約 2005 年以后,我們看到了各種針對大數據量的分析型引擎雨后春筍般出現了(Google 的 Bigtable[6] 論文發表于 2006 年)。
下圖是 column store 的一個發展歷史[8]。(columns store 是基于列式的數據庫系統,和目前流行的 NoSql 概念上還不太一樣,注意圖中 “Re-birth” 一詞的含義)
C-Store 概述
今天要介紹的第一個列式存儲系統為 C-Store。C-Store 不像其他大數據系統那樣出名,但在分析型數據庫歷史中的地位還是非常高的。C-Store 的提出者是 Michael Stonebraker,是數據庫領域四位圖領獎獲得者之一,也是著名的商用分析系統 Vertica、開源數據庫 Postgresql 的發明者。Vertica 正式基于 C-Store 的原型開發的,由此也可見 C-Store 的地位。
在 C-Store 的論文中,作者明確了寫優化與讀優化兩種系統的工作場景,并把前者行優先的存儲方式稱為 row-store,而列的方式稱為 column-store(不知道 column store 一詞是否由此而來)。作者指出了row-store 類型的系統在進行分析任務時運行低效的問題,并給出了 C-Store 如何解決這些問題的。同時,針對 column-store 不擅長的數據更新,作者也給出了自己的解決方案。這些 argue point 簡要列在下面:
需要額外精力處理內存對齊。CPU 運算速率以摩爾定律增長,而相應的主存存取速率卻增長有限。一次性的主存訪問大約需要幾百個 CPU 周期。在這種情況下,數據庫實現者需要仔細考慮內存對齊,以避免數據跨越邊界,造成處理一次數據需要兩次訪問內存的情況。C-Store 不存在這個情況,因為列的長度都是固定的,一次可以處理 N 條數據,使得 N 條數據恰好填滿一個數據塊。
一般的數據塊使用 B-tree 進行索引。索引鍵一般是主鍵。對于其他列,往往通過創建二次索引來進行快速檢索。二次索引在進行點查時需要讀取完數據才知道該條數據符不符合要求。因此為了增加查詢性能,數據庫系統需要引入對讀友好的其他存儲結構,比如 bit-map 索引,交叉表索引以及物化視圖等等。C-Store 的處理方式有些不同。它將一個表的一個到多個列組成一個 projection,多個 projections 的并集為該表,且每個列可能屬于多個 projections。Projection 可以按照某個列進行排序,這樣一個列可能有多個排序方式。在查詢時選擇合適的 projection 就能達到一個比較好的效果。
C-Store 的 projection 帶來了額外的好處。在集群環境下,存儲成本很低,數據往往存在多份拷貝。但是沒有必要為所有數據進行一模一樣的拷貝備份。C-Store 將列分開存儲,涉及查詢頻繁的列可以存放多份拷貝,并且存放在不同的 projection 里,使用不同的 sort order。這樣 C-Store 在高性能和 HA 兩方面取得了很好的平衡
插入刪除更新。雖然數據倉庫更多的使用方式是批量的導入,很少的更新,但是更新也是有必要的。而且,發展趨勢則是使數據流式地進入系統,甚至在線更新數據倉庫中的數據。針對這樣的需求,C-Store 設計了一個兩層的架構,上層是一個面向寫的 Writeable Store(WS),下層有一個面向讀的 Read-Optimized Store(RS),中間運行一個 Tuple Mover 不斷地將數據從 WS 移至 RS:
在支持事務方面,C-Store 使用基于快照的事務方案,用于避免額外的鎖開銷。
數據模型
C-Store 的核心是 projection。一個 projection 就是一個或多個列的“部分表”,多個 projections 可以合成一張表,同時,一個表的一個列也可以出現在多個 projection 當中。一個 projection 中也可以包含其他表的列,前提是該 projection 所屬(原文稱 anchor,即錨表)表含有該其他表列的外鍵。一個 projection 有一個 sort key,用于確定該 projection 數據的排列方式。下面是一張邏輯表,以及幾個可能的 projections:
其中 projection 括號中豎線后邊的鍵為 sort key。另外,一個 projection 可以被按列拆分成多個分區,稱為 segments,存放在不同的節點上。
有了 projections,一個核心問題是如何通過 projections 構建原表(這是所有列式系統共同的問題)。C-Store 的工具有兩個:
Storage Keys(SK): C-Store 為每個 segment 中的每個列賦予一個 SK,SK 對應原邏輯表中對應列的位置。
Join Indices: Join Indices 用于關聯不同 projections,下圖是一個例子:
可以看到,EMP1 和 EMP3 通過一個 Join Indices 相聯系,從而構造出 (Name, Salary, Age) 這個 tuple。值得注意的是,這個 join indices 是單向的。為了構造出整張表,C-Store 需要在多個 projections 的 join indices 中找到一條能夠構造出整張表的path(在上面的例子中,至少需要兩個合理的 join indices,比如從 EMP3 到 EMP1 再到 EMP2,才能構造出原表)。C-Store 的這套 join indices 也有不足,即維護成本很高。所以作者建議一個列最好保存在多個 projections 中,也就是說,盡量避免一個 projection 僅包含一個列這樣瑣碎,以減少 join indices 的數量。如果 projection 包含的列很多,存儲成本會相應加大,但是性能會提升,反之存儲成本下降,性能也下降,這是一個權衡的因素。
WS 和 RS
RS 的主要功能是保存 projections 以及相應的 join indices。為了和 RS 保持一致,減輕 Tuple Mover 的額外開銷,WS 中也保存了同樣的 projections 和 join indices,只不過,WS 中經過了優化,以適應大量的寫:首先 WS 中的 projections 并沒有被壓縮,其次,WS 中使用 B-tree 而不是直接排序來保證存儲的順序。WS 中的列按照 (v, sk) 的方式進行存儲,其中 v 是屬性的值,sk 是該列的 storage key,并且按照 B-tree 索引。對于 sort key,(v, sk) 就簡化為 (s, sk),其中 s 就是 sort key。在進行基于 sort key 的查詢時,首先根據 (s, sk) 找到 sk,然后再根據 sk 和 (v, sk) 獲得其他列的值。
WS 和 RS 通過 Tuple Mover 相聯系。Tuple Mover 使用一個類似于 LSM 的結構將 WS 中的數據持久化,并發往 RS 進行存儲。
事務
C-Store 提供快照隔離的機制包保證事務。具體地說,當記錄被插入時,其獲得一個時間戳,如果查詢某一個快照 S,那么時間戳大于 S 對于時間戳的那些記錄將會被忽略。同時,update 操作也不是原地更新,而是被分成了 delete 和 insert 兩個動作,delete 的 record 會被記錄一個刪除標記。這一點和 Hive ACID-2.0 是一樣的(但是 C-Store 是 05 年的系統)。與 HIVE ACID-2.0 有點不一樣的是,HIVE 使用了WriteId 來跟蹤事務的順序,WriteId 由中心化的 Metastore 產生。WriteId 跟 TransactionId 之間有些關聯,當一個 Transaction 完成后,相應的 WriteId 就被持久化到 Metasotre,之后的讀就可以讀該 WriteId 及之前的記錄了。C-Store 則使用類似的概念,稱為“水位(Water Mark)”,水位有高水位和低水位,只有那些位于兩者之間的記錄才能被讀取。C-Store 的水位用“時間戳”來標識,但這個時間戳不是真正的時間戳,而叫做 epoch。C-Store 引入了一個中心化的 Time Authority TA 節點,它在啟動時將 epoch 設置為 0,此后周期性的將 epoch 加 1,并發送給其他節點。epoch 是時間的最小粒度。epoch 的工作原理如下圖所示,當某個 epoch 結束開始一個新的 epoch 時,TA 發送 end of epoch 給各個節點。各個節點等待在此次 epoch 內的事務(包括之前未完成的事務)完成后發送 Epoch complete 到 TA。等 TA 收集到所有節點的 Epoch complete 事件后,TA 將水位更新。
對于事務的并發控制,C-Store 采用嚴格的兩階段封鎖協議。在事務的提交方面,它使用省略了 PREPARE 階段的兩階段提交協議來實現。
性能
C-Store 論文里給了它與傳統數據庫以及和其他 Columns based 的數據庫的性能對比,第一個表格為數據體積,第二個表格為 TPC-H 中 7 個query 的性能
C-Store 論文里特別提到了它對于數據無解壓處理的能力,并稱這是其性能優異的一個很重要的因素。
總結
本文介紹了 C-Store 系統,它提出了一種混合架構,上層提供了面向寫的 Write store,下層是面向讀的 Read store。其核心設計點為 projections,一種基于列的存儲結構。C-Store 面向列的設計使得其對于分析有著優秀的性能表現。此外,C-Store 還提供了基于快照隔離的事務功能。
參考文獻
[1] E. F. CODD. A Relational Model of Data for Large Shared Data Banks
[2] George P. Copeland, Setrag N. Khoshafian. A decomposition storage model
[3] Michael Stonebraker, Ugur Cetintemel. "One Size Fits All": An Idea Whose Time Has Come and Gone
[4] Clark D. French. One Size Fits All Database Architectures Do Not Work for DSS
[5] Mike Stonebraker, et,al. C-store: a column-oriented DBMS
[6] Fay Chang, Jeffrey Dean, et,al. Bigtable: A Distributed Storage System for Structured Data
[7]?http://www.sybase.com/products/databaseservers/sybaseiq
[8] Column_Store_Tutorial_VLDB09()
聲明
限于本人水平,文中內容僅供參考。更加詳情請閱讀原論文。同時歡迎讀者批評指正,共同探討。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的用户关系表 存储_列式存储系列(一)CStore的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 摩尔庄园拍照功能在哪
- 下一篇: git设置master权限_git怎么控