Windows通过Rust使用DuckDB碰到的问题

DuckDB的一些问题只在windows上有, macOS和Linux上没有, 所以记录一下

具体报错如下:

1
2
3
4
5
6
7
8
9
10
11
error: linking with `link.exe` failed: exit code: 1120
|
= note: "D:\\cpptools\\VC\\Tools\\MSVC\\14.44.35207\\bin\\HostX64\\x64\\link.exe" "/NOLOGO" "C:\\Users\\levit\\AppData\\Local\\Temp\\rustcjagjai\\symbols.o" "<72 object files omitted>" "D:\\RustProject\\swan-lake\\Playground\\duckdb-eval\\target\\debug\\deps/{libduckdb-25263ba118ed6ba4.rlib,libsmallvec-c183f9d47da38243.rlib,libcast-49db356ef74b85f3.rlib,librust_decimal-3a5b9cbf4877a907.rlib,libserde-22d9db03cb63b99f.rlib,libarrayvec-d50e6e157ab9318a.rlib,libfallible_streaming_iterator-04f60b605fc0f73f.rlib,libfallible_iterator-68563fc10ffcf3b8.rlib,libstrum-b79e927e16832d4d.rlib,libhashlink-3ba134715094e3cb.rlib,libarrow-5ac1a72fe2a27e5d.rlib,libarrow_row-e8aed7be06733ce6.rlib,libarrow_string-7973c0bda726cc56.rlib,libregex-710eef4d38ee3fd6.rlib,libregex_automata-db83f4512e1b5c7d.rlib,libaho_corasick-24adfb8458d9ade7.rlib,libregex_syntax-b6714b60b7bc7e4a.rlib,libmemchr-da6ba839bbe8ebcc.rlib,libarrow_cast-1a27c9e1a3dc2694.rlib,libatoi-e6ebe25009fa1943.rlib,libryu-37203d2c467f8da8.rlib,libbase64-0b360acb9dc29ba9.rlib,libcomfy_table-251418f61f10dac3.rlib,libunicode_segmentation-0b8b78906ba6799f.rlib,libunicode_width-5ad620de9423ed41.rlib,liblexical_core-a899d107d4a1caf3.rlib,liblexical_write_float-41eba6196db745d8.rlib,liblexical_write_integer-75f049f9e63a6664.rlib,liblexical_parse_float-43b8fed49b79bfbd.rlib,liblexical_parse_integer-8bdd1cf08a8114d2.rlib,liblexical_util-baf5b3dfc43aa25a.rlib,libstatic_assertions-da84d677a2cbb685.rlib,libarrow_arith-79812951151e328c.rlib,libarrow_ord-4db3a0b5e663b54b.rlib,libarrow_select-55fc1a24d0d3ef75.rlib,libarrow_array-01ea262a82a871bf.rlib,libahash-94b54058c337bbde.rlib,libgetrandom-2a89a66638c7e38e.rlib,libonce_cell-84b94b97ea24f67b.rlib,libzerocopy-e1dffdfb07107702.rlib,libhashbrown-5a1847c4e38fdd05.rlib,libfoldhash-c7e1bc489b06a6d3.rlib,libchrono-d9f9b240257d9e4e.rlib,libwindows_link-71c4caf817584dd3.rlib,libarrow_data-a31197f5464dcbdc.rlib,libarrow_schema-f4ee27f94a7e2fd1.rlib,libbitflags-05ed3484cc55db94.rlib,libarrow_buffer-a0c15988dc061bdc.rlib,libbytes-441da4d76b13cbcb.rlib,libhalf-747f7d890291de0f.rlib,libcfg_if-3d39ba26ec35ebac.rlib,libnum-f0aa498cc9e9b9e3.rlib,libnum_iter-b389ef6f565c05d2.rlib,libnum_rational-1b7cd3981135e2f8.rlib,libnum_complex-2537c84c16f51dd3.rlib,libnum_bigint-737a93fc7600053a.rlib,libnum_integer-e7cba3faece1f759.rlib,libnum_traits-1701bc9abe802865.rlib,liblibduckdb_sys-26b80393c1b83bef.rlib}.rlib" "<sysroot>\\lib\\rustlib\\x86_64-pc-windows-msvc\\lib/{libstd-*,libpanic_unwind-*,libwindows_targets-*,librustc_demangle-*,libstd_detect-*,libhashbrown-*,librustc_std_workspace_alloc-*,libunwind-*,libcfg_if-*,liballoc-*,librustc_std_workspace_core-*,libcore-*,libcompiler_builtins-*}.rlib" "kernel32.lib" "kernel32.lib" "ntdll.lib" "userenv.lib" "ws2_32.lib" "dbghelp.lib" "/defaultlib:msvcrt" "/NXCOMPAT" "/LIBPATH:D:\\RustProject\\swan-lake\\Playground\\duckdb-eval\\target\\debug\\build\\libduckdb-sys-504c6af2e068003e\\out" "/OUT:D:\\RustProject\\swan-lake\\Playground\\duckdb-eval\\target\\debug\\deps\\duckdb_eval.exe" "/OPT:REF,NOICF" "/DEBUG" "/PDBALTPATH:%_PDB%" "/NATVIS:<sysroot>\\lib\\rustlib\\etc\\intrinsic.natvis" "/NATVIS:<sysroot>\\lib\\rustlib\\etc\\liballoc.natvis" "/NATVIS:<sysroot>\\lib\\rustlib\\etc\\libcore.natvis" "/NATVIS:<sysroot>\\lib\\rustlib\\etc\\libstd.natvis"
= note: some arguments are omitted. use `--verbose` to show all linker arguments
= note: 正在创建库 D:\RustProject\swan-lake\Playground\duckdb-eval\target\debug\deps\duckdb_eval.lib 和对象 D:\RustProject\swan-lake\Playground\duckdb-eval\target\debug\deps\duckdb_eval.exp␍
liblibduckdb_sys-26b80393c1b83bef.rlib(0645e750c9ff977b-ub_src_common.o) : error LNK2019: 无法解析的外部符号 RmStartSession,函数 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl duckdb::AdditionalLockInfo(class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> >)" (?AdditionalLockInfo@duckdb@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@3@@Z) 中引用了该符号␍
liblibduckdb_sys-26b80393c1b83bef.rlib(0645e750c9ff977b-ub_src_common.o) : error LNK2019: 无法解析的外部符号 RmEndSession,函数 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl duckdb::AdditionalLockInfo(class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> >)" (?AdditionalLockInfo@duckdb@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@3@@Z) 中引用了该符号␍
liblibduckdb_sys-26b80393c1b83bef.rlib(0645e750c9ff977b-ub_src_common.o) : error LNK2019: 无法解析的外部符号 RmRegisterResources,函数 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl duckdb::AdditionalLockInfo(class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> >)" (?AdditionalLockInfo@duckdb@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@3@@Z) 中引用了该符号␍
liblibduckdb_sys-26b80393c1b83bef.rlib(0645e750c9ff977b-ub_src_common.o) : error LNK2019: 无法解析的外部符号 RmGetList,函数 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl duckdb::AdditionalLockInfo(class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> >)" (?AdditionalLockInfo@duckdb@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@3@@Z) 中引用了该符号␍
D:\RustProject\swan-lake\Playground\duckdb-eval\target\debug\deps\duckdb_eval.exe : fatal error LNK1120: 4 个无法解析的外部命令␍

