Transaction Processing or Analytics?
每一個資料庫都能許多種不同型態的資料,如 blog 貼文、遊戲資料、聯絡人資訊等等,應用系統存取這些資料都是為了公司的商業目的,通常都需要用 key 快速查找資料、使用 index 加速、會依 user 不同的輸入而寫入資料庫,系統是能跟 user 互動的,這種資料存取模式一般稱為 online transaction processing (OLTP) ,
一筆交易 (transaction) 不代表它一定要符合 ACID 特性 (atomicity, consistency, isolation 和 durability),transaction processing 意味著快速寫入和讀取的操作,即時性要求高,反之則是 batch processing,意味者它每隔一段時間執行。
然而,有商業資料以後管理層就需要去看一些統計指標,也就代表需要有人去分析資料,例如:
- 某個寵物商品與哪個商品最常被組合購買?
- 各子公司的 YOY 成長。
- 某間店每月的毛利額。
這些查詢通常是為了某種目的做的商業分析,能幫助管理階層做出更好的決策,這是另一套不同的資料存取模式,一般稱為 online analytic processing (OLAP),OLTP 和 OLAP 比較難明確、清楚的分割,但我們可用下表列出在一些他倆的特點:
屬性 | Transaction proceesing systems (OLTP) | Analytic systems (OLAP) |
---|---|---|
讀取模式 | 查詢的資料筆數少,用 key 來查詢 | 匯總大量的資料 |
寫入模式 | 隨機存取,每一個 input 都是低延遲 | 用大量匯入、ETL或 event streaming 方式寫入 |
主要用於 | 最終端 user 或客戶, 通常用於 web 應用程式 | 內部分析師 |
資料表達 | 資料的最新狀態 | 一段時間 event 的歷史資料 |
資料大小 | GB to TB | TB to PB |
在有一定規模的公司裡,面對 OLAP 這種分析用面向的資料模式,通常我們會為它建立另外一套資料庫,這資料庫稱做 data warehouse。
Data Warehouse (資料倉儲)
公司可能會有數十個不同的 OLTP 系統,可能有網站、供應商系統、客服系統、ERP 系統等等,OLTP 就應該專注處理交易 (transaction),所以分析面向用的 OLAP,就需要透過一些方法將不同的 OLTP 資料匯集至 data warehouse 中 (read-only 複製), 重要的是絕對不能影響 OLTP 資料庫,至於怎麼做呢?
第一步會從 OLTP 資料庫擷取 (Extract)資料,第二步是轉換 (Transform) 成分析友善的 schema、資料清理,最後一步則是載入 (Load) 至 data warehouse 中,這個處理過程就是常聽到的 ETL (Extract-Transform-Load),如下圖說明:
用分隔的資料庫作分析有幾個好處:
- 能設計對分析友善的 schema。
- 不影響面向交易 OLTP 資料庫的效能。
- 能針對 data warehouse 資料庫做面向分析的 index 演算法優化。
Fact Table
直接用下圖說明,在 data warehouse 做分析時,通常會有一個主要的資料表稱 fact table ,這也是主要拿來做計算的 measure table,每一列資料都要有該 event 的發生時間,圖中的例子是銷售資料 ( fact_sales ),然後用 foreign key 連出來的那些資料表稱 dimensional table,通常是表示為 evemt 的 who, what, where, when, how 和 why。
若我們的 fact table 有十幾億筆資料該怎辦,該如何讓 OLAP 查的快速,明天會講 Column-Oriented Storage 的原理。
而我們數據團隊用的 fact table 就是整個 ETtoday 全站的瀏覽行為,所有的標籤、行為分析、dashboard 都是從這張 fact table 出發!我們用 Hadoop 做我們的 data warehouse,然後使用 Spark 做 ETL 與資料查詢,目前已有 100 多億筆資料,我們也是使用 Column-Oriented Storage 來存這些資料。
Column-Oriented Storage
想像一下如果你的 fact table 有上兆筆資料,資料大小是 PB,dimension table 通常比較小 (百萬筆), 該如何儲存或查詢呢?今天就會來聊聊有什麼好方法可用。
fact table 可能會有上百個欄位,但在 data warehouse 做分析時不可能會全用到,差不多就一次撈個 5,6 個欄位做計算,現在我們繼續用上面那個 fact table 來舉例,
現在來試著計算 2013 年水果和糖果的每週銷售數量,我們可以用下面這個 SQL 來查,這個查詢裡我們在 fact_sales 裡只用到 date_key, product_sk, quantity 這 3 毎欄位,其他忽略:
SELECT
dim_date.weekday, dim_product.category,
SUM(fact_sales.quantity) AS quantity_sold
FROM fact_sales
JOIN dim_date ON fact_sales.date_key = dim_date.date_key
JOIN dim_product ON fact_sales.product_sk = dim_product.product_sk
WHERE
dim_date.year = 2013 AND
dim_product.category IN ('Fresh fruit', 'Candy')
GROUP BY
dim_date.weekday, dim_product.category;
我們如何查詢更有效率?
在 relational 資料庫,資料會以 row-oriented 的方式儲存,所有的值是每一列接著每一列的儲存,在 doucment 資料庫也類似,每一整個 documnet 也是連續的儲存,存的方式很像 Log structured and Hash Index 裡的圖 3-1;
我們可以針對 fact_sales.date_key 和 fact_sales.product_sk 建立 index,可是最終資料庫還是得把整筆 row 載入到記憶體中,然後過濾我們的條件,留下我們要用的欄位,若 fact_table 有上百個欄位,這樣很花時間。
column-oriented 的概念很簡單,以 column 的方式來存資料,如果我們只要撈 3 個欄位,我只要去這 3 個檔案查就好了,就節省了 parse 其他不必要欄位的時間了,如下圖說明:
column-oriented 儲存引擎的重點是,每一個 column 檔案的 順序要一樣,所以如果你想查詢第 35 列的資料 (select * from fact_sales_table
) ,就得去所有的 column 檔案裡找到第 35 個值然後組合。
特別介紹:Parquet
這是一個用在 document data model 中的 column storage 格式,支援大多數的 Hadoop 生態圈工具。
Column Compression (欄位壓縮)
壓縮的好處就是節省空間 (廢話),回去看上圖 3-10,會發現有些 column 中有很多值重複了,這是個好訊號,代表我們可以做資料的壓縮,一個在 data warehouse 常用的有效技巧為 bitmap encoding ,如下圖說明:
bitmap 就是針對每個去重複的值,如 product_sk = 29, 30, 31, 68, 69, 74
建立一個用 0, 1 識別的 map,1 代表該列 (row) 有值,首先 product_sk = 29
在該 bitmap 中的相同順序的位置上會填入 1 ,如紅色小箭頭,以此類推把所有的值都填完,
最後在做 run-length encoding,以淺藍色線的 product_sk = 69
舉例,encoding 完後的結果是 0, 4, 12, 2
,表示一開始是沒有 0,接下來連續 4 個 1,再來 12 個 0,最後有 2 個 1 ,如此,就能達到效果非常好的壓縮。
column-oriented storage and column-families
Cassandra 和 HBase 的概念是 column families (來自 Bigtable),這常和 column-oriented 搞混,column families 是隨著 row key 用 row 儲存所有的欄位,它無法做到 column compression。
Sort Order in Column Storage
對 column 做排序有幾個好處,第一是能依業務需求加快查詢速度,類似 index 的概念,如果我們常用 date_key 來計算業績、銷售數量等等,我們就可以選擇 date_key 為主要排序的 column,如此當我們想要撈上個月的資料時,就不需要用全部的 date_key 資料做過濾,能直接找到特定幾列的資料;
第二是壓縮成果會更好,一樣以 date_key 舉例,2013 年若有幾千萬筆銷售,因為有用 date_key 做排序,用上面的 bitmap 技法儲存資料時只要存幾 KB 就好了;壓縮有最大效果的就是首要排序的 column。
Writing to Column-Oriented Storage
前面提到的 Column-Oriented Storage, column compression 和 sort column 技法都是對查詢有幫助,但隨之也會讓寫入變的比較困難,例如你有主要排序欄位,你想寫一筆中間位置的資料進去,這代表你要重新產生所有的 column 檔案,
但有個好消息是,我們可以借用在 Day 介紹過的 LSM-Trees 中的 in-memory store 來解決,概念一樣在記憶體累積一定資料後在合併回 column 檔案裡去,這也是 Vertica 公司做的事,查詢時一樣要去 in-memory 跟 column 檔案 2 個地方查。
總結
這 4 天討論了各種不同的 data storage 跟 index 方式,書中其實有提到更多 index 的方法,值得去買來看看,我們公司的資料有百億筆了,慢慢的就會開始碰到很多資料問題,我想認識這些技巧越多,你就越知道怎麼去設計、優化數據密集的系統,明天開始就是介紹各種不同的 encoding 格式了。