ES索引占用内存原理
一、核心原理:ES索引必须占用的内存
ES索引至少需要占用以下内存(无法完全释放):
索引元数据:分片信息、映射、设置等(每个索引几KB到几MB);
字段数据(Field Data):聚合、排序时加载的字段值(可通过配置避免);
查询缓存:频繁查询的结果缓存(可清理但会自动重建);
分段内存:索引分段的倒排索引、词频统计等(必须加载)。
二、最小化内存占用的方法(从效果显著到辅助优化)
1.将索引设置为只读+关闭字段数据加载(最有效)
适用于备份索引、归档数据等几乎不查询的场景:
powershell
curl -s -k -u elastic:Passw0rd -XPUT "http://es_node:9200/pds_msg_record_auto_backup/_settings" -H "Content-Type: application/json" -d '{
"index.blocks.write": true, #设置为只读
"index.queries.cache.enabled": false, #禁用查询缓存
"index.fielddata.cache.size": "0", #禁用字段数据缓存 选用
"index.refresh_interval": "-1" #关闭自动刷新(减少内存缓冲区) 选用
}'
2.使用最佳压缩策略+优化映射(减少内存中的数据量)
powershell
# 创建索引时指定最佳压缩
curl -s -k -u elastic:Passw0rd -XPUT "http://es_node:9200/pds_msg_record_auto_backup" -H "Content-Type: application/json" -d '{
"settings": {
"index.codec": "best_compression", #使用最高压缩比
"number_of_replicas": 0, #关闭副本(仅备份索引适用)
"index.mapping.total_fields.limit": 1000 #限制字段数
},
"mappings": {
"properties": {
# 使用更节省内存的字段类型
"user_id": { "type": "keyword" },
"timestamp": { "type": "date" },
"status": { "type": "integer" }, #用integer代替keyword
"content": {
"type": "text",
"index": false #不需要搜索的字段设置为不索引
}
}
}
}'
3.强制段合并+刷新策略优化(减少分段内存)
powershell
#合并为最少分段(每个分片1-2个)
curl -s -k -u elastic:Passw0rd -XPOST "http://es_node:9200/pds_msg_record_auto_backup/_forcemerge?max_num_segments=1"
#关闭实时刷新(仅对只读索引)
curl -s -k -u elastic:Passw0rd -XPUT "http://es_node:9200/pds_msg_record_auto_backup/_settings" -d '{
"index.refresh_interval": "-1"
}'
4.使用冻结索引(Frozen Index)(ES 6.6+)
冻结索引将内存中的数据卸载到磁盘,查询时临时加载:
powershell
#冻结索引(大幅减少内存占用,但查询变慢)
curl -s -k -u elastic:Passw0rd -XPOST "http://es_node:9200/pds_msg_record_auto_backup/_freeze"
#解冻索引(恢复正常查询性能)
curl -s -k -u elastic:Passw0rd -XPOST "http://es_node:9200/pds_msg_record_auto_backup/_unfreeze"
三、效果预期
只读+关闭缓存:内存占用减少50%-70%;
冻结索引:内存占用减少80%-90%(但查询延迟增加10-100倍);
快照备份+删除索引:内存占用减少100%(但需从快照恢复才能查询)。
四、验证缓存清理是否生效
1.查看索引缓存统计(精确验证)
powershell
# 查看段内存使用
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/_cat/segments?v"
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/_cat/segments/pds_msg_record_auto_backup"
#清理前查看缓存统计
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/pds_msg_record_auto_backup/_stats/fielddata,query_cache,request_cache?pretty"
-- query_cache.memory_size_in_bytes: 0(查询缓存无内存占用)
-- fielddata.memory_size_in_bytes: 0(字段数据缓存无内存占用)
-- request_cache.memory_size_in_bytes: 0(请求缓存无内存占用)
-- 缓存已被清空:query_cache.cache_size: 0 说明缓存中无任何有效数据,之前的 167 条缓存记录已被驱逐(evictions: 167)
#执行清理
释放指定索引的全部缓存
curl -s -k -u elastic:Passw0rd -XPOST http://es_node:9200/pds_msg_record_auto_backup/_cache/clear
释放指定索引的特定类型缓存:若只需清理某一类缓存(如字段数据、查询缓存),可通过参数指定。
清理字段数据缓存
curl -s -k -u elastic:Passw0rd -XPOST http://es_node:9200/pds_msg_record_auto_backup/_cache/clear?fielddata=true
清理查询缓存
curl -s -k -u elastic:Passw0rd -XPOST http://es_node:9200/pds_msg_record_auto_backup/_cache/clear?query=true
清理分片请求缓存
curl -s -k -u elastic:Passw0rd -XPOST http://es_node:9200/pds_msg_record_auto_backup/_cache/clear?request=true
释放指定索引的特定字段缓存:若要精准清理个别字段的缓存,用fields参数指定字段名,该命令仅清理指定字段相关缓存。
curl -s -k -u elastic:Passw0rd -XPOST http://es_node:9200/pds_msg_record_auto_backup/_cache/clear??fields=userName,age
#清理后立即查看(可能还有残留)
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/pds_msg_record_auto_backup/_stats/fielddata,query_cache,request_cache?pretty"
#等待几分钟后再查看(GC后)
sleep 300
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/pds_msg_record_auto_backup/_stats/fielddata,query_cache,request_cache?pretty"
- 查看节点级缓存使用
powershell
# 查看所有缓存使用情况
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/_nodes/stats/indices/fielddata,query_cache,request_cache?pretty"
- 手动触发 GC(立即释放内存)
powershell
# 清理缓存后手动触发垃圾回收
curl -s -k -u elastic:Passw0rd -XPOST "http://es_node:9200/_nodes/stats/indices/fielddata,query_cache,request_cache?pretty"
# 然后查看内存变化
curl -s -k -u elastic:Passw0rd http://es_node:9200/_cat/nodes?v&h=name,heap.current,heap.percent
内存分析
powershell
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/group_tag_all_boci/_stats/fielddata,query_cache,request_cache?pretty"
{"_shards" : {"total" : 2,"successful" : 2,"failed" : 0},"_all" : {"primaries" : {"query_cache" : {"memory_size_in_bytes" : 34706566,"total_count" : 1229311,"hit_count" : 47719,"miss_count" : 1181592,"cache_size" : 637,"cache_count" : 1880,"evictions" : 1243},"fielddata" : {"memory_size_in_bytes" : 0,"evictions" : 0},"request_cache" : {"memory_size_in_bytes" : 4273432,"evictions" : 0,"hit_count" : 568,"miss_count" : 654}},"total" : {"query_cache" : {"memory_size_in_bytes" : 62748530,"total_count" : 2183787,"hit_count" : 71593,"miss_count" : 2112194,"cache_size" : 1241,"cache_count" : 2572,"evictions" : 1331},"fielddata" : {"memory_size_in_bytes" : 0,"evictions" : 0},"request_cache" : {"memory_size_in_bytes" : 7913128,"evictions" : 0,"hit_count" : 1141,"miss_count" : 1252}}},"indices" : {"group_tag_all_boci" : {"uuid" : "37zIJkIfSLO4s0IeI6sW2Q","primaries" : {"query_cache" : {"memory_size_in_bytes" : 34706566,"total_count" : 1229311,"hit_count" : 47719,"miss_count" : 1181592,"cache_size" : 637,"cache_count" : 1880,"evictions" : 1243},"fielddata" : {"memory_size_in_bytes" : 0,"evictions" : 0},"request_cache" : {"memory_size_in_bytes" : 4273432,"evictions" : 0,"hit_count" : 568,"miss_count" : 654}},"total" : {"query_cache" : {"memory_size_in_bytes" : 62748530,"total_count" : 2183787,"hit_count" : 71593,"miss_count" : 2112194,"cache_size" : 1241,"cache_count" : 2572,"evictions" : 1331},"fielddata" : {"memory_size_in_bytes" : 0,"evictions" : 0},"request_cache" : {"memory_size_in_bytes" : 7913128,"evictions" : 0,"hit_count" : 1141,"miss_count" : 1252}}}}}
核心结论:group_tag_all_boci 索引的缓存占用明显(查询缓存~60MB、请求缓存~7.6MB),且缓存命中率极低(仅 3.2%),缓存不仅没起到优化作用,还占用内存,建议清理缓存并调整配置避免无效缓存堆积。
一、关键缓存状态解读(问题核心)
- 缓存占用情况
查询缓存:总占用~62.7MB(memory_size_in_bytes: 62748530),缓存条目 1241 条;
请求缓存:总占用~7.9MB(memory_size_in_bytes: 7913128);
字段数据缓存:0 占用(正常,无频繁聚合 / 排序操作)。 - 核心问题:缓存命中率极低
查询缓存命中率 = 命中数 ÷ 总查询数 = 71593 ÷ 2183787 ≈ 3.2%;
意味着 96.8% 的查询都没命中缓存,缓存不仅没提升性能,还额外占用内存、增加缓存管理开销(如驱逐旧缓存)。
二、优化方案(先清理再禁用无效缓存)
- 立即清理现有缓存(释放~70MB 内存)
powershell
# 清理 group_tag_all_boci 索引的所有缓存
curl -s -k -u elastic:Passw0rd -XPOST "http://es_node:9200/group_tag_all_boci/_cache/clear"
# 验证清理结果(缓存内存应变为 0)
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/group_tag_all_boci/_stats/fielddata,query_cache,request_cache?pretty"
- 长期优化:调整缓存配置(若后续命中率提升)
若未来查询模式变化(如频繁重复查询),可重新启用并限制缓存大小:
powershell
curl -s -k -u elastic:Passw0rd -XPUT "http://es_node:9200/group_tag_all_boci/_settings" -d '{
"index.queries.cache.enabled": true,
"index.queries.cache.size": "50mb", # 限制查询缓存最大 50MB
"index.request_cache.size": "10mb" # 限制请求缓存最大 10MB
}'
三、为什么会出现低命中率?(避免重复踩坑)
查询模式多样:业务查询多为非重复查询(如不同用户、不同条件的实时查询),缓存无法复用;
数据更新频繁:Hive每天全量更新索引,旧缓存会因数据变化失效,新缓存又难以命中;
缓存配置不合理:默认缓存大小可能过大,导致无效缓存堆积,占用内存。
总结
核心结论:要让备份索引不占用文件系统缓存、不主动加载分段到内存,最彻底的方案是"冻结索引+调整系统缓存策略"------冻结索引让ES主动卸载内存数据,系统层面限制缓存占用,双管齐下解决问题。
一、核心实现方案(从ES配置到系统优化)
1.冻结备份索引(ES层面主动卸载内存)
冻结是ES专为归档/备份索引设计的功能,能完全满足"不占用文件系统缓存、不主动加载分段"的需求:
powershell
# 冻结两个备份索引(立即卸载核心数据和分段,仅保留少量元数据)
curl -s -k -u elastic:Passw0rd -XPOST "http://es_node:9200/pds_msg_record_auto_backup/_freeze"
curl -s -k -u elastic:Passw0rd -XPOST "http://es_node:9200/pds_decision_instance_auto_backup/_freeze"
冻结后效果:
ES会将索引的分段数据从文件系统缓存中驱逐,不再主动加载;
内存占用(RAM)会在1-5分钟内明显下降(节点 es_node的RAM使用率预计从92%降至75%以下);
索引变为只读,无法写入/更新,完美适配备份索引的用途。
- 调整ES索引配置(防止缓存再生)
冻结后补充配置,彻底杜绝备份索引占用缓存:
powershell
curl -s -k -u elastic:Passw0rd -XPUT "http://es_node:9200/pds_msg_record_auto_backup,pds_decision_instance_auto_backup/_settings" -H "Content-Type: application/json" -d '{
"index.queries.cache.enabled": false, # 禁用查询缓存
"index.fielddata.cache.size": "0", # 禁用字段数据缓存
"index.refresh_interval": "-1", # 关闭自动刷新(不产生新分段缓存)
"index.blocks.write": true # 强制只读,避免任何写入操作触发缓存
}'
- 系统层面限制文件系统缓存(可选,进阶优化)
如果冻结后RAM使用率仍偏高,可在ES节点服务器上调整内核参数,限制文件系统缓存对备份索引的占用(需root权限):
powershell
# 1. 临时生效(重启后失效,适合测试)
sysctl -w vm.drop_caches=3 #释放页缓存、目录项和inode缓存
sysctl -w vm.swappiness=10 #降低内存交换倾向,优先释放缓存而非交换
# 2. 永久生效(修改/etc/sysctl.conf,重启服务器生效)
echo "vm.drop_caches=3" >> /etc/sysctl.conf
echo "vm.swappiness=10" >> /etc/sysctl.conf
sysctl -p #加载配置
注意:该操作会释放所有进程的文件系统缓存,执行时需避开业务高峰,仅建议在备份索引所在节点执行。
二、关键原理
1.冻结索引的核心作用
卸载分段数据:冻结后ES会主动将索引的倒排索引、文档数据从文件系统缓存和ES堆内存中移除,仅保留索引元数据(几KB);
禁止主动加载:查询时需手动解冻或临时加载,备份索引查询极少,几乎不会触发加载;
不影响数据安全:数据仍存储在磁盘,解冻后可正常使用。
2.其他方案
仅清理缓存:缓存会因后续查询/操作重新加载,无法根治;
关闭文件系统缓存:会影响业务索引的查询性能,得不偿失;
删除索引+快照备份:虽然完全释放内存,但查询需恢复快照,操作复杂,不适用于偶尔需要查询的备份场景。
三、验证效果(确保备份索引不占用内存)
powershell
# 1.查看节点内存变化(冻结后1-5分钟执行)
curl -s -k -u elastic:Passw0rd http://es_node:9200/_cat/nodes?v&h=name,ram.percent,heap.percent
# 2.查看备份索引状态(确认已冻结)
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/_cat/indices/pds_msg_record_auto_backup?v&h=index,status,docs.count,store.size"
# 3.验证缓存占用(应为0)
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/pds_msg_record_auto_backup/_stats/fielddata,query_cache,request_cache?pretty"
备份索引的各类缓存占用为 0,分段未加载到内存。
索引启用
备份索引查询时,无需长期解冻,用"临时加载查询"或"按需短期解冻"即可------既不占用常驻内存,又能满足偶尔的查询需求,具体方法如下:
一、优先方案:临时加载查询(推荐,不解冻索引)
适合仅需单次查询、查询后无需保留加载状态的场景,查询完成后内存自动释放:
1.执行查询时添加 ignore_throttled=false 参数
冻结索引默认被"限流"(throttled),添加该参数可临时加载分段到内存,查询完成后ES会自动卸载:
powershell
# 示例:查询备份索引中user_id=xxx的数据
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/pds_msg_record_auto_backup/_search?ignore_throttled=false" -H "Content-Type: application/json" -d '{
"query": {
"term": { "user_id": "target_user" }
}
}'
特点:无需手动解冻,查询后内存自动释放,不影响索引冻结状态;
注意:首次查询可能因加载分段略慢(秒级到分钟级,取决于数据量),后续同分段查询会复用缓存(但关闭自动刷新,缓存不会长期保留)。
2.批量查询/复杂查询(如聚合、导出)
若需执行多轮查询或复杂统计,可临时开启索引的"自动加载",查询完成后立即关闭:
powershell
# 1.临时允许自动加载(不解冻,仅开启加载权限)
curl -s -k -u elastic:Passw0rd -XPUT "http://es_node:9200/pds_msg_record_auto_backup/_settings" -d '{
"index.search.throttled": false
}'
# 2. 执行多次查询/复杂查询(如导出数据、聚合统计)
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/pds_msg_record_auto_backup/_search" -d '{...}' # 第1次查询
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/pds_msg_record_auto_backup/_search" -d '{...}' # 第2次查询
# 3. 查询完成后,关闭自动加载(恢复限流)
curl -s -k -u elastic:Passw0rd -XPUT "http://es_node:9200/pds_msg_record_auto_backup/_settings" -d '{
"index.search.throttled": true
}'
特点:多轮查询复用已加载的分段,查询速度更快;关闭后内存逐步释放,不占用常驻资源。
二、备选方案:短期解冻查询(适合高频临时查询)
若需在一段时间内(如1小时)多次查询,可短期解冻,查询完成后重新冻结:
1.解冻索引(恢复正常状态,加载分段到内存)
powershell
curl -s -k -u elastic:Passw0rd -XPOST "http://es_node:9200/pds_msg_record_auto_backup/_unfreeze"
解冻后:索引恢复可写(但已设置 index.blocks.write: true,仍只读),分段加载到内存,查询速度与正常索引一致。
2.执行查询(无任何限制)
powershell
#正常执行各类查询,无需额外参数
curl -s -k -u elastic:Passw0rd -XGET "http://es_node:9200/pds_msg_record_auto_backup/_search" -d '{...}'
3.查询完成后,重新冻结(释放内存)
powershell
curl -s -k -u elastic:Passw0rd -XPOST "http://es_node:9200/pds_msg_record_auto_backup/_freeze"
重新冻结后:内存中的分段会被卸载,恢复"零常驻内存"状态。