或者我应该使用不同的锤子来解决这个问题.
我有一个非常简单的用例来存储数据,实际上是一个稀疏矩阵,我试图将其存储在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
您是否一次插入所有800个元素?如果是这样,在事务中执行插入操作将极大地加速该过程.
见http://www.sqlite.org/faq.html#q19
SQLite可以处理非常大的数据库.见http://www.sqlite.org/limits.html
我查看了你的代码,我认为你可能会因为prepare
和finalize
语句而过度使用它.我绝不是SQLite专家,但每次循环准备一个语句都需要花费很大的开销.
引用SQLite网站:
在通过一次或多次调用评估准备好的语句之后
sqlite3_step()
,可以重置它以便通过调用再次进行评估sqlite3_reset()
.使用sqlite3_reset()
在现有的准备语句,而创建一个新的准备好的声明中避免不必要的来电sqlite3_prepare()
.在许多SQL语句中,运行所需的时间sqlite3_prepare()
等于或超过所需的时间sqlite3_step()
.因此,避免调用sqlite3_prepare()
可以显着提高性能.
http://www.sqlite.org/cintro.html
在您的情况下,您可以尝试将新值绑定到现有语句,而不是每次都准备一个新语句.
所有这些说,我认为索引可能是真正的罪魁祸首,因为随着您添加更多数据,时间不断增加.我对此感到好奇,我计划在周末进行一些测试.
回答我自己的问题,作为一个放置一些细节的地方:
事实证明(如上正确建议的),该索引创建是慢步骤,每次我执行插入的另一个交易时间,更新索引这需要一些时间.我的解决办法是:(A)创建数据表(B)插入我的所有历史数据(几年价值)(C)创建索引
现在所有查找等都非常快,sqlite做得很好.随后每日更新,现在需要几秒钟只插入800条记录,但是这是没有问题的,因为它只能运行每隔10分钟左右.
感谢Robert Harvey和maxwellb提供上述帮助/建议/答案.
由于我们知道在表上没有索引时捕获数据的速度很快,因此实际可行的方法是:
在没有索引的临时表中捕获800个值。
使用采用SELECT语句的INSERT INTO形式将记录复制到主表(包含索引)。
从临时表中删除记录。
该技术基于以下理论:采用SELECT语句的INSERT INTO比执行单个INSERT更快。
如果仍然证明它有些慢,则可以使用“ 异步模块”在后台执行步骤2 。这利用了两次捕获之间的停机时间。