4

我有一个测试设置来将行写入数据库。每个事务插入 10,000 行,没有更新。每一步都比最后一步花费更长的线性时间。前十步执行提交的时间如下(以毫秒为单位)

568、772、942、1247、1717、1906、2268、2797、2922、3816、3945

到将 10,00 行添加到 500,000 行的表时,提交需要 37149 毫秒!

  • 我没有外键约束。

  • 我发现使用 WAL 可以提高性能(给出上图),但仍然是线性退化

  • PRAGMA Synchronous=OFF 无效

  • PRAGMAlocking_mode=EXCLUSIVE 无效

  • 在没有附加索引和附加索引的情况下运行。产生了大致恒定的时间差,因此仍然是线性退化。

我还有其他一些设置

  • 设置自动提交(假)
  • PRAGMA page_size = 4096
  • PRAGMA journal_size_limit = 104857600
  • PRAGMA count_changes = 关闭
  • PRAGMA 缓存大小 = 10000
  • Schema 有 Id INTEGER PRIMARY KEY ASC,其插入是增量的,由 Sqlite 生成

完整架构如下(我已经运行了有索引和没有索引,但已经包含)

create table if not exists [EventLog] (
Id INTEGER PRIMARY KEY ASC, 
DocumentId TEXT NOT NULL, 
Event TEXT NOT NULL, 
Content TEXT NOT NULL, 
TransactionId TEXT NOT NULL, 
Date INTEGER NOT NULL, 
User TEXT NOT NULL)

create index if not exists DocumentId ON EventLog (DocumentId)

create index if not exists TransactionId ON EventLog (TransactionId)

create index if not exists Date ON EventLog (Date)

这是使用在 windows 环境中运行的 sqlite-jdbc-3.7.2

4

2 回答 2

3

SQLite 表和索引在内部组织为 B-Trees。在表中,Rowid 是排序键。(你INTEGER PRIMARY KEY是 Rowid。)


如果您插入的 ID 不大于表中已有的最大 ID,则不会附加记录,而是插入树中间的某个位置。当在一个事务中插入足够多的记录时,如果 ID 的分布是随机的,这意味着几乎数据库中的每一页都必须重写。

为了避免这种情况,

  1. 按递增顺序插入 ID;或者
  2. 插入 ID,NULL以便 SQLite 选择下一个值;或者
  3. INTEGER UNIQUE通过将其声明为(或仅INTEGER当您不需要额外的检查/索引)来防止 SQLite 使用您的 ID 字段为 Rowid ,从而使表排序独立于您的 ID。

在索引的情况下,插入具有随机分布的索引字段需要在随机位置更新索引。与表一样,当在一个事务中插入足够多的记录时,这意味着几乎索引中的每一页都必须重写。

当您加载大量数据时,建议在没有任何索引的情况下执行此操作,然后再重新创建它们。(与其他一些数据库不同,SQLite 没有暂时禁用索引的功能;只需删除它们。)

于 2012-10-01T18:25:58.870 回答
0

FYI, although I haven't limited the structure in terms of the content of the key, in 99.999% of cases, it will be a guid. So to resolve the performance issue I just wrote an algorithm for generating sequential guids using a time based value for the first 8 hex digits. This worked very well, even if blocks of guids are generated using early time values.

于 2012-10-16T14:37:51.333 回答