常用資料庫選型!你做對了嗎?

民工哥發表於2022-02-17

影響資料庫選擇的因素

  • 資料量:是否海量資料,單表資料量太大會考驗資料庫的效能
  • 資料結構:結構化 (每條記錄的結構都一樣) 還是非結構化的 (不同記錄的結構可以不一樣)
  • 是否寬表:一條記錄是 10 個域,還是成百上千個域
  • 資料屬性:是基本資料 (比如使用者資訊)、業務資料 (比如使用者行為)、輔助資料 (比如日誌)、快取資料
  • 是否要求事務性:一個事務由多個操作組成,必須全部成功或全部回滾,不允許部分成功
  • 實時性:對寫延遲,或讀延遲有沒有要求,比如有的業務允許寫延遲高但要求讀延遲低
  • 查詢量:比如有的業務要求查詢大量記錄的少數列,有的要求查詢少數記錄的所有列
  • 排序要求:比如有的業務是針對時間序列操作的
  • 可靠性要求:對資料丟失的容忍度
  • 一致性要求:是否要求讀到的一定是最新寫入的資料
  • 對增刪查改的要求:有的業務要能快速的對單條資料做增刪查改 (比如使用者資訊),有的要求批量匯入,有的不需要修改刪除單條記錄 (比如日誌、使用者行為),有的要求檢索少量資料 (比如日誌),有的要求快速讀取大量資料 (比如展示報表),有的要求大量讀取並計算資料 (比如分析使用者行為)
  • 是否需要支援多表操作

不同的業務對資料庫有不同的要求

SQL 資料庫 & NoSQL 資料庫

SQL 資料庫就是傳統的關係型資料庫
  • 行列式表儲存
  • 結構化資料
  • 需要預定義資料型別
  • 資料量和查詢量都不大,如果資料量大要做分表
  • 對資料一致性、完整性約束、事務性、可靠性要求比較高
  • 支援多表 Join 操作
  • 支援多表間的完整性,要刪除 A 表的某條資料,可能需要先刪除 B 表的某些資料
  • SQL 的增刪改查功能強
  • 較為通用,技術比較成熟
  • 大資料量效能不足
  • 高併發效能不足
  • 無法應用於非結構化資料
  • 擴充套件困難

常用的 SQL 資料庫比如 Oracle、MySQL、PostgreSQL、SQLite

NoSQL 泛指非關係型資料庫
  • 表結構較靈活,比如列儲存,鍵值對儲存,文件儲存,圖形儲存
  • 支援非結構化資料
  • 有的不需要預定義資料型別,有的甚至不需要預定義表
  • 支援大資料量
  • 多數都支援分散式
  • 擴充套件性好
  • 基本查詢能力,高併發能力比較強 (因為採用非結構化、分散式,並犧牲一致性、完整性、事務性等功能)
  • 對資料一致性要求比較低
  • 通常不支援事務性,或是有限支援
  • 通常不支援完整性,複雜業務場景支援較差
  • 通常不支援多表 Join,或是有限支援
  • 非 SQL 查詢語言,或類 SQL 查詢語言,但功能都比較弱,有的甚至不支援修改刪除資料
  • 不是很通用,技術多樣,市場變化比較大
常用的 NoSQL 資料庫比如
  • 列式:HBase、Cassandra、ClickHouse
  • 鍵值:Redis、Memcached
  • 文件:MongoDB
  • 時序:InfluxDB、Prometheus
  • 搜尋:Elasticsearch

SQL 和 NoSQL 是一個互補的關係,應用在不同的場景中。

OLTP & OLAP

OLTP (On-Line Transaction Processing)
  • 主要做實時事務處理
  • 比如處理使用者基本資訊、處理訂單合同、處理銀行轉賬業務、企業的 ERP 系統和 OA 系統,等等
  • 頻繁地,對少量資料,甚至是單條資料,做實時的增刪改查
  • 資料庫經常更新
  • 通常對規範化、實時性、穩定性、事務性、一致性、完整性等有要求
  • 操作較為固定,比如訂單業務,可能永遠就那幾個固定的操作
  • 資料庫主要模型是 3NF 或 BCNF 模型

OLAP (On-Line Analytical Processing)

  • 資料倉儲,主要做歷史資料分析,為商業決策提供支援
  • 比如對大量的使用者行為做分析,對裝置的狀態、使用率、效能做分析
  • 頻率較低地,對大量資料,做讀取、聚合、計算、分析,實時性要求不高,對吞吐能力要求較高
  • 通常列的數量比較多,但每次分析的時候只取少部分列的資料
  • 通常是批量匯入資料
  • 通常資料匯入後不會修改,主要是讀取操作,寫少讀多
  • 通常對規範化、事務性、一致性、完整性等要求較低,甚至一個查詢操作失敗了也不會有什麼影響
  • 操作較為靈活,比如一個海量使用者行為資料表,可以想出許多不同的方法,從不同的角度對使用者做分析
  • 資料庫主要是星型、雪花模型
  • 不使用高效能的 OLAP 之前,更傳統的做法是通過離線業務構建 T+1 的離線資料,比較滯後

OLTP 通常用傳統的關聯式資料庫,如果資料量大要分表,對事務性、一致性、完整性等要求不高的話也可以用 NoSQL

OLAP 通常用 NoSQL,資料量不大的話也可以用傳統的關聯式資料庫

關係型資料庫 Oracle、SQL Server、MySQL、PostgreSQL、SQLite

