2020年更新,大约在问题发布后11年,后来又关闭了,以防止出现新的答案。
此处编写的几乎所有内容都已过时。曾几何时,sqlite的内存容量或2 GB的存储空间(32位)或其他流行数字受到限制……好吧,这是很久以前的事了。
此处列出了官方限制。实际上,只要有可用的存储空间,sqlite就有可能工作。它适用于大于内存的数据集,它最初是在内存不足时创建的,从一开始就很重要。
存储100 GB数据绝对没有问题。它可能会存储一个TB,但是最终这是您需要质疑SQLite是否是完成此任务的最佳工具,并且您可能希望从完整的数据库中获得功能(远程客户端,并发写入,只读副本,分片)的关键所在。等)。

原文:
我知道即使支持超大型数据库文件,sqlite也无法很好地运行(过去对sqlite进行了评论网站上指出,如果您需要1GB以上的文件大小,则可能要考虑使用企业rdbms。找不到了,可能与sqlite的旧版本有关。
但是,出于我的考虑,我想在了解其他解决方案之前先了解它的严重程度。
我正在谈论从2GB开始的数GB范围内的sqlite数据文件。
任何人都对此有任何经验?有任何提示/想法吗?

评论

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

挂钩软件engineering.stackexchange.com/q/332069/24257和wiki.mozilla.org/Performance/…

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

2020年:我更新了问题。封闭11年后,这里的一切都迫切需要更新,从而阻止答案和编辑。编辑问题本身可能并不遵循stackoverflow规则,但是比保留陈旧的信息来误导下一代开发人员而言,这种方法更好。

#1 楼

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

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

其目的是插入足够的数据,直到sqlite文件大约为50GB。

单表

我试图在只有一个表的sqlite文件中插入多行。当文件约为7GB时(抱歉,我无法具体说明行数),插入时间太长。我估计插入所有数据的测试大约需要24小时,但即使在48小时后也无法完成。

这使我得出一个结论,即一个非常大的sqlite表会出现插入问题,并且可能还会发生其他操作。

我想这并不奇怪,因为该表会变大,插入和更新所有索引会花费更长的时间。

多个表

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

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

真空问题

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

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

结论

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

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

我可能还必须监视每个文件的表大小看看什么时候速度会成为问题。

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

评论


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

– eodonohoe
09年5月3日在16:36

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

– Paul Lefebvre
09年5月13日在23:18

是的,每个事务以10000条消息的批次进行插入。

–Snazzer
09年5月14日15:17

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

–东武
2011-2-22在23:07

我主要在Windows上进行测试,因此无法评论linux上的行为。

–Snazzer
2011-3-9的3:59

#2 楼

我们正在平台上使用50 GB +的DBS。没有抱怨效果很好。
请确保您所做的一切正确!您是否在使用预定义语句?
* SQLITE 3.7.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;



希望对其他人有帮助,在这里效果很好

评论


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

–Snazzer
11年7月13日在21:43

也是PRAGMA main.temp_store = MEMORY;。

–维克朗(Vikrant Chaudhary)
2011-10-23 14:40

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

–杰克
2011年11月1日于16:04

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

–mpm
2014-2-17在20:27

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

– 4m1nh4j1
14年7月13日在13:05

#3 楼

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

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

评论


根据您执行的操作,尝试删除8G sqlite数据库中的3000行,您需要花费足够的时间来酿造一台不错的法国印刷机,哈哈

– benjaminz
17年6月28日在15:28

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

–mvp
19年8月5日在16:03

#4 楼

进行插入花费了48个小时以上的大部分原因是由于您的索引。更快地实现以下操作:

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

评论


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

–Snazzer
2010年5月28日在17:22

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

– CAFxX
2012年10月14日7:05



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

–史蒂文·克莱斯卡拉(Steven Kryskalla)
2014-2-19在23:42

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

–mcmillab
19年2月6日在5:29

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

–史蒂文·克莱斯卡拉(Steven Kryskalla)
19年2月6日在19:54

#5 楼

除了通常的建议:


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

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


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

问题/欢迎评论。;-)

评论


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

– Martin Velez
2012年8月15日在7:25

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

–张怡
2012年11月6日在8:41

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

–溜冰鞋
2015年12月20日0:00

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

–张怡
16年1月25日在14:40

这些建议都与使用庞大的SQLite数据库文件无关。自提交此答案以来,该问题是否已被编辑?

– A. Rager
16年4月4日在3:46

#6 楼

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

评论


为什么包含许多重复项的列会降低性能(严重的问题)?

– Martin Velez
2012年8月15日在7:28



低基数的列很难索引:stackoverflow.com/questions/2113181/…

–metrix
2014年1月2日,21:32

#7 楼

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


单进程写入。
没有镜像。
没有复制。


#8 楼

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

从SQLite的最新版本开始,不再需要。在此处了解更多信息。

评论


非常抱歉,我不得不指出这一点,但是2 ^ 18实际上只有256K。

–加布里埃尔·施雷伯(Gabriel Schreiber)
11年11月29日在9:06

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

– elipoultorak
15年8月25日在13:18



#9 楼

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

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