解決SQLite database is locked

    前些時候,同事在站點服務端使用SQlite存儲一些臨時數據,可是在多人併發的時候Sqlite會拋出異常:The database file is locked , database is locked,並且這個是在客戶生產環境下提示出來的,開發環境很難重現,同事實在沒轍,居然想發動全部研發同事經過操做軟件重現問題,我只能呵呵了。既 然是Sqlite的緣由,直接寫個小程序測試下sqlite不就好了,並且就算重現了,難不成要改Sqlite源碼...html

  Sqlite的特色:sql

  1. 簡單(simple):SQLite是一個很是輕量級自包含(lightweight and self-contained)的DBMS:一個頭文件,一個動態庫文件,你就擁有了關係數據庫的全部功能了。簡單,是SQLite最明顯的哲學。它提供 的API少而簡單。只須要一個DLL文件,你的程序立刻就擁有了一個功能強大的數據庫引擎,這是一件很美妙的事。數據庫

  2. 小巧(small):我用VS 2005在Windows下編譯的3.6.11,Release版爲368K,用時不到20秒——而編譯MySQL時,要花上幾分鐘。而當我插入10000條int數據時,內存開銷660K,磁盤開銷92K。小程序

  3. 事務(transaction):事務是現代商業數據處理系統最基本的要求,而Access,不管是在可執行文件大小(看了一下Access2003的可執行文件大小爲6.32M,二者不是一個量級),仍是事務特性,都是不能和SQLite 相比的。服務器

  4. 併發性(Concurrency):因爲SQLite經過OS的文件鎖來實現庫級鎖,粒度很大,可是,它經過一些複雜特殊的處理(具體能夠參見分析系列),儘可能的提高了讀寫的併發度。若是你還有擔憂,你能夠看看這篇文章:http://www.dbanotes.net/database/sqlite_cms.html併發

  5. SQL92:SQLite支持絕大部分的標準SQL語句,你只須要幾百K的空間,就能夠換來須要上百兆的通用DBMS幾乎全部操做了。高併發

  6. 方便(Convenience):若是你的程序要使用SQLite,只須要將拷貝你的程序目錄便可。測試

  7. 開源(Opensource):這是它最強大的地方。開源,意味着你能夠品讀它的源碼,你能夠隨時修改它,加入你本身的特性,而這一切徹底免費的。開源,是一種精神spa

  SQLite只支持庫級鎖,庫級鎖意味着什麼?——意味着同時只能容許一個寫操做,也就是說,即事務T1在A表插入一條數據,事務T2在B表中 插入一條數據,這兩個操做不能同時進行,即便你的機器有100個CPU,也沒法同時進行,而只能順序進行。表級都不能並行,更別說元組級了——這就是庫級 鎖。可是,SQLite儘可能延遲申請X鎖,直到數據塊真正寫盤時才申請X鎖,這是很是巧妙而有效的。.net

  上面的介紹能夠看出Sqlite實際上是一個客戶端嵌入數據庫,在高併發的服務器上是沒法適用的,同事百度後,發現鏈接串中加入 "Journal Mode=WAL;"能夠緩解併發壓力,但是客戶生產環境仍然出現「database is locked」錯誤。

  測試程序以下:

複製代碼

        static void Main(string[] args)
        {             for (int i = 0; i < 140; i++)
             {
                 ParameterizedThreadStart pStart = new      ParameterizedThreadStart(ClientTest.Excute);
                 Thread td = new Thread(pStart);
                 td.Start(1012 + i);
             }
             Console.Read();
        }    class ClientTest
    {        public static void Excute(Object id)
        {            bool flag = true;            while (true)
            {                if (flag)
                {                    string sql = "update asr_info set asr_check = 1 where id = '" + id.ToString() + "'";
                    Sqlite.ExecuteSql(sql);
                    flag = false;
                }                else
                {                    string sql = "update asr_info set asr_check = 0 where id = '" + id.ToString() + "'";
                    Sqlite.ExecuteSql(sql);
                    flag = true;
                }
            }
        }
    }

複製代碼

  測試發現,在i5 2.5Ghz 四核的機器上,跑了不到半分鐘,大概執行了500條Update語句,Sqlite就報錯,提示「 database is locked」,可是在差一點的機器上很難重現,這也就解釋了開發機上難重現而在客戶服務器上報錯的現象。

  解決辦法:

複製代碼

        private static readonly object obj = new object();        private static int ExecuteNonQuery(string StrSQL, CommandType CmdType, SQLiteParameter[] SQLiteParams)
        {
            SQLiteConnection SQLiteConn = new SQLiteConnection(strConn);
            SQLiteCommand SQLiteCmd = SQLiteCommandConstructor(SQLiteConn, StrSQL, CmdType, SQLiteParams);            if (SQLiteConn.State != ConnectionState.Open)
            {
                SQLiteConn.Open();
            }
            Monitor.Enter(obj);            int result = SQLiteCmd.ExecuteNonQuery();
            Monitor.Exit(obj);            //aaa++;            // Console.WriteLine(aaa);            SQLiteCmd.Dispose();
            SQLiteConn.Close();            return result;
        }

複製代碼

  事實證實Sqlite不支持併發執行寫入操做,即便是不一樣的表,只支持庫級鎖,並且這個Sqlite自己沒有實現,必須本身實現這個庫級鎖。

相關文章
相關標籤/搜索