具有大型数据库文件的sqlite的性能特征是什么?[关闭]


325

我知道sqlite即使支持超大数据库文件也不能很好地工作(在sqlite网站上曾经有评论说,如果您需要的文件大小超过1GB,则可能要考虑使用企业rdbms。找不到了,可能与旧版本的sqlite有关)。

但是,出于我的目的,我想在考虑其他解决方案之前先了解一下它的严重程度。

我说的是从2GB开始的数GB范围内的sqlite数据文件。有人对此有经验吗?有任何提示/想法吗?


1
使用线程(每个线程的连接)可能有助于只读取- stackoverflow.com/a/24029046/743263
malkia


23
2016年:我有一个5 GB的数据库,可以在SQLite上正常运行。我在Postgres上安装了完全相同的数据集。SQLite在2.7毫秒内运行复杂的查询,在Postgres于2.5毫秒内运行。我最终选择了Postgres,以获得更轻松的Regex访问和更好的索引功能。但是我对SQLite印象深刻,也可以使用它。
Paulb

Answers:


246

因此,我使用sqlite对非常大的文件进行了一些测试,并得出了一些结论(至少对于我的特定应用程序而言)。

测试涉及具有单个表或多个表的单个sqlite文件。每个表大约有8列,几乎所有整数和4个索引。

想法是插入足够的数据,直到sqlite文件约为50GB。

单桌

我试图将多个行插入到只有一个表的sqlite文件中。当文件大小约为7GB(对不起,我无法确切说明行数)时,插入时间过长。我估计插入所有数据的测试大约需要24小时,但即使在48小时后仍无法完成。

这使我得出一个结论,即一个非常大的sqlite表在插入操作以及其他操作上也会遇到问题。

我猜这并不奇怪,因为表越来越大,插入和更新所有索引花费的时间更长。

多个表

然后,我尝试按时间将数据分成几张表,每天一张表。原始1个表的数据被拆分为700个表。

由于每天都会创建一个新表,因此该设置在插入时没有问题,并且时间不会花费更长的时间。

真空问题

正如i_like_caffeine所指出的,sqlite文件越大,VACUUM命令就是一个问题。随着更多插入/删除操作的完成,磁盘上文件的碎片将变得更糟,因此目标是定期进行VACUUM优化文件并恢复文件空间。

但是,正如文档所指出的那样,数据库的完整副本是用来进行清理的,需要很长时间才能完成。因此,数据库越小,此操作将完成得越快。

结论

对于我的特定应用程序,我可能会每天将数据拆分成多个db文件,以获得真空性能和插入/删除速度的最佳效果。

这使查询变得复杂,但是对我来说,能够对这么多的数据建立索引是一个值得权衡的选择。另一个优点是,我可以删除整个db文件来删除一天的数据量(这是我的应用程序的常用操作)。

我可能还必须监视每个文件的表大小,以查看速度何时会成为问题。

令人遗憾的是,除了自动真空之外,似乎没有其他增量真空方法。我无法使用它,因为我的清理目标是对文件进行碎片整理(文件空间不是什么大问题),而自动清理不会这样做。实际上,文档表明它可能使碎片变得更糟,因此我不得不定期对文件进行完全清理。


5
非常有用的信息。纯粹是推测,但我想知道新的备份API是否可以每天用于创建数据库的非零碎版本,并避免运行VACUUM。
eodonohoe

24
我很好奇,您所有的插入内容都在交易中吗?
Paul Lefebvre,2009年

9
是的,每个事务以10000条消息的批次进行插入。
斯纳兹(Snazzer)

6
您使用了什么文件系统?如果ext {2,3,4}是什么data =设置,是否启用日记功能?除了io模式,sqlite刷新到磁盘的方式可能很重要。
东武

5
我主要在Windows上进行测试,因此无法评论linux上的行为。
爵士(Snazzer)2011年

169

我们正在平台上使用50 GB +的DBS。没有抱怨效果很好。确保您做的一切正确!您是否在使用预定义语句?* SQLITE 3.7.3

  1. 交易次数
  2. 预先声明
  3. 应用这些设置(在创建数据库之后)

    PRAGMA main.page_size = 4096;
    PRAGMA main.cache_size=10000;
    PRAGMA main.locking_mode=EXCLUSIVE;
    PRAGMA main.synchronous=NORMAL;
    PRAGMA main.journal_mode=WAL;
    PRAGMA main.cache_size=5000;

希望这对其他人有帮助,在这里能起到很大的作用


22
最近使用160GB范围的数据库进行了测试,效果也很好。
斯纳泽2011年

10
PRAGMA main.temp_store = MEMORY;
Vikrant Chaudhary

40
@Alex,为什么有两个PRAGMA main.cache_size = 5000 ;?
杰克

23
不要只是盲目地应用这些优化。特别是sync = NORMAL不是崩溃安全的。即,即使没有磁盘故障,在正确的时间发生的进程崩溃也可能损坏您的数据库。sqlite.org/pragma.html#pragma_synchronous
mpm

22
@Alex您能解释一下这些值以及'em和默认值之间的区别吗?
4m1nh4j1 2014年

65

我创建了最大3.5GB的SQLite数据库,没有明显的性能问题。如果我没记错的话,我认为SQLite2可能有一些下限,但我认为SQLite3没有任何此类问题。

根据“ SQLite限制”页面,每个数据库页面的最大大小为32K。数据库中的最大页面数为1024 ^ 3。因此,根据我的数学计算,最大大小为32 TB。我认为您在达到SQLite的标准之前会达到文件系统的极限!


