处理并发

DuckDB 提供两种可配置的并发选项:

  1. 一个进程可以同时对数据库进行读写操作。
  2. 多个进程可以从数据库中读取,但没有进程可以写入(access_mode = 'READ_ONLY')。

在使用选项1时,DuckDB 通过结合使用MVCC(多版本并发控制)和乐观并发控制(参见单个进程内的并发),支持多个写入线程,但所有这些都在单个写入进程中进行。这种并发模型的原因是允许在 RAM 中缓存数据,以便进行更快的分析查询,而不是在每次查询时来回访问磁盘。它还允许缓存函数指针、数据库目录和其他项目,以便在同一连接上进行的后续查询更快。

注意

DuckDB 针对批量操作进行了优化,因此执行许多小事务不是主要设计目标。

单个进程内的并发

DuckDB 根据以下规则支持单个进程内的并发。只要没有写入冲突,多个并发写入将成功。追加操作永远不会冲突,即使是在同一张表上。多个线程也可以同时更新不同的表或同一表的不同子集。当两个线程尝试同时编辑(更新或删除)同一行时,乐观并发控制就会发挥作用。在这种情况下,尝试编辑的第二个线程将因冲突错误而失败。

从多个进程写入 DuckDB

DuckDB 不自动支持从多个进程写入,并且这不是主要设计目标(参见处理并发)。

如果必须从多个进程写入同一个文件,有几种设计模式是可能的,但需要在应用程序逻辑中实现。例如,每个进程可以获取一个跨进程互斥锁,然后以读写模式打开数据库,并在查询完成后关闭它。而不是使用互斥锁,如果另一个进程已经连接到数据库,每个进程可以重试连接(确保在查询完成后关闭连接)。另一种选择是使用 MySQL、PostgreSQL 或 SQLite 数据库进行多进程事务,并使用 DuckDB 的 MySQLPostgreSQLSQLite 扩展定期在该数据上执行分析查询。

其他选项包括将数据写入 Parquet 文件并使用 DuckDB 的能力读取多个 Parquet 文件,采取类似的方法与 CSV 文件,或创建一个 web 服务器以接收请求并管理对 DuckDB 的读写。

乐观并发控制

DuckDB 使用乐观并发控制,这种方法通常被认为是最适合读密集型分析数据库系统的,因为它加快了读取查询的处理速度。因此,任何同时修改同一行的事务都将导致事务冲突错误:

  1. Transaction conflict: cannot update a table that has been altered!

提示

当遇到事务冲突时,一个常见的解决方法是重新运行事务。