原因:
DuckDB 的 AdditionalLockInfo调用了Windows Restart Manager API 函数:
RmStartSession - 启动资源管理器会话
RmEndSession - 结束资源管理器会话
RmRegisterResources - 注册资源
RmGetList - 获取资源列表
这些函数属于 Windows 的 rstrtmgr.dll,需要链接 rstrtmgr.lib 库。

解决方案, 根目录下新建一个 build.rs 文件,写入内容如下:

1
2
3
4
5
6
fn main() {
#[cfg(target_os = "windows")]
{
println!("cargo:rustc-link-lib=rstrtmgr");
}
}

同一进程不支持一个线程的同时是写入多个线程读取

Windows中以写入模式打开文件时,其他线程不能以读取模式打开该文件。因此,如果在一个线程中打开文件进行写入操作,其他线程将无法同时以读取模式打开该文件。这可能导致并发访问冲突和数据不一致的问题。

主要测试了如下的两个场景:

场景一:多数据库并发写入测试
线程1 → database_a.duckdb (products 表)
线程2 → database_b.duckdb (orders 表)
线程3 → database_c.duckdb (users 表)
目的: 验证不同数据库文件间的写入操作是否互相影响
数据量: 每个线程写入5条记录
写入间隔: 每条记录间隔100ms

