SQLite高性能进阶之路

富金
2025-04-22 15:59

1. 引言

SQLite 是一款嵌入式 SQL 数据库引擎,它直接运行在应用程序的进程中,无需独立的服务器 。这种架构上的特点使其在易于部署、管理和移植方面具有显著优势 。SQLite 将整个数据库存储在一个单一的文件中,这既方便了备份和分发,但也对其并发模型带来了特定的考量 。由于其轻量级的特性,SQLite 被广泛应用于各种平台和应用场景,包括移动设备、嵌入式系统和桌面应用程序 。本报告旨在对 SQLite 的性能进行全面的分析,重点考察其在处理并发操作时的能力和局限性,并探讨其在不同并发场景下的表现以及与其他轻量级数据库的对比。  

2. SQLite 性能基准测试

2.1 一般性能特点

早期的基准测试结果表明,对于常见的数据库操作,如 SELECT、INSERT(尤其是在事务中)、UPDATE 和 DELETE,SQLite 的性能通常优于传统的客户端-服务器数据库,例如 PostgreSQL 和 MySQL,尤其是在禁用同步的情况下 。然而,需要注意的是,这些历史基准测试是在较早的数据库版本上进行的 。自那时以来,PostgreSQL 和 MySQL 等数据库技术也经历了显著的发展,并进行了大量的性能优化。因此,当前的性能对比可能与早期的结果有所不同。此外,具体的性能表现高度依赖于工作负载的类型(例如,读密集型与写密集型)以及用于测试的硬件环境 。   

最近针对嵌入式数据库场景的基准测试显示,SQLite 并非总是性能最优的选择。例如,Actian Zen 在 Raspberry Pi 和 Android 设备等嵌入式硬件上的插入、删除和更新操作方面,其性能显著优于 SQLite,有时甚至达到数个数量级 。这表明,对于边缘计算和物联网 (IoT) 应用,如果原始性能(特别是数据修改操作的性能)至关重要,那么 Actian Zen 等替代的嵌入式数据库解决方案可能比 SQLite 更为适合。最终的选择应基于应用程序的具体性能需求 。   

值得强调的是,SQLite 的性能并非一成不变,而是受到多种配置参数的显著影响 。研究表明,仅仅改变日志模式(例如,从 DELETE 模式切换到 WAL 模式)就可能导致超过 11 倍的性能差异。此外,调整同步模式或日志文件大小也可能导致性能的显著变化 。这强调了正确调整和配置 SQLite 以实现最佳性能的重要性。默认设置可能并不适用于所有使用情况,开发人员需要理解不同参数对其应用程序性能的影响。此外,在比较基准测试结果时,仔细检查所报告的配置至关重要,以确保比较的公平性和意义 。   

一个跨多个来源的持续建议是,在使用 SQLite 时,将多个 SQL 操作分组到一个单独的事务中对于最大化性能至关重要 。这样做可以减少与单独执行每个语句相关的开销,例如打开和关闭数据库文件或使缓存失效。有效的事务管理是实现 SQLite 最佳性能的基础,尤其是在写入操作方面。通过在事务中批量处理操作,应用程序可以显著提高写入吞吐量和整体性能,尤其是在涉及多次插入、更新或删除的场景中 。   

2.2 并发性能分析

SQLite 的架构本身支持高度的读取操作并发。它允许无限数量的并发读取器同时访问数据库,而不会相互阻塞 。这使其非常适合读取密集型工作负载和主要涉及读取数据的多并发用户应用程序。对于主要操作是检索数据(例如,内容分发网站、只读数据分析工具)的应用程序,SQLite 处理大量并发读取器的能力是一个显著的优势。   

然而,SQLite 并发性能的主要瓶颈在于其在任何给定时刻只允许一个写入器的限制 。当多个进程或线程尝试同时写入数据库时,它们会被串行化,这可能导致性能下降和潜在的“数据库已锁定”错误,尤其是在写入密集型应用程序或高并发负载下。这个单写入器约束是 SQLite 设计的一个基本方面,在评估其是否适合具有显著写入需求的应用程序时需要仔细考虑。虽然写入操作通常很快(毫秒级),但大量的并发写入请求会导致排队和延迟。   

