日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

分布式数据库产品总结

發(fā)布時間:2024/2/28 数据库 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式数据库产品总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

分布式數(shù)據(jù)庫產(chǎn)品總結(jié)

    • Pivotal
      • Greenplum Database(GPDB)
        • 架構(gòu)
        • 查詢計劃并執(zhí)行
          • 查詢優(yōu)化
        • 索引
          • blink tree
        • 執(zhí)行器
          • 數(shù)據(jù)shuffle
        • 分布式事務(wù)
          • 2PC
            • gp實現(xiàn)
            • gp優(yōu)化
        • MVCC
        • 特點
      • HTAP
      • HAWQ
    • Snowflake Elastic Data Warehouse
      • 數(shù)據(jù)存儲
      • 虛擬數(shù)據(jù)倉庫實例(Virtual Warehouse)
    • AnalyticDB
      • 系統(tǒng)架構(gòu)
      • 保證
      • 存儲引擎
        • 讀寫過程
        • 數(shù)據(jù)合并
        • 行列混存
          • inverted index
        • 元數(shù)據(jù)
      • 索引管理
        • 索引構(gòu)建
      • 優(yōu)化器
      • 執(zhí)行引擎
      • 總結(jié)
    • PolarDB/PolarFS
      • 讀寫流程
      • ParallelRaft
      • PolarFS
      • 事務(wù)的數(shù)據(jù)可見性問題
      • DDL問題
      • Change Buffer問題
    • Polar-X
    • ClickHouse
      • 設(shè)計目標(biāo)
      • ClickHouse存儲引擎
      • 計算引擎
      • ClickHouse總結(jié)
    • 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),可支持大規(guī)模水平擴展的分布式數(shù)據(jù)庫。 GPDB 采用的是 master-worker 模式,每個 worker process 運行在不同的機器上,擁有各自的存儲和運算資源。**客戶端通過 master 把查詢語句分發(fā)到各個機器上,以達(dá)到并行計算來處理海量數(shù)據(jù)。**集群節(jié)點(無論是master還是segemnt)上的每個實例都是一個物理上獨立的PostgrepSQL數(shù)據(jù)庫。

Greenplum數(shù)據(jù)庫是一種shared nothing的分析型MPP數(shù)據(jù)庫。這種模型與高度規(guī)范化的/事務(wù)型的SMP數(shù)據(jù)庫有顯著區(qū)別。Greenplum數(shù)據(jù)庫使用非規(guī)范化的模式設(shè)計會工作得最好,非規(guī)范化的模式適合于MPP分析型處理,例如帶有大型事實表和較小維度表的星形模式或者雪花模式。

Greenplum 在 PostgreSQL 之上還添加了大量其他功能,例如 Append-Optimized 表、列存表、外部表、多級分區(qū)表、細(xì)粒度資源管理器、ORCA 查詢優(yōu)化器、備份恢復(fù)、高可用、故障檢測和故障恢復(fù)、集群數(shù)據(jù)遷移、擴容、MADlib 機器學(xué)習(xí)算法庫、容器化執(zhí)行 UDF、PostGIS 擴展、GPText 套件、監(jiān)控管理、集成 Kubernetes 等。

架構(gòu)

  • master: 保存元數(shù)據(jù)而不保存用戶數(shù)據(jù),有用戶表信息,優(yōu)化器使用這些信息進(jìn)行查詢優(yōu)化和計劃生成
  • segment:每個segment保存用戶數(shù)據(jù)表的一部分。在 Greenplum 中,用戶數(shù)據(jù)按照某種策略分散到不同節(jié)點的不同 segment 實例中。

使用標(biāo)準(zhǔn)的 INSERT SQL 語句可以將數(shù)據(jù)自動按照用戶定義的策略分布到合適的節(jié)點,然而 INSERT 性能較低,僅適合插入少量數(shù)據(jù)。Greenplum 提供了專門的并行化數(shù)據(jù)加載工具以實現(xiàn)高效數(shù)據(jù)導(dǎo)入,詳情可以參考 gpfdist 和 gpload 的官方文檔。

在數(shù)據(jù)分布方面,Greenplum 在這方面不單單做到了基本的分布式數(shù)據(jù)存儲,還提供了很多更高級靈活的特性,譬如多級分區(qū)、多態(tài)存儲。Greenplum 6 進(jìn)一步增強了這一領(lǐng)域,實現(xiàn)了一致性哈希和復(fù)制表,并允許用戶根據(jù)應(yīng)用干預(yù)數(shù)據(jù)分布方法。有這么多種手段,可見Greenplum用戶肯定時遇到了很多數(shù)據(jù)傾斜的問題

Greenplum支持的分區(qū)方法有:

范圍分區(qū):根據(jù)某個列的時間范圍或者數(shù)值范圍對數(shù)據(jù)分區(qū)。譬如以下 SQL 將創(chuàng)建一個分區(qū)表,該表按天分區(qū),從 2016-01-01 到 2017-01-01 把全部一年的數(shù)據(jù)按天分成了 366 個分區(qū):

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') );

列表分區(qū):按照某個列的數(shù)據(jù)值列表,將數(shù)據(jù)分不到不同的分區(qū)。譬如以下 SQL 根據(jù)性別創(chuàng)建一個分區(qū)表,該表有三個分區(qū):一個分區(qū)存儲女士數(shù)據(jù),一個分區(qū)存儲男士數(shù)據(jù),對于其他值譬如 NULL,則存儲在單獨 other 分區(qū)。

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 支持多態(tài)存儲,即單張用戶表,可以根據(jù)訪問模式的不同使用不同的存儲方式存儲不同的分區(qū)。通常不同年齡的數(shù)據(jù)具有不同的訪問模式,不同的訪問模式有不同的優(yōu)化方案。多態(tài)存儲以用戶透明的方式為不同數(shù)據(jù)選擇最佳存儲方式,提供最佳性能。Greenplum 提供以下存儲方式:

  • 堆表(Heap Table):堆表是 Greenplum 的默認(rèn)存儲方式,也是 PostgreSQL 的存儲方式。支持高效的更新和刪除操作,訪問多列時速度快,通常用于 OLTP 型查詢。
  • Append-Optimized 表:為追加而專門優(yōu)化的表存儲模式,通常用于存儲數(shù)據(jù)倉庫中的事實表。不適合頻繁的更新操作。
  • AOCO (Append-Optimized, Column Oriented) 表:AOCO 表為列表,具有較好的壓縮比,支持不同的壓縮算法,適合訪問較少的列的查詢場景。
  • 外部表:外部表的數(shù)據(jù)存儲在外部(數(shù)據(jù)不被 Greenplum 管理),Greenplum 中只有外部表的元數(shù)據(jù)信息。Greenplum 支持很多外部數(shù)據(jù)源譬如 S3、HDFS、文件、Gemfire、各種關(guān)系數(shù)據(jù)庫等和多種數(shù)據(jù)格式譬如 Text、CSV、Avro、Parquet 等。

存儲方式和分區(qū)方式相組合,可以對一張表不同的數(shù)據(jù)區(qū)域有不同的存儲方式。

數(shù)據(jù)分布是任何 MPP 數(shù)據(jù)庫的基礎(chǔ)也是 MPP 數(shù)據(jù)庫是否高效的關(guān)鍵之一。通過把海量數(shù)據(jù)分散到多個節(jié)點上,一方面大大降低了單個節(jié)點處理的數(shù)據(jù)量,另一方面也為處理并行化奠定了基礎(chǔ),兩者結(jié)合起來可以極大的提高整個系統(tǒng)的性能。譬如在一百個節(jié)點的集群上,每個節(jié)點僅保存總數(shù)據(jù)量的百分之一,一百個節(jié)點同時并行處理,性能會是單個配置更強節(jié)點的幾十倍。如果數(shù)據(jù)分布不均勻出現(xiàn)數(shù)據(jù)傾斜,受短板效應(yīng)制約整個系統(tǒng)的性能將會和最慢的節(jié)點相同。因而數(shù)據(jù)分布是否合理對 Greenplum 整體性能影響很大

