clickhouse中update/delete的使用之mutation
Clickhouse是個分析型資料庫。這種場景下,資料一般是不變的,因此Clickhouse對update、delete的支援是比較弱的,實際上並不支援標準的update、delete操作。
1.Clickhouse通過alter方式實現更新、刪除,它把update、delete操作叫做mutation(突變)。
語法為:
ALTER TABLE [db.]table DELETE WHERE filter_expr ALTER TABLE [db.]table UPDATE column1 = expr1 [, ...] WHERE filter_expr
那麼,mutation與標準的update、delete有什麼區別呢?
標準SQL的更新、刪除操作是同步的,即客戶端要等服務端反回執行結果(通常是int值);而Clickhouse的update、delete是通過非同步方式實現的,當執行update語句時,服務端立即反回,但是實際上此時資料還沒變,而是排隊等著。它們是非同步後臺程序,類似於MergeTree表中的合併,用於生成新的“變異”版本的部件。
大多數ALTER TABLE
查詢僅支援*MergeTree表以及Merge和Distributed
1)對於*MergeTree
表,突變通過重寫整個資料部分來執行。沒有原子性 - 部件一旦準備好就被變異部件替換,並且SELECT
在變異期間開始執行的查詢將看到來自已經變異部件的資料以及來自尚未變異部件的資料。
2)突變完全按其建立順序排序,並按該順序應用於每個部分。突變也通過INSERT INTO
查詢部分排序:在提交突變之前插入到表中的資料將被突變,之後插入的資料不會被突變。請注意,突變不會以任何方式阻止插入。
3)新增突變條目後,突變查詢立即返回(如果複製表到 ZooKeeper,對於非複製表 - 到檔案系統)。突變本身使用系統配置檔案設定非同步執行。要跟蹤突變的進度,您可以使用該system.mutations
表。即使重新啟動 ClickHouse 伺服器,成功提交的變更仍將繼續執行。一旦提交,就無法回滾突變,但如果突變由於某種原因被卡住,可以通過KILL MUTATION
查詢取消它。
4)完成突變的條目不會立即刪除(保留條目的數量由finished_mutations_to_keep
儲存引擎引數確定)。舊的突變條目被刪除。
5)對於非複製表,所有ALTER
查詢都是同步執行的。對於複製的表,查詢只是將適當操作的指令新增到ZooKeeper
,並且操作本身會盡快執行。但是,查詢可以等待在所有副本上完成這些操作。對於所有ALTER
查詢,您可以使用replication_alter_partitions_sync設定來設定等待。您可以使用replication_wait_for_inactive_replica_timeout設定指定等待非活動副本執行所有ALTER
查詢的時間(以秒為單位)。!!!對於所有ALTER
查詢,如果replication_alter_partitions_sync = 2
某些副本不活動超過replication_wait_for_inactive_replica_timeout
設定中指定的時間,UNFINISHED
則丟擲異常。
2.檢視mutation佇列
那麼,怎麼檢視資料是否更新完成了呢?
可以通過system.mutations表檢視相關資訊:
SELECT database, table, command, create_time, is_done FROM system.mutations LIMIT 10 ┌─database─┬─table─────────────────┬─command─────────────────────────────────────────────────────────────────────────────┬─────────create_time─┬─is_done─┐ │ app │ scene_model │ UPDATE status = '2' WHERE id = '208209306' │ 2020-03-30 15:38:58 │ 1 │ │ app │ scene_model │ UPDATE status = '2' WHERE id = '100000004' │ 2020-03-30 15:40:00 │ 1 │ │ app │ scene_model │ UPDATE status = '2' WHERE id = '100000004' │ 2020-03-30 15:41:09 │ 1 │ │ app │ user_model │ UPDATE name = 'zhuweiming' WHERE id = '0000000047fd31e40147fd3477cc0000' │ 2020-03-19 18:34:59 │ 1 │ │ app │ work_statistics_total │ UPDATE pv = 10000, uv = 10000 WHERE (id = '1000900') AND (product = 'tracker_view') │ 2020-03-31 14:45:59 │ 1 │ │ app │ work_statistics_total │ UPDATE pv = 10000, uv = 10000 WHERE (id = '1000901') AND (product = 'tracker_view') │ 2020-03-31 14:45:59 │ 1 │ │ app │ work_statistics_total │ UPDATE pv = 10000, uv = 10000 WHERE (id = '1000902') AND (product = 'tracker_view') │ 2020-03-31 14:45:59 │ 1 │ │ app │ work_statistics_total │ UPDATE pv = 10000, uv = 10000 WHERE (id = '1000903') AND (product = 'tracker_view') │ 2020-03-31 14:45:59 │ 1 │ │ app │ work_statistics_total │ UPDATE pv = 10000, uv = 10000 WHERE (id = '1000904') AND (product = 'tracker_view') │ 2020-03-31 14:45:59 │ 1 │ │ app │ work_statistics_total │ UPDATE pv = 10000, uv = 10000 WHERE (id = '1000905') AND (product = 'tracker_view') │ 2020-03-31 14:45:59 │ 1 │ └──────────┴───────────────────────┴─────────────────────────────────────────────────────────────────────────────────────┴─────────────────────┴─────────┘
-
database: 庫名
-
table: 表名
-
command: 更新/刪除語句
-
create_time: mutation任務建立時間,系統按這個時間順序處理資料變更
-
is_done: 是否完成,1為完成,0為未完成
除了上述的,還有一些其他的欄位,詳見官方文件。
通過以上資訊,可以檢視當前有哪些mutation已經完成,is_done為1即表示已經完成。
3.Mutation具體過程
首先,使用where條件找到需要修改的分割槽;
然後,重建每個分割槽,用新的分割槽替換舊的,分割槽一旦被替換,就不可回退;
對於每個分割槽,可以認為是原子性的;但對於整個mutation,如果涉及多個分割槽,則不是原子性的。
4.注意事項
- 更新功能不支援更新有關主鍵或分割槽鍵的列
- 更新操作沒有原子性,即在更新過程中select結果很可能是一部分變了,一部分沒變,從上邊的具體過程就可以知道
- 更新是按提交的順序執行的
- 更新一旦提交,不能撤銷,即使重啟clickhouse服務,也會繼續按照system.mutations的順序繼續執行
- 已完成更新的條目不會立即刪除,保留條目的數量由finished_mutations_to_keep儲存引擎引數確定。 超過資料量時舊的條目會被刪除
- 更新可能會卡住,比如
update intvalue='abc'
這種型別錯誤的更新語句執行不過去,那麼會一直卡在這裡,此時,可以使用KILL MUTATION
來取消,語法:
kill kutation where database='app' and table='test'
-- database、table是system.mutations表中的欄位
當執行刪除更新失敗的時候需注意:對於mutations執行失敗的話,先去查詢system.mutations的任務,檢視is_done=0的資料,並且看下latest_fail_reason的原因。如果發現問題不是sql的原因,則刪除mutations再嘗試執行刪除或者更新,如果是sql或者其他原因,查詢找到原因解決再執行刪除或者更新。
5.使用建議
按照官方的說明,update/delete 的使用場景是一次更新大量資料,也就是where條件篩選的結果應該是一大片資料。
舉例:alter table test update status=1 where status=0 and day='2022-04-01'
,一次更新一天的資料。
那麼,能否一次只更新一條資料呢?例如:alter table test update pv=110 where id=100
當然也可以,但頻繁的這種操作,可能會對服務造成壓力。這很容易理解,如上文提到,更新的單位是分割槽,如果只更新一條資料,那麼需要重建一個分割槽;如果更新100條資料,而這100條可能落在3個分割槽上,則需重建3個分割槽;相對來說一次更新一批資料的整體效率遠高於一次更新一行。
對於頻繁單條更新的這種場景,建議使用ReplacingMergeTree
引擎來變相解決。