clickhouse 刪除操作

OLAP 數據庫設計的宗旨在于分析適合一次插入多次查詢的業務場景,市面上成熟的 AP 數據庫在更新和刪除操作上支持的均不是很好,當然 clickhouse 也不例外。但是不友好不代表不支持,本文主要介紹在 clickhouse 中如何實現數據的刪除,以及最新版本中 clickhouse 所做的一些技術突破

Click_House_Delete_Statement_6fd661d851

一、mutation

剛接觸 clickhouse 的小伙伴或許對 mutation 就很熟悉了,mutation 查詢可以看成 alter 語句的變種。雖然 mutation 能夠最終實現修改和刪除的需求,但不能完全用通常意義的 delete 和 update 來理解,我們需要清醒的認識到它的不同:

  1. mutation 是一個很重的操作,適合批量數據操作
  2. 不支持事務、一旦操作立刻生效無法回滾
  3. mutation 為異步操作

1.1 實操

創建一張表用于測試 mutation 操作

create table mutations_operate
(UserId     UInt64,Score      UInt64,CreateTime DateTime
) engine = MergeTree()partition by toYYYYMMDD(CreateTime)order by UserId;

接下來分別插入兩批不同分區的數據

insert into mutations_operate
select number,abs(number - 100),'2023-08-08 00:00:00'
from system.numbers
limit 1000000;insert into mutations_operate
select number,abs(number - 100),'2023-08-09 00:00:00'
from system.numbers
limit 1000000;

嘗試刪除 20230808 分區中 1000-10000 之間的所有數據,sql 如下

alter table mutations_operate delete where toYYYYMMDD(CreateTime) = 20230808 and UserId between 1000 and 10000;

可以統計一下該分區的數據條數來確認是否成功刪除,從體驗來說目前的數據規模感受不到 mutation 的“重”,感覺像是瞬間完成的。

當然我們也可以查看system.mutations表來監控 mutation 操作的進度

select table, mutation_id, `block_numbers.number` as num, is_done
from system.mutations;Query id: 0878a0f1-a5ff-474c-8f84-518ce5dc5e1d┌─table─────────────┬─mutation_id────┬─num─┬─is_done─┐
│ mutations_operate │ mutation_3.txt │ [3]1 │
└───────────────────┴────────────────┴─────┴─────────┘1 row in set. Elapsed: 0.002 sec.

mutation_id 是一個日志文件,可以在表存儲目錄中查看,完整記錄了本次操作的語句和時間,例如

format version: 1
create time: 2023-08-09 18:54:06
commands: DELETE WHERE (toYYYYMMDD(CreateTime) = 20230808) AND ((UserId >= 1000) AND (UserId <= 10000))

而其中的 3 以及block_numbers.number是 mutation 號,每執行一條 delete 或 update 語句都會對應一個唯一的編號

id_done 表示本次 mutation 操作是否執行完成,1 表示已經完成

1.2 原理

為了探尋 mutation 操作的原理和執行流程重置一下表數據(刪除重建即可),在插入兩批數據后查看磁盤目錄

? du -h0B	./detached
7.7M	./20230809_2_2_0
7.7M	./20230808_1_1_015M	.

可以看到兩個分區目錄均是 7.7M

嘗試執行刪除操作后,可以在日志中看到下面的查詢信息

