Sqlite3性能测试

参考: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/46607163

static 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);
}






SQLite3 是一个轻量级的嵌入式数据库,性能优化可以通过以下几个方面来实现: 1. 使用适当的索引:索引是提高查询性能的关键。在表中创建适当的索引,特别是在经常用于查询的列上创建索引,可以显著提高查询速度。 2. 合理设计表结构:良好的表结构设计可以提高查询和插入/更新性能。例如,避免过度范式化和冗余数据,根据查询需求选择合适的数据类型等。 3. 批量操作:使用事务和批量操作可以显著提高插入和更新的性能。将多个插入或更新操作放在一个事务中执行,可以减少磁盘写入次数,提高性能。 4. 避免频繁的查询和写入:频繁的查询和写入操作会导致锁争用和磁盘IO压力增大,影响性能。合理设计应用程序的访问模式,尽量减少不必要的查询和写入操作。 5. 使用预编译语句:SQLite3 支持预编译语句,可以显著提高查询性能。预编译语句可以将 SQL 查询编译为二进制格式,在后续执行时直接使用编译好的二进制代码,避免每次查询都重新解析和编译 SQL。 6. 选择合适的缓存大小:SQLite3 使用缓存来提高查询性能。根据应用程序的需求和系统资源情况,合理设置缓存大小,避免过大或过小导致性能下降。 7. 定期优化数据库:定期对数据库进行优化操作,如 VACUUM 命令可以释放未使用的空间和优化数据库文件,提高性能。 8. 使用合适的连接方式:SQLite3 支持多种连接方式,如共享缓存模式和内存模式等。根据应用程序的需求选择合适的连接方式,可以提高性能。 需要注意的是,性能优化是一个综合考虑的过程,具体的优化方法需要根据实际情况进行调整和测试。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值