如何强制DuckDB重新连接文件,避免读取已删除数据库的缓存内容?
背景
我正在开发一个基于DuckDB的大型仪表板应用,遇到了以下问题:
# 创建新连接 con = duckdb.connect('/somedir/duckdb.db') # 执行操作 # 删除或替换文件 Path('/somedir/duckdb.db').unlink() shutil.copy('/otherdir/duckdb.db', '/somedir/duckdb.db') # 创建指向同位置新文件的连接 con = duckdb.connect('/somedir/duckdb.db') # 问题:连接仍保留第一个文件的内容!
问题
如何确保创建新的duckdb.DuckDBPyConnection对象时,DuckDB不使用内部缓存?我需要的是指向指定位置的全新、干净连接,若存在问题则抛出异常,完全不受之前操作的影响。
问题复现示例
from pathlib import Path import duckdb filepath = Path("/tmp/test_duckdb/data.db") filepath.parent.mkdir(exist_ok=True, parents=True) con = duckdb.connect(str(filepath)) con.sql(f"CREATE TABLE first AS SELECT 42 AS i, 84 AS j") print("before removing:\n" + str(con.sql("show tables"))) filepath.unlink() con2 = duckdb.connect(str(filepath)) print("after removing:\n" + str(con2.sql("show tables"))) con2.sql(f"CREATE TABLE first AS SELECT 42 AS i, 84 AS j")
实际结果
before removing: ┌─────────┐ │ name │ │ varchar │ ├─────────┤ │ first │ └─────────┘ after removing: ┌─────────┐ │ name │ │ varchar │ ├─────────┤ │ first │ └─────────┘ --------------------------------------------------------------------------- CatalogException Traceback (most recent call last) Cell In[1], line 15 13 con2 = duckdb.connect(str(filepath)) 14 print("after removing:\n" + str(con2.sql("show tables"))) ---> 15 con2.sql(f"CREATE TABLE first AS SELECT 42 AS i, 84 AS j") CatalogException: Catalog Error: Table with name "first" already exists!
期望结果
第二次连接时,因原文件存在未关闭的连接而抛出异常。
说明
此问题出现在大型应用场景中,修复应用逻辑bug并非本次诉求。核心需求是:即使应用逻辑存在bug,如何阻止DuckDB连接已删除/替换的数据库文件?是否存在类似duckdb.clear_all_caches()的方法,或者duckdb.connect(filepath, raise_if_open_connections=True)这类参数?
补充说明:我知道应该关闭连接,现在的问题是:如果连接未关闭,有没有办法解决这个问题?
解决方案
使用排他文件锁强制检测未关闭连接
在创建新连接时指定file_lock='EXCLUSIVE'参数,DuckDB会尝试获取数据库文件的排他锁。如果文件已有其他未关闭的连接占用,新连接会立即抛出IOException,完全符合你期望的“第二次连接时抛出异常”的需求:con2 = duckdb.connect(str(filepath), file_lock='EXCLUSIVE')强制清理进程内所有DuckDB连接
若无法追踪到所有未关闭的连接实例,可以调用duckdb.shutdown()关闭当前进程中所有DuckDB连接并释放相关缓存资源,之后再创建新连接:# 清理所有现有连接和缓存 duckdb.shutdown() # 创建全新连接 con2 = duckdb.connect(str(filepath))注意:此操作会重置进程内所有DuckDB状态,适合需要完全隔离的场景。
通过文件inode校验检测文件替换
删除并重新创建的文件inode会发生变化,可以在创建新连接前校验文件inode,若与初始连接时的inode不一致,则说明文件已被替换,此时可主动抛出异常或清理旧连接:import os # 记录初始连接时的文件inode original_inode = os.stat(filepath).st_ino # 创建新连接前校验 if os.path.exists(filepath): current_inode = os.stat(filepath).st_ino if current_inode != original_inode: raise Exception("数据库文件已被替换,请处理旧连接后重试") con2 = duckdb.connect(str(filepath))
内容的提问来源于stack exchange,提问作者Niko Fohr