<Debug> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): Key condition: unknown, (column 0 in [1000, +Inf)), (column 0 in (-Inf, 10000]), and, and
<Debug> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): Key condition: unknown, (column 0 in [1000, +Inf)), (column 0 in (-Inf, 10000]), and, and
<Debug> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): MinMax index condition: (toYYYYMMDD(column 0) in [20230808, 20230808]), unknown, unknown, and, and
<Debug> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): MinMax index condition: (toYYYYMMDD(column 0) in [20230808, 20230808]), unknown, unknown, and, and
<Trace> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): Running binary search on index range for part 20230808_1_1_0 (124 marks)
<Trace> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): Found (LEFT) boundary mark: 0
<Debug> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): Selected 0/1 parts by partition key, 0 parts by primary key, 0/0 marks by primary key, 0 marks to read from 0 ranges
<Trace> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): Found (RIGHT) boundary mark: 2
<Trace> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): Found continuous range in 13 steps
<Debug> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): Selected 1/1 parts by partition key, 1 parts by primary key, 2/123 marks by primary key, 2 marks to read from 1 ranges
<Trace> delete_operate.mutations_operate (7f120927-b71e-4f85-a06c-21a94b7f89e3) (SelectExecutor): Spreading mark ranges among streams (default reading)
<Trace> MergeTreeInOrderSelectProcessor: Reading 1 ranges in order from part 20230808_1_1_0, approx. 16384 rows starting from 0
<Trace> Aggregator: Aggregation method: without_key
<Trace> AggregatingTransform: Aggregated. 0 to 1 rows (from 0.00 B) in 0.000570041 sec. (0.000 rows/sec., 0.00 B/sec.)
<Trace> Aggregator: Merging aggregated data
<Trace> MutateTask: Part 20230809_2_2_0 doesn't change up to mutation version 3

首先,clickhouse 會使用我們執行的刪除語句中附帶的 where 條件在每個分區中執行 count 查詢,為了判斷哪些分區有需要被刪除的數據,從日志可以看出Reading 1 ranges in order from part 20230808_1_1_0, approx. 16384 rows starting from 0以及Part 20230809_2_2_0 doesn't change up to mutation version 3。注意日志中所說 20230808 的范圍是 0~16384 并不是實際刪除的范圍,而是索引的范圍。我們知道 mergeTree 引擎默認的跳數索引的間隔是 8192 而我們刪除的數據范圍是 1000-10000,顯然作為一個整周期自然是 0-16384(2x8192)

當我們再次查看磁盤目錄

? du -h0B	./detached
7.7M	./20230809_2_2_0
7.7M	./20230808_1_1_00B	./20230809_2_2_0_3
7.6M	./20230808_1_1_0_323M	.

總目錄從 15M 變成了 23M,而兩個分區也都各自生成了一個以 mutation version 為后綴的新分區

因此接下來的邏輯如下:

clickhouse 會創建一個 tmp_mut_ 為前綴、mutation version 為后綴的臨時分區目錄,例如這里的就是 tmp_mut_20230808_1_1_0_3

對于需要刪除的分區,會在 tmp_mut 目錄中生成全新的 .bin 和 .mrk 文件

對于無需刪除的分區,clickhouse 會創建一個 tmp_clone_ 為前綴、mutation version 為后綴的臨時分區目錄并將原分區里面的數據以硬鏈接的方式拷貝過去,并修改目錄名稱為正確的格式

下面是執行的日志情況

<Debug> delete_operate.mutations_operate (4351d317-2cd6-4328-85fe-49d5beeff5c3): Clone part /opt/homebrew/var/lib/clickhouse/store/435/4351d317-2cd6-4328-85fe-49d5beeff5c3/20230809_2_2_0/ to /opt/homebrew/var/lib/clickhouse/store/435/4351d317-2cd6-4328-85fe-49d5beeff5c3/tmp_clone_20230809_2_2_0_3
<Trace> delete_operate.mutations_operate (4351d317-2cd6-4328-85fe-49d5beeff5c3): Renaming temporary part tmp_clone_20230809_2_2_0_3 to 20230809_2_2_0_3 with tid (1, 1, 00000000-0000-0000-0000-000000000000).
<Trace> MergedBlockOutputStream: filled checksums 20230808_1_1_0_3 (state Temporary)
<Trace> delete_operate.mutations_operate (4351d317-2cd6-4328-85fe-49d5beeff5c3): Renaming temporary part tmp_mut_20230808_1_1_0_3 to 20230808_1_1_0_3 with tid (1, 1, 00000000-0000-0000-0000-000000000000)

從磁盤目錄也可以佐證這一點,首先上面的 20230809_2_2_0_3 占用空間為 0B,當然這是 mac 獨有的現實方式,在其它 linux 系統不一定是這么顯示,進入各個分區查看一下

