【问题标题】:Reduce the time needed for a database to be read减少读取数据库所需的时间
【发布时间】:2021-01-30 18:13:14
【问题描述】:

我正在编写一个程序,我必须从数据库(Sqlite)中读取大量数据,然后在窗口中显示获得的值。我需要每秒刷新一次窗口,所以读取所有数据的操作需要持续不到一秒。

数据库由两列组成,一个主键和一个我要显示的值(一个数字),主键只是一个从 1 到行数的渐进数字。就检索到的数据在向量/结构中排序而言,主键对我来说并不重要。

数据库大约有 8*10^7 行,它在磁盘上的大小约为 250MB。

我的想法是通过事务可以减少读取数据所需的时间,我尝试实现一个小程序并测量时间,大约是 20 秒。然后我明白事务对select语句不是很有效(Here

所以我尝试:

using Record = std::vector<std::string>;
using Records = std::vector<Record>;

int select_callback(void *p_data, int num_fields, char **p_fields, char **p_col_names)
{
  Records* records = static_cast<Records*>(p_data);
  try {
    records->emplace_back(p_fields, p_fields + num_fields);
  }
  catch (...) {
    // abort select on failure, don't let exception propogate thru sqlite3 call-stack
    return 1;
  }
  return 0;
}

Records select_stmt(const char* stmt)
{
  Records records;  
  char *errmsg;
  int ret = sqlite3_exec(db, stmt, select_callback, &records, &errmsg);
  if (ret != SQLITE_OK) {
    std::cerr << "Error in select statement " << stmt << "[" << errmsg << "]\n";
  }
  else {
    std::cerr << records.size() << " records returned.\n";
  }

  return records;
}

Records records = select_stmt("SELECT * FROM TABLE");

我有最快的结果(大约 14 秒),但这还不够。

有没有可能达到这样的阅读时间(小于1秒)?

操作系统:Ubuntu 20.10 语言:C++


我用来通过prepare和transaction语句获取数据的代码

std::string msg = "SELECT ZPOS FROM TABLE WHERE ID = ?";

const char* msg_char_ptr = msg.c_str();

memset(sSQL, '\0', BUFFER_SIZE);

sprintf(sSQL, msg_char_ptr);

int rc = sqlite3_prepare_v2(db,  sSQL, BUFFER_SIZE, &stmt, &tail);

int count = 0;

if(rc == SQLITE_OK ) {

    sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, &sErrMsg);

    for (int i = 0; i < number_of_rows; i++)
    {

        // indices vector with all the indices of the table
        sqlite3_bind_int(stmt, 1, indices[i]); 

        while ( sqlite3_step( stmt ) == SQLITE_ROW ) { 

            int result = sqlite3_column_int( stmt, 0 );
            
            // collectData vector with the result
            collectData[count] = result;

            int val = sqlite3_column_count( stmt );

            count ++;

        }

        sqlite3_step(stmt);
        sqlite3_clear_bindings(stmt);
        sqlite3_reset(stmt);
    }

} else {

    printf("SQLite prepare error.\n");

}       

sqlite3_exec(db, "END TRANSACTION", NULL, NULL, &sErrMsg);
sqlite3_finalize(stmt);

【问题讨论】:

  • 首先使用普通的prepared statement api 而不是sqlite3_exec()
  • 我试过了,但我没有更好的结果(我使用 sqlite3_exec("SELECT ZPOS FROM PLANT_DATA WHERE ID = ?") )而且stackoverflow.com/questions/1703203/…
  • 你能提供确切的查询吗?我最好的猜测是你应该在第二列上添加一个索引。如果您使用第二列应用 WHERE 子句或 ORDER BY 子句,则对该列进行索引将使其 millions of times faster 可供读取。
  • 我尝试在第一列上应用索引,但性能相同...
  • 你真的需要每秒读取所有数据吗?您的窗口可能不会同时显示 1000 万个数字。

标签: c++ database performance sqlite query-optimization


【解决方案1】:

如果您必须使用 SQL SELECT 多行,最好的办法是使用一条 SQL SELECT 语句在多行结果集中获取您想要的所有行。不要为您想要的每个 ID 值发出一个 SELECT,而是发出 SELECT ID, ZPOS FROM TABLE` 并设置您的代码以读取多行。

话虽如此,我猜您正尝试将 80 兆行 (250MiB) 从您的表中读取到您应用程序的 RAM 中。每秒读取的数据太多了。即使您编写 C++ 代码只是为了打开一个平面文件并读取 250MiB,也太频繁了。

需要牢记的一些设计要点:

  1. SQL 中并没有什么魔法可以让访问海量数据的速度超快,比文件读取还快。

  2. SQL 的重点是允许像您这样的程序一次处理几行非常大的数据集。你当然可以做类似的事情

    SELECT ID, ZPOS FROM TABLE WHERE ID > ?
    

    您在? 中使用绑定输入的值是您上次读取数据库时收到的最大 ID 值。这会让你获得新记录。

  3. 相对而言,您需要设计程序以将表中的数据缓存在 RAM 中并有效地使用缓存。每秒重新加载一次所有数据太频繁了。

【讨论】:

  • 谢谢,经过一些测试,我得出了相同的结论。
猜你喜欢
  • 2019-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-08
  • 1970-01-01
  • 1970-01-01
  • 2015-06-22
  • 1970-01-01
相关资源
最近更新 更多