从 MySQL 到 ClickHouse 实时数据同步 —— Debezium + Kafka 表引擎

目录

一、总体架构

二、安装配置 MySQL 主从复制

三、安装配置 ClickHouse 集群

四、安装 JDK

五、安装配置 Zookeeper 集群

六、安装配置 Kafaka 集群

七、安装配置 Debezium-Connector-MySQL 插件

1. 创建插件目录

2. 解压文件到插件目录

3. 配置 Kafka Connector

(1)配置属性文件

(2)分发到其它节点

(3)以 distributed 方式启动 Kafka connect

(4)确认 connector 插件和自动生成的 topic

4. 创建 source connector

(1)Debezium 三个必要的配置说明

(2)创建源 mysql 配置文件

(3)创建 mysql source connector

八、在 ClickHouse 中创建库表、物化视图和视图

1. 建库

2. 创建 Kafka 表

3. 创建主表

4. 创建消费者物化视图

5. 创建视图

6. 验证

参考:


        本文介绍从 MySQL 作为源到 ClickHouse 作为目标的整个过程。MySQL 数据库更改通过 Debezium 捕获,并作为事件发布在到 Kafka 上。ClickHouse 通过 Kafka 表引擎按部分顺序应用这些更改,实时并保持最终一致性。相关软件版本如下:

  • MySQL:8.0.16
  • ClickHouse:24.1.8
  • JDK:11.0.22
  • zookeeper:3.9.1
  • Kafka:3.7.0
  • debezium-connector-mysql:2.4.2

        这种方案的优点之一是可以做到 ClickHouse 与 MySQL 的数据最终严格一致。

一、总体架构

        总体结构如下图所示。

        ClickHouse 是由四个实例构成的两分片、每分片两副本集群,票选和协调器使用 ClickHouse 自带的 keeper 组件。分片、副本、keeper 节点、Zookeeper集群、Kafaka集群、Debezium-Connector-MySQL 插件的部署如下表所示。

IP

主机名

实例角色

ClickHouse

Keeper

Zookeeper

Kafka

Debezium

Connector

MySQL

172.18.4.126

node1

分片1副本1

*

172.18.4.188

node2

分片1副本2

*

*

*

*

172.18.4.71

node3

分片2副本1

*

*

*

*

172.18.4.86

node4

分片2副本2

*

*

*

二、安装配置 MySQL 主从复制

        配置好主从复制后,在主库创建测试库表及数据:

-- 建库
create database test;

-- 建表
create table test.t1 (
  id bigint(20) not null auto_increment,
  remark varchar(32) default null comment '备注',
  createtime timestamp not null default current_timestamp comment '创建时间',
  primary key (id));

-- 插入三条测试数据
insert into test.t1 (remark) values ('第一行:row1'),('第二行:row2'),('第三行:row3');
commit;

三、安装配置 ClickHouse 集群

四、安装 JDK

五、安装配置 Zookeeper 集群

六、安装配置 Kafaka 集群

七、安装配置 Debezium-Connector-MySQL 插件

        在 node2 上执行以下步骤。

1. 创建插件目录

mkdir $KAFKA_HOME/plugins

2. 解压文件到插件目录

cd ~
# debezium-connector-mysql
unzip debezium-debezium-connector-mysql-2.4.2.zip -d $KAFKA_HOME/plugins/

3. 配置 Kafka Connector

(1)配置属性文件

# 先备份
cp $KAFKA_HOME/config/connect-distributed.properties $KAFKA_HOME/config/connect-distributed.properties.bak
# 编辑 connect-distributed.properties 文件
vim $KAFKA_HOME/config/connect-distributed.properties

        内容如下:

bootstrap.servers=node2:9092,node3:9092,node4:9092
group.id=connect-cluster
key.converter=org.apache.kafka.connect.json.JsonConverter
value.converter=org.apache.kafka.connect.json.JsonConverter
key.converter.schemas.enable=false
value.converter.schemas.enable=false
offset.storage.topic=connect-offsets
offset.storage.replication.factor=3
offset.storage.partitions=3
config.storage.topic=connect-configs
config.storage.replication.factor=3
status.storage.topic=connect-status
status.storage.replication.factor=3
status.storage.partitions=3
offset.flush.interval.ms=10000
plugin.path=/root/kafka_2.13-3.7.0/plugins

