防止IIS在SQLite数据库文件上保持文件锁

3
我们有一个传统的Web应用程序,使用Classic ASP编写,目前使用MS Access数据库作为其数据存储。与数据库的交互通过Jet OleDB提供程序进行:
Provider=Microsoft.JET.OLEDB.4.0;Data Source=database.mdb

与大多数Web应用程序一样,数据库上定期执行CRUD操作。

应用程序连接到数据库,执行插入更新删除选择语句,然后关闭连接。在连接关闭后立即可以毫不担心地重命名移动MS Access .mdb文件。我们利用了这种行为多年——它对我们的用例是必需的,而且一直表现良好。

出于各种原因,我们正在将MS Access数据库替换为SQLite 3。我们通过ODBC连接到SQLite版本。

与SQLite数据库的交互方式与以前完全相同,即连接到数据库,发出SQL命令,然后关闭连接。

问题在于,在关闭连接后的60秒内,SQLite数据库文件仍然被锁定。这会防止我们重命名或移动数据库文件,因为很可能在这60秒内发生另一个连接,所以该文件似乎永久锁定。

我们创建了一个最小的Classic ASP页面来说明这种行为:

Dim connectionString: connectionString = "DRIVER=SQLite3 ODBC Driver;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;FKSupport=0;NoCreat=1;Database=database.sqlite"

Dim con : Set con = CreateObject("ADODB.Connection")
con.Open connectionString

' Issue some CRUD commands here

con.Close
Set con = Nothing
Response.Write("We are here!!!!!")

为了确定是什么导致了锁定,我们创建了一个 VBScript 文件(使用与上面显示的相同的 SQLite 连接字符串),然后从命令行运行它(因此 IIS 根本不参与)。
在这种情况下,在连接后发出任何 CRUD 命令并关闭数据库之后,SQLite 文件立即可访问。因此我们得出结论,一些方式上 IIS 7.5(或更准确地说是 W3WP.exe)对 SQLite 文件进行了锁定。事实上,如果停止网站的应用程序池,则文件上的锁定会立即释放。
VBScript 文件的代码如下:
Dim connectionString: connectionString = "DRIVER=SQLite3 ODBC Driver;LongNames=0;Timeout=1000;NoTXN=0;SyncPragma=NORMAL;StepAPI=0;FKSupport=0;NoCreat=1;Database=database.sqlite"

Dim con : Set con = CreateObject("ADODB.Connection")
con.Open connectionString

' Issue some CRUD commands here

WScript.Echo "Press [ENTER] to continue..."

' Read dummy input. This call will not return until [ENTER] is pressed.
WScript.StdIn.ReadLine

WScript.Echo "Done."

con.Close
Set con = Nothing

是否有一种在IIS中的设置,使我们可以像当前使用MS Access一样操作SQLite数据库?


如果之前使用MSAccess DB可以正常工作,你怎么得出IIS有问题的结论?更可能的原因是SQLite驱动程序导致了这种行为,而不是IIS。 - user692942
@Lankymart 我已经编辑了我的问题,希望更清楚地表明,从VBScript文件内连接SQLite不会导致文件锁定60秒。但是,在Classic ASP文件内连接SQLite会锁定文件60秒。两种情况下都使用相同的驱动程序和连接字符串。 - Simon Lomax
听起来你正在执行 conn.Close 但没有跟上 Set conn = Nothing - Gord Thompson
可能与连接池有关,尽管对于您正在使用的SQLite驱动程序,池化默认情况下是禁用的。也许IIS只是在做自己的事情。找到了这个链接,声称IIS仅在60秒后释放ODBC连接:http://knowledgebase.progress.com/articles/Article/ODBC-connection-from-ASP-remains-for-60-seconds - mrstebo
@GordThompson 实际上我们正在发出 Set conn = Nothing 命令。我编辑了问题以展示代码。 - Simon Lomax
@mrstebo 很好的发现,根据这个10. Q. How do I clear out the connections in the pool?看起来非常可能。 - user692942
2个回答

4

我重新访问了这个问题,大约一年前发布原始问题,因为我的一个同事实际上发现了适用于我们的解决方案,也许对其他人有所帮助。

可行的解决方案是对注册表进行一些更改。这与CH Werner SQLIte ODBC驱动程序无关。事实上,我们与CH Werner本人进行了电子邮件交流,他确认驱动程序不会影响我们描述的数据库锁定。

解决方案是对注册表进行以下更改:

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{c8b522cb-5cf3-11ce-ade5-00aa0044773d}]
@="MSDASQL"
"OLEDB_SERVICES"=dword:fffffffe
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Wow6432Node\CLSID\{c8b522cb-5cf3-11ce-ade5-00aa0044773d}]
@="MSDASQL"
"OLEDB_SERVICES"=dword:fffffffe

如果你把上面所描述的注册表更改放入一个扩展名为.reg的文件中,就可以在Windows资源管理器中双击该文件以进行更改。

一旦注册表具有这些设置,IIS在连接关闭后不会再锁定SQLite数据库文件60秒。

希望这能帮助其他遇到此问题的人。


哇,那些是非常具体的注册表键。很高兴听到你终于找到了解决方案。 - mrstebo
嗨,伙计,希望你一切都好。是的,Nick S在一个MySql论坛上找到了解决方案。它已经在生产中运行了6个月了。只是一直忘记回来这里发布解决方案。 - Simon Lomax

2
使用 CH Werner SQLite ODBC Driver 时无法关闭连接池。驱动程序中有代码处理 SQL_ATTR_CONNECTION_POOLING 属性,但该属性从未被使用。这意味着默认情况下它将使用连接池。
停止 IIS 锁定数据库需要修改 DLL,包括 ConnectionPooling=True/FalseCPTimeout=n,以便我们可以向 SQLSetEnvAttr 函数发送适当的值。

@Simon,你应该给这项伟大的调查工作点个赞! - user692942
@Lankymart,说得好,我刚刚点了赞。只是让你知道,mrstebo实际上离我只有两个桌子的距离。他正在做同一个项目 :) - Simon Lomax
@SimonLomax 他们仍然找到了答案,可能值得在答案中注明,但对我来说没有改变。如果其他人遇到这个问题,仍然会有帮助。 - user692942

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接