3
根据您执行的操作,尝试在8G sqlite数据库中删除3000行,您需要花费足够的时间来酿造一罐不错的法国报纸,哈哈
benjaminz

4
@benjaminz,您一定做错了。如果在一个事务中包装删除3k行,则它应该几乎是即时的。我本人也犯了这个错误:一次删除1万行花了30分钟。但是,一旦我将所有delete语句包装到一个事务中,就花了5秒钟。
mvp

55

进行插入需要花费48个小时以上的大部分原因是由于您的索引。更快地达到:

1-删除所有索引2-全部插入3-再次创建索引


23
众所周知...但是对于一个长期运行的过程,您不会定期删除索引来重建它们,尤其是当您要查询它们以进行工作时。尽管必须从头开始重建sqlite db,但在完成所有插入操作后才创建索引,因此采用了这种方法。
斯纳泽

24
@Snazzer在类似的情况下,我们使用了“累加器”表:每天一次,然后在一次事务中将累加的行从累加器表移至主表。在需要的地方,一个视图负责将两个表都呈现为一个表。
CAFxX 2012年

4
另一种选择是保留索引,但在插入数据之前按索引顺序对其进行预排序。
Steven Kryskalla 2014年

1
@StevenKryskalla与删除索引并重新创建索引相比有何不同?您知道的任何链接都经过基准测试吗?
mcmillab

1
@mcmillab这是几年前的事,所以我不记得所有的详细信息或基准统计信息,但是凭直觉思考,将N个随机排序的元素插入索引将花费O(NlogN)时间,而插入N个排序的元素将花费O(N ) 时间。
Steven Kryskalla

34

除了通常的建议:

  1. 批量插入的删除索引。
  2. 大型交易中的批量插入/更新。
  3. 调整缓冲区缓存/禁用日志/ w PRAGMA。
  4. 使用64位计算机(以便能够使用大量的cache™)。
  5. [2014年7月添加]使用公用表表达式(CTE)而不是运行多个SQL查询!需要SQLite版本3.8.3。

我从SQLite3的经验中学到了以下内容:

  1. 为了获得最大的插入速度,请不要将架构与任何列约束一起使用。(以后根据需要更改表 您不能使用ALTER TABLE添加约束。
  2. 优化您的架构以存储所需的内容。有时,这意味着在插入数据库之前分解表和/或什至压缩/转换数据。一个很好的例子是将IP地址存储为(长)整数。
  3. 每个db文件一个表-最小化锁争用。(如果要具有单个连接对象,请使用ATTACH DATABASE
  4. SQLite可以在同一列中存储不同类型的数据(动态类型),以利用您的优势。

欢迎提问/评论。;-)


1
您会从“每个db文件一个表”中获得多大的影响?听起来不错。您认为如果您的表只有3个表并且是从头开始构建的,那会很重要吗?
马丁·贝莱斯

4
@martin讨厌这么说,但答案取决于它。想法是将数据划分为可管理的大小。在我的用例中,我从不同的主机收集数据,事后对数据进行报告,因此这种方法很好用。正如其他人所建议的那样,按日期/时间进行分区应该可以很好地处理我想像的很长一段时间的数据。
张敬轩

3
@Lester Cheung:关于您的第二个第一点:根据我的文档和个人经验,到目前为止,SQLite3不支持在创建表后使用ALTER TABLE添加约束。从现有表行中添加约束或从中删除约束的唯一方法是创建具有所需特征的新表并在所有行上进行复制,这可能比用约束插入一次要慢得多。
Mumbleskates 2015年

3
@Widdershins您绝对正确-SQLite中的ALTER TABLE不允许添加约束。我不知道自己在抽什么烟-将更新答案-谢谢。
Lester Cheung

这些建议均与使用庞大的SQLite db文件无关。自提交此答案以来,该问题是否已被编辑?
A. Rager

9

我认为有关sqlite缩放的主要抱怨是:

  1. 单进程写。
  2. 没有镜像。
  3. 没有复制。

9

我有一个7GB的SQLite数据库。使用内部联接执行特定查询需要2.6秒的时间。为了加快速度,我尝试添加索引。根据我添加的索引,有时查询下降到0.1s,有时上升到7s。我认为我的问题是,如果一列是高度重复的,那么添加索引会降低性能:(


9
为什么包含许多重复项的列会降低性能(严重的问题)?
马丁·贝莱斯

6
具有低基数的列是更难指数:stackoverflow.com/questions/2113181/...
Metrix的

9

在SQLite文档中曾经有一个声明,即数据库文件的实际大小限制为几十GB:s。这主要是由于SQLite在启动事务时需要“分配脏页的位图”。因此,数据库中的每个MB都需要256字节的RAM。插入到50 GB的DB文件中将需要大量(2 ^ 8)*(2 ^ 10)= 2 ^ 18 = 256 MB的RAM。

但是从最新版本的SQLite开始,不再需要此功能。在这里阅读更多。


25
我很抱歉,我指出这一点,但2^18实际上只是256 K.
加布里埃尔施雷伯

7
@GabrielSchreiber,以及50GB不是(2 ^ 10)MB的事实,也就是1GB。因此,对于一个50GB的数据库,您需要12.5MB的内存:(2 ^ 8)*(2 ^ 10)* 50
elipoultorak 2015年

8

使用vacuum命令时,大型sqlite文件遇到了问题。

我还没有尝试过auto_vacuum功能。如果您希望经常更新和删除数据,那么值得一看。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.