针对 Web 框架(如 Django)进行的最新基准测试表明,启用预写式日志 (WAL) 模式并使用 IMMEDIATE 事务可以显著提高混合读写工作负载下的吞吐量并减少“数据库已锁定”错误的发生 。这些优化允许读取与单个写入器并发进行,从而提高整体响应能力。对于使用 SQLite 的 Web 应用程序,特别是那些预期会有中等水平的并发写入的应用程序,启用 WAL 模式和使用 IMMEDIATE 事务是关键的优化步骤。这些配置有助于缓解单写入器限制的影响,并提高应用程序更优雅地处理并发请求的能力。   

基于 Go 的基准测试表明,通过仔细管理数据库连接(例如,为读取和写入使用单独的连接)以及策略性地使用 WAL 模式和繁忙超时等功能,即使在高并发负载下,SQLite 也可以实现令人惊讶的高读取和写入操作吞吐量 。在单个连接池中使用应用程序级互斥锁来序列化写入访问也是一个有效的策略。通过勤奋的应用程序级设计和配置,可以优化 SQLite 的并发性能,以处理单个服务器上的大量并发操作,甚至是写入。这通常需要在单写入器模型的约束下工作,并实施有效管理写入访问的策略。   

GreptimeDB 在高通平台上的基准测试显示,其写入 TPS 高于 SQLite,这表明对于特定的时间序列工作负载,其他嵌入式数据库可能提供更好的写入并发性能 。因此,数据库的选择可能取决于应用程序工作负载的特定性能要求。   

3. SQLite 并发模型和限制

3.1 SQLite 的锁定机制

SQLite 采用文件级锁定机制来管理对数据库文件的访问 。这意味着当一个进程需要写入数据库时,它通常会锁定整个文件,以防止与其他进程发生冲突。在默认的“回滚日志”模式下,任何写入操作都需要对整个数据库文件进行独占锁定,这将阻止所有其他进程读取或写入,直到操作完成 。这种模式提供了很强的一致性,但会严重限制并发性,尤其是在具有频繁写入操作的应用程序中。回滚日志模式虽然确保了数据完整性,但由于其限制性的锁定行为,不利于高并发。对于 Web 应用程序或任何期望并发访问和写入操作的系统,通常不推荐使用此模式。   

预写式日志 (WAL) 模式引入了一种更复杂的锁定方案。它允许多个读取器持有数据库上的共享锁,而单个写入器可以持有独占锁将更改附加到单独的 WAL 文件 。这通过允许读取操作在不被写入操作阻塞的情况下并发进行,从而显著提高了并发性,反之亦然(很大程度上)。WAL 模式是 SQLite 并发处理方面的一项关键进步。通过使用单独的日志文件将读取和写入操作分离,与回滚日志模式相比,它实现了更高的并发级别,使 SQLite 成为更广泛应用程序(包括具有中等写入负载的应用程序)的可行选择。   

SQLite 采用不同的锁定状态——SHARED、RESERVED、PENDING 和 EXCLUSIVE——来管理访问并防止写入器饥饿等问题,在这种情况下,持续的读取访问可能会阻止写入器获取必要的锁 。SQLite 管理这些锁定状态的升级,以确保数据完整性和资源访问的一定程度的公平性。理解 SQLite 的锁定状态有助于诊断并发问题和优化性能。   

3.2 写并发限制

尽管 WAL 模式提供了改进,但基本限制仍然存在:在任何给定的时刻,只有一个写入事务可以处于活动状态并提交更改到数据库 。这意味着即使启用了 WAL,如果应用程序具有非常高的并发写入请求率,这些请求也会被排队并按顺序处理。这个单写入器约束是限制 SQLite 在具有极高写入吞吐量要求的应用程序中可伸缩性的主要因素。虽然单个写入操作通常很快(毫秒级),但随着并发写入请求数量的显著增加,序列化会成为瓶颈。   