Oracle:甲骨文開發的商業資料庫,不開源,支援所有主流平臺,效能好,功能強,穩定性好,安全性好,支援大資料量,比較複雜,收費昂貴。

SQL Server:微軟開發的商業資料庫,只能在 Windows 執行。

MySQL:甲骨文擁有的開源資料庫,支援多種作業系統,體積小,功能弱些,簡單的操作效能好,複雜的操作效能差些。** PostgreSQL**:使用 BSD 協議的完全開源免費的專案,支援多種作業系統,功能更強大,可以和多種開源工具配合。

SQLite:開源、輕型、無伺服器、零配置,一個資料庫就只是一個檔案,在應用程式內執行操作,佔用資源小,可用於嵌入式或小型應用。

應用場景

Oracle 多用於銀行等高要求的領域,要求不高的比如網際網路行業多用 MySQL 和 PostgreSQL,而 SQLite 用於嵌入式或作為應用程式內的資料庫使用,SQL Server 用於 Window 伺服器。

HBase (寬表、列式儲存、鍵值對儲存、NoSQL、OLTP)

  • 基於 Hadoop 的 HDFS 分散式檔案系統
  • 分散式資料庫,需要 ZooKeeper 作為節點間的協調器
  • 支援寬表,支援非結構化資料,不需要預定義列和資料型別
  • 列式儲存,每個 HFile 檔案只儲存一個列族的資料,一個列族可以有多個 HFile,而 HFile 內部按 Key-Value 格式儲存,其中 Key 是 rowkey, column family, column, timestamp 的組合並且按 rowkey 在 HFile 中按序儲存,而 value 就是 Column Cell 的值
  • 支援海量資料 (千億級資料表)
  • 資料先寫入記憶體,達到閥值再寫入磁碟,效能好,佔用記憶體大
  • 不支援 SQL,不支援 Join,有自己專用的語句,支援增刪改查
  • 自動分割槽、負載均衡、可線性擴充套件
  • 自動故障遷移
  • 強一致性 (每個分割槽 Region 只由一個 Region Server 負責,容易實現強一致性)
  • CP 模型 (不保證可用性,每個 Region 只由一個 Region Server 負責,Server 掛了得做遷移導致暫時不可用)
  • 不支援事務、二級索引
應用場景

元件比較多,比較重,適用於已有的 Hadoop 平臺,適用於海量寬表資料、需要增刪改查、OLTP 的場景

Phoenix (基於 HBase 的資料庫引擎、關係型、OLTP)

  • 嵌入到 HBase 的 Region Server 的資料庫引擎
  • 支援 SQL
  • 支援 Join
  • 支援事務 (需要在定義表的時候配置)
  • 支援二級索引
  • 支援撒鹽
  • 支援 JDBC
應用場景

用於強化 HBase,主要作為 OLTP,查詢效能要求不高的話也可作為 OLAP,多用於 HDP (HDP 有整合 Phoenix)

Cassandra (寬表、鍵值對儲存、NoSQL、OLTP)

  • 無單點故障:Cassandra 節點按環形排列,沒有中心節點,每個節點獨立互聯地扮演相同角色,每個節點都可以接受讀寫請求,資料可以有多個副本儲存在多個節點,節點之間通過 Gossip (P2P) 協議交換狀態資訊,叢集中有若干節點配為種子節點,用於新加入的節點獲取叢集拓撲結構並啟動 Gossip 協議
  • 提供類 SQL 語言 CQL
  • 適合結構化、非結構化資料
  • Table 需要定義 Partition Key、Clustering Key、以及普通列,其中 Partition Key 用於分割槽和排序,即按照 Partition Key 的 Hash Token 決定了資料被分配到哪個節點,並且在節點內也是按該 Hash Token 按序儲存的,有相同 Partition Key 的資料會存在一起,並且按照 Clustering Key 排序儲存,有點類似於 HBase 的 RowKey、ColumnFamily、Column,不過 HBase 是相同 CF 存一起,內部再按 RowKey 排序儲存,再取 Column 值 (Column 值不排序),而 Cassandra 是先按 Partition Key 的 Token 排序儲存,內部再按 Clustering 排序儲存,再取普通 Column 的值 (Column 值不排序)
  • 高度可擴充套件,允許新增硬體、節點以提高資料容量,同時保持快速的響應時間
  • 通過 Consistency 命令可以配置一致性級別,主要是通知客戶端操作前,必須確保的 replica 的成功數量
  • Cassandra 採用的是最終一致性,是 CAP 理論裡的 AP
  • Cassandra 不支援 Join 和子查詢
應用場景

主要用於 OLTP,要求不高的話也可以作為 OLAP 使用,和 HBase 比需要的元件比較少,維護比較容易

Redis (基於記憶體的 Key-Value 的 NoSQL 資料庫,OLTP)

  • 由 C 語言編寫
  • 支援多種資料型別如 strings,hashes,lists,sets,sorted sets,bitmaps,hyperloglogs,geospatial 等
  • 操作原子性,保證了兩個客戶端同時訪問伺服器將獲得更新後的值
  • 資料儲存在記憶體中
  • 可以配置持久化,週期性的把更新資料寫入磁碟,或週期性地把修改操作寫入追加記錄檔案,也可以關閉持久化功能,將 Redis 作為一個高效的網路快取資料功能使用
  • 支援主從同步,資料可以從主伺服器向任意數量的從伺服器同步,從伺服器可以是關聯其他從伺服器的主伺服器,這使得 Redis 可執行單層樹複製,存檔可以有意無意的對資料進行寫操作,由於完全實現了釋出/訂閱機制,使得從資料庫在任何地方同步樹時,可訂閱一個頻道並接收主伺服器完整的訊息釋出記錄,同步對讀取操作的可擴充套件性和資料冗餘很有幫助
  • 支援訊息的釋出/訂閱(Pub/Sub)模式
  • 單執行緒模式,即網路 IO、資料讀寫,都由一個執行緒完成,正因為如此保證了原子性、穩定性、程式碼容易維護,之所以單執行緒不影響效能,是因為資料都在記憶體,操作本來就高效,當然這裡的單執行緒指網路 IO、資料讀寫這個主功能,實際上還有其他執行緒,比如週期性寫入硬碟的執行緒
  • 高版本在網路 IO 這塊使用了多執行緒 (因為在高併發操作時,網路 IO 成為了瓶頸),但讀寫操作還是單執行緒 (操作記憶體資料效能還是非常高的,能應付高併發場景)