(2)分发到其它节点

scp $KAFKA_HOME/config/connect-distributed.properties node3:$KAFKA_HOME/config/
scp $KAFKA_HOME/config/connect-distributed.properties node4:$KAFKA_HOME/config/
scp -r $KAFKA_HOME/plugins node3:$KAFKA_HOME/
scp -r $KAFKA_HOME/plugins node4:$KAFKA_HOME/

(3)以 distributed 方式启动 Kafka connect

connect-distributed.sh -daemon $KAFKA_HOME/config/connect-distributed.properties 
# 确认日志是否有 ERROR
grep ERROR ~/kafka_2.13-3.7.0/logs/connectDistributed.out

(4)确认 connector 插件和自动生成的 topic

        查看连接器插件:

curl -X GET http://node2:8083/connector-plugins | jq

        从输出中可以看到,Kafka connect 已经识别到了 MySqlConnector source 插件:

[root@vvml-yz-hbase-test~]#curl -X GET http://node2:8083/connector-plugins | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   403  100   403    0     0   3820      0 --:--:-- --:--:-- --:--:--  3838
[
  {
    "class": "io.debezium.connector.mysql.MySqlConnector",
    "type": "source",
    "version": "2.4.2.Final"
  },
  {
    "class": "org.apache.kafka.connect.mirror.MirrorCheckpointConnector",
    "type": "source",
    "version": "3.7.0"
  },
  {
    "class": "org.apache.kafka.connect.mirror.MirrorHeartbeatConnector",
    "type": "source",
    "version": "3.7.0"
  },
  {
    "class": "org.apache.kafka.connect.mirror.MirrorSourceConnector",
    "type": "source",
    "version": "3.7.0"
  }
]
[root@vvml-yz-hbase-test~]#

        查看 topic:

kafka-topics.sh --list --bootstrap-server node2:9092,node3:9092,node4:9092

        从输出中可以看到,Kafka connect 启动时自动创建了 connect-configs、connect-offsets、connect-status 三个 topic:

[root@vvml-yz-hbase-test~]#kafka-topics.sh --list --bootstrap-server node2:9092,node3:9092,node4:9092
__consumer_offsets
connect-configs
connect-offsets
connect-status
[root@vvml-yz-hbase-test~]#

4. 创建 source connector

(1)Debezium 三个必要的配置说明

        Debezium 是一个众所周知的用于读取和解析 MySQL Binlog 的工具。它将 KafkaConnect 作为一个连接器进行集成,并对 Kafka 主题进行每一次更改。

  • 只记录后状态

        默认情况下,Debezium 会向 Kafka 发出每个操作的前状态和后状态的每条记录,这很难被 ClickHouse Kafka 表解析。此外,在执行删除操作的情况下(Clickhouse 同样无法解析),它会创建 tombstone 记录,即具有 Null 值的记录。下表展示了这个行为。

操作

操作前

操作后

附加记录

Create

Null

新纪录

-

Update

更新前的记录

更新后的记录

-

Delete

删除前的记录

Null

墓碑记录

        在 Debezium 配置中使用 ExtractNewRecod 转换器来处理此问题。由于有了这个选项,Debezium 只为创建/更新操作保留 after 状态,而忽略 before 状态。但缺点是,它删除了包含先前状态的 Delete 记录和墓碑记录,换句话说就是不再捕获删除操作。紧接着说明如何解决这个问题。

"transforms": "unwrap",
"transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState"
  • 重写删除事件

        要捕获删除操作,必须添加如下所示的重写配置:

