当前位置:  开发笔记 > 前端 > 正文

SQLite可以处理9000万条记录吗?

如何解决《SQLite可以处理9000万条记录吗?》经验,为你挑选了4个好方法。

或者我应该使用不同的锤子来解决这个问题.

我有一个非常简单的用例来存储数据,实际上是一个稀疏矩阵,我试图将其存储在SQLite数据库中.我创建了一个表:

create TABLE data ( id1 INTEGER KEY, timet INTEGER KEY, value REAL )

我插入了大量数据(每10分钟800个元素,每天45次),一年中大部分时间.(id1,timet)的元组将始终是唯一的.

时间值是自纪元以来的秒数,并且将始终增加.出于所有实际目的,id1是随机整数.虽然可能只有20000个独特的ID.

然后我想访问id1 == someid的所有值或访问timet == sometime的所有元素.在我通过Linux上的C接口使用最新SQLite的测试中,查找其中一个(或此查找的任何变体)大约需要30秒,这对我的用例来说还不够快.

我尝试为数据库定义一个索引,但这减慢了插入到完全不可行的速度(虽然我可能做错了...)

上表导致对任何数据的访问速度非常慢.我的问题是:

SQLite完全是错误的工具吗?

我可以定义索引以显着加快速度吗?

我应该使用像HDF5而不是SQL这样的东西吗?

请原谅我对SQL的基本理解!

谢谢

我包含一个代码示例,显示使用索引时插入速度如何减慢到爬行速度.使用'create index'语句,代码需要19分钟才能完成.没有它,它会在18秒内运行.


#include 
#include 

void checkdbres( int res, int expected, const std::string msg ) 
{
  if (res != expected) { std::cerr << msg << std::endl; exit(1); } 
}

int main(int argc, char **argv)
{
  const size_t nRecords = 800*45*30;

  sqlite3      *dbhandle = NULL;
  sqlite3_stmt *pStmt = NULL;
  char statement[512];

  checkdbres( sqlite3_open("/tmp/junk.db", &dbhandle ), SQLITE_OK, "Failed to open db");

  checkdbres( sqlite3_prepare_v2( dbhandle, "create table if not exists data ( issueid INTEGER KEY, time INTEGER KEY, value REAL);", -1, & pStmt, NULL ), SQLITE_OK, "Failed to build create statement");
  checkdbres( sqlite3_step( pStmt ), SQLITE_DONE, "Failed to execute insert statement" );
  checkdbres( sqlite3_finalize( pStmt ), SQLITE_OK, "Failed to finalize insert");
  checkdbres( sqlite3_prepare_v2( dbhandle, "create index issueidindex on data (issueid );", -1, & pStmt, NULL ), SQLITE_OK, "Failed to build create statement");
  checkdbres( sqlite3_step( pStmt ), SQLITE_DONE, "Failed to execute insert statement" );
  checkdbres( sqlite3_finalize( pStmt ), SQLITE_OK, "Failed to finalize insert");
  checkdbres( sqlite3_prepare_v2( dbhandle, "create index timeindex on data (time);", -1, & pStmt, NULL ), SQLITE_OK, "Failed to build create statement");
  checkdbres( sqlite3_step( pStmt ), SQLITE_DONE, "Failed to execute insert statement" );
  checkdbres( sqlite3_finalize( pStmt ), SQLITE_OK, "Failed to finalize insert");

  for ( size_t idx=0; idx < nRecords; ++idx)
  {
    if (idx%800==0)
    {
      checkdbres( sqlite3_prepare_v2( dbhandle, "BEGIN TRANSACTION", -1, & pStmt, NULL ), SQLITE_OK, "Failed to begin transaction");
      checkdbres( sqlite3_step( pStmt ), SQLITE_DONE, "Failed to execute begin transaction" );
      checkdbres( sqlite3_finalize( pStmt ), SQLITE_OK, "Failed to finalize begin transaction");
      std::cout << "idx " << idx << " of " << nRecords << std::endl;
    }

    const size_t time = idx/800;
    const size_t issueid = idx % 800;
    const float value = static_cast(rand()) / RAND_MAX;
    sprintf( statement, "insert into data values (%d,%d,%f);", issueid, (int)time, value );
    checkdbres( sqlite3_prepare_v2( dbhandle, statement, -1, &pStmt, NULL ), SQLITE_OK, "Failed to build statement");
    checkdbres( sqlite3_step( pStmt ), SQLITE_DONE, "Failed to execute insert statement" );
    checkdbres( sqlite3_finalize( pStmt ), SQLITE_OK, "Failed to finalize insert");

    if (idx%800==799)
    {
      checkdbres( sqlite3_prepare_v2( dbhandle, "END TRANSACTION", -1, & pStmt, NULL ), SQLITE_OK, "Failed to end transaction");
      checkdbres( sqlite3_step( pStmt ), SQLITE_DONE, "Failed to execute end transaction" );
      checkdbres( sqlite3_finalize( pStmt ), SQLITE_OK, "Failed to finalize end transaction");
    }
  }

  checkdbres( sqlite3_close( dbhandle ), SQLITE_OK, "Failed to close db" ); 
}