應用場景

通常作為高效能記憶體資料庫、快取、訊息中介軟體等使用

memcached (基於記憶體的 Key-Value 的 NoSQL 資料庫,OLTP)

  • 開源、高效能、分散式的基於記憶體的 Key-Value 資料儲存,作用類似於 Redis
  • 儲存 String/RawData,不定義資料結構 (Redis 有 hash、list、set 等多種結構)
  • 資料通常由 key,flags,expire time,bytes,value 組成
  • 服務端基本上只能簡單的讀寫資料,服務端能支援的操作比較少
  • 包含 Server 元件和 Client 元件,可以有多個 server 但 server 之間是獨立的,沒有同步廣播等機制,需要選擇哪個 server 由 client 的 API 決定的
  • 資料只在記憶體,不會落到硬碟
  • 沒有安全機制
  • 協議簡單效能高效
應用場景

memcached 比較簡單,作為純粹的 Key-Value 快取效能會比 Redis 好些,但功能沒有 Redis 強大

MongoDB (文件資料庫,NoSQL,OLTP)

之所以說是文件資料庫,是因為它的資料是以 JSON 文件的形式儲存

MongoDB 的概念和很多資料庫不一樣,它的 collection 相當於表,document 相當於行,field 相當於列,比如:

db.user.insert(
    {
        "name": "Lin",
        "age": 30
        "address": {
            "street": "Zhongshan Road",
            "city": "Guangzhou",
            "zip": 510000
        },
        "hobbies": ["surfing", "coding"]
    }
)

這是一條插入語句,這裡的 db 是指當前資料庫,user 就是 collection 相當於表,insert 語句裡面的 JSON 就是 document 相當於其他資料庫的行,name,age,street 這些就是 field 相當於列

相同的文件可以插入多次而不會被覆蓋,實際上 mongodb 會自動建立 \_id 欄位作為 primary key,並分配不同的數值,所以不會重複,也可以 insert 的時候指定 \_id,但如果 \_id 已經存在則會報錯

  • 可以看到,mongodb 是非結構化資料,不需要預定義 collection,也不需要預定義資料結構
  • 提供豐富的查詢表示式
  • 支援二級索引,自動負載平衡,讀效率比寫高
  • 支援分散式、支援故障恢復、資料冗餘、分片、水平擴充套件
  • 可以配置儲存引擎,WiredTiger Storage Engine (預設) 會做記憶體到檔案的對映以提高效能,但記憶體消耗大,In-Memory Storage Engine (企業版支援) 只存在記憶體,不會落盤
  • 高版本支援 Join,支援事務
  • 支援安全認證功能
  • 提供擴充套件,比如實現視覺化的工具,實現 BI 整合的工具
應用場景

mongodb 更適用於高度非結構化,或者源資料就是 JSON,每條資料比較大,以 OLTP 為主的場景,不適合於事務要求比較高,或比較複雜的大資料量的查詢的場景,另外由於 mongodb 的語法和其他資料庫差異比較大,需要一定的學習成本

Hive (基於 HDFS 的資料庫引擎、關係型、OLAP)

  • Hive 是基於 Hadoop 的一個資料倉儲工具
  • 資料儲存在 HDFS,建立表的時候要通過 STORED AS 命令指定儲存格式比如 TEXTFILE、ORCFILE、PARQUET,也可以通過 STORED BY 命令指定為 HBase,可以建立新表也可以建立已有 HBase 表的對映
  • 查詢通過 MapReduce、Spark 等作業完成
  • 提供了類 SQL 查詢語言 HQL (HiveQL),支援使用者定義函式 (UDF)
  • 高版本支援事務 (需要建立表時指定)
  • 支援海量資料
  • 結構化資料
  • 支援增刪改查
應用場景
  • 不適合於 OLTP,主要作為 OLAP 用於大資料批量查詢使用,需要有 Hadoop 平臺

