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表中的合並,用於生成新的“變異”版本的部件。
1)
該查詢刪除表中此表達式取非零值的行。
2)
此查詢將指定列的值更新為行中對應的表達式的值,這些表達式的值filter_expr
取非零值。使用運算符將值轉換為列類型CAST
。不支持更新用於計算主鍵或分區鍵的列。
支持子查詢 但是不支持直接的delete /update join 動作。
一個查詢可以包含多個用逗號分隔的命令。
大多數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.注意事項
1)更新功能不支持更新有關主鍵或分區鍵的列
2)更新操作沒有原子性,即在更新過程中select結果很可能是一部分變了,一部分沒變,從上邊的具體過程就可以知道
3)更新是按提交的順序執行的
4)更新一旦提交,不能撤銷,即使重啟clickhouse服務,也會繼續按照system.mutations的順序繼續執行
5)已完成更新的條目不會立即刪除,保留條目的數量由finished_mutations_to_keep存儲引擎參數確定。 超過數據量時舊的條目會被刪除
6)更新可能會卡住,比如update intvalue='abc'這種類型錯誤的更新語句執行不過去,那么會一直卡在這里,此時,可以使用KILL MUTATION來取消,語法:
kill mutation where database='app' and table='test' -- database、table是system.mutations表中的字段
當執行刪除更新失敗的時候需注意:對於mutations執行失敗的話,先去查詢system.mutations的任務,查看is_done=0的數據,並且看下latest_fail_reason的原因。如果發現問題不是sql的原因,則刪除mutations再嘗試執行刪除或者更新,如果是sql或者其他原因,查詢找到原因解決再執行刪除或者更新。
7)ClickHouse 中的 mutation 操作(update/delete) 默認是異步執行的, 這會導致一種情況的出現: 刪除的數據在一段時間內還能查詢到. 在非事務性的使用場景中這個設置可以加快處理速度, 並且不會影響后來數據的添加, 但在要求事務性的使用場景中(比如新增數據依賴歷史數據), 這個設置會導致后加的數據出現錯誤.
解決方法:在 ClickHouse 的配置文件中加入 mutation_sync 參數, 令其等於 1 或 2
mutation_sync 參數默認為 0, 也就是 mutation 操作異步執行, 設為 1 會在當前機器上阻塞直到操作完成, 設為 2 會一直阻塞直到操作在所有數據副本上執行完成, 主要用於集群。
修改 mutation_sync 這個參數的方法很簡單, 如果只是為了臨時修改一下, 在啟動客戶端命令時加入 -mutations_sync=1 就可以了.
如果想持久化這個修改, 在 /etc/clickhouse-server/users.xml 這個配置文件中 profile 的 default 設置(對所有繼承default設置的用戶生效)或者特定用戶(只對特定用戶生效)的配置信息中加入 <mutations_sync>2</mutations_sync> 即可
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或versionedcollapsingmergetree
引擎來變相解決。