"transforms.unwrap.delete.handling.mode":"rewrite"

        Debezium 使用此配置添加字段 __deleted,对于 delete 操作为 true,对于其他操作为 false。因此,删除将包含以前的状态以及 __deleted:true 字段。

  • 处理非主键更新

        在提供上述配置的情况下,更新记录(主键除外的每一列)会发出一个具有新状态的简单记录。通常在关系数据库系统中,更新后的记录会替换前一个记录,但在 ClickHouse 不行。出于性能考虑,ClickHouse 将行级更新变为多版本插入。在本示例中,MySQL 中的 test.t1 表以 id 列为主键,如果更新了 remark 列,在 ClikHouse 中,最终会得到重复的记录,这意味着 id 相同,但 remark 不同!

        幸运的是有办法应付这种情况。默认情况下,Debezium 会创建一个删除记录和一个创建记录,用于更新主键。因此,如果源更新 id,它会发出一个带有前一个 id 的删除记录和一个带有新 id 的创建记录。带有 __deleted=ture 字段的前一个记录将替换 CH 中的 stall 记录。然后,可以在视图中过滤暗示删除的记录。可以使用以下选项将此行为扩展到其他列:

"message.key.columns": "test.t1:id;test.t1:remark;test.t1:createtime"

        注意:
        通过更改连接器的键列,Debezium 将这些列用作主键,而不是源表的默认主键。因此,与数据库的一条记录相关的不同操作可能最终会出现在 Kafka 中的其他分区。由于记录在不同分区中失去顺序,除非确保 ClickHouse 顺序键和 Debezium 消息键相同,否则可能会导致 Clikchouse 中的数据不一致。

        经验法则如下:

  1. 根据想要的表结构来设计分区键和排序键。
  2. 提取分区和排序键的来源,假设它们是在物化过程中计算的。
  3. 合并所有这些列。
  4. 将步骤 3 的结果定义为 Debezium 连接器配置中的 message.column.keys。
  5. 检查 Clickhouse 排序键是否包含所有这些列。如果没有则添加它们。

        现在,通过将上述所有选项和常用选项放在一起,将拥有一个功能齐全的 Debezium 配置,能够处理 ClickHouse 所需的任何更改。

(2)创建源 mysql 配置文件

# 编辑文件
vim $KAFKA_HOME/plugins/source-mysql.json

        内容如下:

{
 "name": "mysql-source-connector",
 "config": {
     "connector.class": "io.debezium.connector.mysql.MySqlConnector",
     "database.hostname": "172.18.16.156",
     "database.port": "3307",
     "database.user": "dba",
     "database.password": "123456",
     "database.server.id": "1563307",
     "database.server.name": "dbserver1",
     "database.include.list": "test",
     "table.include.list": "test.t1",
     "topic.prefix": "mysql-clickhouse-test",
     "schema.history.internal.kafka.bootstrap.servers": "node2:9092,node3:9092,node4:9092",
     "schema.history.internal.kafka.topic": "schemahistory.mysql-clickhouse-test",
     "message.key.columns": "test.t1:id;test.t1:remark;test.t1:createtime",
     "transforms":"unwrap",
     "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState",
     "transforms.unwrap.delete.handling.mode": "rewrite"
     }
 }

(3)创建 mysql source connector

# 创建 connector
curl -X POST -H 'Content-Type: application/json' -i 'http://node2:8083/connectors' -d @"/root/kafka_2.13-3.7.0/plugins/source-mysql.json"; echo
# 查看 connector 状态
curl -X GET http://node2:8083/connectors/mysql-source-connector/status | jq
# 查看 topic
kafka-topics.sh --list --bootstrap-server node2:9092,node3:9092,node4:9092

        从输出中可以看到,mysql-source-connector 状态为 RUNNING,并自动创建了三个 topic:

[root@vvml-yz-hbase-test~]#curl -X POST -H 'Content-Type: application/json' -i 'http://node2:8083/connectors' -d @"/root/kafka_2.13-3.7.0/plugins/source-mysql.json"; echo
HTTP/1.1 201 Created
Date: Thu, 25 Apr 2024 03:47:26 GMT
Location: http://node2:8083/connectors/mysql-source-connector
Content-Type: application/json
Content-Length: 818
Server: Jetty(9.4.53.v20231009)