wjun :: data/delete_operate/mutations_operate ?stable? ? ll 20230808_1_1_0_3
total 15632
-rw-r-----@ 1 wjun  admin    17863 Aug  9 19:36 CreateTime.bin
-rw-r-----@ 1 wjun  admin      369 Aug  9 19:36 CreateTime.cmrk2
-rw-r-----@ 1 wjun  admin  3968891 Aug  9 19:36 Score.bin
-rw-r-----@ 1 wjun  admin      409 Aug  9 19:36 Score.cmrk2
-rw-r-----@ 1 wjun  admin  3969011 Aug  9 19:36 UserId.bin
-rw-r-----@ 1 wjun  admin      409 Aug  9 19:36 UserId.cmrk2
-rw-r-----@ 1 wjun  admin      490 Aug  9 19:36 checksums.txt
-rw-r-----@ 1 wjun  admin       90 Aug  9 19:36 columns.txt
-rw-r-----@ 1 wjun  admin        6 Aug  9 19:36 count.txt
-rw-r-----@ 1 wjun  admin       10 Aug  9 19:36 default_compression_codec.txt
-rw-r-----@ 1 wjun  admin        1 Aug  9 19:36 metadata_version.txt
-rw-r-----@ 1 wjun  admin        8 Aug  9 19:36 minmax_CreateTime.idx
-rw-r-----@ 1 wjun  admin        4 Aug  9 19:36 partition.dat
-rw-r-----@ 1 wjun  admin      188 Aug  9 19:36 primary.cidx
wjun :: data/delete_operate/mutations_operate ?stable? ? ll 20230809_2_2_0_3
total 15768
-rw-r-----@ 2 wjun  admin    18042 Aug  9 19:35 CreateTime.bin
-rw-r-----@ 2 wjun  admin      375 Aug  9 19:35 CreateTime.cmrk2
-rw-r-----@ 2 wjun  admin  4004938 Aug  9 19:35 Score.bin
-rw-r-----@ 2 wjun  admin      415 Aug  9 19:35 Score.cmrk2
-rw-r-----@ 2 wjun  admin  4004915 Aug  9 19:35 UserId.bin
-rw-r-----@ 2 wjun  admin      415 Aug  9 19:35 UserId.cmrk2
-rw-r-----@ 2 wjun  admin      490 Aug  9 19:35 checksums.txt
-rw-r-----@ 2 wjun  admin       90 Aug  9 19:35 columns.txt
-rw-r-----@ 2 wjun  admin        7 Aug  9 19:35 count.txt
-rw-r-----@ 2 wjun  admin       10 Aug  9 19:35 default_compression_codec.txt
-rw-r-----@ 2 wjun  admin        8 Aug  9 19:35 minmax_CreateTime.idx
-rw-r-----@ 2 wjun  admin        4 Aug  9 19:35 partition.dat
-rw-r-----@ 2 wjun  admin      173 Aug  9 19:35 primary.cidx

20230809_2_2_0_3 分區 inode 被連接次數為 2 表示建立了硬鏈接。

因此 mutation 的刪除邏輯如下:

  1. 每個分區執行附帶刪除操作的 where 條件的 count 查詢,獲取需要執行刪除操作的分區
  2. 對于需要執行刪除操作的分區會創建一個臨時目錄并生成全新(刪除需要刪除的行)的文件,隨后 rename 分區
  3. 對于無需執行刪除操作的分區會創建一個臨時目錄并以硬鏈接的方式拷貝文件,隨后 rename 分區
  4. 原分區在system.parts中會被置為 inactive 狀態
  5. 在下一次 merge 是刪除原分區

而對于更新操作基本邏輯一致,需要注意的是需要執行更新操作的分區會有如下兩種情況:

  1. 分區類型為 wide:只會重新生成受影響行的 bin 和 mrk 文件,不受影響的文件以硬鏈接的方式拷貝
  2. 分區類型為 compact:因為所有列都是一個文件,因此會重新生成 bin 和 mrk 文件

更新和刪除操作流程不一致的原因是:刪除影響全部列而更新只影響部分列

mergeTree 表的分區類型分為 wide 和 compact 兩種受min_bytes_for_wide_partmin_rows_for_wide_part參數影響。wide 類型的分區一個列一個文件,compact 類型的分區所有列公用一個文件,當分區數據的行數和字節較小時為 compact 類型,不管是查詢所有字段或某個字段相對較快;當數據量很大時就會以列式存儲來追求 AP 查詢性能