Impala (基於 HDFS、HBase、Kudu 儲存,平行計算,關係型,OLAP)

  • Cloudera 開發的基於記憶體的分散式平行計算的資料庫查詢引擎
  • 主要由 C++ 實現,和 Hadoop 的互動使用 JNI
  • Impala 使用和 Hive 一樣的 metadata、SQL、ODBC driver、UI,這樣在提高了 HDFS 的 SQL 查詢效能的同時,又提供了相似的使用者使用體驗
  • 和 Hive 一樣可以通過 STORED AS 指定 HDFS 的儲存格式比如 TEXTFILE、ORCFILE、PARQUET
  • 通過 Hive 操作的表,需要手動同步到 Impala
  • Impala 不僅 SQL 和 Hive 一樣,實際上後設資料也存在 Hive 中
  • 表資料除了 HDFS,也可以儲存到 HBase,但需要在 HBase 建表,然後在 Hive 通過 STORED BY 建立對映表,由於 Impala 和 Hive 使用一樣的 metadata,在 Hive 建好表後,只要在 Impala 執行重新整理命令 INVALIDATE METADATA,就可以看到對應的 HBase 表
  • 支援 Join、Aggregate 等功能
  • 支援 JDBC、ODBC
  • 和 Hive 不同,Impala 不依賴於 MapReduce,而是在每個 HDFS DataNode 上執行自己的引擎實現並行處理
  • Impala 的並行處理引擎主要由 state store、catalog service、多個 impala daemon 組成
  • 每個 impala daemon 都可以接收 client 的請求,impala daemon 由 query planner、query coordinator、query executor 組成,planner 接收 client 的 SQL 查詢,然後分解為多個子查詢,由 coordinator 將子查詢分發到各個 daemon 的 executor 執行,daemon 獲取 HDFS、HBase 資料、計算、然後返回給 coordinator,然後由 coordinator 聚合後將最終結果返回給 client
  • Impala 是無中心結構,每個 daemon 都可以接受連線查詢,可以通過 HA Proxy 實現多個 daemon 的負載均衡
  • state store 用於收集監控各個 daemon 的狀態
  • catalog service 將 SQL 做出的後設資料變化通知給叢集中所有的 impala daemon
  • Impala 的計算都在記憶體進行,對記憶體要求比較高
  • Impala 在 2.8 以後才支援 update 操作,但是隻限於 Kudu 儲存,需要安裝 Kudu,並通過 STORED AS 指定 Kudu 作為資料庫的儲存,Kudu 是 Cloudera 開發的列式儲存管理器,目的是做 OLAP,並且平衡 HDFS 和 HBase 的效能,Kude 的隨機讀寫效能比 HDFS(比如 Parquet)好,但是比 HBase 差,而大資料量查詢效能比 HDFS(比如 Parquet)差,但比 HBase 好,Kude 和 Impala 高度整合,也可以和 MapReduce/Spark 整合,用 Kudu 替換 HDFS/HBase 這樣 Impala 就可以做 update,兼顧 OLAP 和改資料的需求,適合於以 OLAP 為主又有一定的 Update 需求的場景,Kudu 可以配置一致性,採用結構化表資料模型,需要定義主鍵,不使用 HDFS 而是有自己的元件儲存和管理資料, 採用 c++ 沒有 full gc 風險
應用場景
  • Impala 不適合於 OLTP,主要作為 OLAP 用於大資料批量查詢使用
  • 需要有 Hadoop 平臺和 Hive
  • 效能比 Hive 好很多
  • 作為 OLAP 的效能比 Phoenix 之類的好
  • 主要是 CDH 在推,CDH 有整合 Impala

Presto (基於多種資料來源,平行計算,關係型,OLAP)

  • Facebook 推出的基於記憶體的分散式平行計算的資料庫查詢引擎
  • 由 coordinator server、discovery server (通常整合在 coordinator 裡,也可以獨立)、多個 worker server 組成
  • coordinator 負責與 client 互動,負責管理 worker,負責解析 statement、規劃 query、建立一系列的 stage、再轉換成一系列的 task 分發到不同 worker 併發執行
  • worker 負責執行 task 和處理資料,會通過 connector 獲取資料,和其他 worker 互動中間資料,最終結果會由 coordinator 返回給 client
  • connector 是介面卡,使得 Presto 可以訪問不同的資料庫
  • 內建的 connector 主要是 Hive,此外有很多三方開發的 connector 比如 cassandra、es、kafka、kudu、redis、mysql、postgresql 等等
  • 需要在配置檔案配置 catalog,這裡 catalog 維護 schema 並通過 connector 指向一個資料來源,定位 presto 表都是從 catalog 開始的,比如 hive.test\_data.test 指的是 hive catalog 下的 test\_data schema 下面的 test 表,而 schema 的概念則依賴於具體的 connector,比如對於 mysql 而言,presto 的 schema 就是 mysql 的 schema,而對於 cassandra 而言,presto 的 schema 就是 cassandra 的 keyspace,可以建立多個 catalog 關聯同一個 connector 比如環境裡有多個 kafka 叢集那可以有 kafka1 和 kafka2 兩個 catalog
  • statement 可以認為就是 presto 收到的 sql 語句,然後會解析成 query plan,然後 query 又被分為多個 stages,這些 stages 組成一個樹的結構,每個 stage 會聚合計算它下面的其他 stages 的結果,每個 stage 又分為一個或多個 tasks,這些 task 會分發到不同的 worker 並行執行,每個 task 處理不同的資料分片,每個 task 又有一個或多個 driver 併發處理資料
  • Presto 支援 JDBC 介面,JDBC 的 URL 格式為 jdbc:presto://host:port/catalog/schema 或 jdbc:presto://host:port/catalog 或 jdbc:presto://host:port
  • 支援 Join 查詢,並且支援多資料來源的 join 查詢 (多張大表的 join 可能會影響效能),跨資料來源查詢的時候需要指定完整的表名即 [catalog].[schema].[table],並且使用 presto://host:port 連線 JDBC,不指定 catalog 和 schema
  • 有限支援子查詢
  • 不支援 update 操作
  • 支援安全機制
  • 支援標準的 ANSI SQL
  • 擴充套件性好
  • 可以和 Tableau 整合
  • 支援 Spark
