Clickhouse表引擎的总结

官方文档https://clickhouse.com/docs/en/engines/table-engines

MergerTree引擎家族,只要带MergerTree的就是
MergerTree
ReplicatedMergeTree
ReplicatedAggregatingMergeTree
ReplicatedReplacingMergeTree
ReplicatedSummingMergeTree
ReplacingMergeTree
SummingMergeTree
AggregatingMergeTree

其他引擎
Distributed
Merge
MaterializedView

最佳实践:
4节点的集群,1-2节点为分片1,且1-2互为对方副本,3-4节点为分片2,且3-4互为对方副本,集群配置文件中的子配置项分片权重的配置不用配置会直接使用默认值1即数据平分到2个现有分片,集群配置文件中的子配置项<internal_replication></internal_replication>数据是否同时写入多个副本的配置配置为true即写操作只选一个正常的副本写入数据然后数据的复制工作交给实际需要写入数据的表本身而不是分布式表。
创建Distributed分布式表其中该分布式表使用ReplicatedMergeTree引擎的表作为子表,分布式表不再需要配置sharding_key,数据插入Distributed分布式表后,此时在4个节点上查看到的分布式表数据都是一样的
创建Distributed分布式表其中该分布式表使用ReplicatedMergeTree引擎的表作为子表,分布式表不再需要配置sharding_key,数据插入各个节点的ReplicatedMergeTree引擎子表,此时在4个节点上查看到的分布式表数据是一样的

创建Distributed分布式表其中该分布式表使用MergeTree引擎的表作为子表,数据库插入Distributed分布式表或各个节点的MergeTree引擎子表后,在4个节上看到的分布式表数据不一致,且经常错乱无章

仅仅使用ENGINE = MergeTree()表引擎的话,Clickhouse缺少了两个功能————数据高可用(HA)和横向扩展。HA的目的是为了如果有一个数据副本丢失或者损坏不至于完全丢失数据,至于横向扩展自然是为了提高数据存储能力了。数据高可用(HA)的实现需要使用ReplicatedMergeTree表引擎,横向扩展的实现需要使用Distributed表引擎,Clickhouse github上有一段总结
if your data is too big to fit/ to process on one server - use sharding
to balance the load between replicas and to combine the result of selects from different shards - use Distributed table.

MergeTree表引擎
https://clickhouse.com/docs/zh/engines/table-engines/mergetree-family/mergetree

ENGINE = MergeTree()括号里面没有参数,所以不受宏配置macros的影响。但是ENGINE = MergeTree()有setting选项enable_mixed_granularity_parts,如果您的表里有很大的行,可以开启enable_mixed_granularity_parts这项配置来提升SELECT查询的性能。

MergeTree 系列的引擎被设计用于插入极大量的数据到一张表当中。数据可以以数据片段的形式一个接着一个的快速写入,数据片段在后台按照一定的规则进行合并。相比在插入时不断修改(重写)已存储的数据,这种策略会高效很多。其实就是批量数据快速插入和后台并发处理的两大优势。

Clickhouse集群4节点,在节点1执行如下