1.3 不足

當我們走一遍 mutation 時發現在刪除任務完成后表 merge 前的這一段時間磁盤空間不減反增,這個就讓用戶很難接受了。因此就可能會出現因為磁盤空間不足想要刪除數據,結果刪除操作導致空間進一步不足的窘境。同時 mutation 會重寫受影響的分區,這也是 mutation 操作重的原因所在。

二、mergeTree

對于 clickhouse 這類高性能分析型數據庫而言,修改源文件是一件非常奢侈且代價昂貴的操作,相對于直接修改源文件,我們將修改和新增操作都轉換為新增操作,即以增代刪將是一個非常不錯的選擇。是不是和 Hbase 的思路十分接近。在 mergeTree 家族中有一個特殊的表引擎叫 CollapsingMergeTree,翻譯過來叫折疊合并樹引擎就是提供了這樣的功能。它通過定義一個 sign 標記字段來記錄數據行的狀態。如果 sign 為 1 表示這是一行有效的數據,如果 sign 為 -1 表示這行數據被刪除。當 CollapsingMergeTree 分區合并時同一分區的 +1、-1 將會被抵消,猶如一張紙折疊一般。

2.1 實操

創建 CollapsingMergeTree 表

create table collapsing_table
(Id         String,Code       Int32,CreateTime DateTime,Sign       Int8
) engine = CollapsingMergeTree(Sign)partition by toYYYYMMDD(CreateTime)order by Id;

注:和其它 mergeTree 引擎一樣 CollapsingMergeTree 依然是以 order by 字段作為后續數據唯一性的依據

插入一批原始數據

insert into collapsing_table values ('A000', 100, '2023-08-09 00:00:00', 1);
insert into collapsing_table values ('A001', 100, '2023-08-09 00:00:00', 1);
insert into collapsing_table values ('A002', 100, '2023-08-09 00:00:00', 1);

修改 A000 的 Code 為 200 并刪除 A002 的數據

# 修改 A000 的 Code 為 200
insert into collapsing_table values ('A000', 100, '2023-08-09 00:00:00', -1);
insert into collapsing_table values ('A000', 200, '2023-08-09 00:00:00', 1);
# 刪除 A002 的數據
insert into collapsing_table values ('A002', 100, '2023-08-09 00:00:00', -1);
# 手動執行一下分區合并操作
optimize table collapsing_table final;

可以觀察到數據已經被刪除和修改。

CollapsingMergeTree 在分區合并折疊數據的時候,遵循下面規則

  1. 如果 sign = 1 比 sign = -1 多一行,最后保留 sign = 1 的數據
  2. 如果 sign = 1 比 sign = -1 少一行,最后保留 sign = -1 的數據
  3. 如果 sign = 1 和 sign = -1 一樣多,且最后一行時 sign = 1,則保留第一行的 sign = -1 和最后一行 sign = 1
  4. 如果 sign = 1 和 sign = -1 一樣多,且最后一行時 sign = -1,則什么也不保留
  5. 其余情況 clickhouse 會打印告警日志,但不會報錯且查詢情況不可預知

2.2 不足

當前表的數據如下

select *
from collapsing_table;Query id: 4b1da757-d02a-4b88-92e5-1fe659ca462c┌─Id───┬─Code─┬──────────CreateTime─┬─Sign─┐
│ A002 │  3002023-08-09 00:00:00-1 │
└──────┴──────┴─────────────────────┴──────┘
┌─Id───┬─Code─┬──────────CreateTime─┬─Sign─┐
│ A002 │  3002023-08-09 00:00:001 │
└──────┴──────┴─────────────────────┴──────┘
┌─Id───┬─Code─┬──────────CreateTime─┬─Sign─┐
│ A000 │  2002023-08-09 00:00:001 │
│ A001 │  1002023-08-09 00:00:001 │
└──────┴──────┴─────────────────────┴──────┘4 rows in set. Elapsed: 0.003 sec.

從操作來看 A002 是要被刪除的

但是如果查詢sql如下