Robert Harve.. 29

您是否一次插入所有800个元素?如果是这样,在事务中执行插入操作将极大地加速该过程.

见http://www.sqlite.org/faq.html#q19

SQLite可以处理非常大的数据库.见http://www.sqlite.org/limits.html



1> Robert Harve..:

您是否一次插入所有800个元素?如果是这样,在事务中执行插入操作将极大地加速该过程.

见http://www.sqlite.org/faq.html#q19

SQLite可以处理非常大的数据库.见http://www.sqlite.org/limits.html



2> Robert Harve..:

我查看了你的代码,我认为你可能会因为preparefinalize语句而过度使用它.我绝不是SQLite专家,但每次循环准备一个语句都需要花费很大的开销.

引用SQLite网站:

在通过一次或多次调用评估准备好的语句之后 sqlite3_step(),可以重置它以便通过调用再次进行评估sqlite3_reset().使用 sqlite3_reset()在现有的准备语句,而创建一个新的准备好的声明中避免不必要的来电 sqlite3_prepare().在许多SQL语句中,运行所需的时间 sqlite3_prepare()等于或超过所需的时间sqlite3_step().因此,避免调用 sqlite3_prepare()可以显着提高性能.

http://www.sqlite.org/cintro.html

在您的情况下,您可以尝试将新值绑定到现有语句,而不是每次都准备一个新语句.

所有这些说,我认为索引可能是真正的罪魁祸首,因为随着您添加更多数据,时间不断增加.我对此感到好奇,我计划在周末进行一些测试.



3> Brian O'Kenn..:

回答我自己的问题,作为一个放置一些细节的地方:

事实证明(如上正确建议的),该索引创建是慢步骤,每次我执行插入的另一个交易时间,更新索引这需要一些时间.我的解决办法是:(A)创建数据表(B)插入我的所有历史数据(几年价值)(C)创建索引

现在所有查找等都非常快,sqlite做得很好.随后每日更新,现在需要几秒钟只插入800条记录,但是这是没有问题的,因为它只能运行每隔10分钟左右.

感谢Robert Harvey和maxwellb提供上述帮助/建议/答案.



4> Robert Harve..:

由于我们知道在表上没有索引时捕获数据的速度很快,因此实际可行的方法是:

    在没有索引的临时表中捕获800个值。

    使用采用SELECT语句的INSERT INTO形式将记录复制到主表(包含索引)。

    从临时表中删除记录。

该技术基于以下理论:采用SELECT语句的INSERT INTO比执行单个INSERT更快。

如果仍然证明它有些慢,则可以使用“ 异步模块”在后台执行步骤2 。这利用了两次捕获之间的停机时间。

推荐阅读
mylvfamily
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有