场景二:并发读写混合测试
读取任务1: 持续读取 database_a.duckdb (每300ms一次,共10次)
读取任务2: 持续读取 database_b.duckdb (每400ms一次,共8次)
写入任务4: 延迟800ms后向 database_a.duckdb 写入数据
目的: 测试同一数据库文件的读写并发性能
读取频率: 不同任务使用不同的读取间隔,模拟真实场景
写入冲突: 特意在读取进行中启动额外写入,测试锁竞争

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use duckdb::{Connection, Result};
use std::thread;
use std::time::Duration;
use tokio::time::sleep;

#[tokio::main()]
async fn main() -> Result<()> {
println!("测试多个 DuckDB 实例并发操作...");

let mut handles = Vec::new();

// 任务1: 写入线程 - 数据库A
let handle1 = tokio::spawn(async move {
println!("线程1: 开始写入数据到 database_a.duckdb");
write_to_database("database_a.duckdb", "products", 1).await
});

// 任务2: 写入线程 - 数据库B
let handle2 = tokio::spawn(async move {
println!("线程2: 开始写入数据到 database_b.duckdb");
write_to_database("database_b.duckdb", "orders", 2).await
});

// 任务3: 写入线程 - 数据库C
let handle3 = tokio::spawn(async move {
println!("线程3: 开始写入数据到 database_c.duckdb");
write_to_database("database_c.duckdb", "users", 3).await
});

handles.push(handle1);
handles.push(handle2);
handles.push(handle3);

// 添加并发读取任务(与写入同时进行)
println!("\n开始并发读写操作...");

// 读取任务1: 持续读取数据库A (与写入同时进行)
let read_handle1 = tokio::spawn(async move {
for i in 1..=10 {
sleep(Duration::from_millis(300)).await;
println!("读取任务1: 读取数据库A- 第{}次尝试:", i);
match read_from_database("database_a.duckdb", "products").await {
Ok(_) => println!("读取任务1: 读取成功"),
Err(e) => println!("读取任务1: 读取失败 - {}", e),
}
}
Ok::<(), duckdb::Error>(())
});

// 读取任务2: 持续读取数据库B
let read_handle2 = tokio::spawn(async move {
for i in 1..=8 {
sleep(Duration::from_millis(400)).await;
println!("读取任务2: 读取数据库B - 第{}次尝试:", i);
match read_from_database("database_b.duckdb", "orders").await {
Ok(_) => println!("读取任务2: 读取成功"),
Err(e) => println!("读取任务2: 读取失败 - {}", e),
}
}
Ok::<(), duckdb::Error>(())
});

// 额外的写入任务(在读取进行时继续写入)
let write_handle4 = tokio::spawn(async move {
sleep(Duration::from_millis(800)).await; // 稍微延迟启动
println!("线程4: 开始第二轮写入数据到 database_a.duckdb");
write_to_database("database_a.duckdb", "products", 4).await
});

handles.push(read_handle1);
handles.push(read_handle2);
handles.push(write_handle4);

// 等待所有任务完成
for handle in handles {
handle.await.unwrap()?;
}

println!("\n测试结束");

Ok(())
}

async fn write_to_database(db_path: &str, table_name: &str, thread_id: u32) -> Result<()> {
let db_path = db_path.to_string();
let table_name = table_name.to_string();

let result=tokio::task::spawn_blocking(move || -> Result<()> {
let conn=Connection::open(&db_path)?;
conn.execute(
&format!("CREATE TABLE IF NOT EXISTS {} (id INTEGER, name TEXT, thread_id INTEGER, timestamp TEXT)", table_name),
[],
)?;
for i in 1..=5 {
conn.execute(
&format!("INSERT INTO {} VALUES (?, ?, ?, now())", table_name),
[&i.to_string(), &format!("Item_{}_from_thread_{}", i, thread_id), &thread_id.to_string()],
)?;

// 模拟一些处理时间
thread::sleep(Duration::from_millis(100));
}
println!("线程{}: 成功写入5条记录到 {} 表 ({})", thread_id, table_name, db_path);
Ok(())

}).await.unwrap();
result
}

