提公升SQLite資料插入效率低 速度慢的方法

2021-06-16 17:59:11 字數 4265 閱讀 6279

本文為本人原創,請尊重個人勞動成果

sqlite資料庫由於其簡單、靈活、輕量、開源,已經被越來越多的被應用到中小型應用中。甚至有人說,sqlite完全可以用來取代c語言中的檔案讀寫操作。因此我最近編寫有關遙感資料處理的程式的時候,也將sqlite引入進來,以提高資料的結構化程度,並且提高大資料的處理能力(sqlite最高支援2pb大小的資料)。但是最開始,我發現,直接使用sql語句的插入效率簡直低的令人髮指的。後來不斷查文件、查資料,才發現了一條快速的「資料插入」之路。本文就以插入資料為例,整合網上和資料書中的各種提高sqlite效率的方法,給出提高sqlite資料插入效率的完整方法。(大神們勿噴)

我使用的電腦是win7 64位系統,使用vc2010編譯,sqlite版本為3.7.15.2 ,電腦cpu為二代i3處理器,記憶體6g。

實驗之前,先建立要插入資料的表:

create table t1 (id integer , x integer , y integer, weight real)

sqlite的api中直接執行sql的函式是:

int sqlite3_exec(  sqlite3*,    const char *sql,   int (*callback)(void*,int,char**,char**),   void *,   char **errmsg)
直接使用insert語句的字串進行插入,程式部分**(完整**見後文),如下:
for(int i=0;i7.826 條/s

3 中速——顯式開啟事務

所謂」事務「就是指一組sql命令,這些命令要麼一起執行,要麼都不被執行。在sqlite中,每呼叫一次sqlite3_exec()函式,就會隱式地開啟了乙個事務,如果插入一條資料,就呼叫該函式一次,事務就會被反覆地開啟、關閉,會增大io量。如果在插入資料前顯式開啟事務,插入後再一起提交,則會大大提高io效率,進而加資料快插入速度。

開啟事務只需在上述**的前後各加一句開啟與提交事務的命令即可:

sqlite3_exec(db,"begin;",0,0,0);

for(int i=0;i

顯式開啟事務後,這個程式執行起來明顯快很多,估算效率達到了34095條/s,較原始方法提公升約5000倍。

4 高速——寫同步(synchronous)

我要使用乙個遙感處理演算法處理10000*10000的影像,中間有一步需要插入100000000條資料到資料庫中,如果按照開啟事務後的速度34095條/s,則需要100000000÷34095 = 2932秒 = 48.9分,仍然不能夠接受,所以我接著找提公升速度的方法。終於,在有關講解sqlite配置的資料中,看到了「寫同步」選項。

在sqlite中,資料庫配置的引數都由編譯指示(pragma)來實現的,而其中synchronous選項有三種可選狀態,分別是full、normal、off。這篇部落格以及官方文件裡面有詳細講到這三種引數的設定。簡要說來,full寫入速度最慢,但保證資料是安全的,不受斷電、系統崩潰等影響,而off可以加速資料庫的一些操作,但如果系統崩潰或斷電,則資料庫可能會損毀。

sqlite3中,該選項的預設值就是full,如果我們再插入資料前將其改為off,則會提高效率。如果僅僅將sqlite當做一種臨時資料庫的話,完全沒必要設定為full。在**中,設定方法就是在開啟資料庫之後,直接插入以下語句:

sqlite3_exec(db,"pragma synchronous = off; ",0,0,0);此時,經過測試,插入速度已經變成了

41851條/s,也就是說,插入100000000條資料,需要2389秒 = 39.8分。

5 極速——執行準備

雖然寫同步設為off後,速度又有小幅提公升,但是仍然較慢。我又一次踏上了尋找提高sqlite插入效率方法的道路上。終於,我發現,sqlite執行sql語句的時候,有兩種方式:一種是使用前文提到的函式sqlite3_exec(),該函式直接呼叫包含sql語句的字串;另一種方法就是「執行準備」(類似於儲存過程)操作,即先將sql語句編譯好,然後再一步一步(或一行一行)地執行。如果採用前者的話,就算開起了事務,sqlite仍然要對迴圈中每一句sql語句進行「詞法分析」和「語法分析」,這對於同時插入大量資料的操作來說,簡直就是浪費時間。因此,要進一步提高插入效率的話,就應該使用後者。

「執行準備」主要分為三大步驟:

1.呼叫函式

int sqlite3_prepare_v2( sqlite3 *db,  const char *zsql,  int nbyte,  sqlite3_stmt **ppstmt,  const char **pztail);
並且宣告乙個指向sqlite3_stmt物件的指標,該函式對引數化的sql語句zsql進行編譯,將編譯後的狀態存入ppstmt中。

2.呼叫函式 sqlite3_step() ,這個函式就是執行一步(本例中就是插入一行),如果函式返回的是sqlite_row則說明仍在繼續執行,否則則說明已經執行完所有操作;

3.呼叫函式 sqlite3_finalize(),關閉語句。

關於執行準備的api的具體語法,詳見官方文件。本文中執行準備的c++**如下:

sqlite3_exec(db,"begin;",0,0,0);

sqlite3_stmt *stmt;

const char* sql = "insert into t1 values(?,?,?,?)";

sqlite3_prepare_v2(db,sql,strlen(sql),&stmt,0);

for(int i=0;i265816條/s,也就是說,插入100000000條資料,需要376秒 = 6.27分。這個速度已經很滿意了。

5 總結

綜上所述啊,sqlite插入資料效率最快的方式就是:事務

+關閉寫同步

+執行準備(儲存過程)

,如果對資料庫安全性有要求的話,就開啟寫同步。

1. sqlite官方文件:

2.《解決sqlite3插入資料很慢的問題》:

3.《the definitive guide to sqlite》apress出版:

(這是本好書)

附最終完整**:

#include #include #include #include #include "sqlite3.h"

const int ncount = 500000;

int main (int argc,char** argv)

{ sqlite3* db;

sqlite3_open("testdb.db" ,&db);

sqlite3_exec(db,"pragma synchronous = off; ",0,0,0);

sqlite3_exec(db,"drop table if exists t1",0,0,0);

sqlite3_exec(db,"create table t1(id integer,x integer,y integer ,weight real)",0,0,0);

clock_t t1 = clock();

sqlite3_exec(db,"begin;",0,0,0);

sqlite3_stmt *stmt;

const char* sql = "insert into t1 values(?,?,?,?)";

sqlite3_prepare_v2(db,sql,strlen(sql),&stmt,0);

for(int i=0;i

sqlite插入資料效率提公升解決方案

sqlite插入效率對比 建表 create table if not existstttable id integer primary key autoincrement,name varchar 100 筆者電腦配置 硬碟是250g三星ssd硬碟 cpu os 記憶體如下 理論 sqlite 插...

sqlite提公升效率

前言 sqlite資料庫由於其簡單 靈活 輕量 開源,已經被越來越多的被應用到中小型應用中。甚至有人說,sqlite完全可以用來取代c語言中的檔案讀寫操作。因此我最近編寫有關遙感資料處理的程式的時候,也將sqlite引入進來,以提高資料的結構化程度,並且提高大資料的處理能力 sqlite最高支援2p...

提公升SQLite資料插入效率低 速度慢的方法

sqlite資料庫由於其簡單 靈活 輕量 開源,已經被越來越多的被應用到中小型應用中。甚至有人說,sqlite完全可以用來取代c語言中的檔案讀寫操作。因此我最近編寫有關遙感資料處理的程式的時候,也將sqlite引入進來,以提高資料的結構化程度,並且提高大資料的處理能力 sqlite最高支援2pb大小...