6

编辑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 秒。我试过重复这个,并且无法预测后续连接是否会几乎立即打开,或者像这样被延迟。

我考虑过使用某种形式的连接池,但是对于单实例单线程应用程序是否值得追求?现在,我正在创建我的 SQLiteDatabase 类的实例,并为我的每个查询调用以下函数。

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(). 从我在本地 PC 上执行的同一 Windows 安装程序运行测试,它在大约 25 分钟内完成,每次调用cnn.Open().

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

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));
}
4

4 回答 4

5

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

于 2014-04-01T12:20:30.290 回答
3

我假设您正在使用开源System.Data.SQLite库。

如果是这样的话,通过Visual Studio Performance Profiler很容易看出该类的Open方法SQLiteConnection存在一些严重的性能问题。另外,请在此处查看此类的源代码:https ://system.data.sqlite.org/index.html/artifact/97648754af51ffd6

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

我的建议是Open()尽可能少地尝试调用,并尝试SQLiteConnection在内存中保留对这个开放对象的引用。一张表演票被提出SQLite Forum

于 2015-08-21T00:05:48.723 回答
1

Having had the same problem, I was looking into this and it seems to be related to permissions on the file or it's parent folders, who created it, and/or how it was created. In my case, the SQLite database file was being created by a script run as a regular user, and then an IIS-hosted service would access the file under a different domain service account.

Every time the service opened a connection, it took over 1.5 seconds, but otherwise operated correctly (it could eventually access the file). A stand-alone program running as the regular user could open a connection to the same file in the same place in a few milliseconds.

Analysis of a procmon trace revealed that in the case of the service, we were getting several ACCESS DENIED logs on the file over the course of about 1.5 seconds, that were not present in the trace when running as the regular user.

Not sure what's going on there. The service worked fine and was able to eventually query the data in the file, albeit slowly.

When we made the service account the owner of the parent folder of the file, and gave him write permission, the ACCESS DENIED logs disappeared and the service operated at full speed.

于 2015-09-18T15:00:54.937 回答
0

您可以将适当用户的“修改”权限添加到包含数据库的文件夹中。右键单击文件夹>属性>安全>编辑>添加(我添加了IIS_Users)>选择“修改”复选框>确定

于 2014-08-07T19:54:41.160 回答