應用場景
  • 適合有多種資料來源的大資料量的 OLAP 查詢
  • 效能和 Impala 可能差不多,但支援多種資料來源,不依賴 Hadoop

Greenplum (基於多個 PostgreSQL,平行計算,關係型,OLAP)

  • 基於多個 PostgreSQL 的分散式平行計算的資料庫查詢引擎
  • 內部的 PostgreSQL 有做改動以適應並行分散式計算
  • 主要由一個 master、多個 segments、一個 interconnect 組成
  • master 維護後設資料,接收並認證 client 的連結,接收 SQL 請求,解析 SQL,生成 query plan,並將任務分發到 segments,協調聚合 segments 的返回結果,並將最終結果返回給 client,可以設定 master 為主從配置
  • 每個 segment 有個獨立的 PostgreSQL 資料庫,每個 segment 負責儲存部分資料,並執行相應的查詢計算,segment 也可以配置備份機制
  • Interconnect 是 Greenplum 的網路層,負責 master 和 segment 的連結,以及各個 segment 之間的連結
  • 連結和 SQL 語法都和 PostgreSQL 相容,支援 JDBC、ODBC
  • 建立表時可以指定是用列儲存、行儲存、外部表 (資料在其他系統比如 HDFS 而 GP 只儲存後設資料)
  • 操作外部資料,需要安裝 PXF (Platform Extension Framework),有了 PXF 可以支援 Hive、HBase、Parquet、S3、MySQL、ORACLE 等等
  • 支援安全、許可權配置
  • 支援分散式事務,支援 ACID,保證資料的強一致性,不是使用鎖,而是使用 MVCC (Multi-Version Concurrency Control) 來保證資料一致性
  • shared-nothing 架構
應用場景

和 Impala、Presto 類似都是並行記憶體計算,但 Greenplum 效能可能稍差一點點,並且 Greenplum 還分開源版和商業版,有的功能只有商業版才支援

Kylin (基於 Hive、HBase,平行計算,關係型,多維度、預計算 OLAP)

傳統 OLAP 根據資料儲存方式的不同分為 ROLAP(Relational OLAP)以及 MOLAP(Multi-Dimension OLAP),ROLAP 以關係模型的方式儲存資料,優點在於體積小,查詢方式靈活,缺點是每次查詢都需要對資料進行聚合計算,而 Kylin 屬於 MOLAP

Kylin 將資料按維度的不同組合,提前計算好結果,形成 Cube (立方體) 結構,這樣查詢速度快,缺點是資料量不容易控制,N 個維度可以有 2**N 種組合,可能會出現維度爆炸的問題,而且資料有改動的話需要重新計算

比如有 Phone 和 Country 兩張維度表,以及 Sale 事實表 (明細表),取手機品牌、國家、日期作為三個維度,有 (null)、(品牌)、(國家)、(日期)、(品牌、國家)、(品牌、日期)、(國家、日期)、(品牌、國家、日期) 共 8 種組合,可以提前計算好這 8 種 group by 組合的 sale 的各種彙總資訊 (sum、count 等),一個維度組合的一個彙總資訊稱為一個 cuboid,所有的 cuboid 合起來就被稱為一個 Cube

Kylin 的資料來源可以是 Hive 或 Kafka (Json 格式訊息,key 就是列名)

Kylin 的預計算結果存到 HBase,RowKey 就是各種維度的組合,相應的明細彙總存在 Column 中,這樣 SQL 就變成對 RowKey 的掃描,並進一步的對 Column 計算 (如果需要的話),這樣查詢效能自然就提升了,可以支援亞秒級查詢

Kylin 支援 ODBC,JDBC,RESTful API 等介面

Kylin 可以和 Tableau、PowerBI 等 BI 工具整合

使用步驟如下
  • 建立 Project
  • 同步 Hive 表或 Kafka 表
  • 建立 Data Model
  • 建立並命名 Model
  • 選擇 Fact Table (事實表) 和 Lookup Table (查詢表,主要是維度資訊),以及 Join 欄位
  • 從 Fact Table 和 Lookup Table 中挑選維度列 (可以被 Cube 做 group by)
  • 從 Fact Table 選擇指標列 (可以被 Cube 做 aggregation)
  • 從 Fact Table 選擇用於日期分割槽的列,不需要就留空
  • 新增 Filter (可以被 Cube 用來做 Where 操作)
  • 建立 Cube
  • 建立並命名 Cube,並選擇要關聯的 Data Model
  • 新增維度列 (必須從 Data Model 配置的維度列中選擇)
  • 新增指標列 (必須從 Data Model 配置的指標列中選擇)
  • 共有 8 種 aggregation 操作可以配置給指標列:SUM, MAX, MIN, COUNT, COUNT\_DISTINCT, TOP\_N, EXTENDED\_COLUMN and PERCENTILE (如果要查 avg 實際上就是用 sum 除以 count 得出,所以這裡不需要配置 avg 等可以通過預計算結果進一步計算出的操作)

build Cube,實際是通過 MapReduce/Spark 計算,任務完成後結果會寫入 HBase

build 成功後即可直接用 SQL 查詢了,實際是根據維度查 RowKey,然後把 Column 存的聚合結果取出,如果必要的話再做進一步計算

如果資料來源有改動,需要重新 build Cube

可以看到 Kylin 是一個純粹的 OLAP 工具,通過預計算提升查詢效能,但無法及時反應出資料來源的改變,預計算可能很耗時並且可能會佔用大量空間,且需要和 Hadoop 整合

基於預計算的 OLAP 資料查詢引擎還有 Druid