长时间运行的写入事务可能会加剧这种限制。当写入事务持有独占锁(即使在 WAL 模式下)时,它会阻止其他写入事务继续进行。如果一个写入事务花费大量时间完成(例如,由于事务中复杂的运算或网络调用),它可能会阻塞其他写入器,并可能导致它们的“数据库已锁定”错误 。因此,在可能存在并发写入活动的 SQLite 环境中,设计应用程序以使用简短、集中的写入事务至关重要。最小化写入锁的持有时间可以提高整体系统响应能力并降低锁争用的可能性。   

3.3 “数据库已锁定”错误

“数据库已锁定”错误(通常表现为 SQLITE_BUSY)是 SQLite 并发限制的一个常见症状,尤其是在多个进程或线程尝试同时写入数据库的场景中 。它表明写入操作无法获取必要的独占锁,因为另一个写入操作已经在进行中。处理此错误的主要机制是使用 PRAGMA busy_timeout 命令配置繁忙超时 。此设置指定 SQLite 在返回 SQLITE_BUSY 错误之前等待锁释放的最长时间(以毫秒为单位)。通过设置适当的超时(例如,5 秒或更长时间),应用程序可以允许写入操作排队并在合理的时间段内重试。实现繁忙超时是使使用 SQLite 的应用程序在并发写入尝试方面更具弹性的基本步骤。它可以防止立即失败,并允许一定程度地优雅处理锁争用。最佳超时值将取决于预期工作负载和典型写入操作的持续时间。   

在某些情况下,“数据库已锁定”错误可能会立即发生,即使设置了繁忙超时。当读取事务尝试将其锁升级为写入锁时,但另一个连接已经修改了数据库或正在修改数据库时,通常会发生这种情况 。在这种情况下,等待锁可能无效,因为初始读取可能基于过时的状态。理解锁升级的细微差别对于诊断“数据库已锁定”错误非常重要。在事务可能从读取操作转换为写入操作的场景中,考虑与其他写入器发生冲突的可能性至关重要。   

为了缓解锁升级问题,尤其是在事务可能以读取操作开始并在以后执行写入操作的应用程序中,使用 BEGIN IMMEDIATE 可能是有益的 。此命令尝试在事务开始时立即获取独占锁。如果无法立即获取锁,它将根据 busy_timeout 设置等待。这种方法可以防止在读取事务处于活动状态时,由于并发活动而稍后无法获取写入锁的情况。对于已知涉及写入操作的事务,使用 BEGIN IMMEDIATE 可以是一种主动管理锁并可能减少“数据库已锁定”错误发生率的策略。然而,需要注意的是,如果只读事务不必要地获取了写入锁,这可能会稍微影响它们的性能。  

 

3.4 预写式日志 (WAL) 模式

如前所述,启用预写式日志 (WAL) 模式 (PRAGMA journal_mode=WAL;) 是提高 SQLite 并发性的基石 。通过在将更改提交到主数据库之前将其写入单独的日志文件,WAL 允许读取操作在写入器处于活动状态时并发继续进行。这比默认的回滚日志模式(任何写入都会阻止所有读取)具有显著优势。因此,对于几乎所有期望任何级别并发访问的应用程序,尤其是涉及读取和写入的应用程序,启用 WAL 模式是 SQLite 的强烈推荐的配置更改。它极大地提高了数据库在并发负载下的整体响应能力和吞吐量。   

WAL 模式还倾向于产生更多的顺序磁盘 I/O 操作,这通常比与回滚日志相关的随机 I/O 模式更有效 。此外,WAL 通常涉及更少的 fsync() 系统调用,这可能导致更快的写入事务,尤其是在将 synchronous 设置放宽为 NORMAL 时 。除了仅启用并发读取之外,WAL 模式还提供了与磁盘 I/O 效率和减少开销相关的其他几个性能优势,从而提高了整体数据库操作速度。   

 