{"name":"mysql-source-connector","config":{"connector.class":"io.debezium.connector.mysql.MySqlConnector","database.hostname":"172.18.16.156","database.port":"3307","database.user":"dba","database.password":"123456","database.server.id":"1563307","database.server.name":"dbserver1","database.include.list":"test","table.include.list":"test.t1","topic.prefix":"mysql-clickhouse-test","schema.history.internal.kafka.bootstrap.servers":"node2:9092,node3:9092,node4:9092","schema.history.internal.kafka.topic":"schemahistory.mysql-clickhouse-test","message.key.columns":"test.t1:id;test.t1:remark;test.t1:createtime","transforms":"unwrap","transforms.unwrap.type":"io.debezium.transforms.ExtractNewRecordState","transforms.unwrap.delete.handling.mode":"rewrite","name":"mysql-source-connector"},"tasks":[],"type":"source"}
[root@vvml-yz-hbase-test~]#curl -X GET http://node2:8083/connectors/mysql-source-connector/status | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   182  100   182    0     0  24045      0 --:--:-- --:--:-- --:--:-- 26000
{
  "name": "mysql-source-connector",
  "connector": {
    "state": "RUNNING",
    "worker_id": "172.18.4.188:8083"
  },
  "tasks": [
    {
      "id": 0,
      "state": "RUNNING",
      "worker_id": "172.18.4.188:8083"
    }
  ],
  "type": "source"
}
[root@vvml-yz-hbase-test~]#kafka-topics.sh --list --bootstrap-server node2:9092,node3:9092,node4:9092
__consumer_offsets
connect-configs
connect-offsets
connect-status
mysql-clickhouse-test
mysql-clickhouse-test.test.t1
schemahistory.mysql-clickhouse-test
[root@vvml-yz-hbase-test~]#

八、在 ClickHouse 中创建库表、物化视图和视图

        ClickHouse 可以利用 Kafka 表引擎将 Kafka 记录放入一个表中。需要定义三个对象:Kafka 表、主表和消费者物化视图。

1. 建库

create database db2 on cluster cluster_2S_2R;

2. 创建 Kafka 表

CREATE TABLE db2.kafka_t1 on cluster cluster_2S_2R
(
    `id` Int64,
    `remark` Nullable(String),
    `createtime` String,
    `__deleted` String
)
ENGINE = Kafka('node2:9092,node3:9092,node4:9092', 'mysql-clickhouse-test.test.t1', 'clickhouse', 'JSONEachRow');

3. 创建主表

        主表具有源结构和 __deleted 字段。这里使用的是 ReplicatedReplacingMergeTree,因为需要用已删除或更新的记录替换 stall 记录。

-- 创建本地表
CREATE TABLE db2.stream_t1 on cluster cluster_2S_2R
(
    `id` Int64,
    `remark` Nullable(String),
    `createtime` timestamp,
    `__deleted` String
)
ENGINE = ReplicatedReplacingMergeTree(
    '/clickhouse/tables/{shard}/db2/t1',
    '{replica}'
)
ORDER BY (id, createtime)
SETTINGS index_granularity = 8192;

-- 创建分布式表,以源表的主键 id 作为分片键,保证同一 id 的数据落在同一分片上
create table db2.t1_replica_all on cluster cluster_2S_2R
as db2.stream_t1
engine = Distributed(cluster_2S_2R, db2, stream_t1, id);

4. 创建消费者物化视图

        在创建物化视图前,先停止MySQL从库的复制。从库停止复制,不影响主库的正常使用,也就不会影响业务。此时从库的数据处于静止状态,不会产生变化,这使得获取存量数据变得轻而易举。然后创建物化视图时会自动将数据写入 db2.t1_replica_all 对应的本地表中。之后在 ClickHouse 集群中的任一实例上,都能从物化视图中查询到一致的 MySQL 存量数据。

-- MySQL 从库停止复制
stop slave;

        Kafka 表的每一条记录只读取一次,因为它的消费者组会改变偏移量,不能读取两次。因此,需要定义一个主表,并通过物化视图将每个 Kafka 表记录具化到它:

-- 注意时间戳的处理
CREATE MATERIALIZED VIEW db2.consumer_t1 on cluster cluster_2S_2R
TO db2.t1_replica_all
(
    `id` Int64,
    `remark` Nullable(String),
    `createtime` timestamp,
    `__deleted` String
) AS
SELECT id, remark, addHours(toDateTime(substring(createtime,1,length(createtime)-1)),8) createtime, __deleted FROM db2.kafka_t1;

5. 创建视图

        最后需要过滤每个被删除的记录,并拥有最新的记录,以防不同的记录具有相同的排序键。可以定义一个简单的视图来隐式完成这项工作:

