sqlite 怎麼開啓wal機制

Sqlite在多線程下的使用方法及注意的事項


Sqlite的三種模式

1.      單線程,這種模式下,沒有進行互斥,多線程使用不安全

2.      多線程,這種模式下,在多線程中使用單個數據庫連接是不安全的,否則就是安全的。也就是不能再多個線程中共享數據庫連接

3.      串行,這種模式下,sqlite是線程安全的。可以在多個線程中不加互斥的使用同一個數據庫連接

數據庫使用經驗:線程模式可以在編譯時(通過源碼編譯sqlite庫時)、啓動時(使用sqlite的應用程序初始化時)或者運行時(創建數據庫連接時)來指定。一般而言,運行時指定的模式將覆蓋啓動時的指定模式,啓動時指定的模式將覆蓋編譯時指定的模式。但是,單線程模式一旦被指定,將無法被覆蓋。所以我們在創建數據庫的時候,就一定要考慮好數據庫使用的模式。

 

Is Sqlite3 thread-safe?

答案當然是肯定的。不過要確保線程安全需要(注意事項)

1.      確保編譯時使用:DTHREADSAFE = 1

2.      不要在多個線程中共享數據庫鏈接

3.      在一些操作系統中,一個線程中只能創建並使用一個數據庫連接(建議使用者在所有操作系統的使用上都堅持此項原則)

4.      Sqlite有一小部分功能(PRAGMAtemp_store_directory等等)是線程不安全的,多線程中應當避免使用

三讀寫不阻塞的Sqlite-Wal模式

3.1 wal工作原理

在引入WAL機制之前,SQLite使用rollbackjournal機制實現原子事務。

rollback journal機制的原理是:在修改數據庫文件中的數據之前,先將修改所在分頁中的數據備份在另外一個地方,然後纔將修改寫入到數據庫文件中;如果事務失敗,則將備份數據拷貝回來,撤銷修改;如果事務成功,則刪除備份數據,提交修改。

WAL機制的原理是:修改並不直接寫入到數據庫文件中,而是寫入到另外一個稱爲WAL的文件中;如果事務失敗,WAL中的記錄會被忽略,撤銷修改;如果事務成功,它將在隨後的某個時間被寫回到數據庫文件中,提交修改。

 

3.2 wal優點:

1.      讀和寫可以完全地併發執行,不會互相阻塞(但是寫之間仍然不能併發)。

2.      WAL在大多數情況下,擁有更好的性能(因爲無需每次寫入時都要寫兩個文件)。

3.      磁盤I/O行爲更容易被預測。

3.3 wal缺點:

1.      訪問數據庫的所有程序必須在同一主機上,且支持共享內存技術。

2.      每個數據庫現在對應3個文件:<yourdb>.db<yourdb>-wal<yourdb>-shm

3.      當寫入數據達到GB級的時候,數據庫性能將下降。

4.      3.7.0之前的SQLite無法識別啓用了WAL機制的數據庫文件。

 

3.4 wal如何記錄數據--checkpoint

使用WAL模式時,改寫操作是附加(append)到WAL文件,而不改動數據庫文件,因此數據庫文件可以被同時讀取。當執行checkpoint操作時,WAL文件的內容會被寫回數據庫文件。當WAL文件達到SQLITE_DEFAULT_WAL_AUTOCHECKPOINT(默認值是1000)頁(默認大小是1KB)時,會自動使用當前COMMIT的線程來執行checkpoint操作。也可以關閉自動checkpoint,改爲手動定期checkpoint 

爲了避免讀取的數據不一致,查詢時也需要讀取WAL文件,並記錄一個結尾標記(end mark)。這樣的代價就是讀取會變得稍慢,但是寫入會變快很多。要提高查詢性能的話,可以減小WAL文件的大小,但寫入性能也會降低。 需要注意的是,低版本的SQLite不能讀取高版本的SQLite生成的WAL文件,但是數據庫文件是通用的。這種情況在用戶進行iOS降級時可能會出現,可以把模式改成delete,再改回WAL來修復。 

要對一個數據庫連接啓用WAL模式,需要執行“PRAGMA journal_mode=WAL;”這條命令,它的默認值是“journal_mode=DELETE”。執行後會返回新的journal_mode字符串值,即成功時爲"wal",失敗時爲之前的模式(例如"delete")。一旦啓用WAL模式後,數據庫會保持這個模式,這樣下次打開數據庫時仍然是 WAL模式。 要停止自動checkpoint,可以使用wal_autocheckpoint指令或sqlite3_wal_checkpoint()函數。手動執行 checkpoint可以使用wal_checkpoint指令或sqlite3_wal_checkpoint()函數。

 

四.Walc++代碼中的應用

    int DataSource::InitDataBaseToWal(std::string sPath, bool isWal)
    {
        char* zErrMsg;

        sqlite3* db = NULL;

        int rc = sqlite3_open_v2(sPath.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, NULL);

        if (rc != SQLITE_OK)
        {
            Logger::LogD("DataSource::sqlite [%s] or [%s] open failed", sPath.c_str(), sqlite3_errmsg(db));
            Logger::LogO("DataSource::sqlite [%s] or [%s] open failed", sPath.c_str(), sqlite3_errmsg(db));

            sqlite3_close(db);

            return -1;
        }

	    if(isWal == true)
	    {
		    rc = sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, 0, &zErrMsg);

		    if (rc != SQLITE_OK)
		    {
			    sqlite3_free(zErrMsg);

			    sqlite3_close(db);

			    return -1;
		    }

		    rc = sqlite3_exec(db, "PRAGMA wal_autocheckpoint=100;", NULL, 0, &zErrMsg);

		    if (rc != SQLITE_OK)
		    {
			    sqlite3_free(zErrMsg);

			    sqlite3_close(db);

			    return -1;
		    }
	    }
	    else
	    {
		    rc = sqlite3_exec(db, "PRAGMA journal_mode=DELETE;", NULL, 0, &zErrMsg);

                   if (rc != SQLITE_OK)
	           {
		        sqlite3_free(zErrMsg);

		        sqlite3_close(db);

		         return -1;
	          }
            }

	    return true;
      }

   

說明:當isWal爲true時,啓動wal模式,isWal爲false時,關閉wal模式。當你看到這裏的時候,對sqlite的wal模式一定有了一定的瞭解,爲了讓你更加相信wal模式的利好,作者告訴你一個祕密,就是作者同時開啓5個線程(3讀2寫)進行了測試,未見異常。

調整checkpoint默認頁值,性能也基本能滿足需求。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章