async fn read_from_database(db_path: &str, table_name: &str) -> Result<()> {
let db_path = db_path.to_string();
let table_name = table_name.to_string();

let result = tokio::task::spawn_blocking(move || -> Result<()> {
let conn = Connection::open(&db_path)?;

let mut stmt = conn.prepare(&format!(
"SELECT id, name, thread_id, timestamp FROM {}",
table_name
))?;
let rows = stmt.query_map([], |row| {
Ok((
row.get::<_, i32>(0)?,
row.get::<_, String>(1)?,
row.get::<_, i32>(2)?,
row.get::<_, String>(3)?,
))
})?;

println!("读取 {} ({}):", table_name, db_path);
for row in rows {
let (id, name, thread_id, timestamp) = row?;
println!(
"ID: {}, Name: {}, Thread: {}, Time: {}",
id, name, thread_id, timestamp
);
}

Ok(())
})
.await
.unwrap();

result
}

cargo.toml:

1
2
3
4
5
6
7
8
[package]
name = "multi-duckdb-write"
version = "0.1.0"
edition = "2024"

[dependencies]
duckdb = { version = "1.3.2", features = ["bundled"] }
tokio = { version = "1.0", features = ["full"] }

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
测试多个 DuckDB 实例并发操作...

开始并发读写操作...
线程1: 开始写入数据到 database_a.duckdb
线程2: 开始写入数据到 database_b.duckdb
线程3: 开始写入数据到 database_c.duckdb
读取任务1: 读取数据库A- 第1次尝试:
读取任务2: 读取数据库B - 第1次尝试:
读取任务1: 读取失败 - IO Error: Cannot open file "database_a.duckdb": ��һ����������ʹ�ô��ļ��������޷����ʡ�

File is already open in
D:\RustProject\swan-lake\Playground\parallel-duckdb-write\target\debug\parallel-duckdb-write.exe (PID 22848)
读取任务2: 读取失败 - IO Error: Cannot open file "database_b.duckdb": ��һ����������ʹ�ô��ļ��������޷����ʡ�

File is already open in
D:\RustProject\swan-lake\Playground\parallel-duckdb-write\target\debug\parallel-duckdb-write.exe (PID 22848)
线程2: 成功写入5条记录到 orders 表 (database_b.duckdb)
线程1: 成功写入5条记录到 products 表 (database_a.duckdb)
线程3: 成功写入5条记录到 users 表 (database_c.duckdb)
读取任务1: 读取数据库A- 第2次尝试:
读取 products (database_a.duckdb):
ID: 1, Name: Item_1_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.241+00
ID: 2, Name: Item_2_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.354+00
ID: 3, Name: Item_3_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.681+00
读取任务1: 读取成功
线程4: 开始第二轮写入数据到 database_a.duckdb
读取任务2: 读取数据库B - 第2次尝试:
读取 orders (database_b.duckdb):
ID: 1, Name: Item_1_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.249+00
ID: 2, Name: Item_2_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.357+00
ID: 3, Name: Item_3_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.681+00
读取任务2: 读取成功
读取任务1: 读取数据库A- 第3次尝试:
读取任务1: 读取失败 - IO Error: Cannot open file "database_a.duckdb": ��һ����������ʹ�ô��ļ��������޷����ʡ�

