可以通过不同的配置选项来限制ArangoDB的内存使用并降低CPU使用率
storage engine
edge cache
server statistics
background threads
V8 (JavaScript features)
operating system / memory allocator (Linux)
有两个功能可能会占用大量内存
Buffers & Caches
WAL(Write Ahead log)
WAL和写缓冲区
RocksDB首先写入 映射到磁盘块的内存缓冲区 在某些时候, 内存缓冲区将已满必须将其写入磁盘
为了支持高写入负载, RocksDB可能会打开许多此类内存缓冲区
在正常情况下, 写缓冲区将使用1GB的内存, 如果内存紧张, 可以设置RocksDB设置
--rocksdb.max-total-wal-size 1024000 byte
--rocksdb.write-buffer-size 2048000
--rocksdb.max-write-buffer-number 2
--rocksdb.total-write-buffer-size 8192000
--rocksdb.dynamic-level-bytes false
以上将设置
限制未完成的内存缓冲区数量
将内存使用量限制在100MBbyte
在导入或者更新的过程中, 内存消耗可能仍会更大
另外一方面这些限制将影响最大写入性能,不应该设置低于上面的数字
读缓冲区
--rocksdb.block-cache-size 2560000
--rocksdb.enforce-block-cache-size-limit true
一旦内存缓冲区一直保存在磁盘上, 回答读取查询就意味着将他们读回到内存中,
上面的选项会将缓冲区数限制为几兆字节,
如果可能应将此设置为与数据集的热设置大小一样大
这些限制可能会影响查询效率
索引和过滤器块缓存
索引和过滤器块默认情况下不缓存, 这意味他们不计入--rocksdb.block-cache-size限制
启用--rocksdb.cache-index-and-fliter-blocks 将其包括上限中的选项
您可以启用其他选项,以避免索引和过滤器块从缓存中逐出。
--rocksdb.cache-index-and-filter-blocks`
--rocksdb.cache-index-and-filter-blocks-with-high-priority
--rocksdb.pin-l0-filter-and-index-blocks-in-cache
--rocksdb.pin-top-level-index-and-filter
边缓存
--cache.size 10485760
此选项将ArangoDB边缘缓存限制为10 MB。如果您没有图的用例并且不使用边集合,则可以使用最小值而不影响性能。通常,这应与热定形的大小相对应。
其他缓存
除所有缓冲区外,查询在执行期间还将使用其他内存,以处理数据并建立结果集。与缓冲区保留的内存相反,该内存仅在查询执行期间使用,之后将被释放。
查询内存使用率固定链接
默认情况下,查询将在内存中建立其完整结果。您可以使用光标逐批获取结果,但每个查询都需要先计算整个结果,然后才能检索第一个批。服务器还需要将结果保存在内存中,直到相应的游标被完全消耗或超时为止。建立完整的结果可以减少服务器不得不花费时间处理主存储器的时间。
在ArangoDB 3.4版中,我们引入了 具有某些倒置属性的流游标:减少了峰值内存使用,对集合的访问时间更长。流可以在文档级别进行,这意味着不能将其应用于所有查询部分。例如,子查询的所有结果的MERGE()不能流式传输(该操作的结果必须完全建立)。但是,周围的查询可能符合流式传输的条件。
除了流游标之外,ArangoDB还提供了指定查询不应超过的内存限制的可能性。如果是这样,查询将被中止。在执行块之间检查内存统计信息,这些块对应于说明输出中的行。这意味着需要功能的查询可能需要更多的内存来进行中间处理,但这不会因为内存而终止查询。
您可以在AQL查询中使用LIMIT操作来减少需要检查和处理的文档数量。然而,这并不总是在幕后发生。其他操作可能会导致在应用任何限制之前计算中间结果。最近,我们向优化器添加了一项新功能:AQL中的 排序限制优化。简而言之,将SORT与LIMIT操作结合使用时,在排序过程中只能将存储的文档数量保持为后续LIMIT所需的数量。从ArangoDB v3.5.0开始自动应用此优化。
统计的关闭开启选项
服务器会定期收集 统计信息,并在Web界面中向您显示。即使您的应用程序由于统计信息处于闲置状态,您的查询负载也很少。您可以根据需要禁用它们:
--server.statistics false
JavaScript和Foxx固定链接
使用嵌入式V8引擎在ArangoDB进程中执行JavaScript:
Web界面的后端部分
Foxx应用
Foxx队列
GraphQL
基于JavaScript的交易
用户定义的AQL功能
有几个V8上下文可以并行执行。您可以将它们视为线程池。它们也称为隔离株。默认情况下,每个隔离区都有几GB的堆。如果不使用JavaScript或使用很少的JavaScript,则可以限制V8:
--javascript.v8-contexts 2
--javascript.v8-max-heap 512
这会将V8隔离株的数量限制为两个。所有与JavaScript相关的请求都将排队,直到其中一个隔离可用于新任务为止。它还将堆大小限制为512 MB,因此在最坏的情况下,两个V8上下文组合使用的内存不能超过1 GB。
V8 for the Desperate
除非有充分的理由,否则不要使用以下设置,例如性能不重要的本地开发系统或硬件资源非常有限的嵌入式系统!
--javascript.v8-contexts 1
--javascript.v8-max-heap 256
您可以将V8的内存使用量减少到256 MB,而只有一个线程。例如,某些操作可能会因为某些操作耗尽内存而中止,例如在Web界面中。另外,JavaScript请求将被一一执行。
如果您的内存非常紧张,并且确定不需要V8,则可以将其完全禁用:
--javascript.enabled false
--foxx.queues false
因此,以下功能将不可用:
Web界面的后端部分
Foxx应用
Foxx队列
GraphQL
基于JavaScript的交易
用户定义的AQL功能
请注意,可以为群集中的DB-Server和Agency节点禁用JavaScript / V8,而没有这些限制。它们适用于单个服务器实例。它们也适用于Coordinator节点,但是您不应在Coordinators上禁用V8,因为某些集群操作依赖于此。
CPU使用率固定链接
我们不能真正减少CPU使用率,但是可以并行运行的线程数。同样,除非有很好的理由(例如嵌入式系统),否则您不应这样做。请注意,这将限制并发请求的性能,这对于只有您作为用户的本地开发系统来说可能是可以的。
可以通过以下方式限制后台线程的数量:
--arangosearch.threads-limit 1
--rocksdb.max-background-jobs 4
--server.maintenance-threads 2
--server.maximal-threads 4
--server.minimal-threads 1
通常,选择线程数以适合机器。但是,每个线程至少需要8 MB的堆栈内存。通过牺牲一些用于并行执行的性能,可以减少此性能。
此选项将使日志记录同步:
--log.force-direct true
除非您仅记录错误和警告,否则不建议这样做。
缓存分配说明
例子固定链接
通常,您应该在标准服务器上调整读取缓冲区和边缘缓存。如果您有图用例,则应该使用更大的边缘缓存。例如,在读取缓冲区和边缘缓存之间以50:50的比例分配内存。如果没有边缘,则使用最小边缘缓存,并将大部分内存用于读取缓冲区。
例如,如果您有一台具有40 GB内存的计算机,并且想要将ArangoDB的内存限制为20 GB,则如果使用图形功能,则将10 GB用于边缘缓存,将10 GB用于读取缓冲区。
请记住,在查询执行期间,额外的内存将临时用于查询结果。如果您的内存不足,则可能需要每个7 GB。
如果您具有嵌入式系统或开发笔记本电脑,则可以使用上述所有设置来进一步减少内存占用。对于正常操作,特别是生产,不建议使用这些设置。
Linux系统配置
ArangoDB的主要部署目标是Linux。正如您所了解的,ArangoDB及其内部结构在内存上的工作量很大。因此,至关重要的是要知道ArangoDB和Linux内核如何就此进行交互。linux内核提供了几种如何管理内存的模式。您可以通过proc文件系统,该文件/etc/sysctl.conf或/etc/sysctl.conf.d/系统在引导时将其应用于内核设置的文件来影响此设置。如下所述的设置适用于sysctl基础结构,这意味着它们以映射到proc文件系统/proc/sys/vm/overcommit_memory。
甲vm.overcommit_memory的设定2可以引起在一些环境问题结合捆绑内存分配器ArangoDB附带(jemalloc)。
分配器要求内核提供连续的内存块,这些内存块也映射到磁盘上的块。这是代表服务器进程(arangod)完成的。该过程可能会长时间使用一个块中的某些块,但仅在短时间内使用其他块,因此会释放内存。然后由分配器将释放的部分返回给内核。因为它只能返回连续的内存块,所以它必须将大块拆分为多个小块,然后可以返回未使用的块。
随着vm.overcommit_memory内核设置值2,分配器可能与拆分现有的内存映射,这使得故障数量 的arangod服务器进程随时间增长的内存映射。这可能导致内核拒绝将更多内存分配给Arangod进程,即使有更多物理内存可用。内核将最多只授予vm.max_map_count 每个进程的内存映射,在许多Linux环境中默认为65530。
运行vm.overcommit_memory设置为2的 jemalloc时的另一个问题是,对于某些工作负载,Linux内核随着已提交的内存跟踪的 内存量也会随着时间的推移而增长,而从未减少。最终, arangod可能仅由于达到配置的过量使用限制(物理RAM * overcommit_ratio+交换空间)而无法获得更多的内存。
解决的办法是 修改的值vm.overcommit_memory 从2至任一0或1。这将解决这两个问题。无论使用过量提交设置如何,使用jemalloc时,我们仍然观察到虚拟内存消耗不断增加,但是实际上这不会引起任何问题。0是Linux内核的默认值,也是我们建议的设置。
为了完整起见,让我们还提到另一种解决问题的方法:使用其他内存分配器。这要求从源代码编译ArangoDB而不使用jemalloc(-DUSE_JEMALLOC=Off在对cmake的调用中)。使用系统的libc分配器,您应该会看到相当稳定的内存使用情况。我们还尝试了另一个分配器,恰好是来自的分配器,libmusl随着时间的推移,这也显示出相当稳定的内存使用情况。阻碍我们更改捆绑分配器的原因在于,这是一次微不足道的更改,因为jemalloc对于大型多线程进程具有非常好的性能特征。
测试减少的I / O缓冲区的效果
效果图
15:50 –开始更大的进口
16:00 –一次开始写入〜60 KB大小的文档
16:45 –添加类似的第二作家
16:55 –使用上面建议的RocksDB写缓冲区配置重新启动ArangoDB
17:20 –缓冲区已满,写入性能下降
17:38 – WAL轮播
您在上面的性能图中看到的是限制写缓冲区的后果。在达到90%的写缓冲区填充率之前,服务器几乎可以遵循加载模式一段时间,但代价是不断增加缓冲区。一旦RocksDB达到90%的缓冲区填充率,它将显着将负载限制在〜50%。根据上游文档,这是预期的 :
如果可变存储器的总大小超过限制的90%,则会触发刷新。如果实际内存超出限制,则即使可变存储器的总大小小于90%,也可能触发更积极的刷新。
由于我们仅测量磁盘I / O字节,因此看不到文档保存操作的请求时间也增加了一倍。