前些時候,同事在站點服務端使用SQlite存儲一些臨時數據,可是在多人併發的時候Sqlite會拋出異常:The database file is locked , database is locked,並且這個是在客戶生產環境下提示出來的,開發環境很難重現,同事實在沒轍,居然想發動全部研發同事經過操做軟件重現問題,我只能呵呵了。既然是Sqlite的緣由,直接寫個小程序測試下sqlite不就好了,並且就算重現了,難不成要改Sqlite源碼...html
Sqlite的特色:sql
SQLite只支持庫級鎖,庫級鎖意味着什麼?——意味着同時只能容許一個寫操做,也就是說,即事務T1在A表插入一條數據,事務T2在B表中插入一條數據,這兩個操做不能同時進行,即便你的機器有100個CPU,也沒法同時進行,而只能順序進行。表級都不能並行,更別說元組級了——這就是庫級鎖。可是,SQLite儘可能延遲申請X鎖,直到數據塊真正寫盤時才申請X鎖,這是很是巧妙而有效的。數據庫
上面的介紹能夠看出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自己沒有實現,必須本身實現這個庫級鎖。測試