然而,WAL 模式还引入了一个检查点过程,在该过程中,来自 WAL 文件的更改会定期写回到主数据库文件 。虽然此过程在很大程度上是自动的,但开发人员应该意识到这一点,尤其是在写入量非常大或长时间运行的读取事务可能干扰检查点操作的场景中。因此,虽然 WAL 模式通常是有益的,但了解检查点过程及其潜在影响(例如,磁盘 I/O 峰值、如果检查点受阻 WAL 文件可能增长)对于优化特定使用情况的性能非常重要。   

3.5 实验性并发特性

SQLite 具有一个实验性特性 BEGIN CONCURRENT,旨在通过使用乐观的页级锁定允许多个写入事务并发进行 。与标准 SQLite 通常只允许一个写入器不同,BEGIN CONCURRENT 会延迟锁定,直到 COMMIT 阶段。在提交时,SQLite 会检查自发出 BEGIN CONCURRENT 命令以来,事务读取的任何数据库页是否已被另一个并发事务修改。如果检测到冲突,则无法提交事务,并且会返回 SQLITE_BUSY_SNAPSHOT 错误,并且必须回滚事务。BEGIN CONCURRENT 代表了一种尝试克服 SQLite 单写入器限制的有趣方向。然而,其乐观性质意味着应用程序需要准备好处理由于冲突而导致的潜在事务回滚。此功能的有效性还可能很大程度上取决于数据库模式和并发写入操作的性质(例如,它们是否倾向于访问相同的数据页)。   

 

另一个实验性项目是 HC-tree,它被设想为 SQLite 的一个高并发后端,利用乐观的行级锁定 。这种方法旨在通过在行级别而不是页级别跟踪冲突,提供比 BEGIN CONCURRENT 更细粒度的并发控制。初步基准测试表明,HC-tree 有可能显著提高并发场景下的写入性能和可伸缩性。HC-tree 是对 SQLite 传统锁定机制的更激进的偏离,并且有望显著增强其处理高水平并发写入活动的能力。然而,由于它仍处于实验阶段,因此不建议用于生产环境,并且可能存在其自身的一系列限制和注意事项。   

 

4. 并发性能优化技术

4.1 配置设置

  • 启用预写式日志 (WAL): 如前所述,这通常是通过允许并发读取和通常更快的写入操作来提高并发性的最具影响力的配置更改 。   
  • 放宽同步模式:PRAGMA synchronous 设置为 NORMAL(甚至对于需要极致性能且承认电源故障时存在数据丢失风险的情况,可以设置为 OFF)可以通过不强制在每次提交后立即执行 fsync 到磁盘来减少 WAL 模式下写入操作的开销 。   
  • 调整缓存大小: 增加 cache_size 可以通过将更多数据库页保存在内存中来减少磁盘 I/O,从而提高读取和写入操作的性能 。最佳大小取决于可用内存和数据库大小。   
  • 启用内存映射 (mmap): 对于适合可用虚拟地址空间的数据库,使用 mmap_size 启用内存映射有时可以通过允许操作系统管理内存中的数据库页来提高性能,从而可能减少系统调用开销 。然而,其益处可能取决于平台。   
  • 设置繁忙超时: 配置适当的 busy_timeout 允许应用程序在数据库被锁定时等待一小段时间,而不是立即失败,这在适度并发的环境中可能是有益的 。   

4.2 事务管理

  • 保持事务简短: 如前所述,最小化事务(尤其是写入事务)的持续时间可以减少数据库被锁定的时间,从而允许其他操作更快地进行 。   
  • 批量操作: 在执行多个插入、更新或删除操作时,将它们包装在一个单独的事务中可以通过减少每个操作的开销来显著提高写入吞吐量 。   
  • 使用 BEGIN IMMEDIATE 对于已知执行写入操作的事务,使用 BEGIN IMMEDIATE 可以通过尝试在事务生命周期的早期获取独占锁来帮助管理锁并可能防止 SQLITE_BUSY 错误 。   

