编辑:我原来的帖子有误;这个问题只是工作集大小和随机访问导致大量页面被访问的问题之一。
在更大的测试范围内,二次模型发散并变得更加线性。另外,我犯了一个错误,并认为我已经排除了随机插入顺序的原因,但该配置是错误的,单调插入确实更快。
测试设置:
测试的变化:
1. CREATE TABLE integers (value INTEGER);
2. CREATE TABLE integers (value INTEGER PRIMARY KEY);
3. CREATE TABLE integers (value INTEGER PRIMARY KEY) WITHOUT ROWID;
4. CREATE TABLE integers (value INT PRIMARY KEY);
近似拟合曲线:
1: Linear: 0 + 2.5E-7 * N
2+3: Quadratic: 1.35E-7 * N^2 + 1.8E-6 * N
4: Quadratic: 1.35E-7 * N^2 + 3.7E-6 * N
我期望从
INTEGER
表 (1) 到 INTEGER PRIMARY KEY
或 WITHOUT ROWID
(2, 3) 应该更快,因为值和 rowid 列的合并提供了更好的页面使用率。令我惊讶的是,使用任何一种形式都会以二次成本项削弱插入性能,最终变得无法使用。 (版本 2 和版本 3 的性能是相同的,因为我相信它们指定了相同的内容。)
版本 (4) 演示了相同的行为,但具有双倍的线性成本分量,反映了
INT PRIMARY KEY
不是像 INTEGER
PK 那样的特殊情况,并且除了聚集索引之外还导致二级索引插入桌子。我认为主键缓慢是一个特殊问题,仅在使用 WITHOUT ROWID
行为时才会出现,但将约束移至辅助索引并不能解决问题。
二次成本是由于混洗的插入造成的。
Sqlite 表和索引存储在 Btree 结构中。
表 (1) 有一个隐藏的
ROWID
列和 value
列。每条新记录都会添加到表的末尾、同一页中,并且 ROWID 不断增加。除了偶尔的 Btree 重新平衡之外,页面读取和写入都保持在最低限度。
表 (2) 和表 (3) 是相同的:
ROWID
不存在,而 value
是表的键,这意味着表实际上聚集在值列上。如果按顺序插入值,您将具有与 (1) 中相同的性能,但是按随机顺序插入它们将导致不断访问许多不同的页面,并且不仅需要查找,还需要更新 Btree更频繁。
表 (4) 与 (1) 相同,但由于
value
被声明为 PRIMARY KEY,因此会自动创建并维护 value
上的索引。因此,您拥有如 (1) 中所示顺序更新表的线性成本加上如 (2) 中所示随机更新 Btree 索引的成本。