select Id, sum(Code), count(Code), avg(Code)
from collapsing_table
group by Id;Query id: 610f6503-1344-4ba0-9564-6327277ffe95┌─Id───┬─sum(Code)─┬─count(Code)─┬─avg(Code)─┐
│ A001 │       1001100 │
│ A000 │       2001200 │
│ A002 │       6002300 │
└──────┴───────────┴─────────────┴───────────┘3 rows in set. Elapsed: 0.005 sec.

此時的結果是不對的,因此需要改寫 sql

select Id, sum(Code * Sign), count(Code * Sign), avg(Code * Sign)
from collapsing_table
group by Id
having sum(Sign) > 0;Query id: a3fe84d0-33a5-4287-bd02-49ab03df1852┌─Id───┬─sum(multiply(Code, Sign))─┬─count(multiply(Code, Sign))─┬─avg(multiply(Code, Sign))─┐
│ A001 │                       1001100 │
│ A000 │                       2001200 │
└──────┴───────────────────────────┴─────────────────────────────┴───────────────────────────┘2 rows in set. Elapsed: 0.005 sec.

當然還有一種方式就是在查詢數據前執行分區合并操作optimize table collapsing_table final;,但這種方式效率極低在生產中慎用

同時 CollapsingMergeTree 還存在一些問題,例如在分區合并前用戶是可以看到所有數據的。當然上面所說的問題都不是最致命的,CollapsingMergeTree 最致命點在于對于 sign 的寫入順序有嚴格的要求,對于一個刪除操作正常的順序應該是先寫入 1 再寫入 -1

insert into collapsing_table values ('A002', 300, '2023-08-09 00:00:00', 1);
insert into collapsing_table values ('A002', 300, '2023-08-09 00:00:00', -1);

但如果顛倒順序

insert into collapsing_table values ('A002', 300, '2023-08-09 00:00:00', -1);
insert into collapsing_table values ('A002', 300, '2023-08-09 00:00:00', 1);

則不會被刪除。而在生產環境一旦 CollapsingMergeTree 在多線程中處理就無法保證寫入順序了。

當然幸運的是 clickhouse 也注意到 CollapsingMergeTree 的缺點并推出了新的表引擎 VersionedCollapsingMergeTree,在 CollapsingMergeTree 的基礎上將按照寫入順序折疊修改為按照版本號順序進行折疊,而版本號交由用戶來管理。VersionedCollapsingMergeTree 引擎的操作就交給讀者來體驗,畢竟下面還有一種更貼合 TP 數據庫操作的刪除操作

三、lightweight

上面介紹了通過 mutation 和 mergeTree 來實現刪除操作,但是 mutation 操作太重,mergeTree 則需要修改 sql 且刪除操作受分區合并時機影響。從 clickhouse v22.8 開始提供了一個輕量級刪除功能且語法為標準 sql 🎉🎉🎉

3.1 實操

準備表和數據

create table lightweight_operate
(UserId     UInt64,Score      UInt64,CreateTime DateTime
) engine = MergeTree()partition by toYYYYMMDD(CreateTime)order by UserId;insert into lightweight_operate
select number,abs(number - 100),'2023-08-08 00:00:00'
from system.numbers
limit 1000000;insert into lightweight_operate
select number,abs(number - 100),'2023-08-09 00:00:00'
from system.numbers
limit 1000000;

同樣刪除 20230808 分區中 1000-10000 之間的所有數據,sql 如下

delete from lightweight_operate where toYYYYMMDD(CreateTime) = 20230808 and UserId between 1000 and 10000;

驗證一下

select count() from lightweight_operate where toYYYYMMDD(CreateTime) = 20230808;Query id: 0344da3b-5ea5-436d-ba29-cfb1a8e3420e┌─count()─┐
│  990999 │
└─────────┘1 row in set. Elapsed: 0.008 sec. Processed 1.00 million rows, 5.00 MB (128.59 million rows/s., 642.93 MB/s.)

成功刪除

3.2 原理

查看磁盤目錄