4.3 连接池和线程

  • 利用连接池: 在处理多个并发请求的应用程序(如 Web 服务器)中,使用连接池来管理一组打开的数据库连接可以减少重复打开和关闭连接的开销,从而提高整体性能并可能通过允许不同的请求使用不同的连接来帮助处理并发 。   
  • 为读取和写入使用单独的连接: 在读取繁重和写入繁重的操作之间存在明显分离的场景中,使用单独的连接池(甚至是一个专用于写入的连接,一个用于读取的连接池)可以成为优化并发性的策略。例如,拥有一个对所有写入操作使用 BEGIN IMMEDIATE 的连接,而一个连接池处理读取请求,可以帮助管理 SQLite 的单写入器限制 。   
  • 考虑将长时间运行的操作放在后台线程中: 对于可能长时间运行的读取或写入操作,将它们卸载到后台线程可以防止阻塞主应用程序线程并提高响应能力,尤其是在 UI 驱动的应用程序中 。   

5. 与其他轻量级数据库的比较

5.1 SQLite 与 LevelDB 和 RocksDB

  • 数据模型: SQLite 是一个关系数据库管理系统 (RDBMS),它将数据存储在具有行和列的表中,并支持使用 SQL 进行查询和数据操作 。另一方面,LevelDB 和 RocksDB 是键值存储,它们将数据存储为简单的键值对,没有预定义的模式或 SQL 支持 。数据模型上的根本差异决定了每种数据库最适合的应用类型。当需要结构化关系数据和 SQL 查询时,SQLite 是合适的,而 LevelDB/RocksDB 更适合基于键的简单数据存储和检索。   
  • 性能特点: LevelDB 和 RocksDB 通常针对非常快速的读取和特别是写入操作进行了优化,由于其日志结构合并树 (LSM 树) 架构,在写入密集型工作负载中通常比 SQLite 实现更高的吞吐量 。SQLite 及其 B 树存储通常在读取密集型工作负载和平衡的读/写场景中表现良好,但在极高的写入量下可能会较慢。如果应用程序的主要性能要求是非常高的写入吞吐量,并且对复杂关系查询的需求很小,那么 LevelDB 或 RocksDB 可能比 SQLite 更适合。   
  • 并发处理: LevelDB 和 RocksDB 被设计为处理高水平的读取和写入操作并发,通常采用比 SQLite 的文件级锁定更细粒度的锁定机制 。如前所述,SQLite 允许无限数量的并发读取器,但在任何给定时间只允许一个写入器。对于需要非常高的读取和写入并发性的应用程序,尤其是在多线程或多进程环境中,由于其更并发的特性,LevelDB 或 RocksDB 可能提供更好的可伸缩性和性能。   
  • 用例: SQLite 通常用于移动应用程序、嵌入式系统、桌面应用程序以及需要轻量级、自包含且支持 SQL 的数据库的中小型网站 。LevelDB 和 RocksDB 经常用于高吞吐量应用程序,如缓存系统、日志存储、实时分析以及作为其他数据库的存储引擎 。应用程序的预期用例是决定选择 SQLite 还是键值存储(如 LevelDB/RocksDB)的主要因素。需要关系特性和 SQL 的应用程序更适合 SQLite,而需要快速基于键的访问和高写入吞吐量的应用程序可能更喜欢 LevelDB/RocksDB。   

5.2 SQLite 与 DuckDB