CREATE VIEW db2.t1 on cluster cluster_2S_2R
(
    `id` Int64,
    `remark` Nullable(String),
    `createtime` String,
    `__deleted` String
) AS
SELECT *
FROM db2.consumer_t1
FINAL
WHERE __deleted = 'false';

6. 验证

        从 clickhouse 视图查询存量数据:

vvml-yz-hbase-test.172.18.4.126 :) select * from db2.t1;

SELECT *
FROM db2.t1

Query id: 2a51fd5e-6b4f-4b78-b522-62b7be32535b

┌─id─┬─remark───────┬─createtime──────────┬─__deleted─┐
│  2 │ 第二行:row2 │ 2024-04-25 11:51:07 │ false     │
└────┴──────────────┴─────────────────────┴───────────┘
┌─id─┬─remark───────┬─createtime──────────┬─__deleted─┐
│  1 │ 第一行:row1 │ 2024-04-25 11:51:07 │ false     │
│  3 │ 第三行:row3 │ 2024-04-25 11:51:07 │ false     │
└────┴──────────────┴─────────────────────┴───────────┘

3 rows in set. Elapsed: 0.007 sec. 

vvml-yz-hbase-test.172.18.4.126 :) 

        可以看到,存量数据已经与 MySQL 同步。

-- MySQL 主库修改数据
insert into test.t1 (remark) values ('第四行:row4');
update test.t1 set remark = '第五行:row5' where id = 4;
delete from test.t1 where id =1;
insert into test.t1 (remark) values ('第六行:row6');
 
-- MySQL 从库启动复制
start slave;

        此时 MySQL 的数据如下:

mysql> select * from test.t1;
+----+------------------+---------------------+
| id | remark           | createtime          |
+----+------------------+---------------------+
|  2 | 第二行:row2     | 2024-04-25 11:51:07 |
|  3 | 第三行:row3     | 2024-04-25 11:51:07 |
|  4 | 第五行:row5     | 2024-04-25 11:56:29 |
|  5 | 第六行:row6     | 2024-04-25 11:56:29 |
+----+------------------+---------------------+
4 rows in set (0.00 sec)

        从 clickhouse 视图查询增量数据:

vvml-yz-hbase-test.172.18.4.126 :) select * from db2.t1;

SELECT *
FROM db2.t1

Query id: b34bb37b-091b-490e-b55b-a0e9eedf5573

┌─id─┬─remark───────┬─createtime──────────┬─__deleted─┐
│  2 │ 第二行:row2 │ 2024-04-25 11:51:07 │ false     │
└────┴──────────────┴─────────────────────┴───────────┘
┌─id─┬─remark───────┬─createtime──────────┬─__deleted─┐
│  4 │ 第五行:row5 │ 2024-04-25 11:56:29 │ false     │
└────┴──────────────┴─────────────────────┴───────────┘
┌─id─┬─remark───────┬─createtime──────────┬─__deleted─┐
│  3 │ 第三行:row3 │ 2024-04-25 11:51:07 │ false     │
└────┴──────────────┴─────────────────────┴───────────┘
┌─id─┬─remark───────┬─createtime──────────┬─__deleted─┐
│  5 │ 第六行:row6 │ 2024-04-25 11:56:29 │ false     │
└────┴──────────────┴─────────────────────┴───────────┘

4 rows in set. Elapsed: 0.008 sec. 

vvml-yz-hbase-test.172.18.4.126 :) 

        可以看到,增量数据已经与 MySQL 同步,现在从 ClickHouse 视图查询的数据与 MySQL 一致。

        查看 Kafka 消费:

kafka-consumer-groups.sh --bootstrap-server node2:9092,node3:9092,node4:9092 --describe --group clickhouse

        输出如下:

[root@vvml-yz-hbase-test~]#kafka-consumer-groups.sh --bootstrap-server node2:9092,node3:9092,node4:9092 --describe --group clickhouse