ClickHouse (列儲存,向量化計算,平行計算,OLAP)

俄羅斯企業 Yandex 開發的 OLAP 資料庫

  • 列儲存對於 OLAP 的好處
  • 由於 OLAP 經常是在大量資料列中檢索少量列,如果採用行儲存,意味著要逐行掃描,並且每行都很大,而採用列儲存只需要掃描要檢索的列,能減少 IO
  • 假設有的記錄並沒有儲存要檢索的列,行儲存依然要掃描該記錄才知道,而對於列儲存則不存在這樣的問題,因為沒儲存,自熱而然就不會掃描到
  • 因為同一列的資料型別、大小比較一致,列儲存更容易壓縮,效率更高,進一步減少 IO
  • IO 的減少還意味著有更多資料可以被快取到記憶體
  • 向量化計算
  • SIMD (Single Instruction,Multiple Data,單指令流多資料流),現在的 CPU 支援這樣的功能,通過一條指令即可利用多核對一組資料 (也就是向量) 進行 CPU 層面的併發計算,適用於純基礎計算的場景,如果有判斷、跳轉、分支的場景則不合適
  • ClickHouse 有一個向量計算引擎,儘可能地使用 SMID 指令,批量並行地處理資料,大大提升了處理能力
  • 主要由 C++ 實現
  • 無中心化結構,由一個叢集的 server 組成,並且每個 server 都可以接受客戶端的連結查詢,server 收到請求後會和其他 server 協調做平行計算,每個 server 都是多執行緒的,server 之間通過 ZooKeeper 協調同步
  • 支援分片(shard),資料可以跨節點儲存在不同分片中,一個分片就是一個節點,或者多個節點組成一個有副本備份的分片,由配置檔案配置
  • 支援分割槽,通過 Partition By 命令建立表
  • 分片和分割槽有時候不好區分,分片更多指的是表的資料分佈在不同節點,而且一個節點可以儲存多個資料庫、多個表的資料,而分割槽更多指的是按某列資料將一個大表分成多個小表,比如按日期列分割槽,每天一個分割槽表,既可以查分割槽表,也可以查大表
  • 支援副本備份、支援資料完整性
  • 表引擎(Table Engine)
  • 在某個 server 建立的表只是該 server 的本地表,不是分散式的,如果要建立分散式表,需要在每個 server 建立相同名字的表,再在其中一臺 server 上建立分散式表(會自動在所有 server 上都建立),這個分散式表是個邏輯表,不真正儲存資料,而是對映到各個 server 的本地表,會自動做平行計算
  • ENGINE = Distributed(cluster\_name, database, table, [sharding\_key])
  • cluster\_name 是在配置檔案裡配置的
  • ENGINE = Memory 資料存在記憶體
  • ENGINE = ODBC(connection\_settings, external\_database, external\_table)
  • ENGINE = JDBC(dbms\_uri, external\_database, external\_table)
  • ENGINE = MySQL('host:port', 'database', 'table', 'user', 'password')
  • ENGINE = PostgreSQL('host:port', 'database', 'table', 'user', 'password')
  • ENGINE = MongoDB(host:port, database, collection, user, password)
  • ENGINE = HDFS(URI, format)
  • ENGINE = Kafka() SETTINGS kafka\_broker\_list = 'host:port', kafka\_topic\_list = 'topic1'
  • ENGINE = Log;
  • ENGINE = TinyLog;
  • ENGINE = MergeTree()
  • ENGINE = AggregatingMergeTree()
  • 建立表的時候要通過 Engine 命令指定要用的表引擎,決定如何儲存資料
  • 最常用的是 MergeTree 系列引擎,比如
  • 比較輕量級的 Log 系列引擎
  • 允許從其他資料來源查詢,比如
  • 特殊型別,比如
  • 分散式
  • 通常使用 MergeTree 儲存,資料可以快速地按序 append 到一顆 MergeTree 的後面,後臺會做合併和壓縮操作,這樣提升了資料插入的效能
  • 主索引,資料按 Primary Key 排序
  • 也可以在建立表時通過 Order By 指定排序的欄位
  • 支援二級索引,也叫跳數索引 data skipping index,比如 minmax 索引,會統計每一段資料內某列資料(或是某個表示式)的最大值和最小值,檢索的時候可以依據 minmax 決定是否跳過這段資料(感覺比較怪,效能應該比重建一張索引表的做法要差吧)
  • 支援 TTL,包括列級別、行級別、分割槽級別的 TTL
  • 支援 HTTP、TCP 介面
  • 支援 JDBC、ODBC
  • 有三方工具支援將其他資料庫如 PG 的資料匯入 ClickHouse
  • 有三方工具支援和一些視覺化工具如 Grafana、DBeaver、Tabix 整合
  • 有三方工具支援 Kafka、Flink、Spark 等
  • 支援 SQL,支援 group by、order by、join、部分子查詢等功能
  • 支援 array、json、tuple、set 等複雜資料型別
  • 支援近似計算,比如計算平均值,可以取部分資料計算,這樣提升了效能,但降低了準度
  • 自適應 Join 演算法,比如優先使用 Hash-Join,如果有多張大表則自動改用 Merge-Join
  • 安全機制、基於 Role 的許可權控制
  • 支援錯誤恢復、擴充套件性好
不足的地方
  • 對高併發的支援不足
  • 沒有成熟的事務功能
  • 修改、刪除資料的效能比較差,並且僅提供有限支援
  • Primary Key 是採用稀疏索引,即索引只能指向一段資料,具體的資料還得一條條查,所以如果是查少量資料,或者查詢單條資料,效能會比較差