SQLite 主要针对在线事务处理 (OLTP) 工作负载进行了优化,其特点是快速读取和写入单个记录 。相比之下,DuckDB 专为在线分析处理 (OLAP) 工作负载而设计,专注于对大型数据集进行快速查询以进行分析,通常涉及聚合、连接和复杂的 SQL 。主要的区别在于工作负载的类型。如果应用程序主要是事务性的,涉及频繁的单个记录操作,那么 SQLite 是一个强大的竞争者。如果应用程序涉及对大型数据集进行复杂的分析查询,那么 DuckDB 可能是更好的选择。   

SQLite 支持并发读取,但限制并发写入以确保数据完整性 。另一方面,DuckDB 允许进行多线程查询执行,使其能够利用多个 CPU 核心来并行处理分析查询 。虽然 SQLite 在读取并发方面表现出色,但 DuckDB 为复杂的分析任务提供了更好的并行性,利用多核处理器实现更快的查询执行。   

SQLite 使用行式存储格式,其中给定记录的所有列都存储在一起 。DuckDB 采用列式存储格式,其中每个列的数据分别存储 。对于通常只涉及读取跨多行的特定列子集的分析查询,列式存储通常更有效。存储格式的选择显著影响不同类型查询的性能。行式存储对于访问整个记录很有效,而列式存储则针对涉及跨特定列进行聚合和选择的分析查询进行了优化。   

DuckDB 提供对直接读取 CSV、Parquet 和 Arrow 等各种文件格式的内置支持,这在数据科学工作流程中很常见 。SQLite 通常依赖于 SQL 语句或外部 API 从此类源加载数据。对于经常处理常见分析文件格式数据的应用程序,DuckDB 的原生文件格式支持简化了数据摄取和处理。   

 

DuckDB 通常被称为“用于分析的 SQLite”,突显了其提供针对分析工作负载优化的嵌入式数据库解决方案的目标,类似于 SQLite 服务于事务性需求的方式 。   

6. SQLite 在高并发现实场景中的应用

6.1 Web 应用程序的适用性

SQLite 通常适用于绝大多数网站,尤其是那些流量较低到中等(通常认为每天低于 10 万次点击)且主要以读取数据为主的网站 。SQLite 官方网站本身就使用 SQLite 作为其后端,并处理大量的流量 。对于很大一部分 Web 应用程序,尤其是内容繁重的应用程序或并发写入操作相对较少的应用程序,SQLite 可以是一个高性能、可靠且易于管理的数据库解决方案。其嵌入式特性消除了对独立数据库服务器的需求,简化了部署架构。   

然而,对于流量非常大的网站或那些写入密集型且可能需要跨多个服务器进行扩展的网站,SQLite 的单写入器限制成为一个重要的约束 。在这种情况下,像 PostgreSQL 或 MySQL 这样的企业级客户端-服务器数据库系统,它们被设计为处理更高水平的并发写入活动并提供分布式部署的功能,将是更合适的选择。当 Web 应用程序预期会有极高的流量或具有高频率的写入操作,这可能导致 SQLite 中单个写入器锁的显著争用时,考虑专门为这种高要求工作负载设计的替代数据库系统至关重要。   

对于流量适中但仍需要处理并发写入的 Web 应用程序,采用优化技术(如启用 WAL 模式、使用 IMMEDIATE 事务、仔细管理连接池以及可能实施应用程序级写入队列)可以帮助缓解 SQLite 并发模型的限制并提高整体性能和响应能力 。通过利用 SQLite 的配置选项和采用适当的应用程序设计模式,开发人员通常可以有效地扩展 SQLite 的可用性,以处理适度并发的 Web 应用程序。然而,持续监控性能并准备在应用程序的需求超出 SQLite 的能力时迁移到更具可伸缩性的解决方案非常重要。   

6.2 多用户应用程序注意事项

在多个用户或进程可能并发访问和修改数据的多用户桌面或服务器应用程序中,如果应用程序遇到大量的并发写入请求,SQLite 的单写入器限制可能会导致性能瓶颈和“数据库已锁定”错误 。虽然 SQLite 可以高效地处理并发读取,但写入的序列化会随着并发写入用户数量的增加而成为限制因素。因此,对于具有显著写入并发需求的多用户应用程序,SQLite 可能无法提供最佳的性能或可伸缩性。锁争用的可能性以及由此产生的 SQLITE_BUSY 错误可能会对用户体验和应用程序的可靠性产生负面影响。   

