- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
在 Elasticsearch 的运行过程中,如何合理分配与设置内存是一件十分重要的事情,否则十分容易出现各种问题。
我们先看下 ES 服务器的总体内存消耗情况:
对于Query Cache、Request Cache、FieldData Cache、Indexing Buffer 以及 Segment 的介绍,在前面的文章以及介绍过了,这里就不重复介绍了:
Elasticsearch搜索引擎之缓存:Request Cache、Query Cache、Fielddata Cache
ElasticSearch搜索引擎:数据的写入流程
要回答 Elasticsearch 为什么这么耗费内存的问题,我们需要从两个角度切入:
(1)ES 是 JAVA 应用,那么就与 JVM 与 GC 息息相关。 我们这里不对 JVM GC 做深入探讨,只需知道:应用层面生成大量长生命周期的对象,是给 heap 造成压力的主要原因。例如读取大量数据在内存中进行排序,或者在 heap 内部缓存大量数据,如果 GC 释放的空间有限,而应用层面持续大量申请新对象,GC 频率就开始上升,不仅会消耗掉很多CPU时间,严重时可能恶性循环,导致整个集群停工。
(2)ES 底层存储引擎是基于 Lucene 的,Lucene 的倒排索引(Inverted Index)是先在内存里生成,然后定期以段文件(segment file)的形式刷到磁盘的。每个段实际就是一个完整的倒排索引,并且一旦写到磁盘上就不会做修改。API 层面的文档更新和删除实际上是增量写入的一种特殊文档,会保存在新的段里,所以不变的段文件非常容易被操作系统缓存,热数据几乎等效于内存访问。
为什么分配给ES的堆内存不能超过物理机内存的一半?
为什么需要预留一半的内存给 Lucene,将所有的内存都分配给 Elasticsearch 不是更好吗?毋庸置疑,堆内存对于 ES 来说绝对是重要的,但还有另外一个非常重要的内存使用者——Lucene。
在讲 Lucene 前,我们先简单介绍一下 segment。每个 segment 段是分别存储到单个文件的,即 segment 文件。它其实是一个包含正排(空间占90~95%) + 倒排(占5~10%) 的完整索引文件,并且一旦写到磁盘上就不会再修改,ES中文档更新和删除实际上是增量写入的一种特殊文档,会保存在新的段里而不修改旧的段。
回到 Lucene,Lucene 实际目的就是把底层 OS 里的数据缓存到内存中。由于 Lucene 的段 segment 是不会变化的,所以很利于缓存,操作系统会将这些段文件缓存起来,以便更快的访问。这些段包括倒排索引(用于全文搜索)和文档值(用于聚合)。
Lucene 的性能依赖于与 OS 的这种交互,如果把所有的内存都给了ES的堆内存,而不留一点给 Lucene,那么全文检索的性能会很差的。所以官方建议是将可用内存的 50% 提供给ES堆,而其他 50% 的剩余内存也并不会被闲置,因为 Lucene 会利用他们来缓存被用读取过的段文件。
ES的文件存储类型默认使用的是 mmap 内存映射,将 Lucene 索引文件用映射到内存中,这样进程就能够直接从内存中读取 Lucene 数据了。由于使用了内存映射,ES 进程读取 Lucene 文件时读取到的数据就会占用了堆外内存的空间。
堆内存为什么不能超过32GB?事实上 JVM 在内存小于 32 G 的时候会采用一种内存对象指针压缩技术。
在 Java 中,所有的对象都分配在堆上,然后有一个指针引用它。指向这些对象的指针大小通常是 CPU 的字长的大小,不是 32 bit 就是 64 bit,这取决于你的处理器,指针指向了你的值的精确位置。对于 32 位系统,你的内存最大可使用 4 G。对于 64 系统可以使用更大的内存。但是 64 位的指针意味着更大的浪费,因为你的指针本身大了。并且比浪费的空间更糟糕的是,更大的指针在主内存和缓存器(例如 LLC,L1 等)之间移动数据的时候,会占用更多的带宽。
Java 使用一个叫内存指针压缩的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示偏移量。这意味着 32 位的指针可以引用 40 亿个对象,而不是 40 亿个字节。最终,也就是说堆内存长到 32 G 的物理内存,也可以用 32 bit 的指针表示。
一旦越过那个神奇的 30 - 32 G 的边界,指针就会切回普通对象的指针,每个对象的指针都变长了,就会使用更多的 CPU 内存带宽,也就是说你实际上失去了更多的内存。事实上当内存到达 40 - 50 GB 的时候,有效内存才相当于使用内存对象指针压缩技术时候的 32 G 内存。
所以,即便你有足够的内存,也尽量不要超过 32 G,因为它浪费了内存,降低了 CPU 的性能,还要让 GC 应对大内存。
分配给 Heap 堆的内存不要超过系统可用物理内存的一半,以确保有足够的物理内存留给 Lucene 系统文件缓存,并且不要超过 32 GB。那么 JVM 参数呢?只需要将最小堆大小(Xms)和最大堆大小(Xmx)设置和 heap 一样大小,避免动态分配 heap size 就好了。确保 Xms 和 Xmx 的大小是相同的,其目的是为了能够在 java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源,可以减轻伸缩堆大小带来的压力。
虽然说 32 GB 是 ES 的一个内存设置限制,那如果机器有很大的内存怎么办?比如现在的机器内存普遍都大,设置有 300 - 500 GB 内存的机器。当然,如果有这种机器,那是极好的,接下来有两个方案:
(1)如果主要做全文检索,可以考虑给 Elasticsearch 32 G 内存,剩下的交给 Lucene 用作操作系统的文件系统缓存,所有的 segment 都缓存起来,会加快全文检索。
(2)如果需要更多的排序和聚合,那就需要更大的堆内存。可以考虑一台机器上创建两个或者更多的 ES 节点,而不要部署一个使用 32 + GB 内存的节点。仍然要坚持 50% 原则,假设你有个机器有 128 G 内存,你可以创建两个 node,使用 32 G 内存。也就是说 64 G 内存给 ES 的堆内存,剩下的 64 G 给 Lucene。
PS:如果选择第二种方案,需要配置 cluster.routing.allocation.same_shard.host: true,防止同一个 shard 的主副本存在同一个物理机上,因为如果存在一个机器上,副本的高可用性就没有了
推荐文章:ES的内存问题分析:
这个问题已经有答案了: Is there any way to accept only numeric values in a JTextField? (20 个回答) It's possible i
我使用戴尔 XPS M1710。笔记本电脑的盖子、侧面扬声器和前置扬声器都有灯(3 组灯可以单独调节)和鼠标垫下方的灯。在 BIOS 中,我可以更改这些灯的颜色,至少是每个组。另外,我可以在鼠标垫下打
我知道我可以使用 在 iOS 5 中打开设置应用 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs://"
我有一个 Django 应用程序,我正在尝试为其设置文档。目录结构如下: - doc - project | - manage.py 我已经设置了路径以便 Sphinx 可以看到东西,但是当我尝试使用
我正在使用 768mb ram 运行 centos 5.5。我一直在日志中获取 server reached MaxClients setting, consider raising the MaxC
我在具有以下配置的服务器内运行了 Drupal 安装: StartServers 5 MinSpareServers 5 MaxSpareServers 15 MaxClien
是否可以使用 Microsoft.Web.Administration 包为给定的 location 配置 asp 设置? 我想以编程方式将以下部分添加到本地 IIS applicationHost.
我一直在阅读为 kube-proxy 提供参数的文档,但没有解释应该如何使用这些参数。我使用 az aks create 创建我的集群使用 azure-cli 程序,然后我获得凭据并使用 kubect
我想知道与在 PHP 中使用 setcookie() 函数相比,在客户端通过 JavaScript 设置一些 cookie 是否有任何明显的优势?我能想到的唯一原因是减少一些网络流量(第一次)。但不是
我有一个按钮可以将 body class 设置为 .blackout 我正在使用 js-cookie设置cookie,下面的代码与我的按钮相关联。 $('#boToggle').on('click'
我有一堆自定义的 HTML div。我将其中的 3 存储在具有 slide 类的 div 中。然后,我使用该幻灯片类调用 slick 函数并应用如下设置: $('.slide').slick({
我正在创建一个应该在 Windows 8(桌面)上运行的应用 我需要: 允许用户使用我的应用启动“文件历史记录”。我需要找到打开“文件历史记录”的命令行。 我需要能够显示“文件历史记录”的当前设置。
我刚买了一台新的 MacBook Pro,并尝试在系统中设置 RVM。我安装了 RVM 并将默认设置为 ➜ rvm list default Default Ruby (for new shells)
由于有关 Firestore 中时间戳行为即将发生变化的警告,我正在尝试更改我的应用的初始化代码。 The behavior for Date objects stored in Firestore
在 ICS 中,网络 -> 数据使用设置屏幕中现在有“限制后台数据”设置。 有没有办法以编程方式为我的应用程序设置“限制后台数据”? 或 有没有办法为我的应用程序调出具有选项的“数据使用”设置? 最佳
我正在尝试使用 NextJS 应用程序设置 Jest,目前在 jest.config.js : module.exports = { testPathIgnorePatterns: ["/.n
我最近升级到 FlashDevelop 4,这当然已经将我之前的所有设置恢复到原来的状态。 我遇到的问题是我无法在新设置窗口的哪个位置找到关闭它在方括号、大括号等之前插入的自动空格的选项。 即它会自动
有没有办法以编程方式访问 iPhone/iPod touch 设置? 谢谢。比兰奇 最佳答案 大多数用户设置可以通过读取存储在 /User/Library/Preferences/ 中的属性列表来访问
删除某些值时,我需要选择哪些设置来维护有序队列。我创建了带有自动增量和主键的 id 的表。当我第一次插入值时,没问题。就像 1,2,3,4,5... 当删除某些值时,顺序会发生变化,例如 1,5,3.
我正在尝试设置示例 Symfony2 项目,如此处所示 http://symfony.com/doc/current/quick_tour/the_big_picture.html 在访问 confi
我是一名优秀的程序员,十分优秀!