当前位置: 首页 > news >正文

doris:高并发导入优化(Group Commit)

在高频小批量写入场景下,传统的导入方式存在以下问题:

  • 每个导入都会创建一个独立的事务,都需要经过 FE 解析 SQL 和生成执行计划,影响整体性能
  • 每个导入都会生成一个新的版本,导致版本数快速增长,增加了后台compaction的压力

为了解决这些问题,Doris 引入了 Group Commit 机制。Group Commit 不是一种新的导入方式,而是对现有导入方式的优化扩展,主要针对:

  • INSERT INTO tbl VALUES(...) 语句
  • Stream Load 导入

通过将多个小批量导入在后台合并成一个大的事务提交,显著提升了高并发小批量写入的性能。同时,Group Commit 与 PreparedStatement 结合使用可以获得更高的性能提升。

Group Commit 模式​

Group Commit 写入有三种模式,分别是:

  • 关闭模式(off_mode

    不开启 Group Commit。

  • 同步模式(sync_mode

    Doris 根据负载和表的 group_commit_interval属性将多个导入在一个事务提交,事务提交后导入返回。这适用于高并发写入场景,且在导入完成后要求数据立即可见。

  • 异步模式(async_mode

    Doris 首先将数据写入 WAL (Write Ahead Log),然后导入立即返回。Doris 会根据负载和表的group_commit_interval属性异步提交数据,提交之后数据可见。为了防止 WAL 占用较大的磁盘空间,单次导入数据量较大时,会自动切换为sync_mode。这适用于写入延迟敏感以及高频写入的场景。

    WAL的数量可以通过FE http接口查看,具体可见这里,也可以在BE的metrics中搜索关键词wal查看。

Group Commit 使用方式​

假如表的结构为:

CREATE TABLE `dt` (`id` int(11) NOT NULL,`name` varchar(50) NULL,`score` int(11) NULL
) ENGINE=OLAP
DUPLICATE KEY(`id`)
DISTRIBUTED BY HASH(`id`) BUCKETS 1
PROPERTIES ("replication_num" = "1"
);

使用 JDBC​

当用户使用 JDBC insert into values方式写入时,为了减少 SQL 解析和生成规划的开销,我们在 FE 端支持了 MySQL 协议的 PreparedStatement 特性。当使用 PreparedStatement 时,SQL 和其导入规划将被缓存到 Session 级别的内存缓存中,后续的导入直接使用缓存对象,降低了 FE 的 CPU 压力。下面是在 JDBC 中使用 PreparedStatement 的例子:

1. 设置 JDBC URL 并在 Server 端开启 Prepared Statement

url = jdbc:mysql://127.0.0.1:9030/db?useServerPrepStmts=true&useLocalSessionState=true&rewriteBatchedStatements=true&cachePrepStmts=true&prepStmtCacheSqlLimit=99999&prepStmtCacheSize=500

2. 配置 group_commit session 变量,有如下两种方式:

  • 通过 JDBC url 设置,增加sessionVariables=group_commit=async_mode
url = jdbc:mysql://127.0.0.1:9030/db?useServerPrepStmts=true&useLocalSessionState=true&rewriteBatchedStatements=true&cachePrepStmts=true&prepStmtCacheSqlLimit=99999&prepStmtCacheSize=500&sessionVariables=group_commit=async_mode

  • 通过执行 SQL 设置
try (Statement statement = conn.createStatement()) {statement.execute("SET group_commit = async_mode;");
}

3. 使用 PreparedStatement

private static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
private static final String URL_PATTERN = "jdbc:mysql://%s:%d/%s?useServerPrepStmts=true&useLocalSessionState=true&rewriteBatchedStatements=true&cachePrepStmts=true&prepStmtCacheSqlLimit=99999&prepStmtCacheSize=50$sessionVariables=group_commit=async_mode";
private static final String HOST = "127.0.0.1";
private static final int PORT = 9087;
private static final String DB = "db";
private static final String TBL = "dt";
private static final String USER = "root";
private static final String PASSWD = "";
private static final int INSERT_BATCH_SIZE = 10;private static void groupCommitInsertBatch() throws Exception {Class.forName(JDBC_DRIVER);// add rewriteBatchedStatements=true and cachePrepStmts=true in JDBC url// set session variables by sessionVariables=group_commit=async_mode in JDBC urltry (Connection conn = DriverManager.getConnection(String.format(URL_PATTERN, HOST, PORT, DB), USER, PASSWD)) {String query = "insert into " + TBL + " values(?, ?, ?)";try (PreparedStatement stmt = conn.prepareStatement(query)) {for (int j = 0; j < 5; j++) {// 10 rows per insertfor (int i = 0; i < INSERT_BATCH_SIZE; i++) {stmt.setInt(1, i);stmt.setString(2, "name" + i);stmt.setInt(3, i + 10);stmt.addBatch();}int[] result = stmt.executeBatch();}}} catch (Exception e) {e.printStackTrace();}
}

注意:由于高频的insert into语句会打印大量的audit log,对最终性能有一定影响,默认关闭了打印prepared语句的audit log。可以通过设置session variable的方式控制是否打印prepared语句的audit log。

# 配置 session 变量开启打印parpared语句的audit log, 默认为false即关闭打印parpared语句的audit log。
set enable_prepared_stmt_audit_log=true;

关于 JDBC 的更多用法,参考使用 Insert 方式同步数据。

使用Golang进行Group Commit​

Golang的prepared语句支持有限,所以我们可以通过手动客户端攒批的方式提高Group Commit的性能,以下为一个示例程序。

package mainimport ("database/sql""fmt""math/rand""strings""sync""sync/atomic""time"_ "github.com/go-sql-driver/mysql"
)const (host     = "127.0.0.1"port     = 9038db       = "test"user     = "root"password = ""table    = "async_lineitem"
)var (threadCount = 20batchSize   = 100
)var totalInsertedRows int64
var rowsInsertedLastSecond int64func main() {dbDSN := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true", user, password, host, port, db)db, err := sql.Open("mysql", dbDSN)if err != nil {fmt.Printf("Error opening database: %s\n", err)return}defer db.Close()var wg sync.WaitGroupfor i := 0; i < threadCount; i++ {wg.Add(1)go func() {defer wg.Done()groupCommitInsertBatch(db)}()}go logInsertStatistics()wg.Wait()
}func groupCommitInsertBatch(db *sql.DB) {for {valueStrings := make([]string, 0, batchSize)valueArgs := make([]interface{}, 0, batchSize*16)for i := 0; i < batchSize; i++ {valueStrings = append(valueStrings, "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")valueArgs = append(valueArgs, rand.Intn(1000))valueArgs = append(valueArgs, rand.Intn(1000))valueArgs = append(valueArgs, rand.Intn(1000))valueArgs = append(valueArgs, rand.Intn(1000))valueArgs = append(valueArgs, sql.NullFloat64{Float64: 1.0, Valid: true})valueArgs = append(valueArgs, sql.NullFloat64{Float64: 1.0, Valid: true})valueArgs = append(valueArgs, sql.NullFloat64{Float64: 1.0, Valid: true})valueArgs = append(valueArgs, sql.NullFloat64{Float64: 1.0, Valid: true})valueArgs = append(valueArgs, "N")valueArgs = append(valueArgs, "O")valueArgs = append(valueArgs, time.Now())valueArgs = append(valueArgs, time.Now())valueArgs = append(valueArgs, time.Now())valueArgs = append(valueArgs, "DELIVER IN PERSON")valueArgs = append(valueArgs, "SHIP")valueArgs = append(valueArgs, "N/A")}stmt := fmt.Sprintf("INSERT INTO %s VALUES %s",table, strings.Join(valueStrings, ","))_, err := db.Exec(stmt, valueArgs...)if err != nil {fmt.Printf("Error executing batch: %s\n", err)return}atomic.AddInt64(&rowsInsertedLastSecond, int64(batchSize))atomic.AddInt64(&totalInsertedRows, int64(batchSize))}
}func logInsertStatistics() {for {time.Sleep(1 * time.Second)fmt.Printf("Total inserted rows: %d\n", totalInsertedRows)fmt.Printf("Rows inserted in the last second: %d\n", rowsInsertedLastSecond)rowsInsertedLastSecond = 0}
}

INSERT INTO VALUES​

  • 异步模式
# 配置 session 变量开启 group commit (默认为 off_mode),开启异步模式
mysql> set group_commit = async_mode;# 这里返回的 label 是 group_commit 开头的,可以区分出是否使用了 group commit
mysql> insert into dt values(1, 'Bob', 90), (2, 'Alice', 99);
Query OK, 2 rows affected (0.05 sec)
{'label':'group_commit_a145ce07f1c972fc-bd2c54597052a9ad', 'status':'PREPARE', 'txnId':'181508'}# 可以看出这个 label, txn_id 和上一个相同,说明是攒到了同一个导入任务中
mysql> insert into dt(id, name) values(3, 'John');
Query OK, 1 row affected (0.01 sec)
{'label':'group_commit_a145ce07f1c972fc-bd2c54597052a9ad', 'status':'PREPARE', 'txnId':'181508'}# 不能立刻查询到
mysql> select * from dt;
Empty set (0.01 sec)# 10 秒后可以查询到,可以通过表属性 group_commit_interval 控制数据可见延迟。
mysql> select * from dt;
+------+-------+-------+
| id   | name  | score |
+------+-------+-------+
|    1 | Bob   |    90 |
|    2 | Alice |    99 |
|    3 | John  |  NULL |
+------+-------+-------+
3 rows in set (0.02 sec)

  • 同步模式
# 配置 session 变量开启 group commit (默认为 off_mode),开启同步模式
mysql> set group_commit = sync_mode;# 这里返回的 label 是 group_commit 开头的,可以区分出是否谁用了 group commit,导入耗时至少是表属性 group_commit_interval。
mysql> insert into dt values(4, 'Bob', 90), (5, 'Alice', 99);
Query OK, 2 rows affected (10.06 sec)
{'label':'group_commit_d84ab96c09b60587_ec455a33cb0e9e87', 'status':'PREPARE', 'txnId':'3007', 'query_id':'fc6b94085d704a94-a69bfc9a202e66e2'}# 数据可以立刻读出
mysql> select * from dt;
+------+-------+-------+
| id   | name  | score |
+------+-------+-------+
|    1 | Bob   |    90 |
|    2 | Alice |    99 |
|    3 | John  |  NULL |
|    4 | Bob   |    90 |
|    5 | Alice |    99 |
+------+-------+-------+
5 rows in set (0.03 sec)

  • 关闭模式
mysql> set group_commit = off_mode;

Stream Load​

假如data.csv的内容为:

6,Amy,60
7,Ross,98

  • 异步模式
# 导入时在 header 中增加"group_commit:async_mode"配置curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:async_mode"  -H "column_separator:,"  http://{fe_host}:{http_port}/api/db/dt/_stream_load
{"TxnId": 7009,"Label": "group_commit_c84d2099208436ab_96e33fda01eddba8","Comment": "","GroupCommit": true,"Status": "Success","Message": "OK","NumberTotalRows": 2,"NumberLoadedRows": 2,"NumberFilteredRows": 0,"NumberUnselectedRows": 0,"LoadBytes": 19,"LoadTimeMs": 35,"StreamLoadPutTimeMs": 5,"ReadDataTimeMs": 0,"WriteDataTimeMs": 26
}# 返回的 GroupCommit 为 true,说明进入了 group commit 的流程
# 返回的 Label 是 group_commit 开头的,是真正消费数据的导入关联的 label

  • 同步模式
# 导入时在 header 中增加"group_commit:sync_mode"配置curl --location-trusted -u {user}:{passwd} -T data.csv -H "group_commit:sync_mode"  -H "column_separator:,"  http://{fe_host}:{http_port}/api/db/dt/_stream_load
{"TxnId": 3009,"Label": "group_commit_d941bf17f6efcc80_ccf4afdde9881293","Comment": "","GroupCommit": true,"Status": "Success","Message": "OK","NumberTotalRows": 2,"NumberLoadedRows": 2,"NumberFilteredRows": 0,"NumberUnselectedRows": 0,"LoadBytes": 19,"LoadTimeMs": 10044,"StreamLoadPutTimeMs": 4,"ReadDataTimeMs": 0,"WriteDataTimeMs": 10038
}# 返回的 GroupCommit 为 true,说明进入了 group commit 的流程
# 返回的 Label 是 group_commit 开头的,是真正消费数据的导入关联的 label

关于 Stream Load 使用的更多详细语法及最佳实践,请参阅 Stream Load。

自动提交条件​

当满足时间间隔 (默认为 10 秒) 或数据量 (默认为 64 MB) 其中一个条件时,会自动提交数据。这两个参数需要配合使用,建议根据实际场景进行调优。

修改提交间隔​

默认提交间隔为 10 秒,用户可以通过修改表的配置调整:

# 修改提交间隔为 2 秒
ALTER TABLE dt SET ("group_commit_interval_ms" = "2000");

参数调整建议:

  • 较短的间隔(如2秒):

    • 优点:数据可见性延迟更低,适合对实时性要求较高的场景
    • 缺点:提交次数增多,版本数增长更快,后台compaction压力更大
  • 较长的间隔(如30秒):

    • 优点:提交批次更大,版本数增长更慢,系统开销更小
    • 缺点:数据可见性延迟更高

建议根据业务对数据可见性延迟的容忍度来设置,如果系统压力大,可以适当增加间隔。

修改提交数据量​

Group Commit 的默认提交数据量为 64 MB,用户可以通过修改表的配置调整:

# 修改提交数据量为 128MB
ALTER TABLE dt SET ("group_commit_data_bytes" = "134217728");

参数调整建议:

  • 较小的阈值(如32MB):

    • 优点:内存占用更少,适合资源受限的环境
    • 缺点:提交批次较小,吞吐量可能受限
  • 较大的阈值(如256MB):

    • 优点:批量提交效率更高,系统吞吐量更大
    • 缺点:占用更多内存

建议根据系统内存资源和数据可靠性要求来权衡。如果内存充足且追求更高吞吐,可以适当增加到128MB或更大。

相关系统配置​

BE 配置​

  1. group_commit_wal_path

    • 描述:group commit 存放 WAL 文件的目录

    • 默认值:默认在用户配置的storage_root_path的各个目录下创建一个名为wal的目录。配置示例:

    group_commit_wal_path=/data1/storage/wal;/data2/storage/wal;/data3/storage/wal
    

  2. group_commit_memory_rows_for_max_filter_ratio

    • 描述:当 group commit 导入的总行数不高于该值,max_filter_ratio 正常工作,否则不工作

    • 默认值:10000

使用限制​

  • Group Commit 限制条件

    • INSERT INTO VALUES 语句在以下情况下会退化为非 Group Commit 方式:

      • 使用事务写入 (Begin; INSERT INTO VALUES; COMMIT)
      • 指定 Label (INSERT INTO dt WITH LABEL {label} VALUES)
      • VALUES 中包含表达式 (INSERT INTO dt VALUES (1 + 100))
      • 列更新写入
      • 表不支持轻量级模式更改
    • Stream Load 在以下情况下会退化为非 Group Commit 方式:

      • 使用两阶段提交
      • 指定 Label (-H "label:my_label")
      • 列更新写入
      • 表不支持轻量级模式更改
  • Unique 模型

    • Group Commit 不保证提交顺序,建议使用 Sequence 列来保证数据一致性。
  • max_filter_ratio 支持

    • 默认导入中,filter_ratio 通过失败行数和总行数计算。
    • Group Commit 模式下,max_filter_ratio 在总行数不超过 group_commit_memory_rows_for_max_filter_ratio 时有效。
  • WAL 限制

    • async_mode 写入会将数据写入 WAL,成功后删除,失败时通过 WAL 恢复。
    • WAL 文件是单副本存储的,如果对应磁盘损坏或文件误删可能导致数据丢失。
    • 下线 BE 节点时,使用 DECOMMISSION 命令以防数据丢失。
    • async_mode 在以下情况下切换为 sync_mode
      • 导入数据量过大(超过 WAL 单目录 80% 空间)
      • 不知道数据量的 chunked stream load
      • 磁盘可用空间不足
    • 重量级 Schema Change 时,拒绝 Group Commit 写入,客户端需重试。

性能​

我们分别测试了使用Stream LoadJDBC在高并发小数据量场景下group commit(使用async mode) 的写入性能。

Stream Load 日志场景测试​

机器配置

  • 1 台 FE:阿里云 8 核 CPU、16GB 内存、1 块 100GB ESSD PL1 云磁盘

  • 3 台 BE:阿里云 16 核 CPU、64GB 内存、1 块 1TB ESSD PL1 云磁盘

  • 1 台测试客户端:阿里云 16 核 CPU、64GB 内存、1 块 100GB ESSD PL1 云磁盘

  • 测试版本为Doris-2.1.5

数据集

  • httplogs 数据集,总共 31GB、2.47 亿条

测试工具

  • doris-streamloader

测试方法

  • 对比 非 group_commit 和 group_commit 的 async_mode 模式下,设置不同的单并发数据量和并发数,导入 247249096 行数据

测试结果

导入方式单并发数据量并发数耗时 (秒)导入速率 (行/秒)导入吞吐 (MB/秒)
group_commit10 KB10330674,7879.8
group_commit10 KB30326475,75010.0
group_commit100 KB10424582,44776.7
group_commit100 KB30366675,54389.0
group_commit500 KB101871,318,661173.7
group_commit500 KB301831,351,087178.0
group_commit1 MB101781,385,148182.5
group_commit1 MB301781,385,148182.5
group_commit10 MB101771,396,887184.0
非group_commit1 MB10282487,53611.5
非group_commit10 MB10450549,44268.9
非group_commit10 MB301771,396,887184.0

在上面的group_commit测试中,BE 的 CPU 使用率在 10-40% 之间。

可以看出,group_commit 模式在小数据量并发导入的场景下,能有效的提升导入性能,同时减少版本数,降低系统合并数据的压力。

JDBC​

机器配置

  • 1 台 FE:阿里云 8 核 CPU、16GB 内存、1 块 100GB ESSD PL1 云磁盘

  • 1 台 BE:阿里云 16 核 CPU、64GB 内存、1 块 500GB ESSD PL1 云磁盘

  • 1 台测试客户端:阿里云 16 核 CPU、64GB 内存、1 块 100GB ESSD PL1 云磁盘

  • 测试版本为Doris-2.1.5

  • 关闭打印parpared语句的audit log以提高性能

数据集

  • tpch sf10 lineitem 表数据集,30 个文件,总共约 22 GB,1.8 亿行

测试工具

  • DataX

测试方法

  • 通过 txtfilereader 向 mysqlwriter 写入数据,配置不同并发数和单个 INSERT 的行数

测试结果

单个 insert 的行数并发数导入速率 (行/秒)导入吞吐 (MB/秒)
10010107,17211.47
10020140,31714.79
10030142,88215.28
在上面的测试中,FE 的 CPU 使用率在 60-70% 左右,BE 的 CPU 使用率在 10-20% 左右。

Insert into sync 模式小批量数据​

机器配置

  • 1 台 FE:阿里云 16 核 CPU、64GB 内存、1 块 500GB ESSD PL1 云磁盘

  • 5 台 BE:阿里云 16 核 CPU、64GB 内存、1 块 1TB ESSD PL1 云磁盘。

  • 1 台测试客户端:阿里云 16 核 CPU、64GB 内存、1 块 100GB ESSD PL1 云磁盘

  • 测试版本为Doris-2.1.5

数据集

  • tpch sf10 lineitem 表数据集。

  • 建表语句为

CREATE TABLE IF NOT EXISTS lineitem (L_ORDERKEY    INTEGER NOT NULL,L_PARTKEY     INTEGER NOT NULL,L_SUPPKEY     INTEGER NOT NULL,L_LINENUMBER  INTEGER NOT NULL,L_QUANTITY    DECIMAL(15,2) NOT NULL,L_EXTENDEDPRICE  DECIMAL(15,2) NOT NULL,L_DISCOUNT    DECIMAL(15,2) NOT NULL,L_TAX         DECIMAL(15,2) NOT NULL,L_RETURNFLAG  CHAR(1) NOT NULL,L_LINESTATUS  CHAR(1) NOT NULL,L_SHIPDATE    DATE NOT NULL,L_COMMITDATE  DATE NOT NULL,L_RECEIPTDATE DATE NOT NULL,L_SHIPINSTRUCT CHAR(25) NOT NULL,L_SHIPMODE     CHAR(10) NOT NULL,L_COMMENT      VARCHAR(44) NOT NULL
)
DUPLICATE KEY(L_ORDERKEY, L_PARTKEY, L_SUPPKEY, L_LINENUMBER)
DISTRIBUTED BY HASH(L_ORDERKEY) BUCKETS 32
PROPERTIES ("replication_num" = "3"
);

测试工具

  • Jmeter

需要设置的jmeter参数如下图所示

jmeter1

jmeter2

  1. 设置测试前的init语句,set group_commit=async_mode以及set enable_nereids_planner=false
  2. 开启jdbc的prepared statement,完整的url为jdbc:mysql://127.0.0.1:9030?useServerPrepStmts=true&useLocalSessionState=true&rewriteBatchedStatements=true&cachePrepStmts=true&prepStmtCacheSqlLimit=99999&prepStmtCacheSize=50&sessionVariables=group_commit=async_mode&sessionVariables=enable_nereids_planner=false
  3. 设置导入类型为prepared update statement。
  4. 设置导入语句。
  5. 设置每次需要导入的值,注意,导入的值与导入值的类型要一一匹配。

测试方法

  • 通过 Jmeter 向Doris写数据。每个并发每次通过insert into写入1行数据。

测试结果

  • 数据单位为行每秒。

  • 以下测试分为30,100,500并发。

30并发sync模式5个BE3副本性能测试

Group commit internal10ms20ms50ms100ms
321.5307.3285.8224.3

100并发sync模式性能测试

Group commit internal10ms20ms50ms100ms
1175.21108.71016.3704.5

500并发sync模式性能测试

Group commit internal10ms20ms50ms100ms
3289.83686.73280.72609.2

Insert into sync 模式大批量数据​

机器配置

  • 1 台 FE:阿里云 16 核 CPU、64GB 内存、1 块 500GB ESSD PL1 云磁盘

  • 5 台 BE:阿里云 16 核 CPU、64GB 内存、1 块 1TB ESSD PL1 云磁盘。注:测试中分别用了1台,3台,5台BE进行测试。

  • 1 台测试客户端:阿里云 16 核 CPU、64GB 内存、1 块 100GB ESSD PL1 云磁盘

  • 测试版本为Doris-2.1.5

数据集

  • tpch sf10 lineitem 表数据集。

  • 建表语句为

CREATE TABLE IF NOT EXISTS lineitem (L_ORDERKEY    INTEGER NOT NULL,L_PARTKEY     INTEGER NOT NULL,L_SUPPKEY     INTEGER NOT NULL,L_LINENUMBER  INTEGER NOT NULL,L_QUANTITY    DECIMAL(15,2) NOT NULL,L_EXTENDEDPRICE  DECIMAL(15,2) NOT NULL,L_DISCOUNT    DECIMAL(15,2) NOT NULL,L_TAX         DECIMAL(15,2) NOT NULL,L_RETURNFLAG  CHAR(1) NOT NULL,L_LINESTATUS  CHAR(1) NOT NULL,L_SHIPDATE    DATE NOT NULL,L_COMMITDATE  DATE NOT NULL,L_RECEIPTDATE DATE NOT NULL,L_SHIPINSTRUCT CHAR(25) NOT NULL,L_SHIPMODE     CHAR(10) NOT NULL,L_COMMENT      VARCHAR(44) NOT NULL
)
DUPLICATE KEY(L_ORDERKEY, L_PARTKEY, L_SUPPKEY, L_LINENUMBER)
DISTRIBUTED BY HASH(L_ORDERKEY) BUCKETS 32
PROPERTIES ("replication_num" = "3"
);

测试工具

  • Jmeter

测试方法

  • 通过 Jmeter 向Doris写数据。每个并发每次通过insert into写入1000行数据。

测试结果

  • 数据单位为行每秒。

  • 以下测试分为30,100,500并发。

30并发sync模式性能测试

Group commit internal10ms20ms50ms100ms
92.2K85.9K84K83.2K

100并发sync模式性能测试

Group commit internal10ms20ms50ms100ms
70.4K70.5K73.2K69.4K

500并发sync模式性能测试

Group commit internal10ms20ms50ms100ms
46.3K47.7K47.4K46.5K

相关文章:

doris:高并发导入优化(Group Commit)

在高频小批量写入场景下&#xff0c;传统的导入方式存在以下问题&#xff1a; 每个导入都会创建一个独立的事务&#xff0c;都需要经过 FE 解析 SQL 和生成执行计划&#xff0c;影响整体性能每个导入都会生成一个新的版本&#xff0c;导致版本数快速增长&#xff0c;增加了后台…...

LLMs之WebRAG:STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略

LLMs之WebRAG&#xff1a;STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略 目录 STORM系统简介 1、Co-STORM 2、更新新闻 STORM系统安装和使用方法 1、安装 pip安装 直接克隆GitHub仓库 2、模型和数据集 两个数据集 FreshWiki数据集 WildSeek数据集 支持…...

鸿蒙HarmonyOS实战-ArkUI动画(页面转场动画)_鸿蒙arkui tab 切换动画

PageTransitionExit({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number}) 在HarmonyOS中&#xff0c;PageTransitionEnter和PageTransitionExit是用于控制页面切换动画的参数。它们分别表示页面进入和退出时的动画。1. type&#xff08;动画类型…...

图漾相机-ROS2-SDK-Ubuntu版本编译(新版本)

文章目录 前言1.Camport ROS2 SDK 介绍1.1 Camport ROS2 SDK源文件介绍1.2 Camport ROS2 SDK工作流程1.2.1 包含头文件1.2.2 2 初始化 ROS 2 节点1.2.3 创建节点对象1.2.4 创建发布者对象并实现发布逻辑1.2.5 启动 ROS 2 1.3 ROS2 SDK环境配置与编译1.3.1 Ubuntu 20.04 下ROS2 …...

小程序的协同工作与发布

1.小程序API的三大分类 2.小程序管理的概念&#xff0c;以及成员管理两个方面 3.开发者权限说明以及如何维护项目成员 4.小程序版本...

解锁维特比算法:探寻复杂系统的最优解密码

引言 在复杂的技术世界中&#xff0c;维特比算法以其独特的魅力和广泛的应用&#xff0c;成为通信、自然语言处理、生物信息学等领域的关键技术。今天&#xff0c;让我们一同深入探索维特比算法的奥秘。 一、维特比算法的诞生背景 维特比算法由安德鲁・维特比在 1967 年提出…...

计算机网络一点事(20)

IEEE802.11 无线局域网 分类有无基础设施 星型拓扑&#xff0c;基本服务集BSS一基站多移动站&#xff0c;服务集标识符SSID不超过32b&#xff0c;可接入802.3 漫游&#xff1a;移动站从一个基本服务集切换到另一个&#xff08;类似换联WiFi&#xff09; 802.11帧&#xff1…...

java求职学习day23

MySQL 单表 & 约束 & 事务 1. DQL操作单表 1.1 创建数据库,复制表 1) 创建一个新的数据库 db2 CREATE DATABASE db2 CHARACTER SET utf8; 2) 将 db1 数据库中的 emp 表 复制到当前 db2 数据库 1.2 排序 通过 ORDER BY 子句 , 可以将查询出的结果进行排序 ( 排序只…...

Vue-cli 脚手架搭建

安装node.js 官网下载node.js安装包&#xff0c;地址&#xff1a;Node.js — Download Node.js 先在node.js即将要安装的路径下创建两个文件夹&#xff1a;node_cache&#xff08;缓存&#xff09;、node_global&#xff08;全局&#xff09; 点击安装包&#xf…...

认识小程序的基本组成结构

1.基本组成结构 2.页面的组成部分 3.json配置文件 4.app.json文件(全局配置文件&#xff09; 5.project.config.json文件 6.sitemap.json文件 7.页面的.json配置文件 通过window节点可以控制小程序的外观...

Spring Boot 热部署实现指南

在开发 Spring Bot 项目时&#xff0c;热部署功能能够显著提升开发效率&#xff0c;让开发者无需频繁重启服务器就能看到代码修改后的效果。下面为大家详细介绍一种实现 Spring Boot 热部署的方法&#xff0c;同时也欢迎大家补充其他实现形式。 步骤一、开启 IDEA 自动编译功能…...

深度学习编译器的演进:从计算图到跨硬件部署的自动化之路

第一章 问题的诞生——深度学习部署的硬件困境 1.1 计算图的理想化抽象 什么是计算图&#xff1f; 想象你正在组装乐高积木。每个积木块代表一个数学运算&#xff08;如加法、乘法&#xff09;&#xff0c;积木之间的连接代表数据流动。深度学习框架正是用这种"积木拼接…...

【数据结构】_顺序表经典算法OJ(力扣版)

目录 1. 移除元素 1.1 题目描述及链接 1.2 解题思路 1.3 程序 2. 合并两个有序数组 1.1 原题链接及题目描述 1.2 解题思路 1.3 程序 1. 移除元素 1.1 题目描述及链接 原题链接&#xff1a;27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a…...

数据结构:队列篇

图均为手绘,代码基于vs2022实现 系列文章目录 数据结构初探: 顺序表 数据结构初探:链表之单链表篇 数据结构初探:链表之双向链表篇 链表特别篇:链表经典算法问题 数据结构:栈篇 文章目录 系列文章目录前言一.队列的概念和结构1.1概念一、动态内存管理优势二、操作效率与安全性…...

第05章 17 Contour 过滤器介绍与例子

vtkContourFilter 是 VTK&#xff08;Visualization Toolkit&#xff09;中的一个关键类&#xff0c;用于从输入数据生成等值线或等值面。它是基于阈值的过滤器&#xff0c;可以从标量字段中提取等值线或等值面。vtkContourFilter 的核心功能是根据用户指定的值生成等值线或等值…...

【落羽的落羽 数据结构篇】顺序表

文章目录 一、线性表二、顺序表1. 概念与分类2. 准备工作3. 静态顺序表4. 动态顺序表4.1 定义顺序表结构4.2 顺序表的初始化4.3 检查空间是否足够4.3 尾部插入数据4.4 头部插入数据4.5 尾部删除数据4.6 头部删除数据4.7 在指定位置插入数据4.8 在指定位置删除数据4.9 顺序表的销…...

AI编程:如何编写提示词

这是小卷对AI编程工具学习的第2篇文章&#xff0c;今天讲讲如何编写AI编程的提示词&#xff0c;并结合实际功能需求案例来进行开发 1.编写提示词的技巧 好的提示词应该是&#xff1a;目标清晰明确&#xff0c;具有针对性&#xff0c;能引导模型理解问题 下面是两条提示词的对…...

DeepSeek-R1 论文解读 —— 强化学习大语言模型新时代来临?

近年来&#xff0c;人工智能&#xff08;AI&#xff09;领域发展迅猛&#xff0c;大语言模型&#xff08;LLMs&#xff09;为通用人工智能&#xff08;AGI&#xff09;的发展开辟了道路。OpenAI 的 o1 模型表现非凡&#xff0c;它引入的创新性推理时缩放技术显著提升了推理能力…...

高阶C语言|深入理解字符串函数和内存函数

文章目录 前言1.求字符串长度1.1 字符串长度函数&#xff1a;strlen模拟实现 2.长度不受限制的字符串函数2.1 字符串拷贝函数&#xff1a;strcpy模拟实现 2.2 字符串连接函数&#xff1a;strcat模拟实现 2.3 字符串比较函数&#xff1a;strcmp模拟实现 3.长度受限制的字符串函数…...

UE学习日志#17 C++笔记#3 基础复习3

19.2 [[maybe_unused]] 禁止编译器在未使用某些内容时发出警告 19.3 [[noreturn]] 永远不会把控制权返回给调用点 19.4 [[deprecated]] 标记为已弃用&#xff0c;仍然可以使用但是不鼓励使用 可以加参数表示弃用的原因[[deprecated("")]] 19.5 [[likely]]和[[un…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...