不依賴 Hadoop、列儲存、向量化、並行、多執行緒、多儲存引擎

單表查詢效能極好,比 Impala、Presto 之類的要好很多

多表查詢效能差些,比 Impala、Presto 之類的要差

Elasticsearch (倒索引、分詞、搜尋引擎)

  • Elastic Stack 是一組元件包括 Elasticsearch、Logstash、Filebeat、Kibana 等
  • Elasticsearch 是基於 Apache Lucene 開發的搜尋引擎,主要由 Java 開發
  • Elasticsearch 叢集主要由 master、data、ingest、coordinating 節點組成
  • 每個節點可以同時配置多種角色,比如即是 master 又是 data,但在大型叢集中,通常每個節點只負擔一種功能
  • coordinating 是每個節點都會有的功能,不需要配置,即無論 master 還是 data 都會有 coordinating 功能,用於接收 client 的讀寫請求,然後將請求轉發給相應節點處理,然後將處理結果合併後返回給 client,在大型叢集中為了不對 master/data 等節點造成太大壓力,可以配置多個專門的 coordinating,通過將 role 配置為空或是將 master、data、ingest 設定為 false (取決於不同版本) 即可,這樣這些 coordinating 節點就只負責接收響應 client 請求不做其他工作,就像是一個反向代理的負載均衡一樣,能提高併發效能
  • master 負責管理整個叢集,負責對 index 的建立刪除等管理操作,決定資料要分片到哪個節點,管理其他節點的狀態等等,可以配置多個 master 做 HA,需要單數個,至少要 3 個,系統實際上自動選舉其中一個做 master,如果該 master 掛了,會從其他配置為 master 的節點中重新選舉一個,master 的配置可以低一些
  • data 負責儲存、計算、處理資料,對資源要求比較高,data 還可以進一步配置,指定節點用於儲存 hot data、warm data、cold data 等等
  • Ingest 是可選節點,專門用於針對某些資料和操作,做流水線預處理
  • Elasticsearch 的資料儲存主要由 index,type,document 組成
  • index 就類似於 SQL 中的一個資料庫,可以直接建立 index,也可以通過 index template 作為模板建立 index,模板通常用於具有相同配置的多個 index,比如每天都建立一個 index 用於儲存當天的日誌
  • type 就類似於 SQL 中的表 (這種說法不完全對,官方有澄清過,因為不同的 type 並不是完全獨立無關的),早期版本中,一個 index 下可以有多個 type,從 6.0 開始只能有一個 type,從 7.0 開不建議使用 type 這個概念,據說從 8.0 開始將完全不支援 type
  • document 就是像是 SQL 中的一行記錄,document 使用的是非結構化的資料,由 JSON 格式表示,JSON 的每個 field 就相當於一個列
  • 每個 document 會一個唯一的 \_id,如果不指定則由系統自動生成
  • 每個 document 有嚴格遞增的序號 \_seq\_no 代表文件寫入/更新的先後順序
  • 每個 document 有 \_version 欄位,每次更改這個欄位就加 1
  • 可以先建立 index,也可以不提前建立 index,寫入資料時自動建立
  • 不需要提前設定 document 的 field,寫入資料時自動建立,每個 document 的 field 可以不一樣,也可以提前設定 field,使得每個 document 的 field 必須一樣
  • Elasticsearch 會自動對所有 field 建立索引,並且會自動做分詞處理,即把一個句子比如 "hello world" 自動分成 "hello" 和 "world" 兩個詞做索引,沒有分詞的 "hello world" 是 keyword,大小限制是 256,經過分詞的比如 "hello" 是一個 text
  • Elasticsearch 採用倒索引 (inverted index),主要由三部分組成:Term Index (單詞索引)、Term Dictionary (單詞字典)、Posting List (索引項列表)
  • Term Index 存在記憶體中,不儲存所有單詞,而是儲存單詞的字首,比如儲存 he、wor、ad、sar 等等,指出以這些字首作為開頭的單詞在 Term Dictionary 中的起始位置,這樣 Term Index 的大小就比較小,可以存在記憶體中,並且可以幫助快速定位要讀取的內容在 Term Dictionary 中的位置,可以大大減少磁碟 IO 的次數
  • Term Dictionary 存在磁碟中,通常單詞量會非常大,記錄著 index 的每個單詞到 Posting List 的關聯關係,通過 B+ 樹或 Hash 表方式以滿足高效能的插入與查詢
  • Posting List 記錄著:出現該單詞的所有 document 的 id,該單詞在該 document 中出現的次數,該單詞在該 document 中的位置
  • 搜尋引擎通過這樣的倒排序索引找到對應的 document,再做進一步處理
  • 由於會對所有 field 做索引,資料量會非常大
  • 資料先寫入記憶體,然後定期將資料落盤形成一個個 segment,當 segment 多了之後會進行 merge 組成更大的 segment
  • 為了防止記憶體中還沒落盤的資料丟失,會寫入 translog,類似於 HBase 的 WAL,其實這也需要磁碟 IO,會影響效能,但比落盤資料要簡單
  • segment 由多個檔案組成,記錄著後設資料、field 資訊、正排序的索引資訊 (即從 document id 找到相應的資料)、field 資料 (相當於按列儲存)、倒排序索引資料、等等
  • 支援 REST API 介面操作,通過在 Body 的 JSON 格式資料提高豐富的語法,可以執行很多操作
  • 支援 Event Query Language (EQL):for event-based time series data, such as logs, metrics, and traces, 通過 REST API 的 Body 指定
  • 支援 JDBC、ODBC,這裡 table 指定的是 index,而 column 指定的是 field,這裡的 SQL 不支援 JOIN
  • 不支援事務
  • 跨表查詢不完全支援,而且要定義父子文件,要定義 join 型別的 field,比較複雜
  • 讀寫有一定延時性,即寫入的資料無法立刻索引到,至少要等 1 秒鐘
  • 和傳統的資料庫有一定差異,需要一定的學習成本