解决多用户场景中并发限制的一种策略(尤其是在数据共享要求不严格的情况下)是采用每个用户的数据库方法,其中每个用户或会话都与自己的独立 SQLite 数据库文件进行交互 。这有效地分片了数据并减少了对单个数据库文件的争用。然而,这种方法需要仔细管理多个数据库文件,并且可能不适用于需要跨用户进行实时数据共享的应用程序。因此,在数据隔离可以接受的多用户应用程序中,按用户分片可能是一种可行的扩展 SQLite 的技术。这种方法可以通过消除不同用户之间对单个写入锁的争用来显著提高并发性。   

SkyPilot 描述的真实世界经验,其中来自不同进程的大量并发写入操作导致 SQLITE_BUSY 错误,突显了即使使用 WAL 模式和增加的锁定超时等优化,在高度并发写入场景中使用 SQLite 所面临的挑战 。这些经验表明,对于具有非常高的并发写入需求的应用程序,可能需要专门为这种工作负载设计的数据库系统。   

7. 结论

SQLite 为各种应用程序提供了一个引人注目的性能和简单性组合,尤其是在读取密集型工作负载或低到中等流量的应用程序中。其对于许多常见数据库操作的速度可能相当令人印象深刻,尤其是在正确配置并在其预期范围内使用时。然而,其并发能力主要面向处理大量的并发读取器。在任何给定时间只允许一个写入器的基本限制对于需要高水平并发写入活动的应用程序来说是一个重要的制约因素。虽然 WAL 模式和其他优化技术可以在一定程度上帮助缓解此限制,但这仍然是一个需要考虑的关键因素。

SQLite 的主要优势包括其易用性和集成性(作为一个嵌入式库),由于其单文件数据库格式而具有出色的可移植性,对于读取操作和许多事务性工作负载通常具有良好的性能,零配置特性以及低资源占用,使其适用于各种环境,包括资源受限的设备。它还拥有一个成熟且经过广泛测试的代码库,并支持 ACID 事务。

SQLite 在本报告中讨论的主要缺点是其有限的写入并发性,这在具有高写入吞吐量要求或大量并发写入用户或进程的应用程序中可能成为瓶颈。即使在 WAL 模式下进行了改进,其文件级锁定机制在高写入负载下也可能导致争用和“数据库已锁定”错误。此外,其对于分布式、多服务器架构的适用性有限,并且缺乏企业级数据库系统中发现的一些高级功能,例如用户级别的细粒度访问控制。

SQLite 仍然是各种应用程序的绝佳选择,包括桌面和移动应用程序中的本地数据存储、嵌入式系统以及主要以读取为主的低到中等流量网站。其简单性和性能通常使其成为这些用例中非常有效的解决方案。对于预期会有中等水平并发写入的 Web 应用程序,启用 WAL 模式并考虑使用 IMMEDIATE 事务、调整缓存和同步设置以及实施连接池和潜在的应用程序级写入管理策略至关重要。仔细设计事务以使其简短和批量处理也有助于在 SQLite 的并发限制内最大化性能。然而,对于需要非常高的并发写入吞吐量、需要跨多个服务器进行水平扩展或具有频繁且同时写入访问同一数据的多用户系统,通常建议考虑专门为处理此类高要求工作负载而设计的替代数据库系统。企业级 RDBMS(如 PostgreSQL 或 MySQL)或 NoSQL 解决方案(如 LevelDB 或 RocksDB,取决于数据模型和查询需求)可能是这些场景中更合适的选择。诸如 BEGIN CONCURRENT 和 HC-tree 之类的实验性功能为未来的写入并发性提供了潜在的改进,但尚未准备好用于生产环境。

全部评论