CREATE TABLE lukestest1.table_mergetree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = MergeTree() ORDER BY id;
insert into lukestest1.table_mergetree (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17') ('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17'),('12','2020-11-17');CREATE TABLE lukestest2.table_mergetree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = MergeTree() ORDER BY id;
insert into lukestest2.table_mergetree (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17') ('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17'),('12','2020-11-17');

以下结果只在节点1可以查询到结果12行,其他节点查询的结果都是0行

select * from lukestest1.table_mergetree;
select * from lukestest2.table_mergetree;
`create table lukestest1.table1 (country String, province String, value String ) engine=MergeTree() partition by (country, province) order by value;` 

–不指定cluster cluster_2shared2replicas表示只在当前节点创建表

insert into lukestest1.table1 (country, province, value) values ('China','JXi','good'), ('China','SXi','good'), ('China','SD','good'), ('China','JS','good');

–engine=MergeTree()只在当前节点insert数据
在节点1创建表,发现节点2,3,4都没有这张表,数据也只在节点1有

create table lukestest1.table2 on cluster cluster_2shared2replicas (country String, province String, value String ) engine=MergeTree() partition by (country, province) order by value; 

指定cluster cluster_2shared2replicas表示只在所有节点创建表

insert into lukestest1.table2 (country, province, value) values ('China','JXi','good'), ('China','SXi','good'), ('China','SD','good'), ('China','JS','good');

–engine=MergeTree()只在当前节点insert数据
在节点1创建表并插入数据,发现节点2,3,4有这张表,但是只有节点1有数据,节点2,3,4都没有数据

ReplicatedMergeTree表引擎
https://clickhouse.com/docs/zh/engines/table-engines/mergetree-family/replication

ENGINE = ReplicatedMergeTree()括号里面没有cluster但是有shard和replica参数,所以受宏配置macros的影响

Clickhouse只有MergeTree 系列里的表可支持副本, 其中可以是Summing,Replacing,Aggregating等名称的ReplicatedMergeTree的表就是MergeTree系列里面支持副本的引擎,ReplicatedMergeTreez只是其中可支持数据副本的一种引擎,副本是表级别的,不是整个服务器级的。所以,服务器里可以同时有复制表和非复制表。副本不依赖分片。每个分片有它自己的独立副本。ClickHouse 使用 Apache ZooKeeper 存储副本的元信息。请使用 ZooKeeper 3.4.5 或更高版本。要使用副本,需在 Zookeeper 服务器的配置部分设置相应参数。

Replicated*MergeTree 参数和建表语句
zoo_path — ZooKeeper 中该表的路径。–见下例中的/clickhouse/tables/{layer}-{shard}/table_name
replica_name — ZooKeeper 中的该表的副本名称。 --见下例中的{replica}
示例:

CREATE TABLE table_name
(EventDate DateTime,CounterID UInt32,UserID UInt32
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/table_name', '{replica}')
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)

/clickhouse/tables/ 是公共前缀,我们推荐使用这个。
{layer}-{shard} 是分片标识部分。
table_name 是该表在 ZooKeeper 中的名称。
{replica}副本名称用于标识同一个表分片的不同副本。
你也可以显式指定这些参数,而不是使用宏macros替换。使用大型集群时,我们建议使用宏替换,因为它可以降低出错的可能性。我个人环境中的宏macros配置如下(1-2节点配置01,3-4节点配置02,的话每个节点配置为自己的服务器名即可比如DAILACHDBUD001配置为DAILACHDBUD001)。

<macros><shard>01</shard><replica>DAILACHDBUD001</replica>
</macros>

Clickhouse集群4节点,在节点1执行如下

CREATE TABLE lukestest1.table_ReplicatedMergeTree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
insert into lukestest1.table_ReplicatedMergeTree (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17') ('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17'),('12','2020-11-17');CREATE TABLE lukestest2.table_ReplicatedMergeTree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
insert into lukestest2.table_ReplicatedMergeTree (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17') ('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17'),('12','2020-11-17');

以下结果在节点1和节点2可以查询到结果12行,节点3和节点4查询的结果都是0行,原因是因为节点1和节点2是同一个shard分片下面的replica,也就是节点1和节点2互为对方的副本

select * from lukestest1.table_ReplicatedMergeTree;
select * from lukestest2.table_ReplicatedMergeTree;

ReplicatedReplacingMergeTree表引擎
Clickhouse集群4节点,在节点1执行如下

CREATE TABLE lukestest1.table_ReplicatedReplacingMergeTree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedReplacingMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
insert into lukestest1.table_ReplicatedReplacingMergeTree (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17') ('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17'),('12','2020-11-17');CREATE TABLE lukestest2.table_ReplicatedReplacingMergeTree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedReplacingMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
insert into lukestest2.table_ReplicatedReplacingMergeTree (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17') ('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17'),('12','2020-11-17');

以下结果在节点1和节点2可以查询到结果12行,节点3和节点4查询的结果都是0行,原因是因为节点1和节点2是同一个shard分片下面的replica,也就是节点1和节点2互为对方的副本

select * from lukestest1.table_ReplicatedReplacingMergeTree;
select * from lukestest2.table_ReplicatedReplacingMergeTree;

ReplicatedAggregatingMergeTree表引擎
Clickhouse集群4节点,在节点1执行如下

CREATE TABLE lukestest1.table_ReplicatedAggregatingMergeTree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedAggregatingMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
insert into lukestest1.table_ReplicatedAggregatingMergeTree (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17') ('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17'),('12','2020-11-17');CREATE TABLE lukestest2.table_ReplicatedAggregatingMergeTree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedAggregatingMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
insert into lukestest2.table_ReplicatedAggregatingMergeTree (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17') ('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17'),('12','2020-11-17');

以下结果在节点1和节点2可以查询到结果12行,节点3和节点4查询的结果都是0行,原因是因为节点1和节点2是同一个shard分片下面的replica,也就是节点1和节点2互为对方的副本

select * from lukestest1.table_ReplicatedAggregatingMergeTree;
select * from lukestest2.table_ReplicatedAggregatingMergeTree;

Distributed引擎
https://clickhouse.com/docs/zh/engines/table-engines/special/distributed

ENGINE = Distributed()括号里面有cluster和sharding_key参数,不受宏配置macros的影响,但是受集群配置中指定集群的影响,比如个人案例受cluster_2shared2replicas这个指定集群的影响

ENGINE = Distributed()分布式表引擎本身不存储数据,但可以在多个服务器上进行分布式查询,实际上ENGINE = Distributed()分布式表是一种view,映射到ClickHouse集群的本地表。读是自动并行的。读取时,远程服务器表的索引(如果有的话)会被使用。
通过分布式引擎可以像使用本地服务器一样使用集群。但是,集群不是自动扩展的:你必须编写集群配置到集群环境下的所有服务器的配置文件中。

查询一个Distributed表时,SELECT查询被发送到所有的分片,不管数据是如何分布在分片上的(它们可以完全随机分布)。当您添加一个新分片时,您不必将旧数据传输到它。相反,您可以使用更重的权重向其写入新数据——数据的分布会稍微不均匀,但查询将正确有效地工作。

向集群写数据的方法有两种:
一,自已指定要将哪些数据写入哪些服务器,并直接在每个分片上执行写入。换句话说,在ENGINE = Distributed()分布式表上执行select,在数据表(ENGINE = Distributed()分布式表的子表)上INSERT。 这是最灵活的解决方案,也是最佳解决方案,因为数据可以完全独立地写入不同的分片。
二,在ENGINE = Distributed()分布式表上执行INSERT。在这种情况下,分布式表会跨服务器分发插入数据。 为了写入分布式表,必须要配置分片键sharding_key。当然,如果只有一个分片,则写操作在没有分片键的情况下也能工作,因为这种情况下分片键没有意义。每个分片的写入权重可以在集群配置文件中的子配置项中定义。默认情况下,权重等于1。数据依据分片权重按比例分发到分片上。例如,如果有两个分片,第一个分片的权重是9,而第二个分片的权重是10,则发送9/19的行到第一个分片,10/19的行到第二个分片。分片数据该写入一个副本还是写入所有副本,可在集群配置文件中的子配置项<internal_replication></internal_replication>中定义。此参数设置为true时,写操作只选一个正常的副本写入数据。如果分布式表的子表是复制表(*ReplicaMergeTree),请使用此方案。换句话说,这其实是把数据的复制工作交给实际需要写入数据的表本身而不是分布式表。若此参数设置为false(默认值),写操作会将数据写入所有副本。

备注:在创建ENGINE = Distributed()表时指定sharding_key或不指定sharding_key或随意指定sharding_key值,并不会影响各个节点执行select count(*) from ENGINE = Distributed()表的总行数

Clickhouse集群4节点,在节点1执行如下

CREATE TABLE lukestest1.table_Distributed_mergetree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest1, table_mergetree,rand());
insert into lukestest1.table_Distributed_mergetree (id ,create_time) values ('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17'),('24','2020-11-17'),('25','2020-11-17'),('26','2020-11-17'),('27','2020-11-17'),('28','2020-11-17');CREATE TABLE lukestest1.table_Distributed_ReplicatedMergeTree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest1, table_ReplicatedMergeTree,rand());
insert into lukestest1.table_Distributed_ReplicatedMergeTree (id ,create_time) values ('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17'),('24','2020-11-17'),('25','2020-11-17'),('26','2020-11-17'),('27','2020-11-17'),('28','2020-11-17');CREATE TABLE lukestest2.table_Distributed_mergetree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest2, table_mergetree,rand());
insert into lukestest2.table_Distributed_mergetree (id ,create_time) values ('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17'),('24','2020-11-17'),('25','2020-11-17'),('26','2020-11-17'),('27','2020-11-17'),('28','2020-11-17');CREATE TABLE lukestest2.table_Distributed_ReplicatedMergeTree ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest2, table_ReplicatedMergeTree,rand());
insert into lukestest2.table_Distributed_ReplicatedMergeTree (id ,create_time) values ('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17'),('24','2020-11-17'),('25','2020-11-17'),('26','2020-11-17'),('27','2020-11-17'),('28','2020-11-17');

以下结果在节点1可以分别查询到14(20),20,18(20),20行
以下结果在节点2可以分别查询到0(6),20,0(2),20行
以下结果在节点3可以分别查询到6(20),20,0(18),20行
以下结果在节点4可以分别查询到0(14),20,2(20),20行

select * from lukestest1.table_Distributed_mergetree;
select * from lukestest1.table_Distributed_ReplicatedMergeTree;
select * from lukestest2.table_Distributed_mergetree;
select * from lukestest2.table_Distributed_ReplicatedMergeTree;

以下结果在节点1可以分别查询到14,15,18,18行
以下结果在节点2可以分别查询到0,15,0,18行
以下结果在节点3可以分别查询到6,5,0,2行
以下结果在节点4可以分别查询到0,5,2,2行

select * from lukestest1.table_mergetree; 
select * from lukestest1.table_ReplicatedMergeTree; 
select * from lukestest2.table_mergetree; 
select * from lukestest2.table_ReplicatedMergeTree; 

已有数据表的情况下,即当Distributed表指向当前服务器上已经存在的一个表时,需要使用CREATE TABLE Distributed_table_name ON CLUSTER CLUSTER_NAME AS existed_table_name ENGINE = Distributed()

Clickhouse集群4节点,已有数据表是MergeTree()引擎,执行如下情况

CREATE TABLE lukestest1.tableMergeTree1 ON CLUSTER cluster_2shared2replicas (id String,create_time datetime) ENGINE = MergeTree() ORDER BY id;
--节点1执行
insert into lukestest1.tableMergeTree1 (id ,create_time) values ('11','2020-11-17'),('12','2020-11-17'),('13','2020-11-17'),('14','2020-11-17');
--节点1执行
insert into lukestest1.tableMergeTree1 (id ,create_time) values ('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17'),('24','2020-11-17');
--节点2执行
insert into lukestest1.tableMergeTree1 (id ,create_time) values ('31','2020-11-17'),('32','2020-11-17'),('33','2020-11-17'),('34','2020-11-17');
--节点3执行
insert into lukestest1.tableMergeTree1 (id ,create_time) values ('41','2020-11-17'),('42','2020-11-17'),('43','2020-11-17'),('44','2020-11-17');
--节点4执行CREATE TABLE lukestest1.tableMergeTree1_view ON CLUSTER cluster_2shared2replicas AS lukestest1.tableMergeTree1 ENGINE = Distributed(cluster_2shared2replicas, lukestest1, tableMergeTree1);
--节点1执行
CREATE TABLE lukestest1.tableMergeTree1_view01 ON CLUSTER cluster_2shared2replicas AS lukestest1.tableMergeTree1 ENGINE = Distributed(cluster_2shared2replicas, lukestest1, tableMergeTree1,01);
--节点1执行
CREATE TABLE lukestest1.tableMergeTree1_view02 ON CLUSTER cluster_2shared2replicas AS lukestest1.tableMergeTree1 ENGINE = Distributed(cluster_2shared2replicas, lukestest1, tableMergeTree1,02);
--节点1执行
CREATE TABLE lukestest1.tableMergeTree1_view_rand ON CLUSTER cluster_2shared2replicas AS lukestest1.tableMergeTree1 ENGINE = Distributed(cluster_2shared2replicas, lukestest1, tableMergeTree1,rand());
--节点1执行

以下结果在节点1可以分别查到4,8行数据库,分别是看到只有自己的11-14,看到只有11-14和31-34的8行
以下结果在节点2可以分别查到4,8行数据库,分别是看到只有自己的21-24,看到只有21-24和41-44的8行
以下结果在节点3可以分别查到4,8行数据库,分别是看到只有自己的31-34,看到只有11-14和31-34的8行
以下结果在节点4可以分别查到4,8行数据库,分别是看到只有自己的41-44,看到只有21-24和41-44的8行

select * from lukestest1.tableMergeTree1;
select * from lukestest1.tableMergeTree1_view;

备注:也就是1-3节点看到的Distributed数据一致,2-4节点看到的Distributed数据一致,而且不管Distributed中shard_key是如何设置,每个节点看到Distributed引擎的表的数据都只有8行而不是16行。Distributed中shard_key设置可以为如下Distributed(cluster_2shared2replicas, lukestest1, tableMergeTree1),Distributed(cluster_2shared2replicas, lukestest1, tableMergeTree1,01),Distributed(cluster_2shared2replicas, lukestest1, tableMergeTree1,02),Distributed(cluster_2shared2replicas, lukestest1, tableMergeTree1,rand())

Clickhouse集群4节点,已有数据表是ReplicatedMergeTree()引擎,执行如下情况

CREATE TABLE lukestest1.table_ReplicatedMergeTree1 ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
--节点1执行insert into lukestest1.table_ReplicatedMergeTree1 (id ,create_time) values ('11','2020-11-17'),('12','2020-11-17'),('13','2020-11-17'),('14','2020-11-17');
--节点1执行
insert into lukestest1.table_ReplicatedMergeTree1 (id ,create_time) values ('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17'),('24','2020-11-17');
--节点2执行
insert into lukestest1.table_ReplicatedMergeTree1 (id ,create_time) values ('31','2020-11-17'),('32','2020-11-17'),('33','2020-11-17'),('34','2020-11-17');
--节点3执行
insert into lukestest1.table_ReplicatedMergeTree1 (id ,create_time) values ('41','2020-11-17'),('42','2020-11-17'),('43','2020-11-17'),('44','2020-11-17');
--节点4执行CREATE TABLE lukestest1.table_ReplicatedMergeTree1_view ON CLUSTER cluster_2shared2replicas AS lukestest1.table_ReplicatedMergeTree1 ENGINE = Distributed(cluster_2shared2replicas, lukestest1, table_ReplicatedMergeTree1,rand());
--节点1执行

以下结果在节点1可以分别查到8,16行数据库,分别是看到只有自己的11-14和节点2的21-24,看到所有16行
以下结果在节点2可以分别查到8,16行数据库,分别是看到只有自己的21-24和节点1的11-14,看到所有16行
以下结果在节点3可以分别查到8,16行数据库,分别是看到只有自己的31-34和节点4的41-44,看到所有16行
以下结果在节点4可以分别查到8,16行数据库,分别是看到只有自己的41-44和节点3的31-44,看到所有16行

select * from lukestest1.table_ReplicatedMergeTree1;
select * from lukestest1.table_ReplicatedMergeTree1_view;

创建Distributed分布式表其中该分布式表使用ReplicatedMergeTree引擎的表作为子表,数据插入各个节点的ReplicatedMergeTree引擎子表,此时在4个节点上查看到的分布式表数据是一致的,如下实验

CREATE TABLE lukestest1.table_ReplicatedMergeTree2 ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
--节点1执行
CREATE TABLE lukestest1.table_Distributed_ReplicatedMergeTree2 ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest1, table_ReplicatedMergeTree2,rand());
--节点1执行
insert into lukestest1.table_ReplicatedMergeTree2 (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17');
--节点1执行
insert into lukestest1.table_ReplicatedMergeTree2 (id ,create_time) values ('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17'),('24','2020-11-17');
--节点2执行
insert into lukestest1.table_ReplicatedMergeTree2 (id ,create_time) values ('31','2020-11-17'),('32','2020-11-17'),('33','2020-11-17'),('34','2020-11-17');
--节点3执行
insert into lukestest1.table_ReplicatedMergeTree2 (id ,create_time) values ('41','2020-11-17'),('42','2020-11-17'),('43','2020-11-17'),('44','2020-11-17');
--节点4执行

以下结果在节点1可以分别查询到8,16行,8行数据与节点2数据一致,16行数据和其他节点一致
以下结果在节点2可以分别查询到8,16行,8行数据与节点1数据一致,16行数据和其他节点一致
以下结果在节点3可以分别查询到8,16行,8行数据与节点4数据一致,16行数据和其他节点一致
以下结果在节点4可以分别查询到8,16行,8行数据与节点3数据一致,16行数据和其他节点一致

select * from lukestest1.table_ReplicatedMergeTree2;
select * from lukestest1.table_Distributed_ReplicatedMergeTree2;

ENGINE = Distributed的表不能做update操作

ALTER TABLE lukestest1.tableMergeTree1_view ON CLUSTER cluster_2shared2replicas UPDATE id = 11 where id=15;
ALTER TABLE lukestest1.tableMergeTree1_view ON CLUSTER cluster_2shared2replicas UPDATE create_time = '2022-11-17' where create_time = '2020-11-17';
两者都报错DB::Exception: Table engine Distributed doesn't support mutations. (NOT_IMPLEMENTED) (version 23.4.2.11 (official build))

ENGINE = MergeTree()的表不能对ORDER BY的字段做update

ALTER TABLE lukestest1.tableMergeTree1 ON CLUSTER cluster_2shared2replicas UPDATE id = 101 where id=11;--报错DB::Exception: Cannot UPDATE key column `id`. (CANNOT_UPDATE_COLUMN) (version 23.4.2.11 (official build))
ALTER TABLE lukestest1.tableMergeTree1 ON CLUSTER cluster_2shared2replicas UPDATE create_time = '2022-11-17' where create_time = '2020-11-17';
正常执行,不再报错

创建ENGINE = Distributed分布式表,分布式表名和子表名(子表就是Distributed(cluster_2shared2replicas, lukestest1, table_distributed1,rand())里面的表名)不能一致

CREATE TABLE lukestest1.table_distributed1 ON CLUSTER cluster_2shared2replicas (id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest1, table_distributed1,rand());
报错DB::Exception: Distributed table table_distributed1 looks at itself. (INFINITE_LOOP) (version 23.4.2.11 (official build)). (INFINITE_LOOP)

创建ENGINE = Distributed分布式表,分布式表名lukestest1.table_distributed1,子表名lukestest1.table_MergeTree1,但是子表名不存在,此时分布式表可以创建成功,但是插入数据会报错子表不存在,必须先在各个节点创建好子表后再插入数据

CREATE TABLE lukestest1.table_distributed1 ON CLUSTER cluster_2shared2replicas (id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest1, table_MergeTree1,rand());
--表正常创建,不再报错
insert into lukestest1.table_distributed1 (id ,create_time) values ('11','2020-11-17'),('12','2020-11-17'),('13','2020-11-17'),('14','2020-11-17');报错DB::Exception: Table lukestest1.table_MergeTree1 doesn't exist. (UNKNOWN_TABLE)

额外的实验1,ENGINE = Distributed和ENGINE = MergeTree(),数据只插入节点1和节点2,或数据只插入节点1和节点3,或数据只插入节点1

CREATE TABLE lukestest1.table_mergetree3 ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = MergeTree() ORDER BY id;
--节点1执行
CREATE TABLE lukestest1.table_Distributed_mergetree3 ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest1, table_mergetree3,rand());
--节点1执行insert into lukestest1.table_mergetree3 (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17');
--节点1执行
insert into lukestest1.table_mergetree3 (id ,create_time) values ('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17'),('24','2020-11-17');
--节点2执行

以下结果在节点1可以分别查询到4,4行,第一个4行数据与节点2数据不一致,第二个4行数据与节点2数据不一致,节点1的数据全是自己的1,2,3,4
以下结果在节点2可以分别查询到4,4行,第一个4行数据与节点1数据不一致,第二个4行数据和节点1数据不一致,节点1的数据全是自己的21,22,23,24
以下结果在节点3可以分别查询到0,4行,4行数据与节点1一致,是1,2,3,4
以下结果在节点4可以分别查询到0,4行,4行数据与节点2一致,是21,22,23,24

select * from lukestest1.table_mergetree3;
select * from lukestest1.table_Distributed_mergetree3;
insert into lukestest1.table_mergetree3 (id ,create_time) values ('11','2020-11-17'),('12','2020-11-17'),('13','2020-11-17'),('14','2020-11-17');
--节点1执行
insert into lukestest1.table_mergetree3 (id ,create_time) values ('31','2020-11-17'),('32','2020-11-17'),('33','2020-11-17'),('34','2020-11-17');
--节点3执行

以下结果在节点1可以分别查询到8,12行,8行数据是1,2,3,4,11,12,13,14,12行数据是1,2,3,4,11,12,13,14,31,32,33,34
以下结果在节点2可以分别查询到4,8行,4行数据是21,22,23,24,8行数据是21,22,23,24,31,32,33,34
以下结果在节点3可以分别查询到4,8行,4行数据是31,32,33,34,8行数据是21,22,23,24,31,32,33,34
以下结果在节点4可以分别查询到0,8或4行,4行和8行不定期出现一会出现4行一会出现8行,4行数据是21,22,23,24,8行数据是1,2,3,4,11,12,13,14

select * from lukestest1.table_mergetree3;
select * from lukestest1.table_Distributed_mergetree3;
insert into lukestest1.table_mergetree3 (id ,create_time) values ('111','2020-11-17'),('112','2020-11-17'),('113','2020-11-17'),('114','2020-11-17');
--节点1执行

以下结果在节点1可以分别查询到12,12或16行,12行是1,2,3,4,11,12,13,14,111,112,113,114,分布式表有时出现12行和有时出现16行
以下结果在节点2可以分别查询到4,4或8行,4行数据是21,22,23,24,分布式表有时出现4行和有时出现8行
以下结果在节点3可以分别查询到4,8或16行,4行数据是31,32,33,34,分布式表有时出现8行和有时出现16行
以下结果在节点4可以分别查询到0,4或12行,分布式表有时出现4行和有时出现12行

select * from lukestest1.table_mergetree3;
select * from lukestest1.table_Distributed_mergetree3;

额外的实验2,ENGINE = Distributed和ENGINE = ReplicatedMergeTree(),数据只插入节点1和节点2,或数据只插入节点1和节点3,或数据只插入节点1

数据只插入节点1和节点2

CREATE TABLE lukestest1.table_ReplicatedMergeTree3 ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
--节点1执行
CREATE TABLE lukestest1.table_Distributed_ReplicatedMergeTree3 ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest1, table_ReplicatedMergeTree3,rand());
--节点1执行
insert into lukestest1.table_ReplicatedMergeTree3 (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17');
--节点1执行
insert into lukestest1.table_ReplicatedMergeTree3 (id ,create_time) values ('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17'),('24','2020-11-17');
--节点2执行

以下结果在节点1可以分别查询到8,8行,第一个8行数据与节点2数据一致,第二个8行数据和其他节点一致
以下结果在节点2可以分别查询到8,8行,第一个8行数据与节点1数据一致,第二个8行数据和其他节点一致
以下结果在节点3可以分别查询到0,8行,8行数据和其他节点一致
以下结果在节点4可以分别查询到0,8行,8行数据和其他节点一致

select * from lukestest1.table_ReplicatedMergeTree3;
select * from lukestest1.table_Distributed_ReplicatedMergeTree3;

数据只插入节点1和节点3

insert into lukestest1.table_ReplicatedMergeTree3 (id ,create_time) values ('11','2020-11-17'),('12','2020-11-17'),('13','2020-11-17'),('14','2020-11-17');
--节点1执行
insert into lukestest1.table_ReplicatedMergeTree3 (id ,create_time) values ('31','2020-11-17'),('32','2020-11-17'),('33','2020-11-17'),('34','2020-11-17');
--节点3执行

以下结果在节点1可以分别查询到12,16行,12行数据与节点2数据一致,16行数据和其他节点一致
以下结果在节点2可以分别查询到12,16行,12行数据与节点1数据一致,16行数据和其他节点一致
以下结果在节点3可以分别查询到4,16行,4行数据与节点4数据一致,16行数据和其他节点一致
以下结果在节点4可以分别查询到4,16行,4行数据与节点3数据一致,16行数据和其他节点一致

select * from lukestest1.table_ReplicatedMergeTree3;
select * from lukestest1.table_Distributed_ReplicatedMergeTree3;

数据只插入节点1

insert into lukestest1.table_ReplicatedMergeTree3 (id ,create_time) values ('111','2020-11-17'),('112','2020-11-17'),('113','2020-11-17'),('114','2020-11-17');
--节点1执行

以下结果在节点1可以分别查询到16,20行,16行数据与节点2数据一致,20行数据和其他节点一致
以下结果在节点2可以分别查询到16,20行,16行数据与节点1数据一致,20行数据和其他节点一致
以下结果在节点3可以分别查询到4,20行,4行数据与节点4数据一致,20行数据和其他节点一致
以下结果在节点4可以分别查询到4,20行,4行数据与节点3数据一致,20行数据和其他节点一致

select * from lukestest1.table_ReplicatedMergeTree3;
select * from lukestest1.table_Distributed_ReplicatedMergeTree3;

额外的实验3
节点1执行

CREATE TABLE lukestest1.table41 ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
CREATE TABLE lukestest1.table41_all ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = Distributed(cluster_2shared2replicas, lukestest1, table41,rand());
insert into lukestest1.table41_all (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17'),('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17');

table4_all在四个节点的数据一致
table4在节点1、2数据一致,table4在节点3、4一致,shard1的数据落在节点1、2上,shard2的数据落在节点3、4上,节点1(或节点2)+节点3(或节点4)的数据刚好等于table4_all

额外的实验4
节点1执行

CREATE TABLE lukestest1.table51 ON CLUSTER cluster_2shared2replicas(id String,create_time datetime) ENGINE = ReplicatedMergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
insert into lukestest1.table51 (id ,create_time) values ('1','2020-11-17'),('2','2020-11-17'),('3','2020-11-17'),('4','2020-11-17'),('5','2020-11-17'),('6','2020-11-17'),('7','2020-11-17'),('8','2020-11-17'),('9','2020-11-17'),('10','2020-11-17'),('11','2020-11-17');

只有节点1、2有数据且数据一致且是全量数据,因为节点1和节点2互为对方副本,节点3、4该表存在但是都没有数据

Merge引擎
https://clickhouse.com/docs/zh/engines/table-engines/special/merge
Merge引擎跟MergeTree引擎不是同一个概念不要混淆两者, Merge引擎本身不存储数据,但可用于同时从任意多个其他的表中读取数据。 读是自动并行的,不支持写入。读取时,那些被真正读取到数据的表的索引(如果有的话)会被使用。
Merge 引擎的参数:一个数据库名和一个用于匹配表名的正则表达式。
示例:Merge(hits, ‘^WatchLog’)表示数据会从 hits 数据库中表名匹配正则 ‘^WatchLog’ 的表中读取。

CREATE TABLE lukestest1.table_mergetest1 (id String,create_time datetime) ENGINE = MergeTree() ORDER BY id;
insert into lukestest1.table_mergetest1 (id ,create_time) values ('1','2020-11-17'),('11','2020-11-17');CREATE TABLE lukestest1.table_mergetest12 (id String,create_time datetime) ENGINE = MergeTree() ORDER BY id;
insert into lukestest1.table_mergetest12 (id ,create_time) values ('12','2020-11-17'),('122','2020-11-17');CREATE TABLE lukestest1.table_mergetest23 (id String,create_time datetime) ENGINE = MergeTree() ORDER BY id;
insert into lukestest1.table_mergetest23 (id ,create_time) values ('23','2020-11-17'),('233','2020-11-17');CREATE TABLE lukestest1.table_mergetest1_Merge ENGINE=Merge(lukestest1, '^table_mergetest1');
select * from lukestest1.table_mergetest1_Merge;
--看到lukestest1.table_mergetest1和table_mergetest12两表的数据
CREATE TABLE lukestest1.table_mergetest2_Merge ENGINE=Merge(lukestest1, '^table_mergetest2');
select * from lukestest1.table_mergetest2_Merge;
--只看到lukestest1.table_mergetest23表的数据

MaterializedView 引擎
https://clickhouse.com/docs/zh/engines/table-engines/special/materializedview
https://clickhouse.com/docs/zh/sql-reference/statements/create/view
它需要使用一个不同的引擎来存储数据,这个引擎要在创建物化视图时指定。当从表中读取时,它就会使用该引擎。
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT …
创建不带TO [db].[table]的物化视图时,必须指定ENGINE – 用于存储数据的表引擎。
使用TO [db].[table] 创建物化视图时,不得使用POPULATE。
ClickHouse 中的物化视图更像是插入触发器。 如果视图查询中有一些聚合,则它仅应用于一批新插入的数据。 对源表现有数据的任何更改(如更新、删除、删除分区等)都不会更改物化视图。
物化视图是查询结果的持久化,可以理解为一张时刻在与计算的表,创建过程中用了一个特殊引擎。但是对更新、删除 操作支持并不好,更像是个插入触发器。
如果指定POPULATE,则在创建视图时将现有表数据插入到视图中,就像创建一个CREATE TABLE … AS SELECT …一样。 否则,查询仅包含创建视图后插入表中的数据。 我们不建议使用POPULATE,因为在创建视图期间插入表中的数据不会插入其中。

创建MaterializedView时必须指定engine,且这个engine的格式和创建表时一样,比如使用engine=MergeTree() 后面必须加order by 这些。

CREATE TABLE lukestest1.table_mergetest2 (id String,create_time datetime) ENGINE = MergeTree() ORDER BY id;
insert into lukestest1.table_mergetest2 (id ,create_time) values ('11','2020-11-17'),('12','2020-11-17'),('13','2020-11-17'),('21','2020-11-17'),('22','2020-11-17'),('23','2020-11-17');create materialized view lukestest1.table_mergetest2_materialized engine=MergeTree() POPULATE as select * from lukestest1.table_mergetest2 where id like '%2%';--报错DB::Exception: ORDER BY or PRIMARY KEY clause is missing. Consider using extended storage definition syntax with ORDER BY or PRIMARY KEY clause. With deprecated old syntax (highly not recommended) storage MergeTree requires 3 to 4 parameters:create materialized view lukestest1.table_mergetest2_materialized engine=MergeTree() order by id POPULATE as select * from lukestest1.table_mergetest2 where id like '%2%' ;
create materialized view lukestest1.table_mergetest2_materialized_nopopulate engine=MergeTree() order by id as select * from lukestest1.table_mergetest2 where id like '%2%';
select * from lukestest1.table_mergetest2_materialized;
--有四条数据
select * from lukestest1.table_mergetest2_materialized_nopopulate;
--没有数据
insert into lukestest1.table_mergetest2 (id ,create_time) values ('222','2020-11-17'),('223','2020-11-17'),('224','2020-11-17');
select * from lukestest1.table_mergetest2_materialized;
--有7条数据
select * from lukestest1.table_mergetest2_materialized_nopopulate;
--有三条数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/189036.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL的安装步骤教程以及基本操作--详细讲解

MySQL的安装过程以及使用 安装步骤安装MariaDB&#xff1a;配置&#xff1a;查看配置&#xff1a; 数据库的基本操作库的操作&#xff1a;数据类型数据库表的操作&#xff1a;表中数据的增删改查&#xff1a;表中数据的增删改查(进阶)&#xff1a;子集索引数据库事务&#xff1…

#zookeeper集群+kafka集群

kafka3.0之前是依赖于zookeeper的。 zookeeper是开源&#xff0c;分布式的架构。提供协调服务&#xff08;Apache项目&#xff09; 基于观察者模式涉及的分布式服务管理架构。 存储和管理数据。分布式节点上的服务接受观察者的注册。一旦分布式节点上的数据发生变化&#xf…

公共英语三级阅读理解一篇,附带答案

公共英语三级阅读理解 The food we eat seems to have profound effects on our health&#xff0e;Although science has made enormous steps in making food more fit to eat, it has, at the same time, made many foods unfit to eat&#xff0e;Some research has shown…

【快速见刊|投稿优惠】2024年机电一体与自动化技术国际学术会议(IACMAT 2024)

2024年机电一体与自动化技术国际学术会议(IACMAT 2024) 2024 International Academic Conference on Mechatronics and Automation Technology(IACMAT 2024) 一【会议简介】 2024年机电一体与自动化技术国际学术会议(IACMAT 2024)即将召开&#xff0c;它以“机电一体&#xff0…

2023年【安全员-B证】最新解析及安全员-B证免费试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全员-B证最新解析是安全生产模拟考试一点通生成的&#xff0c;安全员-B证证模拟考试题库是根据安全员-B证最新版教材汇编出安全员-B证仿真模拟考试。2023年【安全员-B证】最新解析及安全员-B证免费试题 1、【多选题…

用友U8 ERP和面粉行业专版系统接口集成方案

面粉加工行业面临着数据管理和业务流程自动化的挑战。众诚ERP系统和用友U8系统的数据集成是解决这一挑战的关键。 解决方案 轻易云平台提供了一套完善的数据同步和集成解决方案&#xff0c;包括以下几个方面&#xff1a; 基础资料同步&#xff1a;包括物料、客户、供应商、仓…

解决:AttributeError: ‘NoneType’ object has no attribute ‘shape’

解决&#xff1a;AttributeError: ‘NoneType’ object has no attribute ‘shape’ 文章目录 解决&#xff1a;AttributeError: NoneType object has no attribute shape背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结束了 背景 在使用之前的代码时&…

【数值计算方法(黄明游)】矩阵特征值与特征向量的计算(二):Jacobi 过关法(Jacobi 旋转法的改进)【理论到程序】

文章目录 一、Jacobi 旋转法1. 基本思想2. 注意事项 二、Jacobi 过关法1. 基本思想2. 注意事项 三、Python实现迭代过程&#xff08;调试&#xff09; 矩阵的特征值&#xff08;eigenvalue&#xff09;和特征向量&#xff08;eigenvector&#xff09;在很多应用中都具有重要的数…

Spring Task

Spring Task 是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 **定位&#xff1a;**定时任务框架 **作用&#xff1a;**定时自动执行某段Java代码 cron表达式 cron表达式其实就是一个字符串&#xff0c;通过cron表达式可以定义任务触…

c语言:模拟实现atoi函数

atoi函数的功能和用法&#xff1a; 主要功能&#xff1a;将字符串转换为整数。例如&#xff0c;将字符类型的“123”转换为整数123. #include <stdio.h> #include <stdlib.h>int main() {char str[] "123";int num atoi(str);printf("Converted …

【matlab程序】画海洋流场

【matlab程序】画海洋流场 clear;clc; file ( ‘0227.nc’); latncread(file,‘latitude’); lonncread(file,‘longitude’); uncread(file,‘water_u’); vncread(file,‘water_v’); [x,y]meshgrid(lon,lat); xx’; yy’; interval4; figure (1) set(gcf,‘color’,[1 1 1…

Period of an Infinite Binary Expansion(分析+欧拉)

传送阵&#xff1a;NEFU2022-Eulers totient function - Virtual Judge 思路&#xff1a; 对于一个小于1的数&#xff0c;化为二进制&#xff0c;找第一次进入循环的位置和最小循环周期。 我们设第一次进入循环的位置是i&#xff0c;第一次循环结束后&#xff0c;再次进入循…

3D云参观红色革命纪念馆允许更多人在线交流、体验

生活在和平年代的新一代青少年&#xff0c;可能对革命先烈英勇事迹难以有很深的体会&#xff0c;无法切实感受到中国共产党无畏牺牲、誓死保家卫国的红色精神&#xff0c;因此借助VR虚拟现实制作技术&#xff0c;让参观者们走近革命先烈中&#xff0c;感受老一辈无产阶级革命家…

高德地图全国行政区域信息

行政区域查询-API文档-开发指南-Web服务 API | 高德地图API private static void tm1(String s) throws IOException {String url"https://restapi.amap.com/v3/config/district?keywords中华人民共和国&subdistrict3&key用户key";String sx OkHttpUtils.g…

domjudge题目配置和开比赛

系统使用的是7.3.3&#xff0c;domjudge配置的方法请参考前文 domjudge配置-CSDN博客 题目导入 传统比较 首先可以去domjudge中随便下载一个题目&#xff0c;下载下来的压缩包应该是这样的 │ domjudge-problem.ini │ problem.pdf │ problem.yaml │ └─data└─sec…

模型层(回顾补充)

1.1基本使用 orm框架---》对象关系映射 数据库中&#xff1a;一个个表 &#xff1a;user表&#xff0c;book表&#xff0c;一条条的记录 程序中&#xff1a;一个个类&#xff0c;一个个对象 以后数据库中一张表---》对应程序中一个类 以后数据库中一条记录--》对应…

12月01日,每日信息差//阿里国际发布3款AI设计生态工具//美团买菜升级为“小象超市”//外国人永居证换新、6国游客免签来华

_灵感 &#x1f396; 阿里国际发布3款AI设计生态工具 &#x1f384; AITO问界系列11月交付新车18827辆 &#x1f30d; 美团买菜升级为“小象超市” &#x1f30b; 全球首个金融风控大模型国际标准出炉&#xff0c;由腾讯牵头制定 &#x1f381; 支付宝&#xff1a;支持外国人…

Python函数关键字参数及用法

在定义 Python 函数时可定义形参&#xff08;形式参数的意思&#xff09;&#xff0c;这些形参的值要等到调用时才能确定下来&#xff0c;由函数的调用者负责为形参传入参数值。简单来说&#xff0c;就是谁调用函数&#xff0c;谁负责传入参数值。 Python 函数的参数名不是无意…

Appium 元素定位与常用方法,让你轻松玩转自动化测试!

对测试人来说&#xff0c;Appium 是非常重要的一个开源跨平台自动化测试工具&#xff0c;它允许测试人员在不同的平台&#xff08;iOS、Android 等&#xff09;使用同一套 API 来写自动化测试脚本&#xff0c;这样可大幅提升代码复用率和工作效率。 本文汇总了从 Appium 基础到…

Python列表切片操作详解:提取、复制、反转等应用示例

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在Python中&#xff0c;列表切片是处理列表数据非常强大且灵活的方法。本文将全面探讨Python中列表切片的多种用法&#xff0c;包括提取子列表、复制列表、反转列表等操作&#xff0c;结合丰富的示例代码进行详细…