GROUP           TOPIC                         PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG             CONSUMER-ID                                                                                  HOST            CLIENT-ID
clickhouse      mysql-clickhouse-test.test.t1 0          8               8               0               ClickHouse-vvml-yz-hbase-test.172.18.4.126-db2-kafka_t1-26e6aa8e-1f08-4491-8af7-f1822f1a7e94 /172.18.4.126   ClickHouse-vvml-yz-hbase-test.172.18.4.126-db2-kafka_t1
[root@vvml-yz-hbase-test~]#

        可以看到,最后被消费的消息偏移量是8,MySQL 的存量、增量数据都已经通过 Kafka 消息同步到了 ClickHouse。

参考:

  • Apply CDC from MySQL to ClickHouse
  • New Record State Extraction
  • 基于 HBase & Phoenix 构建实时数仓(5)—— 用 Kafka Connect 做实时数据同步
  • Greenplum 实时数据仓库实践(5)——实时数据同步

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习-18】特征筛选:提升模型性能的关键步骤

一、引言 在机器学习领域,特征筛选是一个至关重要的预处理步骤。随着数据集的日益庞大和复杂,特征的数量往往也随之激增。然而,并非所有的特征都对模型的性能提升有所贡献,有些特征甚至可能是冗余的、噪声较大的或者与目标变量无关…

SpringBoot Aop使用篇

Getting Started SpringBoot AOP的实践 AOP相关的概念: Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。就是抽离出来的逻辑类,比如日志、权限…

《苍穹外卖》Day08部分知识点记录

一、useGeneratedKeys和keyProperty useGeneratedKeys和keyProperty是<insert>标签中的两个属性&#xff0c;用于处理自动生成的主键值。 1. useGeneratedKeys userGeneratedKeys"true"表示启用自动生成主键功能&#xff1b;当useGeneratedKeys设置为true时…

VScode使用cmake编译

一&#xff1a;输入 ctrlshiftp打开用于命令执行的输入框 二&#xff1a;输入cmake&#xff0c;选择quick start 模式 三&#xff1a;选择版本最高的gcc版本 四&#xff1a;输入项目名称 选择C 五&#xff1a;选择executable 这样便创建好了最简单的cmake例程&#xff0c;一个…

同态加密原理解析

目录 1.数学介绍2.使用多项式环进行加密2.1 私钥和公钥的产生2.2 加密2.3 解密 3.同态计算3.1 同态加法3.2 同态乘法 1.数学介绍 同态加密方案基于一个难以计算的问题Ring Learning with Errorsred。这些方案中的数据在加密和未加密时都用多项式表示。 这里举一个简单的多项式…

AWTK MODBUS Client channel 模型

名称&#xff1a;modbus_client_channel 功能&#xff1a;通过 modbus 协议访问远程 slave 设备上的数据&#xff0c;需要配合 modbus_client模型一起使用。用于将 modbus client 中的 channel 包装成view_model或者view_model_array 一般来说不需要&#xff0c;直接使用modbus…

docker常用基本命令

把jar包和 dockerfile文件放到同一目录下#构建Docker镜像 注意后面的 . 不能省略 docker build -t your-image-name .#运行并创建一个容器 docker run -d -p 8080:8080 --name container_name your-image-name# 停止容器 767fce4cb990 容器ID (容器名也可以) docker stop 767f…

柱形图“变个装”,跟上时尚步伐!

前言 职场中&#xff0c;日报、周报、月报、年度总结&#xff0c;都离不开图表的制作&#xff0c;而柱状图又是最常用的一种&#xff0c;怎样的柱状图&#xff0c;才能让领导更容易阅读&#xff0c;甚至是眼前一亮呢&#xff1f;今天小编就将为大家介绍一下如何借助葡萄城公司…

4月26日 阶段性学习汇报

1.毕业设计与毕业论文 毕业设计已经弄完&#xff0c;加入了KNN算法&#xff0c;实现了基于四种常见病的判断&#xff0c;毕业论文写完&#xff0c;格式还需要调整&#xff0c;下周一发给指导老师初稿。目前在弄答辩ppt&#xff08;25%&#xff09;。25号26号两天都在参加校运会…

六西格玛管理培训并未过气:深挖其现代价值与应用

