我知道sqlite对于非常大的数据库文件表现不佳,即使它们受支持(曾经在sqlite网站上发表评论,说明如果你需要的文件大小超过1GB,你可能要考虑使用企业rdbms。 再找不到它,可能与旧版本的sqlite有关。
但是,出于我的目的,我想在考虑其他解决方案之前了解它到底有多糟糕。
我说的是数千兆字节的sqlite数据文件,从2GB开始。
有人对此有经验吗? 任何提示/想法?
-
使用线程(每个线程的连接)可能只对读取有用 - stackoverflow.com/a/24029046/743263
-
挂钩softwareengineering.stackexchange.com/q/332069/24257和wiki.mozilla.org/Performance/
-
2016年:我有一个5 GB的数据库,可以在SQLite上运行而没有任何问题。 我在Postgres上安装了完全相同的数据集。 SQLite在2.7毫秒内运行了一个复杂的查询,Postgres在2.5毫秒内运行。 我最终在Postgres上获得了更容易的Regex访问和更好的索引功能。 但我对SQLite印象深刻,也可以使用它。
所以我用sqlite对非常大的文件做了一些测试,得出了一些结论(至少对我的具体应用来说)。
测试涉及单个sqlite文件,包含单个表或多个表。每个表有大约8列,几乎所有整数和4个索引。
想法是插入足够的数据,直到sqlite文件大约50GB。
单表
我试图在一个只有一个表的sqlite文件中插入多行。当文件大约7GB(抱歉我不能具体说明行数)时,插入时间太长了。我估计我插入所有数据的测试需要24小时左右,但即使在48小时后也没有完成。
这使我得出结论,单个非常大的sqlite表将存在插入问题,并且可能还有其他操作。
我想这并不奇怪,因为表变大,插入和更新所有索引需要更长时间。
多个表格
然后,我尝试将数据按时间分成几个表,每天一个表。原始1表的数据被分成约700个表。
这种设置没有插入问题,随着时间的推移,它不需要更长的时间,因为每天都会创建一个新表。
真空问题
正如i_like_caffeine所指出的,VACUUM命令是一个问题,sqlite文件越大。随着更多插入/删除操作,磁盘上文件的碎片将变得更糟,因此目标是定期VACUUM优化文件并恢复文件空间。
但是,正如文档所指出的那样,数据库的完整副本可以实现真空,需要很长时间才能完成。因此,数据库越小,此操作完成的速度就越快。
结论
对于我的特定应用程序,我可能会将数据分成几个db文件,每天一个,以获得最佳的真空性能和插入/删除速度。
这使查询变得复杂,但对我来说,能够索引这么多数据是值得的权衡。另一个优点是我可以删除整个db文件以丢弃一天的数据(我的应用程序的常见操作)。
我可能还需要监控每个文件的表大小,以查看速度何时成为问题。
除了自动真空之外似乎没有增量真空方法太糟糕了。我不能使用它,因为我的真空目标是对文件进行碎片整理(文件空间不是很大),而自动真空无法做到。事实上,文档说它可能会使碎片变得更糟,所以我不得不求助于定期对文件进行全真空。
-
非常有用的信息。纯粹的推测,但我想知道是否可以使用新的备份api每天创建一个非碎片版本的数据库,并避免运行VACUUM。
-
我很好奇,你的所有INSERTS都在交易中吗?
-
是的,插入是每批交易10000封邮件完成的。
-
你用了什么文件系统?如果ext {2,3,4},data =设置是什么,启用了日记功能?除了io模式,sqlite刷新到磁盘的方式可能很重要。
-
我主要在windows上测试,所以无法评论linux上的行为。
-
如果你还有它,你可以发布你使用的代码/ sql吗?
-
不幸的是,代码与我工作的软件非常相关,因此难以理解。我认为我的描述应该足以复制它...如果您需要其他信息,请告诉我。
-
有意思......您是否尝试过使用多个数据库进行基准测试(最多可以附加62个数据库),每个表对应多表格方法一个表?
-
如果在表上定义了索引,INSERT会减慢更多数据是正常的。
我们在平台上使用50 GB +的DBS。没有抱怨很有效。
确保你做的一切正确!您使用的是预定义语句吗?
* SQLITE 3.7.3
交易
预先陈述
应用这些设置(在创建数据库后立即)
1 2 3 4 5 6
| 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范围内使用dbs测试,效果也很好。
-
还PRAGMA main.temp_store = MEMORY;。
-
@Alex,为什么有两个PRAGMA main.cache_size = 5000;?
-
@VikrantChaudhary和Alex:不会改变page_size搞乱sqlite.org/pragma.html#pragma_page_size上描述的优化吗?
-
@VikrantChaudhary:temp_store = MEMORY应该是大多数设置中的默认设置。
-
不要盲目地应用这些优化。特别是synchronous = NORMAL不是崩溃安全的。即,即使在没有磁盘故障的情况下,正确的时间进程崩溃也可能破坏您的数据库。 sqlite.org/pragma.html#pragma_synchronous
-
@Alex你可以解释一下这些值以及'和默认值之间的区别吗?
-
你在50G和160G数据库@Snazzer和@Alex中拥有多少个表?
我创建了最大3.5GB的SQLite数据库,没有明显的性能问题。如果我没记错的话,我认为SQLite2可能有一些下限,但我不认为SQLite3有任何这样的问题。
根据SQLite Limits页面,每个数据库页面的最大大小为32K。并且数据库中的最大页数为1024 ^ 3。所以根据我的数学计算,最大尺寸为32TB。我想你会在点击SQLite之前达到文件系统的限制!
-
根据您正在执行的操作,尝试在8G sqlite数据库中删除3000行,您需要足够的时间来酿造一个很好的法式印刷机,大笑
-
@benjaminz,你一定是做错了。如果在一个事务中包装3k行的删除,它应该几乎是即时的。我自己犯了这个错误:一个接一个地删除10k行需要30分钟。但是,一旦我将所有删除语句包装到一个事务中,它花了5秒。
花费大约48小时进行插入的大部分原因是因为你的索引。它非常快:
1 - 删除所有索引
2 - 做所有插入
3 - 再次创建索引
-
这是众所周知的...但是对于长时间运行的过程,您不会定期删除索引来重建它们,尤其是当您要查询它们进行工作时。这是在必须从头开始重建sqlite db时采用的方法,在完成所有插入后创建索引。
-
@Snazzer在类似情况下我们使用了"累加器"表:每天一次我们将累积的行从累加器表移动到单个事务中的主表。在需要时,视图负责将两个表作为单个表格呈现。
-
另一个选项是保留索引,但在插入数据之前按索引顺序对数据进行预排序。
-
@StevenKryskalla与删除索引并重新创建它们相比如何?您知道哪些链接已经过基准测试?
-
@mcmillab这是几年前所以我不记得所有细节或基准统计数据,但直观地思考,将N个随机排序的元素插入索引将花费O(NlogN)时间,而插入N个已排序元素将需要O(N ) 时间。
除了通常的建议:
批量插入的掉落指数。
批量插入/更新大型事务。
调整缓冲区缓存/禁用日志/ w PRAGMA。
使用64位机器(能够使用大量缓存?)。
[2014年7月添加]使用公用表表达式(CTE)而不是运行多个SQL查询!需要SQLite版本3.8.3。
我从SQLite3的经验中学到了以下内容:
要获得最大插入速度,请不要将模式与任何列约束一起使用。 (稍后根据需要更改表 del>您不能使用ALTER TABLE添加约束)。
优化您的架构以存储您需要的内容。有时这意味着在插入数据库之前分解表和/或甚至压缩/转换数据。一个很好的例子是将IP地址存储为(长)整数。
每个db文件一个表 - 以最小化锁争用。 (如果要拥有单个连接对象,请使用ATTACH DATABASE。
SQLite可以在同一列中存储不同类型的数据(动态类型),使用它对您有利。
欢迎提问/评论。 ;-)
-
您从"每个db文件一个表"中获得了多少影响?听起来不错。如果您的桌子只有3张桌子并且是从头开始构建,您认为这会很重要吗?
-
@martin讨厌说出来,但答案取决于它。这个想法是将数据划分为可管理的大小。在我的用例中,我从不同的主机收集数据并在事后报告数据,因此这种方法运行良好。按照其他人的建议按日期/时间进行分区应该适用于我想象的很长一段时间的数据。
-
@Lester Cheung:关于你的第二个#1:我从文档和个人经验中了解到,到目前为止,SQLite3不支持在创建表后使用ALTER TABLE添加约束。从现有表行添加或删除约束的唯一方法是创建具有所需特征的新表并复制所有行,这可能比使用约束插入一次要慢得多。
-
@Widdershins你绝对正确 - SQLite中的ALTER TABLE不允许添加约束。我不知道我在吸烟 - 会更新答案 - 谢谢。
-
这些建议都与使用庞大的SQLite数据库文件无关。自提交此答案后,问题是否已编辑?
我有一个7GB的SQLite数据库。
使用内部联接执行特定查询需要2.6s
为了加快速度,我尝试添加索引。根据我添加的索引,有时查询下降到0.1秒,有时上升到7秒。
我认为我的问题是,如果列高度重复,那么添加索引会降低性能:(
-
为什么具有多个重复项的列会降低性能(严重问题)?
-
基数较低的列更难索引:stackoverflow.com/questions/2113181/
在SQLite文档中曾经有一条声明,数据库文件的实际大小限制是几十GB。这主要是因为每当您启动事务时,SQLite都需要"分配脏页的位图"。因此,数据库中的每个MB需要256字节的RAM。插入50 GB的DB文件需要大量(2 ^ 8)*(2 ^ 10)= 2 ^ 18 = 256 MB的RAM。
但是,从SQLite的最新版本开始,不再需要这样做了。在这里阅读更多。
-
我很抱歉,我必须指出这一点,但2^18实际上只有256 K.
-
@GabrielSchreiber,以及50GB不是(2 ^ 10)MB的事实,那只有1GB。所以对于50GB的数据库,你需要12.5MB的内存:(2 ^ 8)*(2 ^ 10)* 50
我认为关于sqlite扩展的主要抱怨是:
单进程写。
没有镜像。
没有复制。
使用vacuum命令时,我遇到了大型sqlite文件的问题。
我还没有尝试过auto_vacuum功能。如果您希望经常更新和删除数据,那么这值得关注。