? ll
total 16
drwxr-x---@ 16 wjun  admin  512 Aug  9 21:09 20230808_1_1_0
drwxr-x---@ 18 wjun  admin  576 Aug  9 21:10 20230808_1_1_0_3
drwxr-x---@ 16 wjun  admin  512 Aug  9 21:09 20230809_2_2_0
drwxr-x---@ 15 wjun  admin  480 Aug  9 21:10 20230809_2_2_0_3
drwxr-x---@  2 wjun  admin   64 Aug  9 21:09 detached
-rw-r-----@  1 wjun  admin    1 Aug  9 21:09 format_version.txt
-rw-r-----@  1 wjun  admin  171 Aug  9 21:10 mutation_3.txt? du -h0B	./detached
7.7M	./20230809_2_2_0
7.7M	./20230808_1_1_00B	./20230809_2_2_0_328K	./20230808_1_1_0_315M	.

可以看出輕量刪除依然是一個 mutation 操作,從system.mutations表也可以驗證,但輕量刪除生成的新的分區 20230808_1_1_0_3 僅 28K,那么輕量刪除和 mutation 刪除的區別在哪

查看 20230808_1_1_0_3 磁盤目錄

wjun :: data/delete_operate/lightweight_operate ?stable? ? ll 20230808_1_1_0_3
total 15800
-rw-r-----@ 2 wjun  admin    18042 Aug  9 21:09 CreateTime.bin
-rw-r-----@ 2 wjun  admin      375 Aug  9 21:09 CreateTime.cmrk2
-rw-r-----@ 2 wjun  admin  4004938 Aug  9 21:09 Score.bin
-rw-r-----@ 2 wjun  admin      415 Aug  9 21:09 Score.cmrk2
-rw-r-----@ 2 wjun  admin  4004915 Aug  9 21:09 UserId.bin
-rw-r-----@ 2 wjun  admin      415 Aug  9 21:09 UserId.cmrk2
-rw-r-----@ 1 wjun  admin     4493 Aug  9 21:10 _row_exists.bin
-rw-r-----@ 1 wjun  admin      236 Aug  9 21:10 _row_exists.cmrk2
-rw-r-----@ 1 wjun  admin      589 Aug  9 21:10 checksums.txt
-rw-r-----@ 1 wjun  admin      110 Aug  9 21:10 columns.txt
-rw-r-----@ 2 wjun  admin        7 Aug  9 21:09 count.txt
-rw-r-----@ 1 wjun  admin       10 Aug  9 21:10 default_compression_codec.txt
-rw-r-----@ 1 wjun  admin        1 Aug  9 21:10 metadata_version.txt
-rw-r-----@ 2 wjun  admin        8 Aug  9 21:09 minmax_CreateTime.idx
-rw-r-----@ 2 wjun  admin        4 Aug  9 21:09 partition.dat
-rw-r-----@ 2 wjun  admin      173 Aug  9 21:09 primary.cidx

發現多了一組 _row_exists 文件而其余文件的 inode 連接數均為 2,也就是說輕量刪除是真正的給字段添加了一個標記。

在查詢的時候過濾

lightweight_deletes_v2_b891b54446

在分區合并的時候刪除

lightweight_delete_merge_1_a2519ab507

比 mutation 輕的點在于輕量刪除不會重構整個分區目錄而是重寫 _row_exists 文件這樣涉及到的修改會少很多,至于分區的拷貝和不涉及刪除操作的分區操作邏輯則和上面介紹的 mutation 流程一致

3.3 不足

輕量刪除的設計思路相比之前的會好上很多,但 clickhouse 畢竟不是 TP 數據庫,目前輕量刪除依然存在一些問題和限制,如:

  1. 輕量刪除是異步的,只有在分區合并的時候才會被真正刪除(輕量刪除執行完是邏輯上刪除)
  2. 對 wide 類型分區友好,對于 compact 類型分區會產生較大的磁盤 IO
  3. 會修改分區在磁盤中的名稱,可能會影響備份

對于 mutation 是否為異步操作可以通過參數進行配置,只需將mutations_sync置為 true 即可

set mutations_sync = true;

至于其它的不足需要用戶結合實際場景進行取舍

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/37390.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/37390.shtml
英文地址,請注明出處:http://en.pswp.cn/news/37390.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

單鏈表相關操作(插入,刪除,查找)

