使用System.Data.SQLite在c#应用程序中缓慢打开SQLite连接

本文关键字:SQLite 缓慢 连接 System Data 使用 应用程序 | 更新日期: 2023-09-27 17:54:04

编辑3:

我想我的问题暂时解决了…我更改了我的服务和测试应用程序,以SYSTEM帐户而不是NetworkService帐户运行。更改用户帐户的好处是否会持续存在,还是只是暂时的,还有待观察。

原始问题:

我注意到我的小224kB SQLite DB在我的c#应用程序中打开非常慢,从几毫秒到1.5秒或更长时间不等。下面是我的代码,以及我今天下午添加的所有额外的调试语句。我已经将其缩小到呼叫cnn.Open();,如日志中所示:

2014-03-27 15:05:39,864 DEBUG - Creating SQLiteConnection...
2014-03-27 15:05:39,927 DEBUG - SQLiteConnection Created!
2014-03-27 15:05:39,927 DEBUG - SQLiteConnection Opening...
2014-03-27 15:05:41,627 DEBUG - SQLiteConnection Opened!
2014-03-27 15:05:41,627 DEBUG - SQLiteCommand Creating...
2014-03-27 15:05:41,627 DEBUG - SQLiteCommand Created!
2014-03-27 15:05:41,627 DEBUG - SQLiteCommand executing reader...
2014-03-27 15:05:41,658 DEBUG - SQLiteCommand executed reader!
2014-03-27 15:05:41,658 DEBUG - DataTable Loading...
2014-03-27 15:05:41,767 DEBUG - DataTable Loaded!

可以看到,在这个实例中,打开连接花费了1.7秒。我试着重复这个,但无法预测后续连接是否会立即打开,还是像这样延迟。

我考虑过使用某种形式的连接池,但是对于一个单实例单线程应用程序来说,它值得吗?现在,我正在创建我的sqliteddatabase类的实例,并为我的每个查询调用下面的函数。

public DataTable GetDataTable(string sql)
{
    DataTable dt = new DataTable();
    try
    {
        Logging.LogDebug("Creating SQLiteConnection...");
        using (SQLiteConnection cnn = new SQLiteConnection(dbConnection))
        {
            Logging.LogDebug("SQLiteConnection Created!");
            Logging.LogDebug("SQLiteConnection Opening...");
            cnn.Open();
            Logging.LogDebug("SQLiteConnection Opened!");
            Logging.LogDebug("SQLiteCommand Creating...");
            using (SQLiteCommand mycommand = new SQLiteCommand(cnn))
            {
                Logging.LogDebug("SQLiteCommand Created!");
                mycommand.CommandText = sql;
                Logging.LogDebug("SQLiteCommand executing reader...");
                using (SQLiteDataReader reader = mycommand.ExecuteReader())
                {
                    Logging.LogDebug("SQLiteCommand executed reader!");
                    Logging.LogDebug("DataTable Loading...");
                    dt.Load(reader);
                    Logging.LogDebug("DataTable Loaded!");
                    reader.Close();
                }
            }
            cnn.Close();
        }
    }
    catch (Exception e)
    {
        throw new Exception(e.Message);
    }
    return dt;
}

编辑:

当然,dbConnection是连接字符串,由下面的函数设置。inputFile只是要打开的文件名的字符串路径。

public SqLiteDatabase(String inputFile)
{
    dbConnection = String.Format("Data Source={0}", inputFile);
}

在这一点上,我认为sql是无关紧要的,因为当cnn.Open()停止时,它没有达到这一点。

编辑2:

好的,我又做了一些测试。在本地运行测试,它在大约5秒内完成1000次迭代循环,每次调用cnn.Open()大约5ms。我在本地PC上使用相同的windows安装程序运行测试,大约25分钟完成,平均每次调用cnn.Open() 1468毫秒。

我做了一个小的测试程序,它只调用服务程序中的TestOpenConn()函数(与Windows服务中运行的代码完全相同),运行在位于测试目录中的文件副本上。在服务器或我的本地PC上运行此程序会获得可接受的性能(在服务器上每次调用1.95ms,在本地PC上每次调用4ms):

namespace EGC_Timing_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Logging.Init("log4net.xml", "test.log");
            var db = new SqLiteDatabase("config.sqlite");
            db.TestOpenConn();
        }
    }
}
下面是测试函数:
public void TestOpenConn()
{
    // TODO: Remove this after testing loop of opening / closing SQLite DB repeatedly:
    const int iterations = 1000;
    Logging.LogDebug(String.Format("Running TestOpenConn for {0} opens...", iterations));
    var startTime = DateTime.Now;
    for (var i = 0; i < iterations; i++)
    {
        using (SQLiteConnection cnn = new SQLiteConnection(dbConnection))
        {
            Logging.LogDebug(String.Format("SQLiteConnection Opening, iteration {0} of {1}...", i, iterations));
            var startTimeInner = DateTime.Now;
            cnn.Open();
            var endTimeInner = DateTime.Now;
            var diffTimeInner = endTimeInner - startTimeInner;
            Logging.LogDebug(String.Format("SQLiteConnection Opened in {0}ms!", diffTimeInner.TotalMilliseconds));
            cnn.Close();
        }
    }
    var endTime = DateTime.Now;
    var diffTime = endTime - startTime;
    Logging.LogDebug(String.Format("Done running TestOpenConn for {0} opens!", iterations));
    Logging.LogInfo(String.Format("{0} iterations total:'t{1}", iterations, diffTime));
    Logging.LogInfo(String.Format("{0} iterations average:'t{1}ms", iterations, diffTime.TotalMilliseconds/iterations));
}

使用System.Data.SQLite在c#应用程序中缓慢打开SQLite连接

我想我的问题暂时解决了…我更改了我的服务和测试应用程序,以SYSTEM帐户而不是NetworkService帐户运行。更改用户帐户的好处是否会持续存在,还是只是暂时的,还有待观察。

我假设您使用的是开源的System.Data.SQLite库。

如果是这样的话,很容易通过Visual Studio性能分析器看出SQLiteConnection类的Open方法有一些严重的性能问题。另外,查看一下这个类的源代码:https://system.data.sqlite.org/index.html/artifact/97648754af51ffd6

读取XML配置和Windows环境变量需要进行大量的磁盘访问。

我的建议是尝试尽可能少地调用Open(),并尝试在内存中保持对这个开放SQLiteConnection对象的引用。使用SQLite Forum

引发性能罚单

有同样的问题,我正在调查这个,它似乎与文件或它的父文件夹的权限有关,谁创建了它,和/或它是如何创建的。在我的例子中,SQLite数据库文件是由作为普通用户运行的脚本创建的,然后iis托管的服务将在不同的域服务帐户下访问该文件。

每次服务打开一个连接,它需要超过1.5秒,但其他操作是正确的(它最终可以访问文件)。作为普通用户运行的独立程序可以在几毫秒内打开到同一位置的相同文件的连接。

对procmon跟踪的分析显示,在该服务的情况下,我们在大约1.5秒的过程中获得了文件上的几个ACCESS DENIED日志,这些日志在作为普通用户运行时不存在于跟踪中。

不知道发生了什么。该服务运行良好,最终能够查询文件中的数据,尽管速度很慢。

当我们将服务帐户设置为文件父文件夹的所有者,并赋予他写权限时,ACCESS DENIED日志消失,服务全速运行

您可以在数据库文件夹中添加适当用户的"Modify"权限。右键单击文件夹>属性>安全>编辑>添加(我添加了IIS_Users)>选择"修改"复选框> OK