【发布时间】:2011-03-10 19:30:42
【问题描述】:
或者我应该使用不同的锤子来解决这个问题。
我有一个用于存储数据的非常简单的用例,实际上是一个稀疏矩阵,我尝试将其存储在 SQLite 数据库中。我创建了一个表:
create TABLE data ( id1 INTEGER KEY, timet INTEGER KEY, value REAL )
一年中的大部分时间,我都会在其中插入大量数据(每 10 分钟 800 个元素,每天 45 次)。 (id1,timet) 的元组总是唯一的。
timet 值是自纪元以来的秒数,并且会一直增加。出于所有实际目的,id1 是一个随机整数。不过,可能只有 20000 个唯一 ID。
然后我想访问 id1==someid 的所有值或访问 timet==sometime 的所有元素。在我通过 Linux 上的 C 接口使用最新 SQLite 的测试中,查找其中一个(或此查找的任何变体)大约需要 30 秒,这对于我的用例来说不够快。
我尝试为数据库定义一个索引,但这将插入速度减慢到完全不可行的速度(虽然我可能做错了......)
上表导致对任何数据的访问都非常缓慢。我的问题是:
- SQLite 是否完全是错误的工具?
- 我可以定义索引来显着加快速度吗?
- 我是否应该为此使用 HDF5 而不是 SQL?
请原谅我对 SQL 的基本了解!
谢谢
我包含一个代码示例,该示例显示了在使用索引时插入速度如何减慢到爬行。使用“创建索引”语句,代码需要 19 分钟才能完成。没有它,它会在 18 秒内运行。
#include <iostream>
#include <sqlite3.h>
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<float>(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" );
}
【问题讨论】:
-
在任何给定时间您需要访问多少历史数据?您可以将旧数据存档到另一个表中以保持持久性并节省查询“相关”数据的时间。
-
我
需要访问所有历史数据。如果需要,我可以每年将其拆分为一个数据集,但我希望 SQLite 可以让我不必管理这些细节。 -
能否请您发布使用 sqlite3* 函数的整个代码(省略其他部分)?如果进程“爬到完全停止”,那肯定是不对的。
-
您需要相同粒度的历史数据吗?如果没有,RRDB 可能会很有趣。
-
为什么需要查找id1==someid?这是为了让您以后可以选择“随机”数据吗?从您的情况来看,您似乎不需要 someid 是唯一的,因此在 2010-01-01 记录 #3 和在 2010-06-06 记录 #79832759385 可能具有相同的 id1 字段。同样,对于每秒插入的多条记录,您在 HHMMSS 上存在冲突。因此,在情况 1 中,您选择 id==someId 并获得零个、一个或多个稀疏记录;在情况 2 中,您会得到零个、一个或多个相邻记录。如果这些是您的需求,您可能会受益于不同的运行时查询方法。