通過上一節我們知道順序表的優點&#xff1a; 可隨機存儲&#xff08;O(1)&#xff09;&#xff1a;查找速度快 存儲密度高&#xff1a;每個結點只存放數據元素&#xff0c;而單鏈表除了存放數據元素之外&#xff0c;還需存儲指向下一個節點的指針 http://t.csdn.cn/p7OQf …

【2023年11月第四版教材】《第4章-信息系統管理(合集篇)》

第4章-信息系統管理之管理方法&#xff08;第四版新增章節&#xff09;&#xff08;第一部分&#xff09; 章節說明1 管理方法1.1 信息系統四個要素1.2 信息系統四大領域1.3 信息系統戰略三角1.4 信息系統架構轉換1.5 信息系統體系架構1.6 信息系統運行1.7 運行和監控1.8 管理和…

北郵鄧中亮:深度融合5G+北斗,實現高精準定位

如今&#xff0c;萬物互聯時代&#xff0c;物與物、物與人、人與人之間需要實現更多的互聯。在如此復雜多變的環境中&#xff0c;定位技術面臨著著更多挑戰和需求&#xff0c;需要不斷的創新和改進。唯有如此&#xff0c;才能滿足未來智能交通、無人駕駛和工業互聯網等領域的高…

kafka基本概念及操作

kafka介紹 Kafka是最初由Linkedin公司開發&#xff0c;是一個分布式、支持分區的&#xff08;partition&#xff09;、多副本的 &#xff08;replica&#xff09;&#xff0c;基于zookeeper協調的分布式消息系統&#xff0c;它的最大的特性就是可以實時的處理大量數據以滿足各…

【LeetCode】242 . 有效的字母異位詞

242 . 有效的字母異位詞&#xff08;簡單&#xff09; 方法&#xff1a;哈希表 思路 首先判斷兩個字符串長度是否相等&#xff0c;不相等直接返回 false&#xff1b;接下來設置一個長度為26 的哈希表&#xff0c;分別對應26個小寫字母&#xff1b;遍歷兩個字符串&#xff0c;…

Go語言工程實踐之測試與Gin項目實踐

Go 語言并發編程 及 進階與依賴管理_軟工菜雞的博客-CSDN博客 03 測試 回歸測試一般是QA(質量保證)同學手動通過終端回歸一些固定的主流程場景 集成測試是對系統功能維度做測試驗證,通過服務暴露的某個接口,進行自動化測試 而單元測試開發階段&#xff0c;開發者對單獨的函數…

day-21 代碼隨想錄算法訓練營(19)二叉樹part07

530.二叉搜索樹的最小絕對差 思路一&#xff1a;二叉搜索樹的中序遍歷必為升序數組&#xff0c;加入數組后計算相鄰兩個數差值&#xff0c;即可求出最小絕對差 思路二&#xff1a;同樣的思路&#xff0c;中序遍歷&#xff0c;直接使用指針記錄上一個節點&#xff0c;同時更新…

KAFKA第二課之生產者(面試重點)

生產者學習 1.1 生產者消息發送流程 在消息發送的過程中&#xff0c;涉及到了兩個線程——main線程和Sender線程。在main線程中創建了一個雙端隊列RecordAccumulator。main線程將消息發送給RecordAccumulator&#xff0c;Sender線程不斷從RecordAccumulator中拉取消息發送到K…

03-基礎入門-搭建安全拓展

基礎入門-搭建安全拓展 1、涉及的知識點2、常見的問題3、web權限的設置4、演示案例-環境搭建&#xff08;1&#xff09;PHPinfo&#xff08;2&#xff09;wordpress&#xff08;3&#xff09;win7虛擬機上使用iis搭建網站&#xff08;4&#xff09;Windows Server 2003配置WEB站…

C#應用處理傳入參數 - 開源研究系列文章

今天介紹關于C#的程序傳入參數的處理例子。 程序的傳入參數應用比較普遍&#xff0c;特別是一個隨操作系統啟動的程序&#xff0c;需要設置程序啟動的時候不顯示主窗體&#xff0c;而是在后臺運行&#xff0c;于是就有了傳入參數問題&#xff0c;比如傳入/h或者/min等等。所以此…

