在本系统运行中,偶尔会出现奇怪的错误提示,根据后台日志提示,在系统并发比较大的时候,容易出现The database file is locked的错误,具体错误信息在后台log文件的记录如下:
2008-12-19 06:54:59,609 [20] ERROR- 系统出现未处理错误:
System.Data.SQLite.SQLiteException: The database file is locked
database is locked
在 System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
在 System.Data.SQLite.SQLiteDataReader.NextResult()
在 System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
在 System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
在 LevenBlog.Data.Sqlite.SqliteHelper.ExecuteNonQuery(IDbProvider provider, String sqlString, IDataParameter[] sqlParas)
在 LevenBlog.Data.Sqlite.SqliteState.Update(IDbProvider provider, lb_state state)
在 LevenBlog.Service.Mvc.BlogBaseController.OnActionExecuting(ActionExecutingContext filterContext)
在 System.Web.Mvc.Controller.System.Web.Mvc.IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
在System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilterfilter, ActionExecutingContext preContext, Func`1 continuation)
在System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassc.<>c__DisplayClasse.<InvokeActionMethodWithFilters>b__b()
在System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(MethodInfomethodInfo, IDictionary`2 parameters, IList`1 filters)
在 System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
查了下sqlite的信息,发现Sqlite在并发的情况下确实有一些问题,FAQ的信息如下:
(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 数据库放入子过程中,否则会出错。
因此可以肯定,系统出现lock错误是因为同时2个或者2个以上的线程对数据库执行写操作,本来数据库写入应该在很短的时间内完成,而其他进程也可以顺序写入数据,但是某些时候老f的服务器可能会资源不够用导致某个写入进程老半天没写入完成而导致其他写入进程超时错误,从而出现了lock的错误.
解决方案:sqlite毕竟是面向嵌入式系统的小型数据库,我们无法要求它拥有很高的并发能力,因此我们可以考虑在程序上对这个问题进行修正,比如在对写入数据库的操作加锁,使系统可以避免此类错误,当然,更好的选择是使用SqlServer版的系统,比如51mvc.com就没出现这样的问题.最近没啥时候,而且问题并不严重,看了下出现的几率还是很小的,所以决定稍等等在修正这个问题.