簡體   English   中英

使用 System.Data.SQLite 在 C# 應用程序中緩慢打開 SQLite 連接

[英]Slow opening SQLite connection in C# app using System.Data.SQLite

編輯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()大約需要 5 毫秒。 從我在本地 PC 上執行的相同 Windows 安裝程序中運行測試,它在大約 25 分鍾內完成,每次調用cnn.Open()平均需要 1468 cnn.Open()

我制作了一個小型測試程序,它只從服務程序(與 Windows 服務中運行的代碼完全相同TestOpenConn()調用TestOpenConn()函數,針對位於測試目錄中的文件副本運行。 在服務器或我的本地 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));
}

我想我的問題暫時已經解決了……我將我的服務和測試應用程序都更改為以SYSTEM帳戶而不是NetworkService帳戶運行。 更改用戶帳戶的好處是否會持續存在,或者是否只是暫時的,還有待觀察。

我假設您使用的是開源System.Data.SQLite庫。

如果是這種情況,很容易通過 Visual Studio Performance Profiler 看到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 日志消失,服務全速運行。

您可以將適當用戶的“修改”權限添加到您的數據庫文件夾中。 右鍵單擊文件夾> 屬性> 安全性> 編輯> 添加(我添加了IIS_Users)> 選擇“修改”復選框> 確定

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM