概述 如果表沒有定義主鍵,則預設情況下它是僅追加 表類型(Append Only Table)。 根據桶(Bucket)的定義,我們有兩種不同的僅追加模式:"Append For Scalable Table"和"Append For Queue";兩種模式支持不同的場景,提供不同的功能。 只能向表 ...
概述
如果表沒有定義主鍵,則預設情況下它是僅追加 表類型(Append Only Table)。 根據桶(Bucket)的定義,我們有兩種不同的僅追加模式:"Append For Scalable Table"和"Append For Queue";兩種模式支持不同的場景,提供不同的功能。
只能向表中插入一條完整的記錄。 不支持刪除或更新,並且不能定義主鍵。 此類表適合 不需要更新的用例(例如日誌數據同步)。
Append 場景特指"無主鍵"的場景,比如日誌數據的記錄,不具有直接Upsert更新的能力。
Append For Scalable Table
其支持的功能如下:
- 支持批讀批寫 INSERT OVERWRITE
- 支持流讀流寫 自動合併小文件
- 支持湖存儲特性 ACID、Time Travel
- order與z-order排序
Definition
通過在表屬性中定義 'bucket' = '-1'
,可以為此表分配特殊模式(我們稱之為"unaware-bucket 模式")。 在這種模式下,一切都不同了。 我們已經沒有了桶的概念,也不保證流式讀取的順序。 我們將此表視為批量離線表(儘管我們仍然可以流式讀寫)。 所有記錄都會進入一個目錄(為了相容性,我們將它們放在bucket-0中),並且我們不再維護順序。 由於我們沒有桶的概念,所以我們不會再按桶對輸入記錄進行混洗,這將加快插入速度。
使用此模式,可以將 Hive 表替換為 Lake 表。
Compaction
在unaware-bucket模式下,我們不在writer中進行壓縮,而是使用Compact Coordinator掃描小文件並將壓縮任務提交給Compact Worker。 這樣,我們就可以輕鬆地對一個簡單的數據目錄進行並行壓縮。 在流模式下,如果在flink中運行insert sql,拓撲將是這樣的:
它會儘力壓縮小文件,但是當一個分區中的單個小文件長時間保留並且沒有新文件添加到該分區時,壓縮協調器會將其從記憶體中刪除以減少記憶體使用。 重新啟動作業後,它將掃描小文件並將其再次添加到記憶體中。 控制緊湊行為的選項與 Append For Qeueue 完全相同。 如果將 write-only 設置為 true,Compact Coordinator 和 Compact Worker 將從拓撲中刪除。
自動壓縮僅在 Flink 引擎流模式下支持。還可以通過 paimon 中的 flink 操作在 flink 中啟動壓縮作業,並通過 set write-only 禁用所有其他壓縮。
Sort Compact
每個分區中的數據亂序會導致選擇緩慢,壓縮可能會減慢插入速度。 將插入作業設置為只寫是一個不錯的選擇,並且在每個分區數據完成後,觸發分區排序壓縮操作。
Streaming Source
Unaware-bucket模式 Append Only Table 支持流式讀寫,但不再保證順序。 你不能把它看作一個隊列,而是一個有bin的湖。每次提交都會生成一個新的binbin存儲記錄 來讀取增量,但是一個 bin 中的記錄會流向它們想要的任何地方,並且我們以任何可能的順序獲取它們。 在Append For Queue模式下,記錄不存儲在bin中,而是存儲在record pipe中。 記錄 存儲,我們可以通過讀取新的存儲記錄 來讀取增量,但是一個 bin 中的記錄會流向它們想要的任何地方,並且我們以任何可能的順序獲取它們。 在Append For Queue模式下,記錄不存儲在bin中,而是存儲在record pipe中。
bin:儲物箱
Streaming Multiple Partitions Write
由於Paimon-sink需要處理的寫入任務數量為:數據寫入的分區數量 * 每個分區的桶數量。 因此,我們需要儘量控制每個paimon-sink任務的寫任務數量,使其分佈在合理的範圍內。 如果每個sink-task處理過多的寫任務,不僅會導致小文件過多的問題,還可能導致記憶體不足的錯誤。
另外,寫入失敗會引入孤兒文件,這無疑增加了維護paimon的成本。 我們需要儘可能避免這個問題。
對於啟用自動合併的 flink-jobs,我們建議嘗試按照以下公式來調整 paimon-sink 的並行度(這不僅僅適用於append-only-tables,它實際上適用於大多數場景):
(N*B)/P < 100 (This value needs to be adjusted according to the actual situation)
N(the number of partitions to which the data is written)
B(bucket number)
P(parallelism of paimon-sink)
100 (This is an empirically derived threshold,For flink-jobs with auto-merge disabled, this value can be reduced.
However, please note that you are only transferring part of the work to the user-compaction-job, you still have to deal with the problem in essence,
the amount of work you have to deal with has not been reduced, and the user-compaction-job still needs to be adjusted according to the above formula.)
還可以將 write-buffer-spillable
設置為 true,writer 可以將記錄溢出到磁碟。 這可以儘可能地減少小文件。要使用此選項,的 flink 集群需要有一定大小的本地磁碟。 這對於那些在 k8s 上使用 flink 的人來說尤其重要。
對於僅追加表,您可以為僅追加表設置 write-buffer-for-append
選項。 將此參數設置為true,writer將使用Segment Pool緩存記錄以避免OOM。
Example
以下是創建Append-Only表並指定bucket key的示例。
CREATE TABLE MyTable (
product_id BIGINT,
price DOUBLE,
sales BIGINT
) WITH (
'bucket' = '-1'
);
Append For Queue
其支持的功能如下:
- 嚴格保證順序,可以帶消息隊列
- 支持Watermark且對齊
- 自動合併小文件
- 支持Consumer-ID (類似Group-ID)
Definition
在這種模式下,可以將append-only table看成是一個由bucket分隔的隊列。 同一個桶中的每條記錄都是嚴格排序的,流式讀取會嚴格按照寫入的順序將記錄傳輸到下游。 使用此模式,不需要進行特殊配置,所有數據都會以隊列的形式放入一個桶中。還可以定義bucket
和bucket-key
以實現更大的並行性和分散數據。
Compaction
預設情況下,sink節點會自動進行compaction來控制文件數量。 以下選項控制壓縮策略:
Streaming Source
目前僅 Flink 引擎支持流式源行為。
Streaming Read Order
對於流式讀取,記錄按以下順序生成:
- 對於來自兩個不同分區的任意兩條記錄
- 如果 scan.plan-sort-partition 設置為 true,則首先生成分區值較小的記錄。
- 否則,將先產生分區創建時間較早的記錄。
- 對於來自同一分區、同一桶的任意兩條記錄,將首先產生第一條寫入的記錄。
- 對於來自同一分區但兩個不同桶的任意兩條記錄,不同的桶由不同的任務處理,它們之間沒有順序保證。
Watermark Definition
定義讀取 Paimon 表的watermark:
CREATE TABLE T (
`user` BIGINT,
product STRING,
order_time TIMESTAMP(3),
WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH (...);
-- launch a bounded streaming job to read paimon_table
SELECT window_start, window_end, COUNT(`user`) FROM TABLE(
TUMBLE(TABLE T, DESCRIPTOR(order_time), INTERVAL '10' MINUTES)) GROUP BY window_start, window_end;
還可以啟用 Flink Watermark 對齊,這將確保沒有源/拆分/分片/分區將其 Watermark 增加得遠遠超出其他部分:
Bounded Stream
Streaming Source 也可以是有界的,指定 scan.bounded.watermark
來定義有界流模式的結束條件,流讀取將結束,直到遇到更大的 watermark 快照。
快照中的watermark 是由writer生成的,例如,指定kafka源並聲明watermark 的定義。當使用此kafka源寫入Paimon表時,Paimon表的快照將生成相應的watermark,以便流式讀取此Paimon表時可以使用有界watermark的功能。
CREATE TABLE kafka_table (
`user` BIGINT,
product STRING,
order_time TIMESTAMP(3),
WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH ('connector' = 'kafka'...);
-- launch a streaming insert job
INSERT INTO paimon_table SELECT * FROM kakfa_table;
-- launch a bounded streaming job to read paimon_table
SELECT * FROM paimon_table /*+ OPTIONS('scan.bounded.watermark'='...') */;
Example
以下是創建Append-Only表並指定bucket key的示例。
CREATE TABLE MyTable (
product_id BIGINT,
price DOUBLE,
sales BIGINT
) WITH (
'bucket' = '8',
'bucket-key' = 'product_id'
);
參考
基於 Apache Paimon 的 Append 表處理
Apache Paimon 實時數據湖 Streaming Lakehouse 的存儲底座