Sqlite3性能测试

来源:互联网 发布:荧光颜料淘宝 编辑:程序博客网 时间:2024/06/08 19:12

参考:http://blog.csdn.net/majiakun1/article/details/46607163,感谢作者分享。


Sqlite3最简单的提升读写性能的方法有:

1. 关闭写同步,也就是设置synchronous。Sqlite3是一个文件数据库,所谓的设置写同步就是设置每次写完数据之后刷新IO缓存的频率,如果写同步设置为Full,那么每次写都会刷新缓存,这样保证数据总能写到文件里,十分安全的做法,防止机器掉电等意外,但是不断地刷新缓存效率低下,一般使用没有这么高的安全要求。关闭写同步就是不去手动刷新缓存,这样效率会极大地提升。关闭写同步只要执行"PRAGMA synchronous = OFF"即可。如Slite3提供的C接口代码,

  const char* kTurnOffSynchronous = "PRAGMA synchronous = OFF;";  sqlite3_exec(db, kTurnOffSynchronous, NULL, NULL, NULL);

2. 执行SQL语句时,编译一次多次使用,而不是每执行一次都进行编译执行等操作。如Sqlite3提供的C接口代码:

  const char* kInsertSql = "INSERT INTO PERFORMANCE_TEST VALUES(?,?,?,?);";  sqlite3_stmt* stmt = NULL;  sqlite3_prepare_v2(db, kInsertSql, strlen(kInsertSql), &stmt, NULL);  for (int i = 0; i < data_count; ++i) {    sqlite3_reset(stmt);    sqlite3_bind_int(stmt, 1, i);    sqlite3_bind_int(stmt, 2, i);    sqlite3_bind_int(stmt, 3, i);    sqlite3_bind_int(stmt, 4, i);    sqlite3_step(stmt);  }  if (turn_on_transaction) {    sqlite3_exec(db, kCommitTransaction, NULL, NULL, NULL);  }  sqlite3_finalize(stmt);

3. 显式开启事务。Sqlite3的语句执行操作隐式都开启了事务,比如写十万条数据,就会进行十万次事务,极大地限制了效率,解决方法就是显式开启事务,在写数据之前开启事务,写十万条数据之后,进行提交事务。这样写十万条数据只开启了一次事务。直接执行语句“begin”和“commit”即可开启和提交事务。


简单的测试结果如下(固态硬盘,只能简单地作为参考):


测试完整代码如下:

