SQLite与并发编写的替代方案(Delphi)



(在WAL模式下使用Delphi 2010+最新SQLite)

我正在将DISQLite(SQLite的Delphi端口)与我的多线程客户端应用程序(尚未发布,因此如果我真的必须更改DB引擎)

我的探查器清楚地说这是一个愚蠢的决定,我把它追溯到了2-3个非常简单的SQL语句,当在单线程应用程序中执行时,这些语句会飞起来,但因为线程锁定/等待(SQLite在多个线程试图同时写的情况下确实表现不佳)

我尽了最大努力优化我的代码/避免瓶颈,但经过几周的努力,我现在想知道转储SQLite&选择不同的数据库引擎(?)

我的要求是:

  1. 非常好的同时读/写(记录级别)支持
  2. (非常)快速;稳定DB引擎
  3. B-树
  4. Delphi 2010支持

我只使用带索引的基本INSERT/UPDATE/DELETE命令,没有什么特别之处。因此,我的SQL需求是相对基本的(我不需要联接或其他"更高级"的SQL内容)。

我也对NQL解决方案持开放态度,只要它支持上述要求。

我的研究引出了Berkley DB,如果我理解正确的话,它是一个具有并发写作支持的SQLite的修改版本,但问题是它并不适用于delphi。

我也读过关于京都内阁的文章,但话说回来,没有delphi支持:(

任何建议都非常受欢迎,

谢谢!

如果:,您的应用程序速度是多少

  • 所有线程只使用一个DB连接
  • 您可以使用全局关键部分来保护数据库连接访问

然后你可以尝试我们的Sqlite3静态绑定,它是在没有线程互斥的情况下编译的:

#define SQLITE_THREADSAFE 2
//  assuming multi-thread safety is made by caller - in our framework, there is
// only one thread using the database connection at the same time, but there could
// be multiple database connection at the same time (previous was 0 could be unsafe)
#define SQLITE_OMIT_SHARED_CACHE 1
// no need of shared cache in a threadsafe calling model

我们在我们的mORMotORM框架中使用了这样一个模型,并与四个级别的缓存相关联:

  • 用于SQL语句重用的语句缓存,以及动态绑定参数
  • 数据库级别的全局JSON结果缓存,在任何INSERT/UPDATE上全局刷新
  • 为服务器端的指定表或记录在CRUD/RESTful级别上调整的记录缓存
  • 为客户端上的指定表或记录在CRUD/RESTful级别调整的记录缓存

由此产生的性能一点也不差——它在多线程访问中可以很好地扩展,即使有全局关键部分也是如此。当然,SQlite3的扩展能力并不如Oracle!但我在实际应用程序中使用过SQlite,有很多客户端。您可以考虑使用FireBird,它为客户端服务器提供了更复杂(经过调优)的体系结构。

关于让写作更快,你可以将你的写作分组到一个事务中,然后它会更快这就是我用于加速写入的方法,您可以将这一概念扩展到多个客户端:在服务器端,您将写入重新组合为一个共享事务,该事务将在超时期(例如一秒钟)后提交。

SQLite3对于这样的添加来说非常快(使用带有绑定参数的准备好的INSERT语句更是如此),但对于单个添加来说速度很慢,因为它必须使用低级API锁定整个文件,这非常慢。为了使其成为ACID,请确保始终处理提交。事实上,其他数据库引擎通过隐藏在后台的类似进程实现了良好的并发速度。SQLite3默认的编写方法应该是这样的,以确保从多个进程访问同一个文件-但在客户机-服务器应用程序中,您可以依赖这样一个事实,即您将是唯一一个访问SQLite3数据库文件的人,因此它是安全的。

像Firebird DB的嵌入式版本有什么帮助吗?

FirbirdSQL.org下载页面

我过去曾成功地使用过这个。

只需将表(可以同时写入)拆分为单独的SQLite数据库文件,并使用主连接将它们连接在一起。

FWIW,我最终决定坚持使用DISQLite以及这个"丑陋"、黑客化的解决方案:

  • 做了一些(不太小的)更改,以尽可能减少线程内对数据库的写入(每个线程需要插入两个数据库)

  • 当我在线程内工作时,必须向DB写入一些东西时,我使用了SQL查询参数&将它们写在一个特殊的文件夹中(写入文件非常快),即

C: \我的项目\挂起的sql\insert_SOME-GUID.txt

每个文件看起来像这样:

Param1|Param2|Param3|Param4|

  • 一旦我处理完线程(或者如果我的应用程序崩溃),我就会调用一个例程来扫描这个文件夹,提取SQL参数,并使用准备好的语句(封装在事务中)运行它们。

  • 任何包含少于4个参数的文件都将被视为已损坏,并将被跳过。

这是一个非常糟糕的算法(我真遗憾!),但它很有效,速度很快,有点像ACID,我不必花几个月的时间学习另一个可能合适(也可能不合适)的DB引擎。

我只是想感谢大家的帮助,时间压力让我无法切换到另一个DB引擎,至少在这个项目中是这样。

这是我的观点:

绝对数据库是一个不错的选择。

NexusDB可以做到这一切,嵌入式版本是免费的。它支持德尔福成为一等公民。

最新更新