SQLite和MySQL数据库的区别与应用
sqlite3的进程安全访问测试
https://blog.csdn.net/LostSpeed/article/details/83452393
想写个demo, 由C程序写sqlite3数据库中的表A, 由java程序去读sqlite3数据库中的表A.
这样,就涉及到sqlite3的线程安全和进程安全访问。
写好测试程序后,跑起来,很失望。
sqlite3同一个表无法由2个进程同时读写表记录。
总有一个进程会出现“database is locked”的报错,去查了资料。大概意思是:因为是文件型数据库,无法使2个进程同时向同一个文件写数据。我的应用是一个读(读完一条记录删一条),一个写,应该会触发sqlite的文件读写锁吧。
尝试sql执行失败后重试,倒是可以继续跑。但是每一个SQL都有可能失败,都要重试。这执行效率就下来了。
换其他数据库了,失望。
其实,sqlite作者是可以解决这个问题的。只需要封装sqlite3_xx的接口实现不去直接操作文件,而是由同一个数据库代理程序(第一次打开数据库时创建数据库代理程序的实例)访问数据库文件,这样,就可以达到多个进程共享一个数据库文件,数据库的打开和关闭,带上引用计数就好。改动的代码也不大。谁知道sqlite作者咋想的呢,哪个数据库的使用者没有多进程访问同一个数据库的需求呢?
如果使用者非要使用sqlite3, 来支持多进程安全访问。只能自己再封装一组接口,由自己的程序来控制数据库文件的访问(同一个数据库文件只有一个访问者,对使用者隐藏了数据库文件的操作). sqlite3自己可以保证线程安全性。这样也能进程安全,其实这活应该由sqlite3数据库来完成。如果使用者要想那么多,谁还来用这么难用的数据库。毕竟大部分数据库都是可以进程安全的。
说来说去,其实sqlite3需要加一个服务程序,那些sqlite3_xx接口,都是通过本地socket来向服务程序读写数据,这样就正常了。这样改动也不大。
sqlite的线程安全问题
https://blog.csdn.net/fyfywg/article/details/9006057
SQLite的FAQ里面已经专门说明,先贴出来。供以后像我目前的入门者学习。
以后详细再看。估计互斥锁应该可以在这个问题上应用。
看来从版本3.3.1基本上已经支持线程句柄的传递功能。具体限制我标记了一下。
(6) Is SQLite threadsafe?
SQLite is threadsafe. We make this concession since many users choose to ignore the advice given in the previous paragraph. But in order to be thread-safe, SQLite must be compiled with the SQLITE_THREADSAFE preprocessor macro set to 1. Both the Windows and linux precompiled binaries in the distribution are compiled this way. If you are unsure if the SQLite library you are linking against is compiled to be threadsafe you can call the sqlite3_threadsafe() interface to find out.
Prior to version 3.3.1, an sqlite3 structure could only be used in the same thread that called sqlite3_open() to create it. You could not open a database in one thread then pass the handle off to another thread for it to use. This was due to limitations (bugs?) in many common threading implementations such as on RedHat9. Specifically, an fcntl() lock created by one thread cannot be removed or modified by a different thread on the troublesome systems. And since SQLite uses fcntl() locks heavily for concurrency control, serious problems arose if you start moving database connections across threads.
The restriction on moving database connections across threads was relaxed somewhat in version 3.3.1. With that and subsequent versions, it is safe to move a connection handle across threads as long as the connection is not holding any fcntl() locks.
You can safely assume that no locks are being held if no transaction is pending and all statements have been finalized.
只要不在事务过程当中,并且所有的statements已经被finalized,那么不同线程间共享句柄就是安全的.
Under UNIX, you should not carry an open SQLite database across a fork() system call into the child process. Problems will result if you do.
(7) 多个应用程序或者同一个应用程序的多个例程能同时存取同一个数据库文件吗?
多进程可以同时打开同一个数据库,也可以同时 SELECT 。但只有一个进程可以立即改数据库。
SQLite使用读/写锁定来控制数据库访问。(Win95/98/ME 操作系统缺乏读/写锁定支持,在低于 2.7.0 的版本中,这意味着在 windows 下在同一时间内只能有一个进程读数据库。在版本 2.7.0 中 这个问题通过在 windows 接口代码中执行一个用户间隔几率读写锁定策略解决了。) 但如果数据库文件在一个 NFS 文件系统中,控制并发读书的锁定机制可以会出错。因为 NFS 的fcntl() 文件锁定有时会出问题。如果有多进程可能并发读数据库则因当避免把数据库文件放在 NFS 文件系统中。 根据微软的文档,如果不运行 Share.exe 后台程序则 FAT 文件系统中的锁定可能不工作。对 Windows 非常有经验的人告诉我网络文件的锁定有许多问题并且不可靠。如果是这样,在2个或以上 Windows 系统中共享一个 SQLite 数据库文件会导致不可预知的问题。
我们知道没有其他的嵌入式 SQL数据库引擎比SQLite支持更多的并发性。 SQLite允许多进程 同时打开和读取数据库。任何一个进程需要写入时,整个数据库将在这一过程中被锁定。但这一般仅耗时 几毫秒。其他进程只需等待然后继续其他事务。其他嵌入式SQL数据库引擎往往只允许单进程访问数据库。
但是,client/server型的数据库引擎 (如 PostgreSQL, MySQL, 以及 Oracle) 通常支持更高的并发度, 并支持多进程同时写入同一个数据库。由于总有一个控制良好的服务器协调数据库的访问,这才保证了以上 特性的实现。如果你的应用需要很高的并发度,你应该考虑使用client/server数据库。事实上,经验告诉 我们大多数应用所需要的并发度比他们的设计者们想象的要少得多。
当 SQLite 尝试操作一个被另一个进程锁定的文件时,缺省的行为是返回 SQLITE_BUSY。你可以用 C代码更改这一行为。 使用 sqlite3_busy_handler() 或sqlite3_busy_timeout() API函数。
如果两个或更多进程同时打开同一个数据库,其中一个进程创建了新的表或索引,则其它进程可能不能立即看见新的表。其它进程可能需要关闭并重新连结数据库。
(8) SQLite是线程安全的吗?
有时候是的。为了线程安全,SQLite 必须在编译时把 THREADSAFE 预处理宏设为1。在缺省的发行的已编译版本中 Windows 版的是线程安全的,而 Linux 版的不是。如果要求线程安全,Linux 版的要重新编译。
“线程安全”是指二个或三个线程可以同时调用独立的不同的sqlite3_open() 返回的"sqlite3"结构。而不是在多线程中同时使用同一个 sqlite3 结构指针。
一个sqlite3结构只能在调用 sqlite3_open创建它的那个进程中使用。你不能在一个线程中打开一个数据库然后把指针传递给另一个线程使用。这是因为大多数多线程系统的限制(或 Bugs?)例如RedHat9上。在这些有问题的系统上,一个 线程创建的fcntl()锁不能由另一个线程删除或修改。由于SQLite依赖fcntl()锁来进行并发控制,当在线程间传递数据库连接时会出现严重的问题。
也许在Linux下有办法解决fcntl()锁的问题,但那十分复杂并且对于正确性的测试将是极度困难的。因此,SQLite目前不允许在线程间共享句柄。
在UNIX下,你不能通过一个 fork() 系统调用把一个打开的 SQLite 数据库放入子过程中,否则会出错。