#include <iostream>#include <string>#include <sstream>#include <vector>#include <list>#include <cstdio>#include "sqlite3.h"#include "common.h"// 参考:http://blog.csdn.net/majiakun1/article/details/46607163static const char* kDatabaseName = "test.db";//--------------------------------------------------------------static void TestTimer();//--------------------------------------------------------------static bool PrepareDB(sqlite3** db, bool create_table);static void CreateTable();static void ClearTable();static void TestExec(bool turn_off_synchronous = false);static void TestNoSynchronous();static void TestTransactionExec();static void TestStep(bool turn_on_transaction = true);static void PerformanceTest();//--------------------------------------------------------------int main() {  //TestTimer();  //ClearTable();  CreateTable();  PerformanceTest();  return 0;}//--------------------------------------------------------------static void PerformanceTest() {  TestExec(false);  TestExec(true);  TestNoSynchronous();  TestTransactionExec();  TestStep(true);  TestStep(false);}// 1.直接执行sqlite3_exec。static void TestExec(bool turn_off_synchronous) {  sqlite3* db = NULL;  if (!PrepareDB(&db, false)) {    return;  }  if (turn_off_synchronous) {    std::cout << "1.关闭写同步执行sqlite3_exec : " << std::endl;  } else {    std::cout << "2.直接执行sqlite3_exec : " << std::endl;  }  utility::Timer timer;  std::stringstream sstream(std::stringstream::out);  const int kDataCount = 1000;  const char* kTurnOffSynchronous = "PRAGMA synchronous = OFF;";  if (turn_off_synchronous) {    sqlite3_exec(db, kTurnOffSynchronous, NULL, NULL, NULL);  }  // Insert.  timer.Start();  for (int i = 0; i < kDataCount; ++i) {    sstream << "INSERT INTO PERFORMANCE_TEST VALUES("            << i << "," << i << "," << i << "," << i << ");";    sqlite3_exec(db, sstream.str().c_str(), NULL, NULL, NULL);    sstream.str("");  }  double rate = kDataCount / timer.GetSeconds();  std::cout << "插入数据: " << rate <<"条/秒" << std::endl;  // Delete.  timer.Start();  for (int i = 0; i < kDataCount; ++i) {    sstream << "DELETE FROM PERFORMANCE_TEST WHERE ID1 = " << i;    sqlite3_exec(db, sstream.str().c_str(), NULL, NULL, NULL);    sstream.str("");  }  rate = kDataCount / timer.GetSeconds();  std::cout << "删除数据: " << rate << "条/秒\n" << std::endl;  sqlite3_close(db);}// 2.显式开启事务,执行sqlite3_exec。// (A)所谓”事务“就是指一组SQL命令,这些命令要么一起执行,要么都不被执行。// (B)在SQLite中,每调用一次sqlite3_exec()函数,就会隐式地开启了一个事务,如果插入一条数据,就调用该函数一次,事务就会被反复地开启、关闭,会增大IO量。// (C)如果在插入数据前显式开启事务,插入后再一起提交,则会大大提高IO效率,进而加数据快插入速度。static void TestTransactionExec() {  sqlite3* db = NULL;  if (!PrepareDB(&db, false)) {    return;  }  std::cout << "3.显式开启事务执行sqlite3_exec : " << std::endl;  utility::Timer timer;  std::stringstream sstream(std::stringstream::out);  const int kDataCount = 100000;  const char* kBeginTransaction = "begin";  const char* kCommitTransaction = "commit";  // Insert.  timer.Start();  sqlite3_exec(db, kBeginTransaction, NULL, NULL, NULL);  for (int i = 0; i < kDataCount; ++i) {    sstream << "INSERT INTO PERFORMANCE_TEST VALUES("      << i << "," << i << "," << i << "," << i << ");";    sqlite3_exec(db, sstream.str().c_str(), NULL, NULL, NULL);    sstream.str("");  }  sqlite3_exec(db, kCommitTransaction, NULL, NULL, NULL);  double rate = kDataCount / timer.GetSeconds();  std::cout << "插入数据: " << rate << "条/秒" << std::endl;  // Delete.  timer.Start();  sqlite3_exec(db, kBeginTransaction, NULL, NULL, NULL);  for (int i = 0; i < kDataCount; ++i) {    sstream << "DELETE FROM PERFORMANCE_TEST WHERE ID1 = " << i;    sqlite3_exec(db, sstream.str().c_str(), NULL, NULL, NULL);    sstream.str("");  }  sqlite3_exec(db, kCommitTransaction, NULL, NULL, NULL);  rate = kDataCount / timer.GetSeconds();  std::cout << "删除数据: " << rate << "条/秒\n" << std::endl;  sqlite3_close(db);}// 3.关闭写同步且显式开启事务执行sqlite3_exec。// (A)在SQLite中,数据库配置的参数都由编译指示(pragma)来实现的。// (B)synchronous选项有三种可选状态,分别是full、normal、off。// 当synchronous设置为FULL,SQLite数据库引擎在紧急时刻会暂停以确定数据已经写入磁盘。这使系统崩溃或电源出问题时能确保数据库在重起后不会损坏。FULL synchronous很安全但很慢。// 当synchronous设置为NORMAL, SQLite数据库引擎在大部分紧急时刻会暂停,但不像FULL模式下那么频繁。 NORMAL模式下有很小的几率(但不是不存在)发生电源故障导致数据库损坏的情况。// 但实际上,在这种情况 下很可能你的硬盘已经不能使用,或者发生了其他的不可恢复的硬件错误。// 当为synchronous OFF时,SQLite在传递数据给系统以后直接继续而不暂停。若运行SQLite的应用程序崩溃, 数据不会损伤,但在系统崩溃或写入数据时意外断电。// (C)SQLite3中,该选项的默认值就是full,如果我们再插入数据前将其改为off,则会提高效率。static void TestNoSynchronous() {  sqlite3* db = NULL;  if (!PrepareDB(&db, false)) {    return;  }  std::cout << "4.关闭写同步且显式开启事务执行sqlite3_exec : " << std::endl;  utility::Timer timer;  std::stringstream sstream(std::stringstream::out);  const int kDataCount = 100000;  const char* kTurnOffSynchronous = "PRAGMA synchronous = OFF;";  const char* kBeginTransaction = "begin";  const char* kCommitTransaction = "commit";  sqlite3_exec(db, kTurnOffSynchronous, NULL, NULL, NULL);  // Insert.  timer.Start();  sqlite3_exec(db, kBeginTransaction, NULL, NULL, NULL);  for (int i = 0; i < kDataCount; ++i) {    sstream << "INSERT INTO PERFORMANCE_TEST VALUES("      << i << "," << i << "," << i << "," << i << ");";    sqlite3_exec(db, sstream.str().c_str(), NULL, NULL, NULL);    sstream.str("");  }  sqlite3_exec(db, kCommitTransaction, NULL, NULL, NULL);  double rate = kDataCount / timer.GetSeconds();  std::cout << "插入数据: " << rate << "条/秒" << std::endl;  // Delete.  timer.Start();  sqlite3_exec(db, kBeginTransaction, NULL, NULL, NULL);  for (int i = 0; i < kDataCount; ++i) {    sstream << "DELETE FROM PERFORMANCE_TEST WHERE ID1 = " << i;    sqlite3_exec(db, sstream.str().c_str(), NULL, NULL, NULL);    sstream.str("");  }  sqlite3_exec(db, kCommitTransaction, NULL, NULL, NULL);  rate = kDataCount / timer.GetSeconds();  std::cout << "删除数据: " << rate << "条/秒\n" << std::endl;  sqlite3_close(db);}// 4. 使用sqlite3_step执行。// (A)SQLite执行SQL语句的时候,有两种方式:一种是使用前文提到的函数sqlite3_exec(),该函数直接调用包含SQL语句的字符串;// 另一种方法就是“执行准备”(类似于存储过程)操作,即先将SQL语句编译好,然后再一步一步(或一行一行)地执行。// (B)如果采用前者的话,就算开起了事务,SQLite仍然要对循环中每一句SQL语句进行“词法分析”和“语法分析”。// (C)“执行准备”主要分为三大步骤:并且声明一个指向sqlite3_stmt对象的指针,该函数对参数化的SQL语句zSql进行编译,将编译后的状态存入ppStmt中。// 调用函数 sqlite3_step() ,这个函数就是执行一步(本例中就是插入一行),如果函数返回的是SQLite_ROW则说明仍在继续执行,否则则说明已经执行完所有操作。// 调用函数 sqlite3_finalize(),关闭语句。// (D)综上所述啊,SQLite插入数据效率最快的方式就是:事务+关闭写同步+执行准备(存储过程),如果对数据库安全性有要求的话,就开启写同步。static void TestStep(bool turn_on_transaction) {  sqlite3* db = NULL;  if (!PrepareDB(&db, false)) {    return;  }  std::string title = "5.直接执行sqlite3_step : ";  if (turn_on_transaction) {    title = "6.显式开启事务执行sqlite3_step : ";  }  std::cout << title << std::endl;  utility::Timer timer;  int data_count = 100;  if (turn_on_transaction) {    data_count = 10000;  }  const char* kBeginTransaction = "begin";  const char* kCommitTransaction = "commit";  // Insert.  timer.Start();  if (turn_on_transaction) {    sqlite3_exec(db, kBeginTransaction, NULL, NULL, NULL);  }  const char* kInsertSql = "INSERT INTO PERFORMANCE_TEST VALUES(?,?,?,?);";  sqlite3_stmt* stmt = NULL;  sqlite3_prepare_v2(db, kInsertSql, strlen(kInsertSql), &stmt, NULL);  for (int i = 0; i < data_count; ++i) {    sqlite3_reset(stmt);    sqlite3_bind_int(stmt, 1, i);    sqlite3_bind_int(stmt, 2, i);    sqlite3_bind_int(stmt, 3, i);    sqlite3_bind_int(stmt, 4, i);    sqlite3_step(stmt);  }  if (turn_on_transaction) {    sqlite3_exec(db, kCommitTransaction, NULL, NULL, NULL);  }  sqlite3_finalize(stmt);  double rate = data_count / timer.GetSeconds();  std::cout << "插入数据: " << rate << "条/秒" << std::endl;  // Delete.  timer.Start();  if (turn_on_transaction) {    sqlite3_exec(db, kBeginTransaction, NULL, NULL, NULL);  }  const char* kDeleteSql = "DELETE FROM PERFORMANCE_TEST WHERE ID1 = ?;";  sqlite3_prepare_v2(db, kDeleteSql, strlen(kDeleteSql), &stmt, NULL);  for (int i = 0; i < data_count; ++i) {    sqlite3_reset(stmt);    sqlite3_bind_int(stmt, 1, i);    sqlite3_step(stmt);  }  if (turn_on_transaction) {    sqlite3_exec(db, kCommitTransaction, NULL, NULL, NULL);  }  sqlite3_finalize(stmt);  rate = data_count / timer.GetSeconds();  std::cout << "删除数据: " << rate << "条/秒\n" << std::endl;  sqlite3_close(db);}static bool PrepareDB(sqlite3** db, bool create_table) {  int rc = sqlite3_open(kDatabaseName, db);  if (rc != SQLITE_OK) {    std::cout << "Failed to open " << kDatabaseName << std::endl;    std::cout << "Error msg: " << sqlite3_errmsg(*db) << std::endl;    return false;  }  if (!create_table) {    return true;  }  const char* kCreateTableSql = "CREATE TABLE PERFORMANCE_TEST(\                                                                 ID1 INT, ID2 INT, ID3 INT, ID4 INT\                                                                                                  );";  char* error_msg = NULL;  rc = sqlite3_exec(*db, kCreateTableSql, NULL, NULL, &error_msg);  if (rc != SQLITE_OK) {    std::cout << "Failed to create table PERFORMANCE_TEST." << std::endl;    std::cout << "Error msg: " << error_msg << std::endl;    sqlite3_free(error_msg);    return false;  }  return true;}static void CreateTable() {  sqlite3* db = NULL;  PrepareDB(&db, true);  sqlite3_close(db);}static void ClearTable() {  sqlite3* db = NULL;  PrepareDB(&db, false);  const char* kClrearTableSql = "DELETE FROM PERFORMANCE_TEST;";  char* error_msg = NULL;  int rc = sqlite3_exec(db, kClrearTableSql, NULL, NULL, &error_msg);  if (rc != SQLITE_OK) {    std::cout << "Failed to clear table!" << std::endl;    std::cout << "Error msg: " << error_msg << std::endl;    sqlite3_free(error_msg);  }  sqlite3_close(db);}