sqlite3 "database is locked"
阿新 • • 發佈:2018-12-31
問題原因
sqlite3_exec對於執行create table、insert、update等操作,會對整個資料庫加鎖,導致加鎖期間其他連線執行sqlite3_exec()失敗,返回“database is locked”,錯誤碼為5。
測試:建立兩個執行緒,執行緒1向test表插入數值1,執行緒2向test表插入數值2,每個執行緒內迴圈執行10次,觀察現象。
#include "sqlite3.h" #include <iostream> #include <windows.h> using namespace std; //執行緒入口函式 DWORD WINAPI Sqlite3ExecSql(LPVOID lpParamter) { char *msg = NULL; sqlite3 *db = NULL; int nRC = 0; char szSqlite3Path[1024] = { 0 }; int nCount = 0; char *pszSql = (char*)lpParamter; _snprintf(szSqlite3Path, sizeof(szSqlite3Path), "%s\\%s", "F:\\VS2013Workspace\\SqliteTest\\SqliteTest", "LockTest.db"); while (1){ if (nCount == 10) break; //開啟資料庫 nRC = sqlite3_open(szSqlite3Path, &db); if (SQLITE_OK != nRC) { cout << "error code:" << nRC << endl; return -1; } //執行sql語句 nRC = sqlite3_exec(db, pszSql, 0, 0, &msg); if (nRC != 0){ cout << "failed: " << pszSql << "\t" << "err msg:" << msg << ", " << "code: " << nRC << endl << endl; } else{ cout << "succeed: " << pszSql << endl << endl; } sqlite3_close(db); nCount++; Sleep(100); } return 0; } void main(){ int nRC = 0; char szSqlite3Path[1024] = { 0 }; sqlite3 *db = NULL; char *msg = NULL; _snprintf(szSqlite3Path, sizeof(szSqlite3Path), "%s\\%s", "F:\\VS2013Workspace\\SqliteTest\\SqliteTest", "LockTest.db"); //建立資料庫 nRC = sqlite3_open(szSqlite3Path, &db); if (SQLITE_OK != nRC) { cout << "error code:" << nRC << endl; return; } //建立表 nRC = sqlite3_exec(db, "create table IF NOT EXISTS test(a int)", 0, 0, &msg); if (nRC != 0){ cout << "error code:" << nRC << ",msg: "<<msg << endl; return; } //建立執行緒1,每隔100ms插入一條資料,值為1 HANDLE hThread1 = CreateThread(NULL, 0, Sqlite3ExecSql, "insert into test values(1)", 0, NULL); //建立執行緒2,每隔100ms插入一條資料,值為2 HANDLE hThread2 = CreateThread(NULL, 0, Sqlite3ExecSql, "insert into test values(2)", 0, NULL); while (1){ Sleep(5); } }
執行結果:
結論:
當其中一個執行緒正在執行SQL語句期間,發生執行緒排程,另一個執行緒也開始執行SQL語句,此時執行緒一已對資料庫加鎖且未釋放,執行緒二沒有執行許可權,導致執行緒二執行sqlite3_exec返回錯誤碼5,錯誤資訊"database is locked"。
解決方案
呼叫sqlite3_exec()之前,呼叫sqlite3_busy_timeout()設定鎖等待時間。sqlite3_busy_timeout針對資料庫控制代碼,設定執行sql語句的預設等待時間,如果在執行期間,資料庫被鎖住,sqlite3_exec()會不斷重試,直到執行成功或者等待超時。
#include "sqlite3.h" #include <iostream> #include <windows.h> using namespace std; //執行緒入口函式 DWORD WINAPI Sqlite3ExecSql(LPVOID lpParamter) { char *msg = NULL; sqlite3 *db = NULL; int nRC = 0; char szSqlite3Path[1024] = { 0 }; int nCount = 0; char *pszSql = (char*)lpParamter; _snprintf(szSqlite3Path, sizeof(szSqlite3Path), "%s\\%s", "F:\\VS2013Workspace\\SqliteTest\\SqliteTest", "LockTest.db"); while (1){ if (nCount == 10) break; //開啟資料庫 nRC = sqlite3_open(szSqlite3Path, &db); if (SQLITE_OK != nRC) { cout << "error code:" << nRC << endl; return -1; } //設定超時等待時間:1s sqlite3_busy_timeout(db, 1000); //執行sql語句 nRC = sqlite3_exec(db, pszSql, 0, 0, &msg); if (nRC != 0){ cout << "failed: " << pszSql << "\t" << "err msg:" << msg << ", " << "code: " << nRC << endl << endl; } else{ cout << "succeed: " << pszSql << endl << endl; } sqlite3_close(db); nCount++; Sleep(100); } return 0; } void main(){ int nRC = 0; char szSqlite3Path[1024] = { 0 }; sqlite3 *db = NULL; char *msg = NULL; _snprintf(szSqlite3Path, sizeof(szSqlite3Path), "%s\\%s", "F:\\VS2013Workspace\\SqliteTest\\SqliteTest", "LockTest.db"); //建立資料庫 nRC = sqlite3_open(szSqlite3Path, &db); if (SQLITE_OK != nRC) { cout << "error code:" << nRC << endl; return; } //建立表 nRC = sqlite3_exec(db, "create table IF NOT EXISTS test(a int)", 0, 0, &msg); if (nRC != 0){ cout << "error code:" << nRC << ",msg: "<<msg << endl; return; } //建立執行緒1,每隔100ms插入一條資料,值為1 HANDLE hThread1 = CreateThread(NULL, 0, Sqlite3ExecSql, "insert into test values(1)", 0, NULL); //建立執行緒2,每隔100ms插入一條資料,值為2 HANDLE hThread2 = CreateThread(NULL, 0, Sqlite3ExecSql, "insert into test values(2)", 0, NULL); while (1){ Sleep(5); } }
執行結果:
結論:在等待時間內,執行緒重新獲得CPU控制權並且鎖已被另一個執行緒釋放,sqlite3_exec就可以執行成功,因此所有插入全部執行成功。