Greenplum 6 提供了以下數(shù)據(jù)分布策略。

  • 哈希分布:數(shù)據(jù)使用哈希分布,每個分布鍵可以包含多個字段,分布的時候?qū)φ麄€分布鍵下的tuple算哈希,然后放入對應(yīng)的segment
  • 隨機分布:如果不能確定一張表的哈希分布鍵或者不存在合理的避免數(shù)據(jù)傾斜的分布鍵,則可以使用隨機分布。隨機分布會采用循環(huán)的方式將一次插入的數(shù)據(jù)存儲到不同的節(jié)點上。隨機性只在單個 SQL 中有效,不考慮跨 SQL 的情況。譬如如果每次插入一行數(shù)據(jù)到隨機分布表中,最終的數(shù)據(jù)會全部保存在第一個節(jié)點上。
  • 復(fù)制表(Replicated Table):整張表在每個節(jié)點上都有一個完整的拷貝
  • 查詢計劃并執(zhí)行

    PostgreSQL 生成的查詢計劃只能在單節(jié)點上執(zhí)行,Greenplum 需要將查詢計劃并行化以充分發(fā)揮集群的優(yōu)勢

    Greenplum 引入 Motion 算子(操作符)實現(xiàn)查詢計劃的并行化。Motion 算子實現(xiàn)數(shù)據(jù)在不同節(jié)點間的傳輸,它為其他算子隱藏了 MPP 架構(gòu)和單機的不同,使得其他大多數(shù)算子不用關(guān)心是在集群上執(zhí)行還是在單機上執(zhí)行。每個 Motion 算子都有發(fā)送方和接收方。此外 Greenplum 還對某些算子進(jìn)行了分布式優(yōu)化,譬如聚集。

    • 開源分布式數(shù)據(jù)庫Greenplum并行執(zhí)行引擎揭秘
      • motion:MPP架構(gòu)下必須有motion算子,協(xié)調(diào)數(shù)據(jù)分布,這是執(zhí)行計劃中很重要的一步。
      • dispatcher:分配QE資源,配置Slice,分發(fā)任務(wù)(plan+slicetable --> 還可以分發(fā)一些純文本的命令、兩階段提交、CdbDispatchUtilityStatementvectoring中的分發(fā)語法樹(一些語法信息只有QE上有)),協(xié)調(diào)控制(控制、等待下發(fā)的任務(wù)的狀態(tài))
      • interconnect:QE之間數(shù)據(jù)傳送的模塊
        • 引入udp主要是為了解決OLAP查詢在大集群中使用連接資源過多的問題
          • 可靠的UDP (RUDP)
        • interconnect主要遇到的問題就是連接數(shù)不夠用和穩(wěn)定性這兩方面的問題,所以考慮:
          • QUIC協(xié)議
          • Proxy協(xié)議(感覺有點服務(wù)治理的意思了)
    查詢優(yōu)化

    索引

    • greenplum中的索引都是二級索引(非聚集索引)
      • 物理上是存儲在獨立的文件中的(獨立于表的數(shù)據(jù)文件)
      • 并且也是按分片存儲在每個segment上,其索引內(nèi)容對應(yīng)segement上的數(shù)據(jù)分片
    • 不同于原生blink樹,兄弟節(jié)點之間使用右向指針,greenplum采用雙向指針
    • 物理結(jié)構(gòu)上,每個頁包含索引元組(和boltdb差不多),special中存儲了頁面級的元信息:
      • 兄弟指針
      • 頁面類型
      • 等等
    • 葉子節(jié)點的填充率最高是90%,內(nèi)部節(jié)點(非葉子節(jié)點)的填充率最高是70%,不填滿是為了方式insert會造成頻繁的頁分裂。
    blink tree

    要點總結(jié)

  • 構(gòu)建blink tree
  • 節(jié)點分裂與合并,分裂的過程有可能導(dǎo)致樹層數(shù)的增加,(不知道合并會不會涉及樹層數(shù)的減少)
  • 樸素并發(fā)控制
  • search操作逐層下降,加鎖操作先獲取當(dāng)前節(jié)點鎖,然后再釋放上層鎖
  • insert/update也是由根節(jié)點觸發(fā)逐層下降,寫鎖會在所有層都加鎖,但是如果確定當(dāng)前的節(jié)點是安全節(jié)點(在某個節(jié)點上插入一個新數(shù)據(jù)之后,不會觸發(fā)它的分裂,那么這個節(jié)點就稱為安全節(jié)點),那么其上層父親的鎖就可以被釋放了
  • 正確性證明:
  • 讀讀操作:只涉及讀鎖,完全并發(fā),–>正確
  • 寫寫/讀寫操作:查詢操作路徑每次都是先獲取鎖再釋放鎖,最終只可能鎖住葉子節(jié)點,更新操作由于全部加鎖,因此不存在并發(fā)修改,–>正確
  • 是否死鎖:加鎖都是逐層下降的,因此不會死鎖
  • 問題:由于每次都是從根節(jié)點下降的時候都需要加鎖,因此靠近根節(jié)點的位置鎖沖突概率比較高,另外再路徑下降時加的這些鎖大概率馬上就會被釋放掉,所以效率較低
  • greenplum并發(fā)控制優(yōu)化:
  • blink樹希望可以放松在insert過程中對Btree加的鎖類型,可以只加讀鎖,不加寫鎖
  • 由此帶來的問題是,并發(fā)寫有可能導(dǎo)致某個寫操作索引到了錯誤的葉子節(jié)點
  • 因此blink樹通過引入high key和右兄弟指針,用于及時發(fā)現(xiàn)節(jié)點已經(jīng)被分裂,如果分裂,所查詢的鍵值一定在右兄弟節(jié)點上。
  • 執(zhí)行器

    • QD(Query Dispatcher、查詢調(diào)度器):Master 節(jié)點上負(fù)責(zé)處理用戶查詢請求的進(jìn)程稱為 QD(PostgreSQL 中稱之為 Backend 進(jìn)程)。 QD 收到用戶發(fā)來的 SQL 請求后,進(jìn)行解析、重寫和優(yōu)化,將優(yōu)化后的并行計劃分發(fā)給每個 segment 上執(zhí)行,并將最終結(jié)果返回給用戶。此外還負(fù)責(zé)整個 SQL 語句涉及到的所有的 QE 進(jìn)程間的通訊控制和協(xié)調(diào),譬如某個 QE 執(zhí)行時出現(xiàn)錯誤時,QD 負(fù)責(zé)收集錯誤詳細(xì)信息,并取消所有其他 QEs;如果 LIMIT n 語句已經(jīng)滿足,則中止所有 QE 的執(zhí)行等。QD 的入口是 exec_simple_query()。主要是火山模型
    • QE(Query Executor、查詢執(zhí)行器):Segment 上負(fù)責(zé)執(zhí)行 QD 分發(fā)來的查詢?nèi)蝿?wù)的進(jìn)程稱為 QE。Segment 實例運行的也是一個 PostgreSQL,所以對于 QE 而言,QD 是一個 PostgreSQL 的客戶端,它們之間通過 PostgreSQL 標(biāo)準(zhǔn)的 libpq 協(xié)議進(jìn)行通訊。對于 QD 而言,QE 是負(fù)責(zé)執(zhí)行其查詢請求的 PostgreSQL Backend 進(jìn)程。通常 QE 執(zhí)行整個查詢的一部分(稱為 Slice)。QE 的入口是 exec_mpp_query()。
    • Slice:為了提高查詢執(zhí)行并行度和效率,Greenplum 把一個完整的分布式查詢計劃從下到上分成多個 Slice,每個 Slice 負(fù)責(zé)計劃的一部分。劃分 slice 的邊界為 Motion,每遇到 Motion 則一刀將 Motion 切成發(fā)送方和接收方,得到兩顆子樹。每個 slice 由一個 QE 進(jìn)程處理。上面例子中一共有三個 slice。
    • Gang:在不同 segments 上執(zhí)行同一個 slice 的所有 QEs 進(jìn)程稱為 Gang
    數(shù)據(jù)shuffle

    相鄰 Gang 之間的數(shù)據(jù)傳輸稱為數(shù)據(jù)洗牌(Data Shuffling)。數(shù)據(jù)洗牌和 Slice 的層次相吻合,從下到上一層一層通過網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)傳輸,不能跨層傳輸數(shù)據(jù)。根據(jù) Motion 類型的不同有不同的實現(xiàn)方式,譬如廣播和重分布。

    Greenplum 實現(xiàn)數(shù)據(jù)洗牌的技術(shù)稱為 interconnect,它為 QEs 提供高速并行的數(shù)據(jù)傳輸服務(wù),不需要磁盤 IO 操作,是 Greenplum 實現(xiàn)高性能查詢執(zhí)行的重要技術(shù)之一。interconnect 只用來傳輸數(shù)據(jù)(表單的元組),調(diào)度、控制和錯誤處理等信息通過 QD 和 QE 之間的 libpq 連接傳輸。

    Interconnect 有 TCP 和 UDP 兩種實現(xiàn)方式,TCP interconnect 在大規(guī)模集群中會占用大量端口資源,因而擴展性較低。Greenplum 默認(rèn)使用 UDP 方式。UDP interconnect 支持流量控制、網(wǎng)絡(luò)包重發(fā)和確認(rèn)等特性。

    分布式事務(wù)

    Greenplum 使用兩階段提交(2PC)協(xié)議實現(xiàn)分布式事務(wù)。2PC 是數(shù)據(jù)庫經(jīng)典算法,此處不再贅述。本節(jié)概要介紹兩個 Greenplum 分布式事務(wù)的實現(xiàn)細(xì)節(jié):

    (更正:應(yīng)該不是2pc,應(yīng)該就是MVCC)

    • 分布式事務(wù)快照:實現(xiàn) master 和不同 segment 間一致性
    • 共享本地快照:實現(xiàn) segment 內(nèi)不同 QEs

    在 QD 開始一個新的事務(wù)(StartTransaction)時,它會創(chuàng)建一個新的分布式事務(wù) id、設(shè)置時間戳及相應(yīng)的狀態(tài)信息;在獲取快照(GetSnapshotData)時,QD 創(chuàng)建分布式快照并保存在當(dāng)前快照中。和單節(jié)點的快照類似,分布式快照記錄了 xmin/xmax/xip 等信息。這些信息被用于確定元組的可見性(HeapTupleSatisfiesMVCC)。

    和 PostgreSQL 的提交日志 clog 類似,Greenplum 需要保存全局事務(wù)的提交日志,以判斷某個事務(wù)是否已經(jīng)提交。這些信息保存在共享內(nèi)存中并持久化存儲在 distributedlog 目錄下。

    為了提高判斷本地 xid 可見性的效率,避免每次訪問全局事務(wù)提交日志,Greenplum 引入了本地事務(wù)-分布式事務(wù)提交緩存

    2PC

    greenplum基于PG,雖然PG沒有分布式事務(wù)管理器,但是支持2階段提交2pc

    PREPARE TRANSACTION COMMIT PREPARED ROLLBACK PREPARED

    prepare之后持有的行鎖不會被釋放,就算宕機,重啟pg節(jié)點之后鎖還是在的(數(shù)據(jù)庫會將prepare寫進(jìn)事務(wù)日志,需要進(jìn)行負(fù)責(zé)恢復(fù)),方便后續(xù)協(xié)調(diào)者cordinator繼續(xù)對該事務(wù)進(jìn)行操作

    gp實現(xiàn)

    gp在pg的基礎(chǔ)上,實現(xiàn)了:

  • 分布式事務(wù)管理
  • 分布式事務(wù)的創(chuàng)建、狀態(tài)遷移等
  • QD向QE發(fā)起兩階段提交
  • 分布式快照(全局的一致性快照)
  • QE向QE發(fā)送全局快照信息
  • Writer QE和Reader QE共享本地快照信息
  • Distributed log:分布式事務(wù)提交日志
  • 由于判斷分布式事務(wù)是否提交,擢用和PG的commmit log(clog)類似,基于simple LRU實現(xiàn)。
  • 分布式死鎖檢測
  • gp優(yōu)化

    理論上,如果參與者只有1個,2pc可以簡化為1pc(Bernstein稱為協(xié)調(diào)權(quán)的轉(zhuǎn)移,因為只有一個參與者的話,就不需要協(xié)調(diào)者了)

    • 滿足1pc的條件:
      • 有寫操作,但是參與者只有1個
      • 只讀事務(wù),也不需要2pc

    MVCC

    • Greenplum內(nèi)核揭秘之MVCC并發(fā)控制

    主流數(shù)據(jù)庫三大并發(fā)控制方法:

  • MVCC
  • 2PC
  • 樂觀鎖
  • 事務(wù)的本質(zhì)就是將多個步驟捆綁為原子的步驟,要么都成功,要么都不成功,事務(wù)的中間狀態(tài)不應(yīng)該被其他事務(wù)看到(隔離isolation)。

    greenplum只實現(xiàn)了read commited和repeatable read.

    MVCC主要為了解決讀寫沖突。

    • heaptuple用于整整存儲每行數(shù)據(jù) |t_xmin|t_xmax|t_cid|t_ctid|t_infomaskt2|t_infomask|NULL_bitmap|userdata|
      • Xmin:創(chuàng)建tuple的事務(wù)ID
      • Xmax:刪除tuple的事務(wù)ID,有時用于行鎖(有事務(wù)在更新該行,通過配合infomask中的HEAP_XMAX_EXCL_LOCK完成)
      • cid:事物內(nèi)的查詢命令編號,用戶跟蹤事務(wù)內(nèi)部的可見性,創(chuàng)建cursor(游標(biāo))和更改cursor中的內(nèi)容,這兩步需要保證看到的事務(wù)是一樣的。
      • ctid:指向下一個版本tuple的指針,由兩個成員blocknumber和offset組成
      • 這個和mysql不同,mysql存儲的是增量信息
      • t_infomask用以加速可見性的查詢,標(biāo)記優(yōu)化,比用每次都去看mvcc快照或者pg_clog
    • heappage用于存每個heaptuple的偏移量
    • greenplum使用mvcc快照,在pg的2pc之上提供隔離級別的保證
      • 快照理論上是一個正在運行的事務(wù)列表
      • greenplum使用快照判斷一個事務(wù)是否已提交,其中包含如下信息:
        • Xmin:所有小于Xmin的事務(wù)都已經(jīng)提交
        • Running:正在執(zhí)行的事務(wù)列表,這里面保存的所有事務(wù)都是未提交的,不在這里面,并且處于水位之間的id就是已經(jīng)提交的
        • Xmax:所有大于等于Xmax的事務(wù)都未提交
        • 該快照中只存儲了事務(wù)commited或running狀態(tài),如果需要查詢abort狀態(tài),需要查詢pg_clog(事務(wù)日志)。(事務(wù)在abort的時候不需要更改行中的t_xmax)
      • 在READ COMMITTED隔離級別下,每個查詢開始時生成快照
      • 在REPEATABLE READ隔離界別下,在每個事務(wù)開始時生成快照
    • 在一眾版本中,某個事務(wù)必然只能看到其中一個,所以事務(wù)需要判斷快照的可見性
      • 在視頻22:50講解的很詳細(xì)
    • 寫寫沖突情況下,后面的事務(wù)會阻塞直到前面一個事務(wù)完成

    一個transaction共有三種狀態(tài), committed,running,abort

    特點

  • mvcc有清理需求
  • 在更新tuple的時候會創(chuàng)建一個新的tuple,所以舊的tuple需要清理
  • 在刪除tuple時,只會標(biāo)記xmax,不會立即刪除
  • 失敗的事務(wù)abort掉之后,tuple也就失效了
  • 三種情況下產(chǎn)生的垃圾tuple在一段時間之后就都不可見了
  • 何時刪除?
  • 訪問到某個page的時候,順手清理 --> 單頁面清理
  • 通過vacuum命令清理整個表
  • 如果更新頻繁,tuple特別特別多,那么在當(dāng)前的page就不新生成index了,而是每個tuple通過鏈表的形式進(jìn)行組織。節(jié)約index空間
  • HTAP

    2021 sigmod論文《Greenplum: A Hybrid Database for Transactional and Analytical Workloads

    HAWQ

    針對 Hadoop 存儲的 SQL 執(zhí)行引擎。HAWQ 通過數(shù)據(jù)接口可以直接讀取 Hive 表里的數(shù)據(jù)(也支持原生存儲格式),然后用 SQL 執(zhí)行引擎來計算得到查詢結(jié)果。與 HiveQL 通過把 SQL 解析成一連串的 MapReduce job 的執(zhí)行模式相比,速度要快好幾個量級。HAWQ 雖然在開發(fā)執(zhí)行引擎過程中借鑒了很多 GPDB 的東西,但畢竟是一款不同的數(shù)據(jù)庫引擎,Pivotal 因此希望有一款兼容的優(yōu)化器能夠服務(wù)于它。由此,研發(fā)了開源優(yōu)化器 ORCA。

    Snowflake Elastic Data Warehouse

    除了使用了 vec-exec(畢竟,聯(lián)合創(chuàng)始人 Marcin 的博士畢業(yè)論文就是關(guān)于 vec-exec 的),Snowflake 也是一款 100%計算和存儲分離,面向云原生的數(shù)據(jù)倉庫系統(tǒng)。本文內(nèi)容主要參考他們發(fā)表于 SIGMOD-16 的 paper: The Snowflake Elastic Data Warehouse。

    Snowlake 是 2012 年成立的,2015 年正式推出商用版本。2012 年,正是云服務(wù)起步不久,大數(shù)據(jù)熱火朝天的時候。當(dāng)時,數(shù)據(jù)倉庫的主流趨勢是 SQL On Hadoop。Cloudera, Hontornworks, MapR, Greenplum HAWQ, Facebook 的 Presto,算是百花齊放。但主創(chuàng)團隊認(rèn)為,RDBMS 不會消失,用戶們會因為上云的趨勢,想要一款完全適配云端的數(shù)據(jù)倉庫。

    文章簡單介紹了市面上通常的 on-prem 分布式數(shù)據(jù)倉庫的一些缺點。首先就是計算和存儲硬件是耦合的,即每個服務(wù)器同時負(fù)責(zé)存儲數(shù)據(jù),并且執(zhí)行 SQL 語句得到結(jié)果。耦合的劣勢在于,不能針對不同的 workloads 做優(yōu)化。二就是服務(wù)器的 node membership 改變(無論是因為服務(wù)器損壞,或者是因為數(shù)據(jù)量提升需要擴容)對用戶來說都不友善。一,就是要進(jìn)行大量數(shù)據(jù)的 reshuffle。二是,為了做到高可用,可能會保留一部分 node 作為 stand-by replica,當(dāng)主節(jié)點有問題時,馬上接替主節(jié)點,這相當(dāng)于變相提高了數(shù)據(jù)成本。總結(jié)來說,on-prem 的數(shù)據(jù)倉庫要做到同時保持可伸縮性(elasticity)和高可用性(availability)并兼顧成本,是很難魚與熊掌兼得的。三就是對服務(wù)進(jìn)行升級比較麻煩。

    由于云服務(wù)的出現(xiàn),很多上述的問題,變得不再是問題了。一就是,云服務(wù)通常會提供多種類型的服務(wù)器來針對特定的 usecase;二,服務(wù)器的下線,上線,擴容在云服務(wù)上都屬于基本操作;三是,云上有高可用,低成本的存儲系統(tǒng);四是,服務(wù)更新非常方便。基于這些原因,Snowflake 選擇了完完全全的計算和存儲分離的架構(gòu)設(shè)計。整個架構(gòu)分成三個大模塊:

  • 數(shù)據(jù)存儲:完全交給 AWS 的 S3 來存儲數(shù)據(jù)。
  • Virtual Warehouse(VW) 虛擬數(shù)據(jù)倉庫實例(下面簡稱 VW):由多個 Virtual Node(AWS 中的 EC2 instance)組成的一個 Virtual Cluster,負(fù)責(zé)執(zhí)行各種 SQL 語句,因此稱為 Virtual Warehouse。數(shù)據(jù)庫的執(zhí)行引擎是也是自己構(gòu)建的分布式引擎。
  • Cloud Services:整個 Snowflake 的大腦:負(fù)責(zé)管理數(shù)據(jù)存儲和 VW,以及其他一系列的操作,比如安全,登陸,事物管理,用戶隔離,等等。值得注意的是,你可以大致認(rèn)為整個 AWS,所有的用戶,共享這一個大腦實例(當(dāng)然,這個實例本身是多中心復(fù)制,高可用加高備份的),但每個用戶只能管理屬于自己的數(shù)據(jù)和 VW。
  • 數(shù)據(jù)存儲

    在設(shè)計存儲系統(tǒng)的時候,Snowflake 有糾結(jié)過,是應(yīng)該使用 AWS 的 S3,還是自行設(shè)計類似于 HDFS 的存儲系統(tǒng)。最終,在經(jīng)過了各種比較,利弊權(quán)衡后,決定使用 S3。雖然,S3 的性能并不是最快;并且,由于是網(wǎng)絡(luò)接入,也不是最穩(wěn)定。但是,勝在高可用性和高可靠性上。團隊決定基于 S3 打造數(shù)據(jù)存儲系統(tǒng),同時,可以把精力放在優(yōu)化 local caching 和數(shù)據(jù)傾斜(skew resilience)上。

    相對于本地文件系統(tǒng),S3 的 access latency 會更高,并且,由于是網(wǎng)絡(luò)接入(尤其是用 https),CPU 使用率也更高。而且,S3 本身就是一個簡單的 blob 存儲,支持的主要創(chuàng)建,刪除和讀取文件,即,不能對現(xiàn)有文件進(jìn)行更新,更新相當(dāng)于重新創(chuàng)建一個更新過的文件。但是,S3 的讀取有一大好處在于,可以讀取部分文件。

    S3 的這些屬性,對于整個 Snowflake 的數(shù)據(jù)存儲和并行控制設(shè)計有重大的影響。首先,表數(shù)據(jù)被水平(horizontally partitioned)地切分成多個不可變的 blob 文件;每個文件通過列存(column-store)的形式保存數(shù)據(jù),Snowflake 具體使用的存儲格式是 PAX 的 Hybrid-column store(挖個坑,可以單獨講一期這個)。每個數(shù)據(jù)文件包含數(shù)據(jù)頭用來存儲元數(shù)據(jù)。基于 S3 的下載部分文件的 API,對于運行的 SQL 語句,優(yōu)化器會選擇只下載必須用到的數(shù)據(jù) block 即可。這也就意味著所有snowflake的事務(wù)都是基于快照隔離Snapshot Isolation(SI)

    值得一提的是,Snowflake 不單單使用 S3 來存儲表數(shù)據(jù)文件,也用 S3 來存儲臨時生成的 intermediate result(語句執(zhí)行中,某個 operator 產(chǎn)生的臨時結(jié)果集)。一旦這些結(jié)果集的大小超過了本地磁盤空間,spill 到磁盤上的文件就會以 S3 的形式存儲。這樣的好處在于,可以讓 Snowflake 真正可以處理巨大的數(shù)據(jù)而不用擔(dān)心內(nèi)存或者本地磁盤空間吃緊。另一個好處在于,這些臨時結(jié)果集也可能被利用作為 cache 使用。

    最后文中還提到了數(shù)據(jù)庫的其他元數(shù)據(jù)存儲,包括有哪些 caching 文件,每個表存在了哪些 S3 文件中,等等,都是存儲在一個 transactional 的 key-value store 中,并不在 S3 里。

    虛擬數(shù)據(jù)倉庫實例(Virtual Warehouse)

    執(zhí)行 SQL 語句:每個語句 instance 都只會運行在一個 VW 上;每個 VW 有多個 WN;每個 WN 只隸屬于一個 VW,不會被共享。(這邊有注解說,WN 變成共享的會是一個未來的工作,因為可以更好地提升使用率并且會進(jìn)一步降低用戶成本)。當(dāng)一個語句被運行時,所有的 WN 在這個 VW 上,(或者也可能是一部分 WN,如果優(yōu)化器認(rèn)為這是一個非常輕量級的語句),都會起一個 worker process,這個進(jìn)程的生命周期就是這句語句的執(zhí)行周期。worker process ,在執(zhí)行的過程中,不會對外部資源造成任何變化,換言之,no side effect,即使是 update 語句。為什么這么說呢,因為所有的表數(shù)據(jù)文件都是 immutable 的。這樣帶來的好處就是,如果 worker process 由于各種原因崩潰了, 通常只是需要 retry 即可,沒有其他善后事宜要做。現(xiàn)在 VW 里還不支持 partial retry,這也在未來計劃的工作中。

    由于 VW 的可伸縮性(elasticity),通常情況下,可以通過起一個更大 size 的 VW 來提升語句的性能,但保持一樣的使用成本。例如,**一個復(fù)雜的分析語句在一個 4 節(jié)點 VW 上需要運行 15 個小時,但在一個 32 節(jié)點 VW 上只需要 2 小時。**因為是云原生,用戶只需要支付運行 VW 時的費用即可。因此,在價格不變的情況下,用戶體驗和查詢速度卻大幅度提升。這也是 Snowflake 云原生數(shù)據(jù)倉庫的一大賣點。

    • 本地緩存: 每個 WN 都會用本地文件為表數(shù)據(jù)做本地緩存,即已經(jīng)被從 S3 那讀取的數(shù)據(jù)文件。這些文件是包含元數(shù)據(jù)信息和要用到的 column 的數(shù)據(jù)。這些緩存的數(shù)據(jù)文件可以被多個 worker process 共享(如果需要讀取一樣的數(shù)據(jù)),文中提到維護(hù)了一個簡單的 LRU 的 cache replacement 策略,效果非常不錯。為了進(jìn)一步提升 hit rate,同一份數(shù)據(jù)文件被多個 WN 節(jié)點保存,優(yōu)化器會用 consistent hashing 算法,來分配哪些節(jié)點保存哪些數(shù)據(jù)。同時,對于后續(xù)要讀取對應(yīng)數(shù)據(jù)的語句,優(yōu)化器也會根據(jù)這個分配發(fā)送到對應(yīng)節(jié)點。
    • 數(shù)據(jù)傾斜處理:一些節(jié)點可能相對于其他節(jié)點,運行更慢,比如硬件問題或者是單純網(wǎng)絡(luò)問題。Snowflake 的優(yōu)化是,每個 WN 在讀取了相應(yīng)的數(shù)據(jù)文件后,當(dāng)它發(fā)現(xiàn)其他 WN 還在讀取,他會發(fā)送請求給其他 WN 要求分擔(dān)更多的數(shù)據(jù),而且這些數(shù)據(jù)直接從 S3 讀取。從而來確保不要把過多的數(shù)據(jù)處理放在速度慢的 WN 上。
    • 執(zhí)行引擎:雖說可以通過增加節(jié)點來提升性能,但是 Snowflake 依然希望每一個節(jié)點的單體性能都能做到極致。因此,Snowflake 構(gòu)建了自己的,基于列存向量執(zhí)行(vec-exec),并且是 push-based(推模式)的執(zhí)行引擎。
      • Columnar: 沒啥爭議,對于 OLAP 語句來說,Columnar-store 無論從存儲,讀取效率和執(zhí)行效率來說,都優(yōu)于 row-store。
      • Vec-exec:也沒有爭議,Marcin 肯定把 Vec-Exec 這套運行優(yōu)化放到執(zhí)行器上。
      • push-based: 相對于 Volcano 的拉模式,是下方的 operator,當(dāng)處理完數(shù)據(jù)后,把數(shù)據(jù) push 到上方的 operator(從執(zhí)行計劃角度來看上下),類似于 code-gen,這樣的好處是提高了 cache 的利用率,因為可以避免不必要的循環(huán)控制語句。
      • 另一點就是,一些其他傳統(tǒng)數(shù)據(jù)庫系統(tǒng)在執(zhí)行語句時需要考慮的麻煩,對于 Snowflake 來說沒有。比如,不用 transaction management,因為所有的語句都是沒有 side effect 的。(原因是S3中的文件不可以更改)

    AnalyticDB

    數(shù)據(jù)庫帶來的新挑戰(zhàn):

  • 在線化和高可用:離線和在線的邊界越來越模糊,一切數(shù)據(jù)皆服務(wù)化、一切分析皆在線化;
  • 高并發(fā)低延時:越來越多的數(shù)據(jù)系統(tǒng)直接服務(wù)終端客戶,對系統(tǒng)的pp和處理延時提出了新的交互性挑戰(zhàn);
  • 混合負(fù)載:一套實時分析系統(tǒng)既要支持?jǐn)?shù)據(jù)加工處理,又要支持高并發(fā)低延時的交互式查詢;
  • 融合分析:隨著對數(shù)據(jù)新的使用方式探索,需要解決結(jié)構(gòu)化與非結(jié)構(gòu)化數(shù)據(jù)融合場景下的數(shù)據(jù)檢索和分析問題。
  • Oracle RAC --> Greenplum --> HBase --> AnalyticDB

    ADB主要是OLAP系統(tǒng),同時要顧及各種點查詢、優(yōu)化的速度。底層采用盤古,所以數(shù)據(jù)庫主要的創(chuàng)新點在數(shù)據(jù)格式、優(yōu)化器、執(zhí)行器等等

    系統(tǒng)架構(gòu)

    AnalyticDB主要分為以下幾個部分:

  • Coordinator(協(xié)調(diào)節(jié)點):協(xié)調(diào)節(jié)點負(fù)責(zé)接收J(rèn)DBC/ODBC連接發(fā)過來的請求,并將請求分發(fā)給讀節(jié)點或者寫節(jié)點
  • Write Node(寫節(jié)點):只處理寫請求(如INSERT、DELETE、UPDATE)的節(jié)點。
  • 某個寫節(jié)點會被選為主節(jié)點,其他寫節(jié)點選為從節(jié)點,主節(jié)點和從節(jié)點之間通過ZooKeeper來進(jìn)行通信。每個節(jié)點會獨立負(fù)責(zé)某些一級分區(qū)的數(shù)據(jù),主節(jié)點的任務(wù)就是決定每個節(jié)點負(fù)責(zé)哪些一級分區(qū)。協(xié)調(diào)節(jié)點會將寫請求分發(fā)到對應(yīng)的寫節(jié)點上,寫節(jié)點收到請求后,會將寫SQL語句放到內(nèi)存buffer中,這些buffer中的SQL語句稱為log數(shù)據(jù)。
  • 寫節(jié)點會將buffer中的log數(shù)據(jù)刷到盤古上,當(dāng)刷盤古成功后,寫節(jié)點會返回一個版本號(即LSN)給協(xié)調(diào)節(jié)點,表示寫完成了。每個一級分區(qū)在其對應(yīng)的寫節(jié)點上,都會獨立地對應(yīng)一個版本號,每次寫節(jié)點將某個一級分區(qū)的log數(shù)據(jù)刷到盤古后,都會增大這個版本號,并將最新版本號返回給協(xié)調(diào)節(jié)點。
  • 當(dāng)盤古上的log數(shù)據(jù)達(dá)到一定規(guī)模時,AnalyticDB會在伏羲上啟動MapReduce任務(wù),以將log數(shù)據(jù)轉(zhuǎn)換成真實存儲數(shù)據(jù)+索引
  • Read Node(讀節(jié)點):只處理讀請求(如SELECT)的節(jié)點。
  • 每個讀節(jié)點也獨立負(fù)責(zé)某些一級分區(qū)的數(shù)據(jù)。在每個讀節(jié)點初始化時,它會從盤古上讀取最新版本數(shù)據(jù)(包括索引)。之后,基于這份數(shù)據(jù),讀節(jié)點會從寫節(jié)點的內(nèi)存buffer中將寫請求log周期性地拉取過來,并在本地進(jìn)行replay,replay之后的數(shù)據(jù)不會再存儲到盤古中(而是存到本地ssd中?)。讀節(jié)點根據(jù)replay之后的數(shù)據(jù),服務(wù)到來的讀請求。
  • 由于讀節(jié)點需要去從寫節(jié)點上拉取寫請求數(shù)據(jù),因此讀節(jié)點為用戶提供了兩種可見性級別:實時(real-time)可見和延時(bounded-staleness)可見。實時可見允許讀節(jié)點立即讀到寫節(jié)點寫入的數(shù)據(jù),延時可見允許讀節(jié)點在一段時間后才讀到寫節(jié)點上寫入的數(shù)據(jù)。AnalyticDB默認(rèn)使用的可見性級別為延時可見。(我猜延時可見就是用某種方式讀polarFS,這個架構(gòu)有點像Aurora的一寫14讀了)
  • Pangu(盤古):高可靠分布式存儲系統(tǒng),是AnalyticDB依賴的基礎(chǔ)模塊。寫節(jié)點會將寫請求的數(shù)據(jù)刷到盤古上進(jìn)行持久化。
  • Fuxi(伏羲):資源管理與任務(wù)調(diào)度系統(tǒng),是AnalyticDB依賴的基礎(chǔ)模塊。伏羲合理使用集群機器的空閑資源,以進(jìn)行相關(guān)計算任務(wù)的異步調(diào)度執(zhí)行。
  • 為便于大規(guī)模分析處理,AnalyticDB對數(shù)據(jù)表進(jìn)行分區(qū)。AnalyticDB數(shù)據(jù)表有兩個分區(qū)級別:一級分區(qū)和二級分區(qū)。

    選擇具有較高基數(shù)(cardinality)的列作為一級分區(qū)鍵,以保證數(shù)據(jù)行能均勻地分布到每個一級分區(qū),最大化并行。用戶還可以根據(jù)需要定義二級分區(qū),以便進(jìn)行數(shù)據(jù)的自動管理。二級分區(qū)擁有最大分區(qū)數(shù),當(dāng)二級分區(qū)的實際數(shù)目超過了這個最大分區(qū)數(shù)后,最老的二級分區(qū)會被自動刪除。通常,選擇時間列(天、周或月)作為二級分區(qū)列,這樣,包含相同時間序列的數(shù)據(jù)行,會被劃分到同一個二級分區(qū)中。

    傳統(tǒng)OLAP系統(tǒng)在同一個鏈路上同時處理讀寫請求,因此,所有的并發(fā)讀寫請求都共享同一個資源池,也會互相影響。但是當(dāng)讀寫并發(fā)同時非常大時,這種設(shè)計會由于過度的資源競爭而導(dǎo)致不好的性能。如圖5所示,為了解決這個問題,同時確保讀和寫的高性能,AnalyticDB采用的架構(gòu)為讀寫分離架構(gòu),即AnalyticDB有獨立的讀寫節(jié)點各自處理讀寫請求,且寫節(jié)點和讀節(jié)點完全互相隔離。

    保證

  • 可靠性:寫節(jié)點自己選主,并且負(fù)責(zé)負(fù)載均衡,用戶可以指定每個讀節(jié)點的副本個數(shù)。既保證了可靠性,又保證了讀寫帶寬
  • 擴展性:當(dāng)有新寫節(jié)點加入時,自動負(fù)責(zé)負(fù)載均衡
  • 多租戶:使用CGroup負(fù)責(zé)多租戶的隔離(CPU/內(nèi)存/網(wǎng)絡(luò)帶寬)(一個AnalyticDB實例會根據(jù)對應(yīng)的資源創(chuàng)建上面提到的各種節(jié)點)
  • 存儲引擎

    AnalyticDB存儲層采用Lambda架構(gòu),讀節(jié)點上的數(shù)據(jù)包括基線數(shù)據(jù)增量數(shù)據(jù)兩部分。增量數(shù)據(jù)又分為Incremental Data和Deleted bitset,按照行列混存的架構(gòu)存放在讀節(jié)點的SSD上。真正讀取是,basline數(shù)據(jù)要和增量數(shù)據(jù)做UNION和MINUS之后,才能輸出有效數(shù)據(jù)。

    對于每張表,每k行的數(shù)據(jù)組成一個Row Group。Row Group中的數(shù)據(jù)連續(xù)存放在磁盤中。整個Row Group中,又將數(shù)據(jù)按照列(聚集列)分別順序存放。AnalyticDB會對每列構(gòu)建一份元數(shù)據(jù),用于維護(hù)列數(shù)據(jù)的統(tǒng)計信息(包括Cardinality、Sum和Min/Max等)、字典數(shù)據(jù)(采用字典編碼)以及物理映射等。AnalyticDB默認(rèn)會對每一列數(shù)據(jù)建立索引,索引中的Key是列的值,Value是值出現(xiàn)的所有行號集合,采用后臺異步構(gòu)建模式。由于增量數(shù)據(jù)部分沒有索引,隨著數(shù)據(jù)的不斷實時寫入,增量數(shù)據(jù)的查詢性能會越來越慢。AnalyticDB采用后臺任務(wù)來合并基線數(shù)據(jù)和增量數(shù)據(jù)形成一個新的基線數(shù)據(jù),并基于新的基線數(shù)據(jù)構(gòu)建全量索引。

    讀寫過程

    使用copy on write技術(shù)(OLAP讀多寫少)來支持MVCC,delete數(shù)據(jù)被轉(zhuǎn)化在Deleted bitset上,而update操作則被分為Incremental Data和Deleted bitset分別存放。每個寫操作都會分配獨立的LSN,從而達(dá)到MVCC

    由于建立了全列倒排索引,所以執(zhí)行引擎處理返回結(jié)果的時候用到了多路歸并

    數(shù)據(jù)合并

    由于沒有全局索引,隨著數(shù)據(jù)的不斷實時寫入,增量數(shù)據(jù)的查詢性能會越來越慢。因此ADB會在后臺通過伏羲啟動一個MapReduce 任務(wù)來合并基線數(shù)據(jù)和增量數(shù)據(jù)(同時去掉標(biāo)記為刪除的數(shù)據(jù))形成一個新的基線數(shù)據(jù),并基于新的基線數(shù)據(jù)構(gòu)建全量索引。

    在合并任務(wù)開始時,一部分增量數(shù)據(jù)會標(biāo)記為immutable,并執(zhí)行合并,合并完成之后,之前的baseline data和immutable會被刪除

    行列混存

    在海量數(shù)據(jù)分析場景下,數(shù)據(jù)分析業(yè)務(wù)主要有以下三類workload:

  • OLAP場景下的大規(guī)模多維分析:海量數(shù)據(jù)的統(tǒng)計分析和多表關(guān)聯(lián),比較適合列存格式
  • 高并發(fā)的點查:通常需要撈取出一整行的明細(xì)數(shù)據(jù),比較適合行存。
  • 高寫入吞吐:每秒千萬的高吞吐實時寫入,比較適合行存。
  • 在ADB的實現(xiàn)中,每K行數(shù)據(jù)實現(xiàn)了Row Group,每個row group中的每個列存放在自己的block中,Row group按照索引排列

    inverted index

    為了應(yīng)對ad-hoc,ADB對每列建立了倒排索引,從而提高復(fù)雜數(shù)據(jù)的查詢效率。(每列都建立索引,不就是倒排索引了)

    元數(shù)據(jù)

    為了加速查詢,AnalyticDB對每列構(gòu)建一份元數(shù)據(jù),并保存在一個叫detail_meta的單獨文件中。detail_meta文件通常較小(小于1MB),首次查詢時被加載在內(nèi)存中。如圖8左邊所示,元數(shù)據(jù)主要包括4部分:

    • Header。包括版本號,文件長度以及一些統(tǒng)計信息。
    • 列統(tǒng)計信息。包括行數(shù),NULL值數(shù),cardinality,SUM,MAX和MIN 值。優(yōu)化器根據(jù)這些信息來生成最佳執(zhí)行計劃。
    • 字典。對于cardinality較少(小于1024)的列,AnalyticDB采用字典編碼,數(shù)據(jù)文件里保存字典號碼。字典保存在該字段中。
    • 塊地址信息。保存塊號到數(shù)據(jù)文件起始地址和長度的映射關(guān)系。(我猜測是每次合并的時候更新)

    索引管理

    AnalyticDB設(shè)計和實現(xiàn)了一個新的索引引擎,在不影響寫入性能的情況下,支持結(jié)構(gòu)化和非結(jié)構(gòu)化數(shù)據(jù)類型索引。它將構(gòu)建過程從寫入鏈路中移除,采用后臺異步構(gòu)建模式,支持對所有列構(gòu)建索引,從而解決了OLAP任意查詢的性能問題

    AnalyticDB默認(rèn)對所有列構(gòu)建索引,并保存在一個單獨的文件中。與傳統(tǒng)的數(shù)據(jù)庫不同,AnalyticDB索引中的key是列的值,value是該值出現(xiàn)的所有行號集合,并支持所有的條件同時走索引查詢。多個列的操作去做union或者intersect

    AnalyticDB在索引引擎是實現(xiàn)上也做了大量的優(yōu)化,包括:多路流式歸并、索引選擇CBO和索引結(jié)果緩存。

    • 多路流式歸并:傳統(tǒng)數(shù)據(jù)庫大多采用2路歸并策略,在條件數(shù)特別多的場景下,會導(dǎo)致大量中間結(jié)果,計算效率很低。AanlyticDB采用K路流式歸并算法,可以支持多個集合并行歸并,避免產(chǎn)生大量中間結(jié)果集合,提升了整個歸并的速度。
    • 索引選擇CBO:當(dāng)where條件中包括多個條件,并不是所有的條件走索引掃描能取得最佳的性能。利用索引中的統(tǒng)計信息,提前估算出各個條件可能的選擇率,對于選擇率很高的條件走索引查詢,其他條件直接在上層進(jìn)行過濾操作。例如對于where id = 1 and 0 < x < 1000000的情況下,id = 1這個條件的選擇率已經(jīng)很高,則0<x<1000000條件不走索引查詢效率會更高。
    • 索引結(jié)果緩存:在OLAP分析場景中,多個查詢條件中,可能會出現(xiàn)部分條件固定不變或重復(fù)多次出現(xiàn)。針對這種場景AnalyticDB 實現(xiàn)了一個高效的無鎖緩存,緩存的的key為等值或range條件,value為行號集合。這樣在出現(xiàn)重復(fù)查詢情況下,可以直接讀取緩存,避免索引IO掃描開銷。

    索引構(gòu)建

    為了支持每秒千萬的實時數(shù)據(jù)寫入,避免同步構(gòu)建索引影響實時寫入的性能,AnalyticDB并沒有采用同步構(gòu)建索引的策略,而是采用異步后臺進(jìn)程構(gòu)建索引的方式。索引引擎會根據(jù)時間或增量數(shù)據(jù)的大小來決定是否啟動后臺進(jìn)程來構(gòu)建索引。該后臺進(jìn)程讀取Pangu上的歷史全量數(shù)據(jù)和新寫入的增量日志數(shù)據(jù),完成數(shù)據(jù)合并形成新的全量數(shù)據(jù),并對該全量數(shù)據(jù)重新構(gòu)建索引。該過程通過伏羲的MapReduce任務(wù)執(zhí)行,選擇負(fù)載較低的機器執(zhí)行,對用戶完全透明。

    優(yōu)化器

    創(chuàng)新性引入了兩個關(guān)鍵功能:存儲感知的優(yōu)化和高效實時采樣。因為ADB獨特的索引結(jié)構(gòu)和分布式的數(shù)據(jù)存儲

    執(zhí)行引擎

    在優(yōu)化器之下,AnalyticDB在MPP架構(gòu)基礎(chǔ)上,采用流水線執(zhí)行的DAG架構(gòu),構(gòu)建了一個適用于低延遲和高吞吐量工作負(fù)載的執(zhí)行器。AnalyticDB的列式執(zhí)行引擎能夠充分利用底層的行列混合存儲。與行式執(zhí)行引擎相比,當(dāng)前的向量化執(zhí)行引擎更加緩存友好,能避免將不必要的數(shù)據(jù)加載到內(nèi)存中。

    與許多 OLAP 系統(tǒng)一樣,AnalyticDB在運行時利用代碼生成器(CodeGen) 來提高 CPU 密集型計算的性能。AnalyticDB的CodeGen基于 ANTLR ASM來動態(tài)生成表達(dá)式的代碼樹。同時此 CodeGen 引擎還將運行時因素納入考慮,讓AnalyticDB能在Task級別利用異構(gòu)新硬件的能力。例如,如果集群中CPU支持 AVX-512指令集,我們通過生成字節(jié)碼使用SIMD來提高性能。在此之外,通過整合內(nèi)部數(shù)據(jù)表示形式,在存儲層和執(zhí)行引擎之間,AnalyticDB是能夠直接對序列化二進(jìn)制數(shù)據(jù)進(jìn)行操作,而不是Java 對象。這有助于消除序列化和去序列化的開銷,這在大數(shù)據(jù)量shuffle時可能會節(jié)約20%以上的時間。

    總結(jié)

    得益于流水線處理、全列索引、行列混存、運行時索引路徑選擇、K路歸并、向量化執(zhí)行引擎、CodeGen等優(yōu)化機制,AnalyticDB獲得了最優(yōu)的TCP-H測試運行時間,并比Greenplum快了近2倍。

    PolarDB/PolarFS

    使用共享存儲解決MySQL主從結(jié)構(gòu)遇到的一系列問題

    系統(tǒng)結(jié)構(gòu):

    • libpfs是一個用戶空間文件系統(tǒng)庫,負(fù)責(zé)數(shù)據(jù)庫的I/O接入。
    • PolarSwitch運行在計算節(jié)點上,用于轉(zhuǎn)發(fā)數(shù)據(jù)庫的I/O請求。每個請求包含了數(shù)據(jù)庫實例所在的Volume ID、起始偏移和長度。PolarSwitch將其劃分為對應(yīng)的一到多個Chunk,并將請求發(fā)往Chunk所屬的ChunkServer完成訪問。
    • ChunkServer部署在存儲節(jié)點上,用于處理I/O請求和節(jié)點內(nèi)的存儲資源分布。ChunkServer之間通過所謂的ParallelRaft同步數(shù)據(jù)
    • PolarCtrl是系統(tǒng)的控制平面,它包含了一組實現(xiàn)為微服務(wù)的管理者,相應(yīng)地Agent代理被部署到所有的計算和存儲節(jié)點上。主要職責(zé):
      • 監(jiān)控ChunkServer的健康狀況,確定哪些ChunkServer有權(quán)屬于PolarFS集群;
      • Volume創(chuàng)建及Chunk的布局管理(即Chunk分配到哪些ChunkServer);
      • Volume至Chunk的元數(shù)據(jù)信息維護(hù);
      • 向PolarSwitch推送元信息緩存更新;
      • 監(jiān)控Volume和Chunk的I/O性能;
      • 周期性地發(fā)起副本內(nèi)和副本間的CRC數(shù)據(jù)校驗。

    存儲資源管理單元:

    • Volume:是為每個數(shù)據(jù)庫提供的獨立邏輯存儲空間,其上建立了具體文件系統(tǒng)供此數(shù)據(jù)庫使用,其大小為10GB至100TB,可充分適用于典型云數(shù)據(jù)庫實例的容量要求。
    • Chunk:每個Volume內(nèi)部被劃分為多個Chunk,Chunk是數(shù)據(jù)分布的最小粒度,每個Chunk只存放于存儲節(jié)點的單個NVMe SSD盤上,其目的是利于數(shù)據(jù)高可靠和高可用的管理。典型的Chunk大小為10GB,這遠(yuǎn)大于其他類似的系統(tǒng),例如GFS的64MB。雖然chunk很大,但是chunk可以通過在線遷移維持負(fù)載均衡(chunk存儲在固態(tài)盤上、還要在線遷移,這個服務(wù)不可用時間有多長???)
    • Block:在ChunkServer內(nèi),Chunk會被進(jìn)一步劃分為多個Block,其典型大小為64KB。Blocks動態(tài)映射到Chunk 中來實現(xiàn)按需分配。Chunk至Block的映射信息由ChunkServer自行管理和保存,除數(shù)據(jù)Block之外,每個Chunk還包含一些額外Block用來實現(xiàn)Write Ahead Log(寫到optane)。

    讀寫流程

  • POLARDB通過libpfs發(fā)送一個寫請求,經(jīng)由ring buffer發(fā)送到PolarSwitch。
  • PolarSwitch根據(jù)本地緩存的元數(shù)據(jù),將該請求發(fā)送至對應(yīng)Chunk的主節(jié)點。
  • 新寫請求到達(dá)后,主節(jié)點上的RDMA NIC將寫請求放到一個提前分好的buffer中,并將該請求項加到請求隊列。一個I/O輪詢線程不斷輪詢這個請求隊列,一旦發(fā)現(xiàn)新請求到來,它就立即開始處理。
  • 請求通過SPDK寫到硬盤的日志block,并通過RDMA發(fā)向副本節(jié)點。這些操作都是異步調(diào)用,數(shù)據(jù)傳輸是并發(fā)進(jìn)行的。
  • 當(dāng)副本請求到達(dá)副本節(jié)點,副本節(jié)點的RDMA NIC同樣會將其放到預(yù)分buffer中并加入到復(fù)制隊列。
  • 副本節(jié)點上的I/O輪詢線程被觸發(fā),請求通過SPDK異步地寫入Chunk的日志。
  • 當(dāng)副本節(jié)點的寫請求成功回調(diào)后,會通過RDMA向主節(jié)點發(fā)送一個應(yīng)答響應(yīng)。
  • 主節(jié)點收到一個復(fù)制組中大多數(shù)節(jié)點的成功返回后,主節(jié)點通過SPDK將寫請求應(yīng)用到數(shù)據(jù)塊上。就這一步比較重要,其他不用看
  • 隨后,主節(jié)點通過RDMA向PolarSwitch返回。
  • PolarSwitch標(biāo)記請求成功并通知上層的POLARDB。
  • ParallelRaft

    ParallelRaft與Raft最根本的不同在于,當(dāng)某個entry提交成功時,并不意味著之前的所有entry都已成功提交。因此我們需要保證:

  • 在這種情況下,單個存儲的狀態(tài)不會違反存儲語義的正確性;
  • 所有已提交的entry在各種邊界情況下均不會丟失;
  • 有了這兩點,結(jié)合數(shù)據(jù)庫或其他應(yīng)用普遍存在的對存儲I/O亂序完成的默認(rèn)容忍能力,就可以保證它們在PolarFS上的正常運轉(zhuǎn),并獲得PolarFS提供的數(shù)據(jù)可靠性。

    ParallelRaft的亂序執(zhí)行遵循如下原則:

  • 當(dāng)寫入的Log項彼此的存儲范圍沒有交疊,那么就認(rèn)為Log項無沖突可以亂序執(zhí)行;
  • 否則,沖突的Log項將按照寫入次序依次完成。
  • 容易知道,依照此原則完成的I/O不會違反傳統(tǒng)存儲語義的正確性。

    后面說了一大堆,反正就是paxos,因為同一個raft上面,可能會有多個并行的事務(wù),所以一定要亂序提交,亂序確認(rèn)

    PolarFS

    PolarFS設(shè)計中采用了如下技術(shù)以充分發(fā)揮I/O性能:

    • PolarFS采用了綁定CPU的單線程有限狀態(tài)機的方式處理I/O,避免了多線程I/O pipeline方式的上下文切換開銷。
    • PolarFS優(yōu)化了內(nèi)存的分配,采用MemoryPool減少內(nèi)存對象構(gòu)造和析構(gòu)的開銷,采用巨頁來降低分頁和TLB更新的開銷。
    • PolarFS通過中心加局部自治的結(jié)構(gòu),所有元數(shù)據(jù)均緩存在系統(tǒng)各部件的內(nèi)存中,基本完全避免了額外的元數(shù)據(jù)I/O。
    • PolarFS采用了全用戶空間I/O棧,包括RDMA和SPDK,避免了內(nèi)核網(wǎng)絡(luò)棧和存儲棧的開銷。

    PolarFS是共享訪問的分布式文件系統(tǒng),每個文件系統(tǒng)實例都有相應(yīng)的Journal文件和與之對應(yīng)的Paxos文件。Journal文件記錄了metadata的修改歷史,是共享實例之間元數(shù)據(jù)同步的中心。Journal文件邏輯上是一個固定大小的循環(huán)buffer。PolarFS會根據(jù)水位來回收journal。Paxos文件基于Disk Paxos實現(xiàn)了分布式互斥鎖(文件鎖,文件系統(tǒng)里的悲觀鎖,性能如何?)。

    由于journal對于PolarFS非常關(guān)鍵,它們的修改必需被Paxos互斥鎖保護(hù)。如果一個節(jié)點希望在journal中追加項,其必需使用DiskPaxos算法來獲取Paxos文件中的鎖。通常,鎖的使用者會在記錄持久化后馬上釋放鎖。但是一些故障情況下使用者不釋放鎖。為此在Paxos互斥鎖上分配有一個租約lease。其他競爭者可以重啟競爭過程。當(dāng)PolarFS當(dāng)節(jié)點開始同步其他節(jié)點修改的元數(shù)據(jù)時,它從上次掃描的位置掃描到j(luò)ournal末尾,將新entry更新到memory cache中。

    PolarFS的上述共享機制非常適合POLARDB一寫多讀的典型應(yīng)用擴展模式。一寫多讀模式下沒有鎖爭用開銷,只讀實例可以通過原子I/O無鎖獲取Journal信息,從而使得POLARDB可以提供近線性的QPS性能擴展。

    由于PolarFS支持了基本的多寫一致性保障,當(dāng)可寫實例出現(xiàn)故障時,POLARDB能夠方便地將只讀實例升級為可寫實例,而不必?fù)?dān)心底層存儲產(chǎn)生不一致問題,因而方便地提供了數(shù)據(jù)庫實例Failover的功能。(DBFS,單機高可用)

    感覺這個系統(tǒng)從db到libpfs、到后端存儲chunkserver,都有WAL…所以最底層做快照,libpfs可以恢復(fù),然后上層的PolarDB也可以恢復(fù)。

    對底層盤做快照而不是對上層db做快照有一個問題,就是對盤做快照的時候,當(dāng)時正在執(zhí)行的IO,其是否真正落盤了是UB的。PolarDB管這種快照叫做disk outage consistency snapshot,在具體的實現(xiàn)上,如果做快照,PolarCtrl會通知PolarSwitch,在某個時間點的IO上打Tag,chunkserver收到對應(yīng)的tag之后,說明這個tag時間的時間位點就是一個快照點。所以會先做快照,然后再處理打上tag的IO。這樣,做快照的時間就和上層對應(yīng)的某個事務(wù)的LSN聯(lián)系起來了。

    事務(wù)的數(shù)據(jù)可見性問題

    一、MySQL/InnoDB通過Undo日志來實現(xiàn)事務(wù)的MVCC,由于只讀節(jié)點跟讀寫節(jié)點屬于不同的mysqld進(jìn)程,讀寫節(jié)點在進(jìn)行Undo日志Purge的時候并不會考慮此時在只讀節(jié)點上是否還有事務(wù)要訪問即將被刪除的Undo Page,這就會導(dǎo)致記錄舊版本被刪除后,只讀節(jié)點上事務(wù)讀取到的數(shù)據(jù)是錯誤的。

    針對該問題,PolarDB提供兩種解決方式:

    • 所有ReadOnly定期向Primary匯報自己的最大能刪除的Undo數(shù)據(jù)頁,Primary節(jié)點統(tǒng)籌安排;
    • 當(dāng)Primary節(jié)點刪除Undo數(shù)據(jù)頁時候,ReadOnly接收到日志后,判斷即將被刪除的Page是否還在被使用,如果在使用則等待,超過一個時間后還未有結(jié)束則直接給客戶端報錯。

    二、還有個問題,由于InnoDB BP刷臟頁有多種方式,其并不是嚴(yán)格按照oldest modification來的,這就會導(dǎo)致有些事務(wù)未提交的頁已經(jīng)寫入共享存儲,只讀節(jié)點讀到該頁后需要通過Undo Page來重建可見的版本,但可能此時Undo Page還未刷盤,這就會出現(xiàn)只讀上事務(wù)讀取數(shù)據(jù)的另一種錯誤。

    針對該問題,PolarDB解決方法是:

    • 限制讀寫節(jié)點刷臟頁機制,如果臟頁的redo還沒有被只讀節(jié)點回放,那么該頁不能被刷回到存儲上。這就確保只讀節(jié)點讀取到的數(shù)據(jù),它之前的數(shù)據(jù)鏈?zhǔn)峭暾?#xff0c;或者說只讀節(jié)點已經(jīng)知道其之前的所有redo日志。這樣即使該數(shù)據(jù)的記錄版本當(dāng)前的事務(wù)不可見,也可以通過undo構(gòu)造出來。即使undo對應(yīng)的page是舊的,可以通過redo構(gòu)造出所需的undo page。
    • replica需要緩存所有未刷盤的數(shù)據(jù)變更(即RedoLog),只有primary節(jié)點把臟頁刷入盤后,replica緩存的日志才能被釋放。這是因為,如果數(shù)據(jù)未刷盤,那么只讀讀到的數(shù)據(jù)就可能是舊的,需要通過redo來重建出來,參考第一點。另外,雖然buffer pool中可能已經(jīng)緩存了未刷盤的page的數(shù)據(jù),但該page可能會被LRU替換出去,當(dāng)其再次載入所以只讀節(jié)點必須緩存這些redo。

    DDL問題

    如果讀寫節(jié)點把一個表刪了,反映到存儲上就是把文件刪了。對于mysqld進(jìn)程來說,它會確保刪除期間和刪除后不再有事務(wù)訪問該表。但是在只讀節(jié)點上,可能此時還有事務(wù)在訪問,PolarFS在完成文件系統(tǒng)元數(shù)據(jù)同步后,就會導(dǎo)致只讀節(jié)點的事務(wù)訪問存儲出錯。

    PolarDB目前的解決辦法是:如果主庫對一個表進(jìn)行了表結(jié)構(gòu)變更操作(需要拷表),在操作返回成功前,必須通知到所有的ReadOnly節(jié)點(有一個最大的超時時間),告訴他們,這個表已經(jīng)被刪除了,后續(xù)的請求都失敗。當(dāng)然這種強同步操作會給性能帶來極大的影響,有進(jìn)一步的優(yōu)化的空間。

    Change Buffer問題

    Change Buffer本質(zhì)上是為了減少二級索引帶來的IO開銷而產(chǎn)生的一種特殊緩存機制。當(dāng)對應(yīng)的二級索引頁沒有被讀入內(nèi)存時,暫時緩存起來,當(dāng)數(shù)據(jù)頁后續(xù)被讀進(jìn)內(nèi)存時,再進(jìn)行應(yīng)用,這個特性也帶來的一些問題,該問題僅存在于StandBy中。例如Primary節(jié)點可能因為數(shù)據(jù)頁還未讀入內(nèi)存,相應(yīng)的操作還緩存在Change Buffer中,但是StandBy節(jié)點則因為不同的查詢請求導(dǎo)致這個數(shù)據(jù)頁已經(jīng)讀入內(nèi)存,可以直接將二級索引修改合并到數(shù)據(jù)頁上,無需經(jīng)過Change Buffer了。但由于復(fù)制的是Primary節(jié)點的redo,且需要保證StandBy和Primary在存儲層的一致性,所以StandBy節(jié)點還是會有Change Buffer的數(shù)據(jù)頁和其對應(yīng)的redo日志,如果該臟頁回刷到存儲上,就會導(dǎo)致數(shù)據(jù)不一致。

    為了解決這個問題,PolarDB引入shadow page的概念,把未修改的數(shù)據(jù)頁保存到其中,將Change Buffer記錄合并到原來的數(shù)據(jù)頁上,同時關(guān)閉該Mtr的redo,這樣修改后的Page就不會放到Flush List上。也就是StandBy實例的存儲層數(shù)據(jù)跟Primary節(jié)點保持一致。

    Polar-X

    ClickHouse

    ClickHouse擁有多種表引擎類型,在這眾多的表引擎中,MergeTree是比較有代表性的引擎之一,被廣泛使用。

    MergeTree采用列式存儲,類似LSM Tree的架構(gòu)組織數(shù)據(jù)。數(shù)據(jù)導(dǎo)入時被劃分為多個Part,每個Part對應(yīng)一個目錄。Part中包含各個列的數(shù)據(jù),每個列都有獨立的文件。后臺會調(diào)度合并任務(wù),將多個小的Part合并成更大的Part,類似LSM Tree的合并過程。 Part中包含幾類文件:

    • 數(shù)據(jù)文件(.bin),每一列的數(shù)據(jù)都分別存儲在數(shù)據(jù)文件,一般以主鍵排序。數(shù)據(jù)文件中劃分為若干個Block,Block是列存文件的壓縮單元。每個Block又會包含若干個索引Granularity,用于索引定位。
    • 索引文件(.idx),索引文件又分為主鍵索引和二級索引:
      • MergeTree的主鍵索引與傳統(tǒng)數(shù)據(jù)庫的主鍵索引有所不同,MergeTree的主鍵索引只負(fù)責(zé)排序,但是不會去重。主鍵索引文件中,存儲的是每一個Granularity中起始行的主鍵值,可以在掃描過程中過濾部分Granularity。
      • MergeTree的二級索引文件中可以存儲Granularity的minmax、set、bloom_filter、ngrambf_v1等信息。
    • Mark文件(.mrk),由于索引文件是對Granularity進(jìn)行索引,類似于邏輯索引。Mark文件記錄Granularity在數(shù)據(jù)文件中的物理偏移,類似于將邏輯索引轉(zhuǎn)換成物理索引。

    MergeTree對于批量導(dǎo)入支持較好,對OLTP級事務(wù)更新僅有限支持。MergeTree存儲引擎對數(shù)據(jù)實時可見要求非常高的場景是不太友好的。

    TiFlashAnalyticDBClickHouseSqlServer
    存儲結(jié)構(gòu)Delta Tree,磁盤行列混存增量 + 基線,磁盤行列混存MergeTree,磁盤列存Hekaton列存索引,內(nèi)存行列混存
    索引結(jié)構(gòu)主鍵索引全列倒排索引主鍵索引 + 二級索引本身是行存的索引,可以利用行存的其他索引
    數(shù)據(jù)更新方式MVCC事務(wù)隔離,支持TP型事務(wù)和批量導(dǎo)入MVCC事務(wù)隔離,支持TP型事務(wù)批量導(dǎo)入友好,有限支持更新與行存保持一致
    數(shù)據(jù)壓縮通用壓縮字典壓縮通用壓縮RLE等專用壓縮
    • ClickHouse深度揭秘
    • 獨家深度 | 一文看懂 ClickHouse vs Elasticsearch:誰更勝一籌?
    • 干貨連載 | ClickHouse內(nèi)核分析-MergeTree的存儲結(jié)構(gòu)和查詢加速
    • 談?wù)凜lickHouse性能情況以及相關(guān)優(yōu)化
    • 如何看待yandex開源clickhouse這個列式文檔數(shù)據(jù)庫?

    clickhouse極致的列存、查詢優(yōu)化,但是并發(fā)查詢性能不佳,不支持事務(wù)等等,相比其他競品(hadoop、impala。。。)做到了極致的查詢性能

    設(shè)計目標(biāo)

    • OLAP數(shù)據(jù)庫,適用于大寬表,查詢會掃描到大量行但是只用到了少數(shù)幾列。使用列式存儲,
    • 優(yōu)化查詢的吞吐(查詢速度),要求海量數(shù)據(jù)能盡快處理完成。
    • 無需事務(wù),數(shù)據(jù)一致性要求低(可以搭配一款事務(wù)型數(shù)據(jù)庫,CH實時從事務(wù)庫中同步數(shù)據(jù))

    ClickHouse存儲引擎

    • 純列式存儲,然后壓縮(有著十倍甚至更高的壓縮比,節(jié)省存儲空間,降低存儲成本),
    • ClickHouse支持在建表時,指定將數(shù)據(jù)按照某些列進(jìn)行sort by。排序后,保證了相同sort key的數(shù)據(jù)在磁盤上連續(xù)存儲,且有序擺放。在進(jìn)行等值、范圍查詢時,where條件命中的數(shù)據(jù)都緊密存儲在一個或若干個連續(xù)的Block中,而不是分散的存儲在任意多個Block, 大幅減少需要IO的block數(shù)量。另外,連續(xù)IO也能夠充分利用操作系統(tǒng)page cache的預(yù)取能力,減少page fault。
    • ClickHouse支持主鍵索引,它將每列數(shù)據(jù)按照index granularity(默認(rèn)8192行)進(jìn)行劃分,每個index granularity的開頭第一行被稱為一個mark行。主鍵索引存儲該mark行對應(yīng)的primary key的值。對于where條件中含有primary key的查詢,通過對主鍵索引進(jìn)行二分查找,能夠直接定位到對應(yīng)的index granularity,避免了全表掃描從而加速查詢。但是值得注意的是:ClickHouse的主鍵索引與MySQL等數(shù)據(jù)庫不同,它并不用于去重,即便primary key相同的行,也可以同時存在于數(shù)據(jù)庫中。要想實現(xiàn)去重效果,需要結(jié)合具體的表引擎ReplacingMergeTree、CollapsingMergeTree、VersionedCollapsingMergeTree實現(xiàn)。
    • 稀疏索引,ClickHouse支持對任意列創(chuàng)建任意數(shù)量的稀疏索引。其中被索引的value可以是任意的合法SQL Expression,并不僅僅局限于對column value本身進(jìn)行索引。之所以叫稀疏索引,是因為它本質(zhì)上是對一個完整index granularity(默認(rèn)8192行)的統(tǒng)計信息,并不會具體記錄每一行在文件中的位置。目前支持的稀疏索引類型包括:
      • minmax: 以index granularity為單位,存儲指定表達(dá)式計算后的min、max值;在等值和范圍查詢中能夠幫助快速跳過不滿足要求的塊,減少IO。
      • set(max_rows):以index granularity為單位,存儲指定表達(dá)式的distinct value集合,用于快速判斷等值查詢是否命中該塊,減少IO。
      • ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed):將string進(jìn)行ngram分詞后,構(gòu)建bloom filter,能夠優(yōu)化等值、like、in等查詢條件。
      • tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed): 與ngrambf_v1類似,區(qū)別是不使用ngram進(jìn)行分詞,而是通過標(biāo)點符號進(jìn)行詞語分割。
      • bloom_filter([false_positive]):對指定列構(gòu)建bloom filter,用于加速等值、like、in等查詢條件的執(zhí)行。
    • ClickHouse支持單機模式,也支持分布式集群模式。在分布式模式下,ClickHouse會將數(shù)據(jù)分為多個分片,并且分布到不同節(jié)點上。不同的分片策略在應(yīng)對不同的SQL Pattern時,各有優(yōu)勢。
      • 1)random隨機分片:寫入數(shù)據(jù)會被隨機分發(fā)到分布式集群中的某個節(jié)點上。
      • 2)constant固定分片:寫入數(shù)據(jù)會被分發(fā)到固定一個節(jié)點上。
      • 3)column value分片:按照某一列的值進(jìn)行hash分片。
      • 4)自定義表達(dá)式分片:指定任意合法表達(dá)式,根據(jù)表達(dá)式被計算后的值進(jìn)行hash分片。
      • 用戶根據(jù)自身業(yè)務(wù)特點選擇合適的數(shù)據(jù)分片策略,可以有優(yōu)化數(shù)據(jù)傾斜、避免shuffle直接本地oin等優(yōu)點
    • ClickHouse采用類LSM Tree的結(jié)構(gòu),數(shù)據(jù)寫入后定期在后臺Compaction。通過類LSM tree的結(jié)構(gòu),ClickHouse在數(shù)據(jù)導(dǎo)入時全部是順序append寫,寫入后數(shù)據(jù)段不可更改,在后臺compaction時也是多個段merge sort后順序?qū)懟卮疟P。順序?qū)懙奶匦?#xff0c;充分利用了磁盤的吞吐能力,即便在HDD上也有著優(yōu)異的寫入性能。
    • 在分析場景中,刪除、更新操作并不是核心需求。ClickHouse沒有直接支持delete、update操作,而是變相支持了mutation操作,語法為alter table delete where filter_expr,alter table update col=val where filter_expr。目前主要限制為刪除、更新操作為異步操作,需要后臺compation之后才能生效
    • ClickHouse支持PARTITION BY子句,在建表時可以指定按照任意合法表達(dá)式進(jìn)行數(shù)據(jù)分區(qū)操作,比如通過toYYYYMM()將數(shù)據(jù)按月進(jìn)行分區(qū)、toMonday()將數(shù)據(jù)按照周幾進(jìn)行分區(qū)、對Enum類型的列直接每種取值作為一個分區(qū)等。

    總結(jié)來說,極致壓縮,稀疏索引、數(shù)據(jù)分片

    計算引擎

    ClickHouse在計算層做了非常細(xì)致的工作,竭盡所能榨干硬件能力,提升查詢速度。它實現(xiàn)了單機多核并行、分布式計算、向量化執(zhí)行與SIMD指令、代碼生成等多種重要技術(shù)。

  • 多核并行:ClickHouse將數(shù)據(jù)劃分為多個partition,每個partition再進(jìn)一步劃分為多個index granularity,然后通過多個CPU核心分別處理其中的一部分來實現(xiàn)并行數(shù)據(jù)處理。在這種設(shè)計下,單條Query就能利用整機所有CPU。極致的并行處理能力,極大的降低了查詢延時。
  • 分布式計算:除了優(yōu)秀的單機并行處理能力,ClickHouse還提供了可線性拓展的分布式計算能力。ClickHouse會自動將查詢拆解為多個task下發(fā)到集群中,然后進(jìn)行多機并行處理,最后把結(jié)果匯聚到一起。在存在多副本的情況下,ClickHouse提供了多種query下發(fā)策略:
  • 隨機下發(fā):在多個replica中隨機選擇一個;
  • 最近hostname原則:選擇與當(dāng)前下發(fā)機器最相近的hostname節(jié)點,進(jìn)行query下發(fā)。在特定的網(wǎng)絡(luò)拓?fù)湎?#xff0c;可以降低網(wǎng)絡(luò)延時。而且能夠確保query下發(fā)到固定的replica機器,充分利用系統(tǒng)cache。
  • in order:按照特定順序逐個嘗試下發(fā),當(dāng)前一個replica不可用時,順延到下一個replica。
  • first or random:在In Order模式下,當(dāng)?shù)谝粋€replica不可用時,所有workload都會積壓到第二個Replica,導(dǎo)致負(fù)載不均衡。first or random解決了這個問題:當(dāng)?shù)谝粋€replica不可用時,隨機選擇一個其他replica,從而保證其余replica間負(fù)載均衡。另外在跨region復(fù)制場景下,通過設(shè)置第一個replica為本region內(nèi)的副本,可以顯著降低網(wǎng)絡(luò)延時。
  • 向量化執(zhí)行與SIMD:ClickHouse不僅將數(shù)據(jù)按列存儲,而且按列進(jìn)行計算。傳統(tǒng)OLTP數(shù)據(jù)庫通常采用按行計算,原因是事務(wù)處理中以點查為主,SQL計算量小,實現(xiàn)這些技術(shù)的收益不夠明顯。但是在分析場景下,單個SQL所涉及計算量可能極大,將每行作為一個基本單元進(jìn)行處理會帶來嚴(yán)重的性能損耗:
  • 對每一行數(shù)據(jù)都要調(diào)用相應(yīng)的函數(shù),函數(shù)調(diào)用開銷占比高;
  • 存儲層按列存儲數(shù)據(jù),在內(nèi)存中也按列組織,但是計算層按行處理,無法充分利用CPU cache的預(yù)讀能力,造成CPU Cache miss嚴(yán)重;
  • 按行處理,無法利用高效的SIMD指令;
  • ClickHouse實現(xiàn)了向量執(zhí)行引擎(Vectorized execution engine),對內(nèi)存中的列式數(shù)據(jù),一個batch調(diào)用一次SIMD指令(而非每一行調(diào)用一次),不僅減少了函數(shù)調(diào)用次數(shù)、降低了cache miss,而且可以充分發(fā)揮SIMD指令的并行能力,大幅縮短了計算耗時。向量執(zhí)行引擎,通常能夠帶來數(shù)倍的性能提升。
  • 動態(tài)代碼生成Runtime Codegen
  • 在經(jīng)典的數(shù)據(jù)庫實現(xiàn)中,通常對表達(dá)式計算采用火山模型,也即將查詢轉(zhuǎn)換成一個個operator,比如HashJoin、Scan、IndexScan、Aggregation等。為了連接不同算子,operator之間采用統(tǒng)一的接口,比如open/next/close。在每個算子內(nèi)部都實現(xiàn)了父類的這些虛函數(shù),在分析場景中單條SQL要處理數(shù)據(jù)通常高達(dá)數(shù)億行,虛函數(shù)的調(diào)用開銷不再可以忽略不計。另外,在每個算子內(nèi)部都要考慮多種變量,比如列類型、列的size、列的個數(shù)等,存在著大量的if-else分支判斷導(dǎo)致CPU分支預(yù)測失效。
  • ClickHouse實現(xiàn)了Expression級別的runtime codegen,動態(tài)地根據(jù)當(dāng)前SQL直接生成代碼,然后編譯執(zhí)行。如下圖例子所示,對于Expression直接生成代碼,不僅消除了大量的虛函數(shù)調(diào)用(即圖中多個function pointer的調(diào)用),而且由于在運行時表達(dá)式的參數(shù)類型、個數(shù)等都是已知的,也消除了不必要的if-else分支判斷。
  • 近似計算:ClickHouse實現(xiàn)了多種近似計算功能:
  • 近似估算distinct values、中位數(shù),分位數(shù)等多種聚合函數(shù);
  • 建表DDL支持SAMPLE BY子句,支持對于數(shù)據(jù)進(jìn)行抽樣處理;
  • ClickHouse總結(jié)

    近年來ClickHouse發(fā)展趨勢迅猛,社區(qū)和大廠都紛紛跟進(jìn)使用。本文嘗試從OLAP場景的需求出發(fā),介紹了ClickHouse存儲層、計算層的主要設(shè)計。ClickHouse實現(xiàn)了大多數(shù)當(dāng)前主流的數(shù)據(jù)分析技術(shù),具有明顯的技術(shù)優(yōu)勢:

    • 提供了極致的查詢性能:開源公開benchmark顯示比傳統(tǒng)方法快1001000倍,提供50MB200MB/s的高吞吐實時導(dǎo)入能力)
    • 以極低的成本存儲海量數(shù)據(jù): 借助于精心設(shè)計的列存、高效的數(shù)據(jù)壓縮算法,提供高達(dá)10倍的壓縮比,大幅提升單機數(shù)據(jù)存儲和計算能力,大幅降低使用成本,是構(gòu)建海量數(shù)據(jù)倉庫的絕佳方案。
    • 簡單靈活又不失強大:提供完善SQL支持,上手十分簡單;提供json、map、array等靈活數(shù)據(jù)類型適配業(yè)務(wù)快速變化;同時支持近似計算、概率數(shù)據(jù)結(jié)構(gòu)等應(yīng)對海量數(shù)據(jù)處理。

    相比于開源社區(qū)的其他幾項分析型技術(shù),如Druid、Presto、Impala、Kylin、ElasticSearch等,ClickHouse更是一整套完善的解決方案,它自包含了存儲和計算能力(無需額外依賴其他存儲組件),完全自主實現(xiàn)了高可用,而且支持完整的SQL語法包括JOIN等,技術(shù)上有著明顯優(yōu)勢。相比于hadoop體系,以數(shù)據(jù)庫的方式來做大數(shù)據(jù)處理更加簡單易用,學(xué)習(xí)成本低且靈活度高。當(dāng)前社區(qū)仍舊在迅猛發(fā)展中,相信后續(xù)會有越來越多好用的功能出現(xiàn)。

    TiDB

    shared-nothing,raft,很多mysql實現(xiàn)的功能還沒實現(xiàn)。底層KV存儲,TiBD主要負(fù)責(zé)和client對接,然后做優(yōu)化,很多執(zhí)行計劃會下推到TiKV

    我在想TiDB還是有很多問題的,首先TiDB的底座不是云原生的基礎(chǔ)組件(類比snowflake polarDB ADB),很多問題上云之后就沒法解決了

    TiDB目前有兩種存儲節(jié)點,分別是 TiKV 和 TiFlash。TiKV 采用了行式存儲,更適合 TP 類型的業(yè)務(wù);而 TiFlash 采用列式存儲,擅長 AP 類型的業(yè)務(wù)。TiFlash 通過 raft 協(xié)議從 TiKV 節(jié)點實時同步數(shù)據(jù),擁有毫秒級別的延遲,以及非常優(yōu)秀的數(shù)據(jù)分析性能。它支持實時同步 TiKV 的數(shù)據(jù)更新,以及支持在線 DDL。我們把 TiFlash 作為 Raft Learner 融合進(jìn) TiDB 的 raft 體系,將兩種節(jié)點整合在一個數(shù)據(jù)庫集群中,上層統(tǒng)一通過 TiDB 節(jié)點查詢,使得 TiDB 成為一款真正的 HTAP 數(shù)據(jù)庫。

    TiFlash

    TiFlash的列式存儲引擎Delta Tree參考了B+ TreeLSM Tree的設(shè)計思想。

    • Delta Tree將數(shù)據(jù)按照主鍵劃分為Range分區(qū),每個分區(qū)稱為Segment。
    • Segment通過B+ Tree作為索引。也就是說,B+ Tree索引的葉子節(jié)點為Segment。
    • 在Segment內(nèi)部采用類似LSM Tree的分層存儲方式,不過采用固定兩層的LSM Tree,分別為Delta層和Stable層。
      • Delta層保存增量數(shù)據(jù)部分,其中,新寫入的數(shù)據(jù)寫入Delta Cache中,與LSM Tree的MemTable類似。當(dāng)Delta Cache寫滿后,其中的數(shù)據(jù)刷入Delta層的Pack中,類似LSM Tree的L0層。
      • Stable層類似于LSM Tree的L1層,其中的數(shù)據(jù)以主鍵和版本號排序。
      • Delta層的Pack和Stable層需要做全量合并,得到新的Stable層數(shù)據(jù)。
    • 當(dāng)Segment中的數(shù)據(jù)量超過閾值,就會做類似B+ Tree葉子節(jié)點的分裂操作,分裂成兩個Segment。同時,如果相鄰的Segment中的數(shù)據(jù)量都比較小,也會將相鄰的Segment合并成一個Segment。

    C-Store(2005)/Vertica

    大多數(shù)DBMS都是為寫優(yōu)化,C-store是第一個為讀優(yōu)化的OLTP數(shù)據(jù)庫。C-Store 的主要貢獻(xiàn)有以下幾點:通過精心設(shè)計的 projection 同時實現(xiàn)列數(shù)據(jù)的多副本和多種索引方式;用讀寫分層的方式兼顧了(少量)寫入的性能。此外,C-Store 可能是第一個現(xiàn)代的列式存儲數(shù)據(jù)庫實現(xiàn),其的設(shè)計啟發(fā)了無數(shù)后來的商業(yè)或開源數(shù)據(jù)庫,就比如 Vertica。

    在 C-Store 內(nèi)部,邏輯表被縱向拆分成 projections每個 projection 可以包含一個或多個列,甚至可以包含來自其他邏輯表的列(構(gòu)成索引)。當(dāng)然,每個列至少會存在于一個 projections 上。

    Projection 內(nèi)是以列式存儲的:里面的每個列分別用一個數(shù)據(jù)結(jié)構(gòu)存放。為了避免列太長引起問題,也支持每個 projection 以 sort key 的值做橫向切分。

    Projection 是有冗余性的,常常 1 個列會出現(xiàn)在多個 projection 中,但是它們的順序也就是 sort key 并不相同,因此 C-Store 在查詢時可以選用最優(yōu)的一組 projections,使得查詢執(zhí)行的代價最小。

    Apache ORC

    Apache ORC 最初是為支持 Hive 上的 OLAP 查詢開發(fā)的一種文件格式,如今在 Hadoop 生態(tài)系統(tǒng)中有廣泛的應(yīng)用。ORC 支持各種格式的字段,包括常見的 int、string 等,也包括 struct、list、map 等組合字段;字段的 meta 信息就放在 ORC 文件的尾部(這被稱為自描述的)。

    ORC 里的 Stripe 就像傳統(tǒng)數(shù)據(jù)庫的頁,它是 ORC 文件批量讀寫的基本單位。這是由于分布式儲存系統(tǒng)的讀寫延遲較大,一次 IO 操作只有批量讀取一定量的數(shù)據(jù)才劃算。這和按頁讀寫磁盤的思路也有共通之處。

    Apache ORC 提供有限的 ACID 事務(wù)支持。受限于分布式文件系統(tǒng)的特點,文件不能隨機寫,那如何把修改保存下來呢?

    類似于 LSM-Tree 中的 MVCC 那樣,writer 并不是直接修改數(shù)據(jù),而是為每個事務(wù)生成一個 delta 文件,文件中的修改被疊加在原始數(shù)據(jù)之上。當(dāng) delta 文件越來越多時,通過 minor compaction 把連續(xù)多個 delta 文件合成一個;當(dāng) delta 變得很大時,再執(zhí)行 major compaction 將 delta 和原始數(shù)據(jù)合并。這種保持基線數(shù)據(jù)不變、分層疊加 delta 數(shù)據(jù)的優(yōu)化方式在列式存儲系統(tǒng)中十分常見,是一種通用的解決思路

    Dremel (2010) / Apache Parquet

    Dremel 是 Google 研發(fā)的用于大規(guī)模只讀數(shù)據(jù)的查詢系統(tǒng),用于進(jìn)行快速的 ad-hoc 查詢,彌補 MapReduce 交互式查詢能力的不足。為了避免對數(shù)據(jù)的二次拷貝,Dremel 的數(shù)據(jù)就放在原處,通常是 GFS 這樣的分布式文件系統(tǒng),為此需要設(shè)計一種通用的文件格式。

    Dremel 的系統(tǒng)設(shè)計和大多 OLAP 的列式數(shù)據(jù)庫并無太多創(chuàng)新點,但是其精巧的存儲格式卻變得流行起來,Apache Parquet 就是它的開源復(fù)刻版。注意 Parquet 和 ORC 一樣都是一種存儲格式,而非完整的系統(tǒng)。

    Impala

    Impala是Cloudera公司主導(dǎo)開發(fā)的新型查詢系統(tǒng),它提供SQL語義,能查詢存儲在Hadoop的HDFS和HBase中的PB級大數(shù)據(jù)。已有的Hive系統(tǒng)雖然也提供了SQL語義,但由于Hive底層執(zhí)行使用的是MapReduce引擎,仍然是一個批處理過程,難以滿足查詢的交互性。相比之下,Impala的最大特點也是最大賣點就是它的快速。Impala完全拋棄了MapReduce這個不太適合做SQL查詢的范式,而是像Dremel一樣借鑒了MPP并行數(shù)據(jù)庫的思想另起爐灶,因此可做更多的查詢優(yōu)化,從而省掉不必要的shuffle、sort等開銷。

    Impala與Hive類似不是數(shù)據(jù)庫而是數(shù)據(jù)分析工具,集群有以下幾類節(jié)點

  • Impalad,Impala的核?組件,負(fù)責(zé)讀寫數(shù)據(jù),執(zhí)行查詢?nèi)蝿?wù),并將結(jié)果返回協(xié)調(diào)者
  • Impalad服務(wù)由三個模塊組成:Query Planner、Query Coordinator和Query Executor,前兩個模塊組成前端,負(fù)責(zé)接收SQL查詢請求,解析SQL并轉(zhuǎn)換成執(zhí)?計劃,交由后端執(zhí)?。
  • statestored,負(fù)責(zé)監(jiān)控集群中Impalad的健康狀況,并將集群健康信息同步給Impalad。
  • catalogd,Impala執(zhí)?的SQL語句引發(fā)元數(shù)據(jù)發(fā)?變化時,catalog服務(wù)負(fù)責(zé)把這些元數(shù)據(jù)的變化同步給其它Impalad進(jìn)程
  • 查詢流程

  • Client提交任務(wù)
    • Client發(fā)送?個SQL查詢請求到任意?個Impalad節(jié)點,會返回?個queryId?于之后的客戶端操作。
  • 生成查詢計劃(單機計劃、分布式執(zhí)行計劃)
    • SQL提交到Impalad節(jié)點之后,Analyser依次執(zhí)?SQL的詞法分析、語法分析、語義分析等操作;從MySQL元數(shù)據(jù)庫中獲取元數(shù)據(jù),從HDFS的名稱節(jié)點中獲取數(shù)據(jù)地址,以得到存儲這個查詢相關(guān)數(shù)據(jù)的所有數(shù)據(jù)節(jié)點。
    • 單機執(zhí)行計劃:根據(jù)上?步對SQL語句的分析,由Planner先?成單機的執(zhí)?計劃,該執(zhí)?計劃是有PlanNode組成的?棵樹,這個過程中也會執(zhí)??些SQL化,例如Join順序改變、謂詞下推等。
    • 分布式并?物理計劃:將單機執(zhí)?計劃轉(zhuǎn)換成分布式并?物理執(zhí)?計劃,物理執(zhí)?計劃由?個個的Fragment組成,Fragment之間有數(shù)據(jù)依賴關(guān)系,處理過程中要在原有的執(zhí)?計劃之上加??些ExchangeNode和DataStreamSink信息等。
      • Fragment : sql?成的分布式執(zhí)?計劃的?個?任務(wù);
      • DataStreamSink:傳輸當(dāng)前的Fragment輸出數(shù)據(jù)到不同的節(jié)點;
  • 任務(wù)調(diào)度和分發(fā)
    • Coordinator將Fragment(?任務(wù))根據(jù)數(shù)據(jù)分區(qū)信息發(fā)配到不同的Impalad節(jié)點上執(zhí)?。Impalad節(jié)點接收到執(zhí)?Fragment請求交由Executor執(zhí)?。
  • Fragment之間的數(shù)據(jù)依賴
    • 每?個Fragment的執(zhí)?輸出通過DataStreamSink發(fā)送到下?個Fragment,Fragment運?過程中不斷向coordinator節(jié)點匯報當(dāng)前運?狀態(tài)。
  • 結(jié)果匯總
    • 查詢的SQL通常情況下需要有?個單獨的Fragment?于結(jié)果的匯總,它只在Coordinator節(jié)點運?,將多個節(jié)點的最終執(zhí)?結(jié)果匯總,轉(zhuǎn)換成ResultSet信息。
  • 獲取結(jié)果
    • 客戶端調(diào)?獲取ResultSet的接?,讀取查詢結(jié)果。
  • Druid

    Druid可以對多列數(shù)據(jù)構(gòu)建倒排索引(bitmap-based inverted indexes)

    Pinot

    mongoDB

    • MongoDB是如何實現(xiàn)事務(wù)的ACID?

    參考鏈接

  • 數(shù)據(jù)庫內(nèi)核雜談(九):開源優(yōu)化器 ORCA
  • Greenplum :基于 PostgreSQL 的分布式數(shù)據(jù)庫內(nèi)核揭秘 (上篇)
  • Greenplum:基于 PostgreSQL 的分布式數(shù)據(jù)庫內(nèi)核揭秘 (下篇)
  • Exploring Postgres with Bruce Momjian
  • 時序數(shù)據(jù)庫連載系列: 時序數(shù)據(jù)庫一哥InfluxDB之存儲機制解析
  • boltdb 1.3.0實現(xiàn)分析(一)
  • 處理海量數(shù)據(jù):列式存儲綜述(系統(tǒng)篇)
  • Greenplum數(shù)據(jù)庫文檔
  • postgreSQL中文社區(qū)
  • Postgres Internals Presentations
  • postgresql optimizer/README
  • Hadoop 怎么了,大數(shù)據(jù)路在何方
  • 如何理解 大數(shù)據(jù)、數(shù)據(jù)倉庫領(lǐng)域的ad-hoc這個詞?即席查詢
  • 海量實時計算(Ad-Hoc Query) 概覽
  • 前沿 | VLDB論文解讀:阿里云超大規(guī)模實時分析型數(shù)據(jù)庫AnalyticDB
  • 面向云數(shù)據(jù)庫,超低延遲文件系統(tǒng)PolarFS誕生了
  • 阿里云PolarDB及其存儲PolarFS技術(shù)實現(xiàn)分析(上)
  • 阿里云PolarDB及其存儲PolarFS技術(shù)實現(xiàn)分析(下)
  • POLARDB(DRDS)、AnalyticDB、OceanBase核心技術(shù)實現(xiàn)八股背誦
  • 重識 SQLite,簡約不簡單
  • impala 概述
  • Impala架構(gòu)和查詢原理
  • Druid概述超多文章
  • Kylin、Druid、ClickHouse核心技術(shù)對比 | 知識轉(zhuǎn)載
  • DataBase · 引擎特性 · OLAP/HTAP列式存儲引擎概述要看
  • 數(shù)據(jù)庫干貨匯
  • 總結(jié)

    以上是生活随笔為你收集整理的分布式数据库产品总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。