处理并发
DuckDB有两个可配置的并发选项:
- 一个进程可以同时读取和写入数据库。
- 多个进程可以从数据库中读取,但没有进程可以写入 (
access_mode = 'READ_ONLY'
).
当使用选项1时,DuckDB支持使用MVCC(多版本并发控制)和乐观并发控制(参见单进程内的并发)的组合来支持多个写入线程,但所有这些都在单个写入进程内进行。这种并发模型的原因是为了允许在RAM中缓存数据以加快分析查询的速度,而不是在每次查询期间来回访问磁盘。它还允许缓存函数指针、数据库目录和其他项目,以便在同一连接上的后续查询更快。
DuckDB 针对批量操作进行了优化,因此执行许多小事务并不是其主要设计目标。
单进程内的并发
DuckDB 支持在单个进程内根据以下规则进行并发操作。只要没有写冲突,多个并发写入将会成功。追加操作永远不会冲突,即使是在同一张表上。多个线程也可以同时更新不同的表或同一表的不同子集。当两个线程尝试同时编辑(更新或删除)同一行时,乐观并发控制就会发挥作用。在这种情况下,尝试编辑的第二个线程将会失败,并出现冲突错误。
从多个进程写入DuckDB
从多个进程写入DuckDB不被自动支持,也不是主要设计目标(参见处理并发)。
如果多个进程必须写入同一个文件,可能需要几种设计模式,但需要在应用程序逻辑中实现。例如,每个进程可以获取一个跨进程的互斥锁,然后以读/写模式打开数据库,并在查询完成后关闭它。如果不使用互斥锁,每个进程可以在另一个进程已经连接到数据库时重试连接(确保在查询完成后关闭连接)。另一种选择是在MySQL、PostgreSQL或SQLite数据库上进行多进程事务,并使用DuckDB的MySQL、PostgreSQL或SQLite扩展定期对该数据执行分析查询。
其他选项包括将数据写入Parquet文件,并利用DuckDB的读取多个Parquet文件的能力,对CSV文件采取类似的方法,或者创建一个网络服务器来接收请求并管理对DuckDB的读写操作。
乐观并发控制
DuckDB 使用 乐观并发控制,这种方法通常被认为是最适合读密集型分析数据库系统的,因为它加快了读取查询的处理速度。因此,任何同时修改相同行的事务都会导致事务冲突错误:
Transaction conflict: cannot update a table that has been altered!
提示 当遇到事务冲突时,一个常见的解决方法是重新运行事务。