分布式数据库产品总结
分布式數據庫產品總結
- Pivotal
- Greenplum Database(GPDB)
- 架構
- 查詢計劃并執行
- 查詢優化
- 索引
- blink tree
- 執行器
- 數據shuffle
- 分布式事務
- 2PC
- gp實現
- gp優化
- MVCC
- 特點
- HTAP
- HAWQ
- Snowflake Elastic Data Warehouse
- 數據存儲
- 虛擬數據倉庫實例(Virtual Warehouse)
- AnalyticDB
- 系統架構
- 保證
- 存儲引擎
- 讀寫過程
- 數據合并
- 行列混存
- inverted index
- 元數據
- 索引管理
- 索引構建
- 優化器
- 執行引擎
- 總結
- PolarDB/PolarFS
- 讀寫流程
- ParallelRaft
- PolarFS
- 事務的數據可見性問題
- DDL問題
- Change Buffer問題
- Polar-X
- ClickHouse
- 設計目標
- ClickHouse存儲引擎
- 計算引擎
- ClickHouse總結
- TiDB
- TiFlash
- C-Store(2005)/Vertica
- Apache ORC
- Dremel (2010) / Apache Parquet
- Impala
- 查詢流程
- Druid
- Pinot
- mongoDB
- 參考鏈接
Pivotal
Greenplum Database(GPDB)
**Greenplum Database(GPDB)**是一款基于開源 PostgreSQL 擴展的 MPP(massively parallel processing),可支持大規模水平擴展的分布式數據庫。 GPDB 采用的是 master-worker 模式,每個 worker process 運行在不同的機器上,擁有各自的存儲和運算資源。**客戶端通過 master 把查詢語句分發到各個機器上,以達到并行計算來處理海量數據。**集群節點(無論是master還是segemnt)上的每個實例都是一個物理上獨立的PostgrepSQL數據庫。
Greenplum數據庫是一種shared nothing的分析型MPP數據庫。這種模型與高度規范化的/事務型的SMP數據庫有顯著區別。Greenplum數據庫使用非規范化的模式設計會工作得最好,非規范化的模式適合于MPP分析型處理,例如帶有大型事實表和較小維度表的星形模式或者雪花模式。
Greenplum 在 PostgreSQL 之上還添加了大量其他功能,例如 Append-Optimized 表、列存表、外部表、多級分區表、細粒度資源管理器、ORCA 查詢優化器、備份恢復、高可用、故障檢測和故障恢復、集群數據遷移、擴容、MADlib 機器學習算法庫、容器化執行 UDF、PostGIS 擴展、GPText 套件、監控管理、集成 Kubernetes 等。
架構
- master: 保存元數據而不保存用戶數據,有用戶表信息,優化器使用這些信息進行查詢優化和計劃生成
- segment:每個segment保存用戶數據表的一部分。在 Greenplum 中,用戶數據按照某種策略分散到不同節點的不同 segment 實例中。
使用標準的 INSERT SQL 語句可以將數據自動按照用戶定義的策略分布到合適的節點,然而 INSERT 性能較低,僅適合插入少量數據。Greenplum 提供了專門的并行化數據加載工具以實現高效數據導入,詳情可以參考 gpfdist 和 gpload 的官方文檔。
在數據分布方面,Greenplum 在這方面不單單做到了基本的分布式數據存儲,還提供了很多更高級靈活的特性,譬如多級分區、多態存儲。Greenplum 6 進一步增強了這一領域,實現了一致性哈希和復制表,并允許用戶根據應用干預數據分布方法。有這么多種手段,可見Greenplum用戶肯定時遇到了很多數據傾斜的問題
Greenplum支持的分區方法有:
范圍分區:根據某個列的時間范圍或者數值范圍對數據分區。譬如以下 SQL 將創建一個分區表,該表按天分區,從 2016-01-01 到 2017-01-01 把全部一年的數據按天分成了 366 個分區:
CREATE TABLE sales (id int, date date, amt decimal(10,2)) DISTRIBUTED BY (id) PARTITION BY RANGE (date) ( START (date '2016-01-01') INCLUSIVE END (date '2017-01-01') EXCLUSIVEEVERY (INTERVAL '1 day') );列表分區:按照某個列的數據值列表,將數據分不到不同的分區。譬如以下 SQL 根據性別創建一個分區表,該表有三個分區:一個分區存儲女士數據,一個分區存儲男士數據,對于其他值譬如 NULL,則存儲在單獨 other 分區。
CREATE TABLE rank (id int, rank int, year int, gender char(1), count int ) DISTRIBUTED BY (id) PARTITION BY LIST (gender) ( PARTITION girls VALUES ('F'), PARTITION boys VALUES ('M'), DEFAULT PARTITION other );Greenplum 支持多態存儲,即單張用戶表,可以根據訪問模式的不同使用不同的存儲方式存儲不同的分區。通常不同年齡的數據具有不同的訪問模式,不同的訪問模式有不同的優化方案。多態存儲以用戶透明的方式為不同數據選擇最佳存儲方式,提供最佳性能。Greenplum 提供以下存儲方式:
- 堆表(Heap Table):堆表是 Greenplum 的默認存儲方式,也是 PostgreSQL 的存儲方式。支持高效的更新和刪除操作,訪問多列時速度快,通常用于 OLTP 型查詢。
- Append-Optimized 表:為追加而專門優化的表存儲模式,通常用于存儲數據倉庫中的事實表。不適合頻繁的更新操作。
- AOCO (Append-Optimized, Column Oriented) 表:AOCO 表為列表,具有較好的壓縮比,支持不同的壓縮算法,適合訪問較少的列的查詢場景。
- 外部表:外部表的數據存儲在外部(數據不被 Greenplum 管理),Greenplum 中只有外部表的元數據信息。Greenplum 支持很多外部數據源譬如 S3、HDFS、文件、Gemfire、各種關系數據庫等和多種數據格式譬如 Text、CSV、Avro、Parquet 等。
存儲方式和分區方式相組合,可以對一張表不同的數據區域有不同的存儲方式。
數據分布是任何 MPP 數據庫的基礎,也是 MPP 數據庫是否高效的關鍵之一。通過把海量數據分散到多個節點上,一方面大大降低了單個節點處理的數據量,另一方面也為處理并行化奠定了基礎,兩者結合起來可以極大的提高整個系統的性能。譬如在一百個節點的集群上,每個節點僅保存總數據量的百分之一,一百個節點同時并行處理,性能會是單個配置更強節點的幾十倍。如果數據分布不均勻出現數據傾斜,受短板效應制約,整個系統的性能將會和最慢的節點相同。因而數據分布是否合理對 Greenplum 整體性能影響很大。
Greenplum 6 提供了以下數據分布策略。
查詢計劃并執行
PostgreSQL 生成的查詢計劃只能在單節點上執行,Greenplum 需要將查詢計劃并行化,以充分發揮集群的優勢。
Greenplum 引入 Motion 算子(操作符)實現查詢計劃的并行化。Motion 算子實現數據在不同節點間的傳輸,它為其他算子隱藏了 MPP 架構和單機的不同,使得其他大多數算子不用關心是在集群上執行還是在單機上執行。每個 Motion 算子都有發送方和接收方。此外 Greenplum 還對某些算子進行了分布式優化,譬如聚集。
- 開源分布式數據庫Greenplum并行執行引擎揭秘
- motion:MPP架構下必須有motion算子,協調數據分布,這是執行計劃中很重要的一步。
- dispatcher:分配QE資源,配置Slice,分發任務(plan+slicetable --> 還可以分發一些純文本的命令、兩階段提交、CdbDispatchUtilityStatementvectoring中的分發語法樹(一些語法信息只有QE上有)),協調控制(控制、等待下發的任務的狀態)
- interconnect:QE之間數據傳送的模塊
- 引入udp主要是為了解決OLAP查詢在大集群中使用連接資源過多的問題
- 可靠的UDP (RUDP)
- interconnect主要遇到的問題就是連接數不夠用和穩定性這兩方面的問題,所以考慮:
- QUIC協議
- Proxy協議(感覺有點服務治理的意思了)
- 引入udp主要是為了解決OLAP查詢在大集群中使用連接資源過多的問題
查詢優化
索引
- greenplum中的索引都是二級索引(非聚集索引)
- 物理上是存儲在獨立的文件中的(獨立于表的數據文件)
- 并且也是按分片存儲在每個segment上,其索引內容對應segement上的數據分片
- 不同于原生blink樹,兄弟節點之間使用右向指針,greenplum采用雙向指針
- 物理結構上,每個頁包含索引元組(和boltdb差不多),special中存儲了頁面級的元信息:
- 兄弟指針
- 頁面類型
- 等等
- 葉子節點的填充率最高是90%,內部節點(非葉子節點)的填充率最高是70%,不填滿是為了方式insert會造成頻繁的頁分裂。
blink tree
要點總結
執行器
- QD(Query Dispatcher、查詢調度器):Master 節點上負責處理用戶查詢請求的進程稱為 QD(PostgreSQL 中稱之為 Backend 進程)。 QD 收到用戶發來的 SQL 請求后,進行解析、重寫和優化,將優化后的并行計劃分發給每個 segment 上執行,并將最終結果返回給用戶。此外還負責整個 SQL 語句涉及到的所有的 QE 進程間的通訊控制和協調,譬如某個 QE 執行時出現錯誤時,QD 負責收集錯誤詳細信息,并取消所有其他 QEs;如果 LIMIT n 語句已經滿足,則中止所有 QE 的執行等。QD 的入口是 exec_simple_query()。主要是火山模型
- QE(Query Executor、查詢執行器):Segment 上負責執行 QD 分發來的查詢任務的進程稱為 QE。Segment 實例運行的也是一個 PostgreSQL,所以對于 QE 而言,QD 是一個 PostgreSQL 的客戶端,它們之間通過 PostgreSQL 標準的 libpq 協議進行通訊。對于 QD 而言,QE 是負責執行其查詢請求的 PostgreSQL Backend 進程。通常 QE 執行整個查詢的一部分(稱為 Slice)。QE 的入口是 exec_mpp_query()。
- Slice:為了提高查詢執行并行度和效率,Greenplum 把一個完整的分布式查詢計劃從下到上分成多個 Slice,每個 Slice 負責計劃的一部分。劃分 slice 的邊界為 Motion,每遇到 Motion 則一刀將 Motion 切成發送方和接收方,得到兩顆子樹。每個 slice 由一個 QE 進程處理。上面例子中一共有三個 slice。
- Gang:在不同 segments 上執行同一個 slice 的所有 QEs 進程稱為 Gang。
數據shuffle
相鄰 Gang 之間的數據傳輸稱為數據洗牌(Data Shuffling)。數據洗牌和 Slice 的層次相吻合,從下到上一層一層通過網絡進行數據傳輸,不能跨層傳輸數據。根據 Motion 類型的不同有不同的實現方式,譬如廣播和重分布。
Greenplum 實現數據洗牌的技術稱為 interconnect,它為 QEs 提供高速并行的數據傳輸服務,不需要磁盤 IO 操作,是 Greenplum 實現高性能查詢執行的重要技術之一。interconnect 只用來傳輸數據(表單的元組),調度、控制和錯誤處理等信息通過 QD 和 QE 之間的 libpq 連接傳輸。
Interconnect 有 TCP 和 UDP 兩種實現方式,TCP interconnect 在大規模集群中會占用大量端口資源,因而擴展性較低。Greenplum 默認使用 UDP 方式。UDP interconnect 支持流量控制、網絡包重發和確認等特性。
分布式事務
Greenplum 使用兩階段提交(2PC)協議實現分布式事務。2PC 是數據庫經典算法,此處不再贅述。本節概要介紹兩個 Greenplum 分布式事務的實現細節:
(更正:應該不是2pc,應該就是MVCC)
- 分布式事務快照:實現 master 和不同 segment 間一致性
- 共享本地快照:實現 segment 內不同 QEs
在 QD 開始一個新的事務(StartTransaction)時,它會創建一個新的分布式事務 id、設置時間戳及相應的狀態信息;在獲取快照(GetSnapshotData)時,QD 創建分布式快照并保存在當前快照中。和單節點的快照類似,分布式快照記錄了 xmin/xmax/xip 等信息。這些信息被用于確定元組的可見性(HeapTupleSatisfiesMVCC)。
和 PostgreSQL 的提交日志 clog 類似,Greenplum 需要保存全局事務的提交日志,以判斷某個事務是否已經提交。這些信息保存在共享內存中并持久化存儲在 distributedlog 目錄下。
為了提高判斷本地 xid 可見性的效率,避免每次訪問全局事務提交日志,Greenplum 引入了本地事務-分布式事務提交緩存
2PC
greenplum基于PG,雖然PG沒有分布式事務管理器,但是支持2階段提交2pc
PREPARE TRANSACTION COMMIT PREPARED ROLLBACK PREPAREDprepare之后持有的行鎖不會被釋放,就算宕機,重啟pg節點之后鎖還是在的(數據庫會將prepare寫進事務日志,需要進行負責恢復),方便后續協調者cordinator繼續對該事務進行操作
gp實現
gp在pg的基礎上,實現了:
gp優化
理論上,如果參與者只有1個,2pc可以簡化為1pc(Bernstein稱為協調權的轉移,因為只有一個參與者的話,就不需要協調者了)
- 滿足1pc的條件:
- 有寫操作,但是參與者只有1個
- 只讀事務,也不需要2pc
MVCC
- Greenplum內核揭秘之MVCC并發控制
主流數據庫三大并發控制方法:
事務的本質就是將多個步驟捆綁為原子的步驟,要么都成功,要么都不成功,事務的中間狀態不應該被其他事務看到(隔離isolation)。
greenplum只實現了read commited和repeatable read.
MVCC主要為了解決讀寫沖突。
- heaptuple用于整整存儲每行數據 |t_xmin|t_xmax|t_cid|t_ctid|t_infomaskt2|t_infomask|NULL_bitmap|userdata|
- Xmin:創建tuple的事務ID
- Xmax:刪除tuple的事務ID,有時用于行鎖(有事務在更新該行,通過配合infomask中的HEAP_XMAX_EXCL_LOCK完成)
- cid:事物內的查詢命令編號,用戶跟蹤事務內部的可見性,創建cursor(游標)和更改cursor中的內容,這兩步需要保證看到的事務是一樣的。
- ctid:指向下一個版本tuple的指針,由兩個成員blocknumber和offset組成
- 這個和mysql不同,mysql存儲的是增量信息
- t_infomask用以加速可見性的查詢,標記優化,比用每次都去看mvcc快照或者pg_clog
- heappage用于存每個heaptuple的偏移量
- greenplum使用mvcc快照,在pg的2pc之上提供隔離級別的保證
- 快照理論上是一個正在運行的事務列表
- greenplum使用快照判斷一個事務是否已提交,其中包含如下信息:
- Xmin:所有小于Xmin的事務都已經提交
- Running:正在執行的事務列表,這里面保存的所有事務都是未提交的,不在這里面,并且處于水位之間的id就是已經提交的
- Xmax:所有大于等于Xmax的事務都未提交
- 該快照中只存儲了事務commited或running狀態,如果需要查詢abort狀態,需要查詢pg_clog(事務日志)。(事務在abort的時候不需要更改行中的t_xmax)
- 在READ COMMITTED隔離級別下,每個查詢開始時生成快照
- 在REPEATABLE READ隔離界別下,在每個事務開始時生成快照
- 在一眾版本中,某個事務必然只能看到其中一個,所以事務需要判斷快照的可見性
- 在視頻22:50講解的很詳細
- 寫寫沖突情況下,后面的事務會阻塞直到前面一個事務完成
一個transaction共有三種狀態, committed,running,abort
特點
HTAP
2021 sigmod論文《Greenplum: A Hybrid Database for Transactional and Analytical Workloads》
HAWQ
針對 Hadoop 存儲的 SQL 執行引擎。HAWQ 通過數據接口可以直接讀取 Hive 表里的數據(也支持原生存儲格式),然后用 SQL 執行引擎來計算得到查詢結果。與 HiveQL 通過把 SQL 解析成一連串的 MapReduce job 的執行模式相比,速度要快好幾個量級。HAWQ 雖然在開發執行引擎過程中借鑒了很多 GPDB 的東西,但畢竟是一款不同的數據庫引擎,Pivotal 因此希望有一款兼容的優化器能夠服務于它。由此,研發了開源優化器 ORCA。
Snowflake Elastic Data Warehouse
除了使用了 vec-exec(畢竟,聯合創始人 Marcin 的博士畢業論文就是關于 vec-exec 的),Snowflake 也是一款 100%計算和存儲分離,面向云原生的數據倉庫系統。本文內容主要參考他們發表于 SIGMOD-16 的 paper: The Snowflake Elastic Data Warehouse。
Snowlake 是 2012 年成立的,2015 年正式推出商用版本。2012 年,正是云服務起步不久,大數據熱火朝天的時候。當時,數據倉庫的主流趨勢是 SQL On Hadoop。Cloudera, Hontornworks, MapR, Greenplum HAWQ, Facebook 的 Presto,算是百花齊放。但主創團隊認為,RDBMS 不會消失,用戶們會因為上云的趨勢,想要一款完全適配云端的數據倉庫。
文章簡單介紹了市面上通常的 on-prem 分布式數據倉庫的一些缺點。首先就是計算和存儲硬件是耦合的,即每個服務器同時負責存儲數據,并且執行 SQL 語句得到結果。耦合的劣勢在于,不能針對不同的 workloads 做優化。二就是服務器的 node membership 改變(無論是因為服務器損壞,或者是因為數據量提升需要擴容)對用戶來說都不友善。一,就是要進行大量數據的 reshuffle。二是,為了做到高可用,可能會保留一部分 node 作為 stand-by replica,當主節點有問題時,馬上接替主節點,這相當于變相提高了數據成本。總結來說,on-prem 的數據倉庫要做到同時保持可伸縮性(elasticity)和高可用性(availability)并兼顧成本,是很難魚與熊掌兼得的。三就是對服務進行升級比較麻煩。
由于云服務的出現,很多上述的問題,變得不再是問題了。一就是,云服務通常會提供多種類型的服務器來針對特定的 usecase;二,服務器的下線,上線,擴容在云服務上都屬于基本操作;三是,云上有高可用,低成本的存儲系統;四是,服務更新非常方便。基于這些原因,Snowflake 選擇了完完全全的計算和存儲分離的架構設計。整個架構分成三個大模塊:
數據存儲
在設計存儲系統的時候,Snowflake 有糾結過,是應該使用 AWS 的 S3,還是自行設計類似于 HDFS 的存儲系統。最終,在經過了各種比較,利弊權衡后,決定使用 S3。雖然,S3 的性能并不是最快;并且,由于是網絡接入,也不是最穩定。但是,勝在高可用性和高可靠性上。團隊決定基于 S3 打造數據存儲系統,同時,可以把精力放在優化 local caching 和數據傾斜(skew resilience)上。
相對于本地文件系統,S3 的 access latency 會更高,并且,由于是網絡接入(尤其是用 https),CPU 使用率也更高。而且,S3 本身就是一個簡單的 blob 存儲,支持的主要創建,刪除和讀取文件,即,不能對現有文件進行更新,更新相當于重新創建一個更新過的文件。但是,S3 的讀取有一大好處在于,可以讀取部分文件。
S3 的這些屬性,對于整個 Snowflake 的數據存儲和并行控制設計有重大的影響。首先,表數據被水平(horizontally partitioned)地切分成多個不可變的 blob 文件;每個文件通過列存(column-store)的形式保存數據,Snowflake 具體使用的存儲格式是 PAX 的 Hybrid-column store(挖個坑,可以單獨講一期這個)。每個數據文件包含數據頭用來存儲元數據。基于 S3 的下載部分文件的 API,對于運行的 SQL 語句,優化器會選擇只下載必須用到的數據 block 即可。這也就意味著所有snowflake的事務都是基于快照隔離Snapshot Isolation(SI)
值得一提的是,Snowflake 不單單使用 S3 來存儲表數據文件,也用 S3 來存儲臨時生成的 intermediate result(語句執行中,某個 operator 產生的臨時結果集)。一旦這些結果集的大小超過了本地磁盤空間,spill 到磁盤上的文件就會以 S3 的形式存儲。這樣的好處在于,可以讓 Snowflake 真正可以處理巨大的數據而不用擔心內存或者本地磁盤空間吃緊。另一個好處在于,這些臨時結果集也可能被利用作為 cache 使用。
最后文中還提到了數據庫的其他元數據存儲,包括有哪些 caching 文件,每個表存在了哪些 S3 文件中,等等,都是存儲在一個 transactional 的 key-value store 中,并不在 S3 里。
虛擬數據倉庫實例(Virtual Warehouse)
執行 SQL 語句:每個語句 instance 都只會運行在一個 VW 上;每個 VW 有多個 WN;每個 WN 只隸屬于一個 VW,不會被共享。(這邊有注解說,WN 變成共享的會是一個未來的工作,因為可以更好地提升使用率并且會進一步降低用戶成本)。當一個語句被運行時,所有的 WN 在這個 VW 上,(或者也可能是一部分 WN,如果優化器認為這是一個非常輕量級的語句),都會起一個 worker process,這個進程的生命周期就是這句語句的執行周期。worker process ,在執行的過程中,不會對外部資源造成任何變化,換言之,no side effect,即使是 update 語句。為什么這么說呢,因為所有的表數據文件都是 immutable 的。這樣帶來的好處就是,如果 worker process 由于各種原因崩潰了, 通常只是需要 retry 即可,沒有其他善后事宜要做。現在 VW 里還不支持 partial retry,這也在未來計劃的工作中。
由于 VW 的可伸縮性(elasticity),通常情況下,可以通過起一個更大 size 的 VW 來提升語句的性能,但保持一樣的使用成本。例如,**一個復雜的分析語句在一個 4 節點 VW 上需要運行 15 個小時,但在一個 32 節點 VW 上只需要 2 小時。**因為是云原生,用戶只需要支付運行 VW 時的費用即可。因此,在價格不變的情況下,用戶體驗和查詢速度卻大幅度提升。這也是 Snowflake 云原生數據倉庫的一大賣點。
- 本地緩存: 每個 WN 都會用本地文件為表數據做本地緩存,即已經被從 S3 那讀取的數據文件。這些文件是包含元數據信息和要用到的 column 的數據。這些緩存的數據文件可以被多個 worker process 共享(如果需要讀取一樣的數據),文中提到維護了一個簡單的 LRU 的 cache replacement 策略,效果非常不錯。為了進一步提升 hit rate,同一份數據文件被多個 WN 節點保存,優化器會用 consistent hashing 算法,來分配哪些節點保存哪些數據。同時,對于后續要讀取對應數據的語句,優化器也會根據這個分配發送到對應節點。
- 數據傾斜處理:一些節點可能相對于其他節點,運行更慢,比如硬件問題或者是單純網絡問題。Snowflake 的優化是,每個 WN 在讀取了相應的數據文件后,當它發現其他 WN 還在讀取,他會發送請求給其他 WN 要求分擔更多的數據,而且這些數據直接從 S3 讀取。從而來確保不要把過多的數據處理放在速度慢的 WN 上。
- 執行引擎:雖說可以通過增加節點來提升性能,但是 Snowflake 依然希望每一個節點的單體性能都能做到極致。因此,Snowflake 構建了自己的,基于列存,向量執行(vec-exec),并且是 push-based(推模式)的執行引擎。
- Columnar: 沒啥爭議,對于 OLAP 語句來說,Columnar-store 無論從存儲,讀取效率和執行效率來說,都優于 row-store。
- Vec-exec:也沒有爭議,Marcin 肯定把 Vec-Exec 這套運行優化放到執行器上。
- push-based: 相對于 Volcano 的拉模式,是下方的 operator,當處理完數據后,把數據 push 到上方的 operator(從執行計劃角度來看上下),類似于 code-gen,這樣的好處是提高了 cache 的利用率,因為可以避免不必要的循環控制語句。
- 另一點就是,一些其他傳統數據庫系統在執行語句時需要考慮的麻煩,對于 Snowflake 來說沒有。比如,不用 transaction management,因為所有的語句都是沒有 side effect 的。(原因是S3中的文件不可以更改)
AnalyticDB
數據庫帶來的新挑戰:
Oracle RAC --> Greenplum --> HBase --> AnalyticDB
ADB主要是OLAP系統,同時要顧及各種點查詢、優化的速度。底層采用盤古,所以數據庫主要的創新點在數據格式、優化器、執行器等等
系統架構
AnalyticDB主要分為以下幾個部分:
為便于大規模分析處理,AnalyticDB對數據表進行分區。AnalyticDB數據表有兩個分區級別:一級分區和二級分區。
選擇具有較高基數(cardinality)的列作為一級分區鍵,以保證數據行能均勻地分布到每個一級分區,最大化并行。用戶還可以根據需要定義二級分區,以便進行數據的自動管理。二級分區擁有最大分區數,當二級分區的實際數目超過了這個最大分區數后,最老的二級分區會被自動刪除。通常,選擇時間列(天、周或月)作為二級分區列,這樣,包含相同時間序列的數據行,會被劃分到同一個二級分區中。
傳統OLAP系統在同一個鏈路上同時處理讀寫請求,因此,所有的并發讀寫請求都共享同一個資源池,也會互相影響。但是當讀寫并發同時非常大時,這種設計會由于過度的資源競爭而導致不好的性能。如圖5所示,為了解決這個問題,同時確保讀和寫的高性能,AnalyticDB采用的架構為讀寫分離架構,即AnalyticDB有獨立的讀寫節點各自處理讀寫請求,且寫節點和讀節點完全互相隔離。
保證
存儲引擎
AnalyticDB存儲層采用Lambda架構,讀節點上的數據包括基線數據和增量數據兩部分。增量數據又分為Incremental Data和Deleted bitset,按照行列混存的架構存放在讀節點的SSD上。真正讀取是,basline數據要和增量數據做UNION和MINUS之后,才能輸出有效數據。
對于每張表,每k行的數據組成一個Row Group。Row Group中的數據連續存放在磁盤中。整個Row Group中,又將數據按照列(聚集列)分別順序存放。AnalyticDB會對每列構建一份元數據,用于維護列數據的統計信息(包括Cardinality、Sum和Min/Max等)、字典數據(采用字典編碼)以及物理映射等。AnalyticDB默認會對每一列數據建立索引,索引中的Key是列的值,Value是值出現的所有行號集合,采用后臺異步構建模式。由于增量數據部分沒有索引,隨著數據的不斷實時寫入,增量數據的查詢性能會越來越慢。AnalyticDB采用后臺任務來合并基線數據和增量數據形成一個新的基線數據,并基于新的基線數據構建全量索引。
讀寫過程
使用copy on write技術(OLAP讀多寫少)來支持MVCC,delete數據被轉化在Deleted bitset上,而update操作則被分為Incremental Data和Deleted bitset分別存放。每個寫操作都會分配獨立的LSN,從而達到MVCC
由于建立了全列倒排索引,所以執行引擎處理返回結果的時候用到了多路歸并
數據合并
由于沒有全局索引,隨著數據的不斷實時寫入,增量數據的查詢性能會越來越慢。因此ADB會在后臺通過伏羲啟動一個MapReduce 任務來合并基線數據和增量數據(同時去掉標記為刪除的數據)形成一個新的基線數據,并基于新的基線數據構建全量索引。
在合并任務開始時,一部分增量數據會標記為immutable,并執行合并,合并完成之后,之前的baseline data和immutable會被刪除
行列混存
在海量數據分析場景下,數據分析業務主要有以下三類workload:
在ADB的實現中,每K行數據實現了Row Group,每個row group中的每個列存放在自己的block中,Row group按照索引排列
inverted index
為了應對ad-hoc,ADB對每列建立了倒排索引,從而提高復雜數據的查詢效率。(每列都建立索引,不就是倒排索引了)
元數據
為了加速查詢,AnalyticDB對每列構建一份元數據,并保存在一個叫detail_meta的單獨文件中。detail_meta文件通常較小(小于1MB),首次查詢時被加載在內存中。如圖8左邊所示,元數據主要包括4部分:
- Header。包括版本號,文件長度以及一些統計信息。
- 列統計信息。包括行數,NULL值數,cardinality,SUM,MAX和MIN 值。優化器根據這些信息來生成最佳執行計劃。
- 字典。對于cardinality較少(小于1024)的列,AnalyticDB采用字典編碼,數據文件里保存字典號碼。字典保存在該字段中。
- 塊地址信息。保存塊號到數據文件起始地址和長度的映射關系。(我猜測是每次合并的時候更新)
索引管理
AnalyticDB設計和實現了一個新的索引引擎,在不影響寫入性能的情況下,支持結構化和非結構化數據類型索引。它將構建過程從寫入鏈路中移除,采用后臺異步構建模式,支持對所有列構建索引,從而解決了OLAP任意查詢的性能問題。
AnalyticDB默認對所有列構建索引,并保存在一個單獨的文件中。與傳統的數據庫不同,AnalyticDB索引中的key是列的值,value是該值出現的所有行號集合,并支持所有的條件同時走索引查詢。多個列的操作去做union或者intersect
AnalyticDB在索引引擎是實現上也做了大量的優化,包括:多路流式歸并、索引選擇CBO和索引結果緩存。
- 多路流式歸并:傳統數據庫大多采用2路歸并策略,在條件數特別多的場景下,會導致大量中間結果,計算效率很低。AanlyticDB采用K路流式歸并算法,可以支持多個集合并行歸并,避免產生大量中間結果集合,提升了整個歸并的速度。
- 索引選擇CBO:當where條件中包括多個條件,并不是所有的條件走索引掃描能取得最佳的性能。利用索引中的統計信息,提前估算出各個條件可能的選擇率,對于選擇率很高的條件走索引查詢,其他條件直接在上層進行過濾操作。例如對于where id = 1 and 0 < x < 1000000的情況下,id = 1這個條件的選擇率已經很高,則0<x<1000000條件不走索引查詢效率會更高。
- 索引結果緩存:在OLAP分析場景中,多個查詢條件中,可能會出現部分條件固定不變或重復多次出現。針對這種場景AnalyticDB 實現了一個高效的無鎖緩存,緩存的的key為等值或range條件,value為行號集合。這樣在出現重復查詢情況下,可以直接讀取緩存,避免索引IO掃描開銷。
索引構建
為了支持每秒千萬的實時數據寫入,避免同步構建索引影響實時寫入的性能,AnalyticDB并沒有采用同步構建索引的策略,而是采用異步后臺進程構建索引的方式。索引引擎會根據時間或增量數據的大小來決定是否啟動后臺進程來構建索引。該后臺進程讀取Pangu上的歷史全量數據和新寫入的增量日志數據,完成數據合并形成新的全量數據,并對該全量數據重新構建索引。該過程通過伏羲的MapReduce任務執行,選擇負載較低的機器執行,對用戶完全透明。
優化器
創新性引入了兩個關鍵功能:存儲感知的優化和高效實時采樣。因為ADB獨特的索引結構和分布式的數據存儲
執行引擎
在優化器之下,AnalyticDB在MPP架構基礎上,采用流水線執行的DAG架構,構建了一個適用于低延遲和高吞吐量工作負載的執行器。AnalyticDB的列式執行引擎能夠充分利用底層的行列混合存儲。與行式執行引擎相比,當前的向量化執行引擎更加緩存友好,能避免將不必要的數據加載到內存中。
與許多 OLAP 系統一樣,AnalyticDB在運行時利用代碼生成器(CodeGen) 來提高 CPU 密集型計算的性能。AnalyticDB的CodeGen基于 ANTLR ASM來動態生成表達式的代碼樹。同時此 CodeGen 引擎還將運行時因素納入考慮,讓AnalyticDB能在Task級別利用異構新硬件的能力。例如,如果集群中CPU支持 AVX-512指令集,我們通過生成字節碼使用SIMD來提高性能。在此之外,通過整合內部數據表示形式,在存儲層和執行引擎之間,AnalyticDB是能夠直接對序列化二進制數據進行操作,而不是Java 對象。這有助于消除序列化和去序列化的開銷,這在大數據量shuffle時可能會節約20%以上的時間。
總結
得益于流水線處理、全列索引、行列混存、運行時索引路徑選擇、K路歸并、向量化執行引擎、CodeGen等優化機制,AnalyticDB獲得了最優的TCP-H測試運行時間,并比Greenplum快了近2倍。
PolarDB/PolarFS
使用共享存儲解決MySQL主從結構遇到的一系列問題
系統結構:
- libpfs是一個用戶空間文件系統庫,負責數據庫的I/O接入。
- PolarSwitch運行在計算節點上,用于轉發數據庫的I/O請求。每個請求包含了數據庫實例所在的Volume ID、起始偏移和長度。PolarSwitch將其劃分為對應的一到多個Chunk,并將請求發往Chunk所屬的ChunkServer完成訪問。
- ChunkServer部署在存儲節點上,用于處理I/O請求和節點內的存儲資源分布。ChunkServer之間通過所謂的ParallelRaft同步數據
- PolarCtrl是系統的控制平面,它包含了一組實現為微服務的管理者,相應地Agent代理被部署到所有的計算和存儲節點上。主要職責:
- 監控ChunkServer的健康狀況,確定哪些ChunkServer有權屬于PolarFS集群;
- Volume創建及Chunk的布局管理(即Chunk分配到哪些ChunkServer);
- Volume至Chunk的元數據信息維護;
- 向PolarSwitch推送元信息緩存更新;
- 監控Volume和Chunk的I/O性能;
- 周期性地發起副本內和副本間的CRC數據校驗。
存儲資源管理單元:
- Volume:是為每個數據庫提供的獨立邏輯存儲空間,其上建立了具體文件系統供此數據庫使用,其大小為10GB至100TB,可充分適用于典型云數據庫實例的容量要求。
- Chunk:每個Volume內部被劃分為多個Chunk,Chunk是數據分布的最小粒度,每個Chunk只存放于存儲節點的單個NVMe SSD盤上,其目的是利于數據高可靠和高可用的管理。典型的Chunk大小為10GB,這遠大于其他類似的系統,例如GFS的64MB。雖然chunk很大,但是chunk可以通過在線遷移維持負載均衡(chunk存儲在固態盤上、還要在線遷移,這個服務不可用時間有多長???)
- Block:在ChunkServer內,Chunk會被進一步劃分為多個Block,其典型大小為64KB。Blocks動態映射到Chunk 中來實現按需分配。Chunk至Block的映射信息由ChunkServer自行管理和保存,除數據Block之外,每個Chunk還包含一些額外Block用來實現Write Ahead Log(寫到optane)。
讀寫流程
ParallelRaft
ParallelRaft與Raft最根本的不同在于,當某個entry提交成功時,并不意味著之前的所有entry都已成功提交。因此我們需要保證:
有了這兩點,結合數據庫或其他應用普遍存在的對存儲I/O亂序完成的默認容忍能力,就可以保證它們在PolarFS上的正常運轉,并獲得PolarFS提供的數據可靠性。
ParallelRaft的亂序執行遵循如下原則:
容易知道,依照此原則完成的I/O不會違反傳統存儲語義的正確性。
后面說了一大堆,反正就是paxos,因為同一個raft上面,可能會有多個并行的事務,所以一定要亂序提交,亂序確認
PolarFS
PolarFS設計中采用了如下技術以充分發揮I/O性能:
- PolarFS采用了綁定CPU的單線程有限狀態機的方式處理I/O,避免了多線程I/O pipeline方式的上下文切換開銷。
- PolarFS優化了內存的分配,采用MemoryPool減少內存對象構造和析構的開銷,采用巨頁來降低分頁和TLB更新的開銷。
- PolarFS通過中心加局部自治的結構,所有元數據均緩存在系統各部件的內存中,基本完全避免了額外的元數據I/O。
- PolarFS采用了全用戶空間I/O棧,包括RDMA和SPDK,避免了內核網絡棧和存儲棧的開銷。
PolarFS是共享訪問的分布式文件系統,每個文件系統實例都有相應的Journal文件和與之對應的Paxos文件。Journal文件記錄了metadata的修改歷史,是共享實例之間元數據同步的中心。Journal文件邏輯上是一個固定大小的循環buffer。PolarFS會根據水位來回收journal。Paxos文件基于Disk Paxos實現了分布式互斥鎖(文件鎖,文件系統里的悲觀鎖,性能如何?)。
由于journal對于PolarFS非常關鍵,它們的修改必需被Paxos互斥鎖保護。如果一個節點希望在journal中追加項,其必需使用DiskPaxos算法來獲取Paxos文件中的鎖。通常,鎖的使用者會在記錄持久化后馬上釋放鎖。但是一些故障情況下使用者不釋放鎖。為此在Paxos互斥鎖上分配有一個租約lease。其他競爭者可以重啟競爭過程。當PolarFS當節點開始同步其他節點修改的元數據時,它從上次掃描的位置掃描到journal末尾,將新entry更新到memory cache中。
PolarFS的上述共享機制非常適合POLARDB一寫多讀的典型應用擴展模式。一寫多讀模式下沒有鎖爭用開銷,只讀實例可以通過原子I/O無鎖獲取Journal信息,從而使得POLARDB可以提供近線性的QPS性能擴展。
由于PolarFS支持了基本的多寫一致性保障,當可寫實例出現故障時,POLARDB能夠方便地將只讀實例升級為可寫實例,而不必擔心底層存儲產生不一致問題,因而方便地提供了數據庫實例Failover的功能。(DBFS,單機高可用)
感覺這個系統從db到libpfs、到后端存儲chunkserver,都有WAL…所以最底層做快照,libpfs可以恢復,然后上層的PolarDB也可以恢復。
對底層盤做快照而不是對上層db做快照有一個問題,就是對盤做快照的時候,當時正在執行的IO,其是否真正落盤了是UB的。PolarDB管這種快照叫做disk outage consistency snapshot,在具體的實現上,如果做快照,PolarCtrl會通知PolarSwitch,在某個時間點的IO上打Tag,chunkserver收到對應的tag之后,說明這個tag時間的時間位點就是一個快照點。所以會先做快照,然后再處理打上tag的IO。這樣,做快照的時間就和上層對應的某個事務的LSN聯系起來了。
事務的數據可見性問題
一、MySQL/InnoDB通過Undo日志來實現事務的MVCC,由于只讀節點跟讀寫節點屬于不同的mysqld進程,讀寫節點在進行Undo日志Purge的時候并不會考慮此時在只讀節點上是否還有事務要訪問即將被刪除的Undo Page,這就會導致記錄舊版本被刪除后,只讀節點上事務讀取到的數據是錯誤的。
針對該問題,PolarDB提供兩種解決方式:
- 所有ReadOnly定期向Primary匯報自己的最大能刪除的Undo數據頁,Primary節點統籌安排;
- 當Primary節點刪除Undo數據頁時候,ReadOnly接收到日志后,判斷即將被刪除的Page是否還在被使用,如果在使用則等待,超過一個時間后還未有結束則直接給客戶端報錯。
二、還有個問題,由于InnoDB BP刷臟頁有多種方式,其并不是嚴格按照oldest modification來的,這就會導致有些事務未提交的頁已經寫入共享存儲,只讀節點讀到該頁后需要通過Undo Page來重建可見的版本,但可能此時Undo Page還未刷盤,這就會出現只讀上事務讀取數據的另一種錯誤。
針對該問題,PolarDB解決方法是:
- 限制讀寫節點刷臟頁機制,如果臟頁的redo還沒有被只讀節點回放,那么該頁不能被刷回到存儲上。這就確保只讀節點讀取到的數據,它之前的數據鏈是完整的,或者說只讀節點已經知道其之前的所有redo日志。這樣即使該數據的記錄版本當前的事務不可見,也可以通過undo構造出來。即使undo對應的page是舊的,可以通過redo構造出所需的undo page。
- replica需要緩存所有未刷盤的數據變更(即RedoLog),只有primary節點把臟頁刷入盤后,replica緩存的日志才能被釋放。這是因為,如果數據未刷盤,那么只讀讀到的數據就可能是舊的,需要通過redo來重建出來,參考第一點。另外,雖然buffer pool中可能已經緩存了未刷盤的page的數據,但該page可能會被LRU替換出去,當其再次載入所以只讀節點必須緩存這些redo。
DDL問題
如果讀寫節點把一個表刪了,反映到存儲上就是把文件刪了。對于mysqld進程來說,它會確保刪除期間和刪除后不再有事務訪問該表。但是在只讀節點上,可能此時還有事務在訪問,PolarFS在完成文件系統元數據同步后,就會導致只讀節點的事務訪問存儲出錯。
PolarDB目前的解決辦法是:如果主庫對一個表進行了表結構變更操作(需要拷表),在操作返回成功前,必須通知到所有的ReadOnly節點(有一個最大的超時時間),告訴他們,這個表已經被刪除了,后續的請求都失敗。當然這種強同步操作會給性能帶來極大的影響,有進一步的優化的空間。
Change Buffer問題
Change Buffer本質上是為了減少二級索引帶來的IO開銷而產生的一種特殊緩存機制。當對應的二級索引頁沒有被讀入內存時,暫時緩存起來,當數據頁后續被讀進內存時,再進行應用,這個特性也帶來的一些問題,該問題僅存在于StandBy中。例如Primary節點可能因為數據頁還未讀入內存,相應的操作還緩存在Change Buffer中,但是StandBy節點則因為不同的查詢請求導致這個數據頁已經讀入內存,可以直接將二級索引修改合并到數據頁上,無需經過Change Buffer了。但由于復制的是Primary節點的redo,且需要保證StandBy和Primary在存儲層的一致性,所以StandBy節點還是會有Change Buffer的數據頁和其對應的redo日志,如果該臟頁回刷到存儲上,就會導致數據不一致。
為了解決這個問題,PolarDB引入shadow page的概念,把未修改的數據頁保存到其中,將Change Buffer記錄合并到原來的數據頁上,同時關閉該Mtr的redo,這樣修改后的Page就不會放到Flush List上。也就是StandBy實例的存儲層數據跟Primary節點保持一致。
Polar-X
ClickHouse
ClickHouse擁有多種表引擎類型,在這眾多的表引擎中,MergeTree是比較有代表性的引擎之一,被廣泛使用。
MergeTree采用列式存儲,類似LSM Tree的架構組織數據。數據導入時被劃分為多個Part,每個Part對應一個目錄。Part中包含各個列的數據,每個列都有獨立的文件。后臺會調度合并任務,將多個小的Part合并成更大的Part,類似LSM Tree的合并過程。 Part中包含幾類文件:
- 數據文件(.bin),每一列的數據都分別存儲在數據文件,一般以主鍵排序。數據文件中劃分為若干個Block,Block是列存文件的壓縮單元。每個Block又會包含若干個索引Granularity,用于索引定位。
- 索引文件(.idx),索引文件又分為主鍵索引和二級索引:
- MergeTree的主鍵索引與傳統數據庫的主鍵索引有所不同,MergeTree的主鍵索引只負責排序,但是不會去重。主鍵索引文件中,存儲的是每一個Granularity中起始行的主鍵值,可以在掃描過程中過濾部分Granularity。
- MergeTree的二級索引文件中可以存儲Granularity的minmax、set、bloom_filter、ngrambf_v1等信息。
- Mark文件(.mrk),由于索引文件是對Granularity進行索引,類似于邏輯索引。Mark文件記錄Granularity在數據文件中的物理偏移,類似于將邏輯索引轉換成物理索引。
MergeTree對于批量導入支持較好,對OLTP級事務更新僅有限支持。MergeTree存儲引擎對數據實時可見要求非常高的場景是不太友好的。
| 存儲結構 | Delta Tree,磁盤行列混存 | 增量 + 基線,磁盤行列混存 | MergeTree,磁盤列存 | Hekaton列存索引,內存行列混存 |
| 索引結構 | 主鍵索引 | 全列倒排索引 | 主鍵索引 + 二級索引 | 本身是行存的索引,可以利用行存的其他索引 |
| 數據更新方式 | MVCC事務隔離,支持TP型事務和批量導入 | MVCC事務隔離,支持TP型事務 | 批量導入友好,有限支持更新 | 與行存保持一致 |
| 數據壓縮 | 通用壓縮 | 字典壓縮 | 通用壓縮 | RLE等專用壓縮 |
- ClickHouse深度揭秘
- 獨家深度 | 一文看懂 ClickHouse vs Elasticsearch:誰更勝一籌?
- 干貨連載 | ClickHouse內核分析-MergeTree的存儲結構和查詢加速
- 談談ClickHouse性能情況以及相關優化
- 如何看待yandex開源clickhouse這個列式文檔數據庫?
clickhouse極致的列存、查詢優化,但是并發查詢性能不佳,不支持事務等等,相比其他競品(hadoop、impala。。。)做到了極致的查詢性能
設計目標
- OLAP數據庫,適用于大寬表,查詢會掃描到大量行但是只用到了少數幾列。使用列式存儲,
- 優化查詢的吞吐(查詢速度),要求海量數據能盡快處理完成。
- 無需事務,數據一致性要求低(可以搭配一款事務型數據庫,CH實時從事務庫中同步數據)
ClickHouse存儲引擎
- 純列式存儲,然后壓縮(有著十倍甚至更高的壓縮比,節省存儲空間,降低存儲成本),
- ClickHouse支持在建表時,指定將數據按照某些列進行sort by。排序后,保證了相同sort key的數據在磁盤上連續存儲,且有序擺放。在進行等值、范圍查詢時,where條件命中的數據都緊密存儲在一個或若干個連續的Block中,而不是分散的存儲在任意多個Block, 大幅減少需要IO的block數量。另外,連續IO也能夠充分利用操作系統page cache的預取能力,減少page fault。
- ClickHouse支持主鍵索引,它將每列數據按照index granularity(默認8192行)進行劃分,每個index granularity的開頭第一行被稱為一個mark行。主鍵索引存儲該mark行對應的primary key的值。對于where條件中含有primary key的查詢,通過對主鍵索引進行二分查找,能夠直接定位到對應的index granularity,避免了全表掃描從而加速查詢。但是值得注意的是:ClickHouse的主鍵索引與MySQL等數據庫不同,它并不用于去重,即便primary key相同的行,也可以同時存在于數據庫中。要想實現去重效果,需要結合具體的表引擎ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree實現。
- 稀疏索引,ClickHouse支持對任意列創建任意數量的稀疏索引。其中被索引的value可以是任意的合法SQL Expression,并不僅僅局限于對column value本身進行索引。之所以叫稀疏索引,是因為它本質上是對一個完整index granularity(默認8192行)的統計信息,并不會具體記錄每一行在文件中的位置。目前支持的稀疏索引類型包括:
- minmax: 以index granularity為單位,存儲指定表達式計算后的min、max值;在等值和范圍查詢中能夠幫助快速跳過不滿足要求的塊,減少IO。
- set(max_rows):以index granularity為單位,存儲指定表達式的distinct value集合,用于快速判斷等值查詢是否命中該塊,減少IO。
- ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed):將string進行ngram分詞后,構建bloom filter,能夠優化等值、like、in等查詢條件。
- tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed): 與ngrambf_v1類似,區別是不使用ngram進行分詞,而是通過標點符號進行詞語分割。
- bloom_filter([false_positive]):對指定列構建bloom filter,用于加速等值、like、in等查詢條件的執行。
- ClickHouse支持單機模式,也支持分布式集群模式。在分布式模式下,ClickHouse會將數據分為多個分片,并且分布到不同節點上。不同的分片策略在應對不同的SQL Pattern時,各有優勢。
- 1)random隨機分片:寫入數據會被隨機分發到分布式集群中的某個節點上。
- 2)constant固定分片:寫入數據會被分發到固定一個節點上。
- 3)column value分片:按照某一列的值進行hash分片。
- 4)自定義表達式分片:指定任意合法表達式,根據表達式被計算后的值進行hash分片。
- 用戶根據自身業務特點選擇合適的數據分片策略,可以有優化數據傾斜、避免shuffle直接本地oin等優點
- ClickHouse采用類LSM Tree的結構,數據寫入后定期在后臺Compaction。通過類LSM tree的結構,ClickHouse在數據導入時全部是順序append寫,寫入后數據段不可更改,在后臺compaction時也是多個段merge sort后順序寫回磁盤。順序寫的特性,充分利用了磁盤的吞吐能力,即便在HDD上也有著優異的寫入性能。
- 在分析場景中,刪除、更新操作并不是核心需求。ClickHouse沒有直接支持delete、update操作,而是變相支持了mutation操作,語法為alter table delete where filter_expr,alter table update col=val where filter_expr。目前主要限制為刪除、更新操作為異步操作,需要后臺compation之后才能生效。
- ClickHouse支持PARTITION BY子句,在建表時可以指定按照任意合法表達式進行數據分區操作,比如通過toYYYYMM()將數據按月進行分區、toMonday()將數據按照周幾進行分區、對Enum類型的列直接每種取值作為一個分區等。
總結來說,極致壓縮,稀疏索引、數據分片
計算引擎
ClickHouse在計算層做了非常細致的工作,竭盡所能榨干硬件能力,提升查詢速度。它實現了單機多核并行、分布式計算、向量化執行與SIMD指令、代碼生成等多種重要技術。
ClickHouse總結
近年來ClickHouse發展趨勢迅猛,社區和大廠都紛紛跟進使用。本文嘗試從OLAP場景的需求出發,介紹了ClickHouse存儲層、計算層的主要設計。ClickHouse實現了大多數當前主流的數據分析技術,具有明顯的技術優勢:
- 提供了極致的查詢性能:開源公開benchmark顯示比傳統方法快1001000倍,提供50MB200MB/s的高吞吐實時導入能力)
- 以極低的成本存儲海量數據: 借助于精心設計的列存、高效的數據壓縮算法,提供高達10倍的壓縮比,大幅提升單機數據存儲和計算能力,大幅降低使用成本,是構建海量數據倉庫的絕佳方案。
- 簡單靈活又不失強大:提供完善SQL支持,上手十分簡單;提供json、map、array等靈活數據類型適配業務快速變化;同時支持近似計算、概率數據結構等應對海量數據處理。
相比于開源社區的其他幾項分析型技術,如Druid、Presto、Impala、Kylin、ElasticSearch等,ClickHouse更是一整套完善的解決方案,它自包含了存儲和計算能力(無需額外依賴其他存儲組件),完全自主實現了高可用,而且支持完整的SQL語法包括JOIN等,技術上有著明顯優勢。相比于hadoop體系,以數據庫的方式來做大數據處理更加簡單易用,學習成本低且靈活度高。當前社區仍舊在迅猛發展中,相信后續會有越來越多好用的功能出現。
TiDB
shared-nothing,raft,很多mysql實現的功能還沒實現。底層KV存儲,TiBD主要負責和client對接,然后做優化,很多執行計劃會下推到TiKV
我在想TiDB還是有很多問題的,首先TiDB的底座不是云原生的基礎組件(類比snowflake polarDB ADB),很多問題上云之后就沒法解決了
TiDB目前有兩種存儲節點,分別是 TiKV 和 TiFlash。TiKV 采用了行式存儲,更適合 TP 類型的業務;而 TiFlash 采用列式存儲,擅長 AP 類型的業務。TiFlash 通過 raft 協議從 TiKV 節點實時同步數據,擁有毫秒級別的延遲,以及非常優秀的數據分析性能。它支持實時同步 TiKV 的數據更新,以及支持在線 DDL。我們把 TiFlash 作為 Raft Learner 融合進 TiDB 的 raft 體系,將兩種節點整合在一個數據庫集群中,上層統一通過 TiDB 節點查詢,使得 TiDB 成為一款真正的 HTAP 數據庫。
TiFlash
TiFlash的列式存儲引擎Delta Tree參考了B+ Tree和LSM Tree的設計思想。
- Delta Tree將數據按照主鍵劃分為Range分區,每個分區稱為Segment。
- Segment通過B+ Tree作為索引。也就是說,B+ Tree索引的葉子節點為Segment。
- 在Segment內部采用類似LSM Tree的分層存儲方式,不過采用固定兩層的LSM Tree,分別為Delta層和Stable層。
- Delta層保存增量數據部分,其中,新寫入的數據寫入Delta Cache中,與LSM Tree的MemTable類似。當Delta Cache寫滿后,其中的數據刷入Delta層的Pack中,類似LSM Tree的L0層。
- Stable層類似于LSM Tree的L1層,其中的數據以主鍵和版本號排序。
- Delta層的Pack和Stable層需要做全量合并,得到新的Stable層數據。
- 當Segment中的數據量超過閾值,就會做類似B+ Tree葉子節點的分裂操作,分裂成兩個Segment。同時,如果相鄰的Segment中的數據量都比較小,也會將相鄰的Segment合并成一個Segment。
C-Store(2005)/Vertica
大多數DBMS都是為寫優化,C-store是第一個為讀優化的OLTP數據庫。C-Store 的主要貢獻有以下幾點:通過精心設計的 projection 同時實現列數據的多副本和多種索引方式;用讀寫分層的方式兼顧了(少量)寫入的性能。此外,C-Store 可能是第一個現代的列式存儲數據庫實現,其的設計啟發了無數后來的商業或開源數據庫,就比如 Vertica。
在 C-Store 內部,邏輯表被縱向拆分成 projections,每個 projection 可以包含一個或多個列,甚至可以包含來自其他邏輯表的列(構成索引)。當然,每個列至少會存在于一個 projections 上。
Projection 內是以列式存儲的:里面的每個列分別用一個數據結構存放。為了避免列太長引起問題,也支持每個 projection 以 sort key 的值做橫向切分。
Projection 是有冗余性的,常常 1 個列會出現在多個 projection 中,但是它們的順序也就是 sort key 并不相同,因此 C-Store 在查詢時可以選用最優的一組 projections,使得查詢執行的代價最小。
Apache ORC
Apache ORC 最初是為支持 Hive 上的 OLAP 查詢開發的一種文件格式,如今在 Hadoop 生態系統中有廣泛的應用。ORC 支持各種格式的字段,包括常見的 int、string 等,也包括 struct、list、map 等組合字段;字段的 meta 信息就放在 ORC 文件的尾部(這被稱為自描述的)。
ORC 里的 Stripe 就像傳統數據庫的頁,它是 ORC 文件批量讀寫的基本單位。這是由于分布式儲存系統的讀寫延遲較大,一次 IO 操作只有批量讀取一定量的數據才劃算。這和按頁讀寫磁盤的思路也有共通之處。
Apache ORC 提供有限的 ACID 事務支持。受限于分布式文件系統的特點,文件不能隨機寫,那如何把修改保存下來呢?
類似于 LSM-Tree 中的 MVCC 那樣,writer 并不是直接修改數據,而是為每個事務生成一個 delta 文件,文件中的修改被疊加在原始數據之上。當 delta 文件越來越多時,通過 minor compaction 把連續多個 delta 文件合成一個;當 delta 變得很大時,再執行 major compaction 將 delta 和原始數據合并。這種保持基線數據不變、分層疊加 delta 數據的優化方式在列式存儲系統中十分常見,是一種通用的解決思路。
Dremel (2010) / Apache Parquet
Dremel 是 Google 研發的用于大規模只讀數據的查詢系統,用于進行快速的 ad-hoc 查詢,彌補 MapReduce 交互式查詢能力的不足。為了避免對數據的二次拷貝,Dremel 的數據就放在原處,通常是 GFS 這樣的分布式文件系統,為此需要設計一種通用的文件格式。
Dremel 的系統設計和大多 OLAP 的列式數據庫并無太多創新點,但是其精巧的存儲格式卻變得流行起來,Apache Parquet 就是它的開源復刻版。注意 Parquet 和 ORC 一樣都是一種存儲格式,而非完整的系統。
Impala
Impala是Cloudera公司主導開發的新型查詢系統,它提供SQL語義,能查詢存儲在Hadoop的HDFS和HBase中的PB級大數據。已有的Hive系統雖然也提供了SQL語義,但由于Hive底層執行使用的是MapReduce引擎,仍然是一個批處理過程,難以滿足查詢的交互性。相比之下,Impala的最大特點也是最大賣點就是它的快速。Impala完全拋棄了MapReduce這個不太適合做SQL查詢的范式,而是像Dremel一樣借鑒了MPP并行數據庫的思想另起爐灶,因此可做更多的查詢優化,從而省掉不必要的shuffle、sort等開銷。
Impala與Hive類似不是數據庫而是數據分析工具,集群有以下幾類節點
查詢流程
- Client發送?個SQL查詢請求到任意?個Impalad節點,會返回?個queryId?于之后的客戶端操作。
- SQL提交到Impalad節點之后,Analyser依次執?SQL的詞法分析、語法分析、語義分析等操作;從MySQL元數據庫中獲取元數據,從HDFS的名稱節點中獲取數據地址,以得到存儲這個查詢相關數據的所有數據節點。
- 單機執行計劃:根據上?步對SQL語句的分析,由Planner先?成單機的執?計劃,該執?計劃是有PlanNode組成的?棵樹,這個過程中也會執??些SQL化,例如Join順序改變、謂詞下推等。
- 分布式并?物理計劃:將單機執?計劃轉換成分布式并?物理執?計劃,物理執?計劃由?個個的Fragment組成,Fragment之間有數據依賴關系,處理過程中要在原有的執?計劃之上加??些ExchangeNode和DataStreamSink信息等。
- Fragment : sql?成的分布式執?計劃的?個?任務;
- DataStreamSink:傳輸當前的Fragment輸出數據到不同的節點;
- Coordinator將Fragment(?任務)根據數據分區信息發配到不同的Impalad節點上執?。Impalad節點接收到執?Fragment請求交由Executor執?。
- 每?個Fragment的執?輸出通過DataStreamSink發送到下?個Fragment,Fragment運?過程中不斷向coordinator節點匯報當前運?狀態。
- 查詢的SQL通常情況下需要有?個單獨的Fragment?于結果的匯總,它只在Coordinator節點運?,將多個節點的最終執?結果匯總,轉換成ResultSet信息。
- 客戶端調?獲取ResultSet的接?,讀取查詢結果。
Druid
Druid可以對多列數據構建倒排索引(bitmap-based inverted indexes)
Pinot
mongoDB
- MongoDB是如何實現事務的ACID?
參考鏈接
總結
以上是生活随笔為你收集整理的分布式数据库产品总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Persistent Memory编程简
- 下一篇: BoltDB 源码分析