YOLO v8目標跟蹤詳細解讀(二)

上一篇&#xff0c;結合代碼&#xff0c;我們詳細的介紹了YOLOV8目標跟蹤的Pipeline。大家應該對跟蹤的流程有了大致的了解&#xff0c;下面我們將對跟蹤中出現的卡爾曼濾波進行解讀。 1.卡爾曼濾波器介紹 卡爾曼濾波&#xff08;kalman Filtering&#xff09;是一種利用線性…

歐拉OS 使用 CentOS 7 yum repo

一、下載CentOS的repo的yum文件 任何基于CentOS的yum的repo 的url是這樣的&#xff1a; 但歐拉OS輸出這個變量為&#xff1a;openEuler 20.03 (LTS-SP3) 那明顯歐拉想要使用這個yum的url找不到這個版本&#xff0c; 所以直接講這個變量替換為 7, Centos 7的7 然后執行&…

wget 詳解

wget 詳解 wget 詳解基本用法&#xff1a;命令參數&#xff1a;遞歸下載&#xff1a;斷點續傳&#xff1a;限速下載&#xff1a;后臺下載&#xff1a; 示例 wget 詳解 wget&#xff08;Web Get&#xff09;是一個用于從網絡上下載文件的命令行工具&#xff0c;常用于在 Linux …

從零實戰SLAM-第七課(多視角幾何)

在七月算法報的班&#xff0c;老師講的蠻好。好記性不如爛筆頭&#xff0c;關鍵內容還是記錄一下吧&#xff0c;課程入口&#xff0c;感興趣的同學可以學習一下。 --------------------------------------------------------------------------------------------------------…

整型int溢出引起的crash

線上系統發生了crash&#xff0c;后發現是整型溢出。 1、初始化函數的偽代碼&#xff1a; init_mem(int count, int size){for(int i0; i<count; i)mem_list[i] i*size; # 溢出發生的地方} 2、問題分析&#xff1a; 原有的變量 i、size 為有符號的int類型&#xff0c;i…

設計模式--策略模式

目錄 一.場景 1.1場景 2.2 何時使用 2.3個人理解 二. 業務場景練習 2.1業務: 2.2具體實現 2.3思路 三.總結 3.1策略模式的特點&#xff1a; 3.2策略模式優點 3.3策略模式缺點 一.場景 1.1場景 許多相關的類僅僅是行為有異&#xff0c;也就是說業務代碼需要根據場景不…

Android數字價格變化的動畫效果的簡單實現

原理&#xff1a;使用ValueAnimator屬性動畫類實現&#xff0c;它通過值的改變手動設置對象的屬性值來實現動畫效果。直接貼代碼&#xff1a; public static void doNumberAnim(TextView tvPrice, float startNumber, float endNumber) {ValueAnimator animator ValueAnimato…

C語言中的 RSA加密和解密算法: 深度探索與實現

C語言中的 RSA加密和解密算法: 深度探索與實現 RSA加密算法是一種非對稱加密算法&#xff0c;即公開密鑰加密&#xff0c;私有密鑰解密。在公開密鑰加密和私有密鑰解密的過程中&#xff0c;密鑰是不同的&#xff0c;這是與其他加密算法的主要區別。RSA算法的安全性依賴于大數分…

ssm+mybatis無法給帶有下劃線屬性賦值問題

原因&#xff1a;mybaitis根據配置&#xff0c;將有下劃線的字段名改為了駝峰格式。 具體見&#xff1a;ssmmybatis無法給帶有下劃線屬性賦值問題&#xff0c;無法獲取數據庫帶下劃線的字段值 - 開發者博客 解決方式&#xff1a; 直接將實體類中的下劃線去掉返回值使用resul…

歸并排序 與 計數排序

目錄 1.歸并排序 1.1 遞歸實現歸并排序&#xff1a; 1.2 非遞歸實現歸并排序 1.3 歸并排序的特性總結: 1.4 外部排序 2.計數排序 2.1 操作步驟: 2.2 計數排序的特性總結: 3. 7種常見比較排序比較 1.歸并排序 基本思想: 歸并排序(MERGE-SORT)是建立在歸并操作上的一種…