1. 程式人生 > >sqlite3 "database is locked"

sqlite3 "database is locked"

問題原因

        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就可以執行成功,因此所有插入全部執行成功。