在众多管理培训中&#xff0c;六西格玛管理培训因其卓越的成效和广泛的适用性而备受推崇。尽管有人认为六西格玛管理培训已经过时&#xff0c;但实际上&#xff0c;它在现代企业中仍具有不可忽视的价值和应用。深圳天行健六西格玛培训公司解析如下&#xff1a; 一、六西格玛管理…

1Panel - 现代化、开源的 Linux 服务器运维管理面板

产品介绍 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。 1Panel的官方网站&#xff1a;https://1panel.cn 1Panel的GitHub仓库&#xff1a;https://github.com/1Panel-dev/1Panel 体验环境&#xff1a;https://demo.1panel.cn 1Panel 特点 开源特性 Star 数…

详细解析什么是期权交易的获利方法

期权交易的获利方法 在期权交易之前进行充分的准备工作和风险评估是至关重要的。其中行情结构、策略方法、预期收益和风险评估&#xff0c;是期权交易成功的关键要素。它们能帮助我们更好地制定交易计划&#xff0c;控制风险&#xff0c;并追求稳定的利润。以下是对这四点的详…

比较好的平民衣服品牌有哪些?平价质量好短袖品牌推荐

随着气候变暖&#xff0c;夏天的持续时间似乎越来越长&#xff0c;短袖作为夏季的必备服装&#xff0c;受到了广大男士的青睐。然而&#xff0c;面对市场上众多的短袖品牌和不同的质量&#xff0c;大家都觉得选短袖的时候实在难以找到质量好且合适自己的。 选择合适的短袖确实…

SimCal(ECCV2020)

文章目录 AbstractMethodUsing Existing Long-tail Classification ApproachesLoss Re-weightingFocal Loss略 Proposed SimCal:Calibrating the ClassifierDual Head Inference Experiment创新 原文 代码 Abstract 本文主要研究了长尾分布下的实例分割问题&#xff0c;并提出…

Open CASCADE学习|一个点的坐标变换

gp_Trsf 类是 Open CASCADE Technology (OCCT) 软件库中的一个核心类&#xff0c;用于表示和操作三维空间中的变换。以下是该类的一些关键成员和方法的介绍&#xff1a; 成员变量&#xff1a; scale: Standard_Real 类型&#xff0c;表示变换的缩放因子。 shape: gp_TrsfFor…

网络安全之防范钓鱼邮件

随着互联网的快速发展&#xff0c;新的网络攻击形式“网络钓鱼”呈现逐年上升的趋势&#xff0c;利用网络钓鱼进行欺骗的行为越来越猖獗&#xff0c;对互联网的安全威胁越来越大。网络钓鱼最常见的欺骗方式就是向目标群体发送钓鱼邮件&#xff0c;而邮件标题和内容&#xff0c;…

用于肺结节分类的常规 EHR 的纵向多模态Transformer集成成像和潜在临床特征

Longitudinal Multimodal Transformer Integrating Imaging and Latent Clinical Signatures from Routine EHRs for Pulmonary Nodule Classification 摘要 该研究提出了一种基于Transformer 的多模态策略&#xff0c;用于将重复成像与常规电子健康记录&#xff08;EHRs&…

AbstractRoutingDataSource实现多数据源切换以及事务中无法切换问题

一、AbstractRoutingDataSource实现多数据源切换 为了实现数据源的动态切换&#xff0c;我们采用了AbstractRoutingDataSource结合AOP反射来自定义注解。通过这种机制&#xff0c;我们可以在运行时根据自定义注解来选择不同的数据源&#xff0c;从而实现灵活高效的数据访问策略…

C++内存分布 new和delete介绍

目录 C/C内存分布 栈区 堆区 静态区 常量区 C new和delete 分配空间形式对比 new delete与malloc free的区别 可不可以串着使用new和free呢 C/C内存分布 C的内存分布&#xff0c;大体上分为栈区 堆区 静态区 常量区 栈区 栈区是用于存储函数调用时的局部变量 函…

C语言中,如何判断两个数组是否包含相同元素?

在C语言中判断两个数组是否包含相同元素可以采用多种方法&#xff0c;其中最常见的方法是使用排序和比较两个数组的元素。在解释这个问题之前&#xff0c;我们需要了解一下C语言中的数组、排序算法和比较方法。 数组 数组是C语言中一种基本的数据结构&#xff0c;它是一系列相…