File is already open in
D:\RustProject\swan-lake\Playground\parallel-duckdb-write\target\debug\parallel-duckdb-write.exe (PID 22848)
线程4: 成功写入5条记录到 products 表 (database_a.duckdb)
读取任务2: 读取数据库B - 第3次尝试:
读取 orders (database_b.duckdb):
ID: 1, Name: Item_1_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.249+00
ID: 2, Name: Item_2_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.357+00
ID: 3, Name: Item_3_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.681+00
读取任务2: 读取成功
读取任务1: 读取数据库A- 第4次尝试:
读取 products (database_a.duckdb):
ID: 1, Name: Item_1_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.241+00
ID: 2, Name: Item_2_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.354+00
ID: 3, Name: Item_3_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.681+00
ID: 1, Name: Item_1_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.02+00
ID: 2, Name: Item_2_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.135+00
ID: 3, Name: Item_3_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.249+00
ID: 4, Name: Item_4_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.357+00
ID: 5, Name: Item_5_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.465+00
读取任务1: 读取成功
读取任务2: 读取数据库B - 第4次尝试:
读取 orders (database_b.duckdb):
ID: 1, Name: Item_1_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.249+00
ID: 2, Name: Item_2_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.357+00
ID: 3, Name: Item_3_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.681+00
读取任务2: 读取成功
读取任务1: 读取数据库A- 第5次尝试:
读取 products (database_a.duckdb):
ID: 1, Name: Item_1_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.241+00
ID: 2, Name: Item_2_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.354+00
ID: 3, Name: Item_3_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.681+00
ID: 1, Name: Item_1_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.02+00
ID: 2, Name: Item_2_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.135+00
ID: 3, Name: Item_3_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.249+00
ID: 4, Name: Item_4_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.357+00
ID: 5, Name: Item_5_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.465+00
读取任务1: 读取成功
读取任务1: 读取数据库A- 第6次尝试:
读取任务2: 读取数据库B - 第5次尝试:
读取 products (database_a.duckdb):
ID: 1, Name: Item_1_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.241+00
ID: 2, Name: Item_2_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.354+00
ID: 3, Name: Item_3_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.681+00
ID: 1, Name: Item_1_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.02+00
ID: 2, Name: Item_2_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.135+00
ID: 3, Name: Item_3_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.249+00
ID: 4, Name: Item_4_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.357+00
ID: 5, Name: Item_5_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.465+00
读取任务1: 读取成功
读取 orders (database_b.duckdb):
ID: 1, Name: Item_1_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.249+00
ID: 2, Name: Item_2_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.357+00
ID: 3, Name: Item_3_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.681+00
读取任务2: 读取成功
读取任务1: 读取数据库A- 第7次尝试:
读取 products (database_a.duckdb):
ID: 1, Name: Item_1_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.241+00
ID: 2, Name: Item_2_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.354+00
ID: 3, Name: Item_3_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.681+00
ID: 1, Name: Item_1_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.02+00
ID: 2, Name: Item_2_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.135+00
ID: 3, Name: Item_3_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.249+00
ID: 4, Name: Item_4_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.357+00
ID: 5, Name: Item_5_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.465+00
读取任务1: 读取成功
读取任务2: 读取数据库B - 第6次尝试:
读取 orders (database_b.duckdb):
ID: 1, Name: Item_1_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.249+00
ID: 2, Name: Item_2_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.357+00
ID: 3, Name: Item_3_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.681+00
读取任务2: 读取成功
读取任务1: 读取数据库A- 第8次尝试:
读取 products (database_a.duckdb):
ID: 1, Name: Item_1_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.241+00
ID: 2, Name: Item_2_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.354+00
ID: 3, Name: Item_3_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.681+00
ID: 1, Name: Item_1_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.02+00
ID: 2, Name: Item_2_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.135+00
ID: 3, Name: Item_3_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.249+00
ID: 4, Name: Item_4_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.357+00
ID: 5, Name: Item_5_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.465+00
读取任务1: 读取成功
读取任务2: 读取数据库B - 第7次尝试:
读取 orders (database_b.duckdb):
ID: 1, Name: Item_1_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.249+00
ID: 2, Name: Item_2_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.357+00
ID: 3, Name: Item_3_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.681+00
读取任务2: 读取成功
读取任务1: 读取数据库A- 第9次尝试:
读取 products (database_a.duckdb):
ID: 1, Name: Item_1_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.241+00
ID: 2, Name: Item_2_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.354+00
ID: 3, Name: Item_3_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.681+00
ID: 1, Name: Item_1_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.02+00
ID: 2, Name: Item_2_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.135+00
ID: 3, Name: Item_3_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.249+00
ID: 4, Name: Item_4_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.357+00
ID: 5, Name: Item_5_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.465+00
读取任务1: 读取成功
读取任务1: 读取数据库A- 第10次尝试:
读取任务2: 读取数据库B - 第8次尝试:
读取 orders (database_b.duckdb):
读取 products (database_a.duckdb):
ID: 1, Name: Item_1_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.249+00
ID: 2, Name: Item_2_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.357+00
ID: 1, Name: Item_1_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.241+00
ID: 3, Name: Item_3_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.464+00
ID: 2, Name: Item_2_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.354+00
ID: 4, Name: Item_4_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.572+00
ID: 3, Name: Item_3_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.464+00
ID: 4, Name: Item_4_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.572+00
ID: 5, Name: Item_5_from_thread_2, Thread: 2, Time: 2025-07-30 15:49:27.681+00
ID: 5, Name: Item_5_from_thread_1, Thread: 1, Time: 2025-07-30 15:49:27.681+00
ID: 1, Name: Item_1_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.02+00
ID: 2, Name: Item_2_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.135+00
ID: 3, Name: Item_3_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.249+00
ID: 4, Name: Item_4_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.357+00
ID: 5, Name: Item_5_from_thread_4, Thread: 4, Time: 2025-07-30 15:49:28.465+00
读取任务2: 读取成功
读取任务1: 读取成功

测试结束