部分 REST API 操作的例子
curl localhost:9200                          # 檢視叢集基本資訊
curl localhost:9200/_cluster/health?pretty   # 檢視叢集健康 (pretty 是 JSON 格式化輸出)
curl localhost:9200/_cluster/state?pretty    # 檢視叢集狀態
curl localhost:9200/_cluster/stats?pretty    # 檢視統計資訊
curl localhost:9200/_nodes?pretty            # 檢視節點資訊

curl localhost:9200/_cat                     # 列出可以檢視的各種資訊 (cat 命令列出的資訊比較簡化)
curl localhost:9200/_cat/health
curl localhost:9200/_cat/nodes
curl localhost:9200/_cat/indices

curl -X PUT 'http://localhost:9200/my_index/my_doc/123'  -H 'Content-Type: application/json' -d '{
  "name": "Lin", "title": "senior designer", "age": 30
}'      ## 指定 document id 為 123,會自動建立 my_index,my_doc 以及各個 fields

curl -X POST 'http://localhost:9200/my_index/my_doc'  -H 'Content-Type: application/json' -d '{
  "name": "Wang", "title": "senior designer", "age": 35
}'      ## 由系統自動建立 document id

curl -X POST 'http://localhost:9200/my_index/my_doc_2'  -H 'Content-Type: application/json' -d '{
  "name": "n_1", "type": "t_1", "value": 1
}'      ## 報錯,不允許 index 下有兩個 type

curl -X POST 'http://localhost:9200/my_index/_doc'  -H 'Content-Type: application/json' -d '{
  "name": "n_1", "type": "t_1", "value": 1
}'      ## 允許,_doc 就是 my_doc(可以一開始就只用 _doc 而不需要 type 名)

curl -X POST 'http://localhost:9200/my_index/_doc'  -H 'Content-Type: application/json' -d '{
  "name": "Li",
  "address": {"city": "guangzhou", "district": "tianhe"}
}'      ## 允許新的 fields,允許複雜型別,貌似不支援列表 "address": ["xxx"]

curl localhost:9200/my_index/my_doc/123?pretty        ## 檢視 id 為 123 的記錄
curl localhost:9200/my_index/my_doc/_search?pretty    ## 檢視所有記錄
curl localhost:9200/my_index/_search?pretty

curl localhost:9200/_all?pretty                   ## 列出所有 index 的設定和 mapping (就是 field 的資訊)
curl localhost:9200/my_index?pretty               ## 檢視 my_index 的設定和 mapping
curl localhost:9200/my_index/_mapping?pretty      ## 檢視 my_index 的 mapping

curl -X GET -H "Content-Type: application/json"  localhost:9200/my_index/_search?pretty -d '{
  "query": {
     "term":{
        "name":"lin"
     }
   }
}'        ## 簡單的查詢,還有更多的語法和功能

curl -X GET -H "Content-Type: application/json"  localhost:9200/my_index/_search?pretty -d '{
  "query": {
     "term":{
        "title.keyword":"senior designer"
     }
   }
}'        ## 預設查詢的是分詞,如果要查沒分詞的,應該加上 keyword

curl -X GET -H "Content-Type: application/json"  localhost:9200/my_index/_search?pretty -d '{
  "query": {
     "term":{
        "address.city":"guangzhou"
     }
   }
}'        ## 查詢巢狀的欄位

curl localhost:9200/_search?pretty        ## 在所有 index 中查詢

curl -H "Content-Type: application/json" localhost:9200/my_index/_analyze?pretty -d '{
   "analyzer" : "standard",  
   "text" : "This is the apple"
}'        ## 如何分析一段文字

curl -X PUT 'http://localhost:9200/my_index_3'    ## 建立 index

curl -X PUT -H "Content-Type: application/json" 'http://localhost:9200/my_index_6' -d  '{
    "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 1
    },
    "mappings": {
        "properties": {
            "name": {
                "type": "text"
            },
            "title": {
                "type": "text"
            },
            "value": {
                "type": "long"
            }
        }
    }
}'         ## 建立 index 同時指定 field,在版本 7 以後不需要指定 type (不需要指定 my_doc 之類的)
應用場景

適合於以搜尋為主的業務場景,最開始 ELK (Elasticsearch + Logstash + Kibana) 就是用於日誌收集和搜尋

Spark/Flink

資料庫雖然強大,但如果遇到複雜的邏輯計算也是無能為力,這種情況,就需要有專門的計算工具

Spark 和 Flink 都是高效能的平行計算引擎,Flink 更偏向實時流業務,Spark 更偏向批處理業務,都可以用來高效地處理資料

BI

資料通常要視覺化,比較常用的 BI 工具有 Tableau (收費的) 和 PowerBI

整體系統架構

K8S (容器部署) + SpringCloud (微服務) + Keycloak (認證) + Kafka (資料流) + Spark/Flink (資料處理) + ELK (日誌收集) + PG/MySQL (基本資料) + NoSQL-OLTP (大資料量業務資料) + OLAP (分析業務) + BI (資料視覺化)

相關文章