ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

银河麒麟V10内存不释放?水位判断+定时清理实战

银河麒麟V10内存不释放?水位判断+定时清理实战 简介面向银河麒麟V10服务器运维人员的实用资源包针对系统长期运行中出现的内存不释放内存泄漏问题提供定时清理与监控的脚本化解决方案。包内共3个文件包含2个Shell脚本和1个配置说明文档freemem.sh负责按周期执行内存回收逻辑task.sh用于配合系统定时任务调度txt配置说明则对脚本参数、部署方式与注意事项做了补充说明。压缩包整体仅2KB轻量易用适合已具备基础运维知识的读者快速部署。已有773人学习下载说明该方案在同类运维场景中具有一定参考价值。通过这份资源读者可以掌握定时触发内存释放的实现思路理解Shell脚本结合cron定时任务处理内存异常的基本方法并直接复用或调整脚本以适应自身生产环境提升银河麒麟V10系统的长期稳定性。1. 银河麒麟V10内存不释放先分清真增长还是假增长再动手一台跑着银河麒麟V10的服务器开机跑了两周 free 里 used 一路爬升到 95% 以上业务响应开始飘红重启两三天又原样这种情况在国产化迁移后特别常见。所谓“内存不释放”大多数时候不是进程泄漏而是 Linux 把空闲内存拿去做页缓存又没在业务需要时及时交还——表现出来就是内存膨胀、使用率持续走高。这篇笔记要给的是「定时加水位判断」的回收办法先判断该不该清再由 cron 定时清最后还要能验证清完到底有没有真效果。这套做法适合跑 Web、容器、Java 服务内存余量又不多的麒麟V10服务器也是我们迁移后最先沉淀下来的运维套路之一。2. 先用 free 和 /proc/meminfo 定位内存真紧张还是缓存没交还2.1 用 free -m 看内存buff/cache 才是大头很多运维拿到一台麒麟V10第一件事就是敲free -h看到 used 高就紧张。实际上 free 输出里只有 available 能代表“新开一个进程还能拿到多少内存”这一列从 Linux 3.14 以后才加入麒麟V10基于 4.19 内核自然带。先看基础命令free -m # total used free shared buff/cache available # Mem: 15870 8890 312 125 7660 7159 # Swap: 8191 0 8191注意这个输出used 约 8.7Gavailable 约 7.1G看起来压力并不大。但很多时候你看到的是 used 14.7G、available 只剩 1.1G 的样子——此时再看 buff/cache 还有 7 个多 G说明相当一部分内存被内核拿去做页缓存了。业务真正的独占内存并没有 used 显示得那么大这就是典型的“假性不释放”。这里有一条我常用的判断口径available 持续低于 total 的 20%且业务出现卡顿才有干预的必要如果只是 used 高、available 还有余量完全不用管缓存多恰恰说明内存被有效利用了。另外提醒一句别用 free 里的 free 列做监控指标那列在内存越大、缓存越多的情况下越有误导性。2.2 从 /proc/meminfo 看脏页水位该不该清的判断标准free 只能看总量想进一步判断“缓存里有没有堆积太多没刷盘的脏数据”要看 /proc/meminfogrep -E ^Mem(Available|Free):|^Dirty:|^Writeback:|^Cached: /proc/meminfo输出类似这样MemAvailable: 6432060 kB MemFree: 319488 kB Cached: 8847360 kB Dirty: 1894400 kB Writeback: 30720 kB各字段的实践含义是Dirty 表示内存里改过、还没写回磁盘的页空闲期正常的服务器这个值应该很小Writeback 是正在写回的页持续很大说明磁盘已经跟不上内存的写入速度。Cached 里包含 tmpfs 这类不可随意回收的部分所以不能只看 Cached 判断可回收量。麒麟V10 的 /proc/meminfo 字段和上游一致monitor 脚本可以直接按这个口径取数。我一般会把 Dirty 加进监控当它连续 5 分钟超过物理内存的 10%说明后台回写线程在堆积这时候才值得用清理脚本去触发回写。提示判断清理必要性优先看 MemAvailable 和 Dirty不要盯着 used 列。available 低才是真紧张dirty 高才是该回写。2.3 用 vmstat 观察换页si/so 才是真正的性能信号内存有没有问题老道的做法是看内存是否在持续换页vmstat 1 5 # procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- # r b swpd free buff cache si so bi bo in cs us sy id wa st # 1 0 24512 5120 12401 885101 0 0 12211 14220 1234 3312 8 5 86 0 0si/so 两列是 swap-in 和 swap-out如果它们持续非零且波动大说明内存压力已经传导到 swap 分区进程在被换出换入这通常伴随高 IO wait 和响应延迟。反过来如果 si/so 长期为 0就算 free 看着低也只是缓存策略问题内存并没有真正成为瓶颈。很多用户在麒麟V10 上“内存还是高”但机器又不卡多半属于第二种。判断方法理清楚后后面的定时清理脚本才有意义只有满足「available 低 dirty 高或换页明显」时才介入而不是拿一条 cron 无脑刷缓存。3. 定时清理落地水位阈值 drop_caches 的脚本与 cron 配置3.1 思路不要无脑刷先看水位再动手见过不少人的第一版方案是每分钟执行一次echo 3 /proc/sys/vm/drop_caches这里先把结论说清楚这是饮鸩止渴。把整个 page cache 清空后业务热数据下次访问全部重新读盘瞬间 IO 飙升数据库类应用直接被打穿。正确的姿势是设定水位阈值只有当可用内存低于某个百分比并且缓存量确实够大时才执行回收这样每次清理都有实际收益又不至于把热缓存全部牺牲掉。阈值给两个可用内存占比低于 20% 触发Cached 大于 4GB 才动手。这两个数不是拍脑袋page cache 小于 4GB 时强行清理的收益很小反而白白损失缓存命中率。下面的脚本就是按这两个水位设计的。3.2 完整脚本获取内存、比较阈值、带锁执行#!/usr/bin/env bash # kylin_mem_clean.sh - 银河麒麟V10 内存水位定时清理脚本 # 用法直接由 crontab 调用脚本自带阈值判断和并发锁 set -u # 阈值可用内存占比低于 20% 且 Cached 大于 4GB 才清理 TRIGGER_PERCENT20 MIN_CACHE_MB4096 # 日志路径后面 cron 和排查都用它 LOG_FILE/var/log/kylin_mem_clean.log # 锁文件防止脚本上一次没跑完下一次又进来 LOCK_FILE/tmp/.kylin_mem_clean.lock # 用 flock 做互斥拿不到锁直接退出说明上一个实例还在跑 exec 9${LOCK_FILE} if ! flock -n 9; then echo $(date %F %T) [skip] another instance is running ${LOG_FILE} exit 0 fi # 从 /proc/meminfo 取四个关键数值单位都是 kB TOTAL_KB$(awk /^MemTotal:/ {print $2} /proc/meminfo) AVAIL_KB$(awk /^MemAvailable:/ {print $2} /proc/meminfo) CACHE_KB$(awk /^Cached:/ {print $2} /proc/meminfo) DIRTY_KB$(awk /^Dirty:/ {print $2} /proc/meminfo) # bash 只支持整数运算单位换算成 MB TOTAL_MB$((TOTAL_KB / 1024)) AVAIL_MB$((AVAIL_KB / 1024)) CACHE_MB$((CACHE_KB / 1024)) DIRTY_MB$((DIRTY_KB / 1024)) # 计算可用内存占比 AVAIL_PERCENT$((AVAIL_MB * 100 / TOTAL_MB)) echo $(date %F %T) [check] avail${AVAIL_MB}MB (${AVAIL_PERCENT}%), cached${CACHE_MB}MB, dirty${DIRTY_MB}MB ${LOG_FILE} # 两个水位条件都满足才真正动手 if [[ ${AVAIL_PERCENT} -lt ${TRIGGER_PERCENT} ${CACHE_MB} -gt ${MIN_CACHE_MB} ]]; then # 先 sync 把脏页写回磁盘避免清缓存时把回写压力全压到业务 IO 上 sync # 清理 page cache 和 dentry/inode1page cache, 2inode/dentry, 3两者 echo 3 /proc/sys/vm/drop_caches # 清理后再取一次数据落日志供后续核对效果 AVAIL_KB_AFTER$(awk /^MemAvailable:/ {print $2} /proc/meminfo) AVAIL_MB_AFTER$((AVAIL_KB_AFTER / 1024)) echo $(date %F %T) [clean] avail after${AVAIL_MB_AFTER}MB, dirty0MB ${LOG_FILE} else echo $(date %F %T) [skip] threshold not reached ${LOG_FILE} fi这段脚本有几个细节值得单独说明。第一是flock -ncron 触发的脚本如果执行时间过长前一个还没跑完下一个又启动会出现两条进程同时在刷缓存日志里全是重复记录。用锁文件把并发情况直接 skip 掉保证同一时刻只有一个人在干活。第二是阈值判断用的[[ ... ]]语法两个条件用连接含义是“只在内存真紧张且缓存足够大时才清”缺一个条件都跳过。第三是echo 3之前必须先sync不清的情况下直接 drop_caches大量脏页会被强制写回磁盘瞬间被写满后来我吃过这个亏。如果想把触发调得更保守只需要改两个变量TRIGGER_PERCENT从 20 调到 15表示可用内存低于 15% 才动手MIN_CACHE_MB从 4096 调到 8192表示缓存要堆积到 8GB 以上才回收。数据库或者 Redis 所在的机器建议都调保守一档。3.3 挂到 crontab周期、日志与重启后的检查脚本写好后放到固定目录用crontab -e加入定时任务# 每 5 分钟跑一次标准输出和错误都进同一个日志文件 */5 * * * * /opt/scripts/kylin_mem_clean.sh /var/log/kylin_mem_clean.log 21选每 5 分钟而不是每分钟是因为清理动作本身会带来一次短暂的 IO 抖动5 分钟的间隔给系统留了恢复时间也足够覆盖“内存缓慢爬升”这类场景。如果你的业务内存波动特别快比如夜间的批处理任务会在 1 小时内吃掉大部分内存可以把周期改成*/2但代价是缓存重建频率变高需要后续观察 IO wait。关于 cron 有件容易翻车的事cron 环境变量非常少脚本里所有命令都要用绝对路径比如awk在某些精简系统里不在默认 PATH 里建议脚本开头加上export PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin。另外脚本前的注释不是装饰——排查时定位阈值不好用翻日志是最直接的路径。提示服务器重启后 crontab 任务仍然在不用担心。但flock锁文件在 /tmp 下重启后自动消失不需要手工清理。4. 三种回收姿势对比无脑 echo 3、水位脚本与内核参数调优4.1 定时 echo 3简单但容易把业务缓存打穿第一版方案长这样# 每 10 分钟清一次缓存不推荐用于生产 */10 * * * * /bin/sync; echo 3 /proc/sys/vm/drop_caches它能解决“内存看起来满”的症状但代价是彻底的缓存命中率伤害。我见过一台部署了 Java 应用的麒麟V10用这套跑了一周内存确实降下来了但业务接口延迟从 30ms 涨到平均 120ms——因为热点数据每次都要重新读磁盘。另外一个隐患是它没有条件判断即使系统内存充足、缓存完全健康也会被无差别清除。结论是它只适合两种场景临时测试验证清理机制本身是否生效或者重启前的内存整理。生产环境把它放 cron 里长期跑迟早出事故。4.2 水位阈值脚本折中方案适合大多数 Web 服务器第 3 章给的脚本就是这一档。它在“业务需要内存”和“缓存利用”之间取了个平衡点平时不动缓存只在可用内存跌破 20% 且缓存堆积超过 4GB 时才清一次相当于给内核的缓存回收机制加了一个“定时巡检”。这一档适合大多数 Web 服务器、Spring Boot 应用、容器化节点。这类负载的特点是内存使用率会缓慢爬升但进程本身不存在泄漏爬升的主因是文件缓存和 JVM 堆外内存的累积。水位脚本保证了清理动作只在真正有压力时发生对业务影响降到了最低。在我的实践里Web 服务器跑这套方案三个月最明显的变化是以前一两周就要手动重启业务、释放内存的操作彻底停掉了。日志里每天实际触发清理的次数大概 25 次从未出现连续触发的情况。4.3 调内核参数vm.dirty_ratio 与 swappiness 的治本玩法如果水位脚本还不够或者机器上跑的是数据库、Redis 这类对 IO 极敏感的服务可以考虑从内核参数层面让内存回收更积极。常见做法是调整/etc/sysctl.conf# 脏页达到可用内存 30% 时进程写文件会被阻塞强制刷盘 vm.dirty_ratio 30 # 脏页达到 5% 时内核后台就开始写回避免积压 vm.dirty_background_ratio 5 # 内存压力较大时更积极回收缓存而非换出进程内存 vm.vfs_cache_pressure 100 # 减少 swap 使用避免换页抖动 vm.swappiness 10调完执行sysctl -p生效。dirty_ratio 从默认的 20 提到 30是给业务多点写缓冲dirty_background_ratio 从默认 10 降到 5是让后台回写提前介入避免脏页突然堆积到阈值。这两个参数配合得当很多“内存不释放”的问题在源头上就缓解了脏页不会积到触发 drop_caches 的程度。另外别忽略日志服务占的内存。journald 默认把日志存在/run/log/journal而/run是 tmpfs会真实占用内存。日志量大的机器内存被日志挤掉几百 MB 很常见顺手做下限制# /etc/systemd/journald.conf SystemMaxUse200M RuntimeMaxUse100M三档方案的选择可以参考这个对比方案适用场景潜在风险动手成本定时 echo 3临时排查、验证机制缓存被打穿、IO 飙升最低水位阈值脚本Web、Java、容器节点大促峰值仍需人工关注低一次部署长期有效内核参数调优数据库、Redis、IO 敏感服务参数不当会让写压力前移中需观察调整5. 定时刷内存避坑清单五个高频翻车现场5.1 现象刷完瞬间磁盘打满、系统更卡清理后系统直接卡顿几十秒iostat看到 util 接近 100%业务超时一片。原因是脚本里没有 sync或者 sync 之后立即 drop_caches大量脏页在 drop 时被强制写回压力全部集中到业务 IO 路径上。解决方法是脚本里保留sync并且把清理动作放到业务低峰时段。我后来习惯在脚本里再加一层判断如果当前vmstat 1 1的 wa 已经超过 50%这次直接 skip宁可留着缓存也别雪上加霜。5.2 现象清了没效果free 反而更高、更卡有一种情况是清理后 used 变低了但业务访问数据库的延迟反而升高。原因很直接数据库的 InnoDB buffer pool 本身就在 page cache 里清缓存相当于把数据库热数据全部从内存赶了出去后续查询全部落盘。这属于典型的“误杀”。解决方法是数据库、Redis 所在机器不要用echo 3改用echo 1只清 page cache 保留目录项或者干脆只做内核参数调优让缓存继续留在内存里服务业务。从那以后我给数据库服务器定的规矩是禁用水位清理脚本只调 dirty 参数。5.3 现象清理后内存很快又满了脚本根本没被触发如果日志里全是[skip] threshold not reached但 available 实际已经很低要警惕另一类情况内存是被进程真实占用的不是缓存。常见的是 JVM 堆内存设置过大、WPS 或搜狗输入法这类桌面组件常驻、还有 Java 进程的堆外内存累积。这属于真增长drop_caches 对它们没有任何作用需要另查进程。排查方法是用ps aux --sort-%mem | head -20找 RSS 大户再针对 JVM 用jmap -heap看堆内堆外占比。注意这里别把 JVM 内存模型和系统 page cache 混为一谈前者是应用层的事后者才是我们这套清理脚本的管辖范围。5.4 现象cron 任务没生效日志里什么都没有最常见的原因是脚本没有执行权限或者脚本第一行#!/usr/bin/env bash缺失第二个高发原因是脚本里用了相对路径cron 的工作目录和登录 shell 不一样第三个是我踩过的脚本里有中文注释但文件编码不是 UTF-8在set -u模式下脚本直接报错退出。排查顺序就三步先crontab -l确认任务在再bash -x /opt/scripts/kylin_mem_clean.sh手动跑一遍看输出最后看/var/log/cron里有没有该任务的执行记录。手动能跑、定时不跑基本都出在环境变量或路径上。5.5 现象swap 频繁换页si/so 成了性能瓶颈内存清理后系统仍然慢vmstat的 si/so 持续非零。这个现象说明问题的根源不是缓存堆积而是物理内存确实不够用内核在不停地换页。此时刷缓存只能缓解一时正确的做法是降低 swappiness比如调到 10、检查是否有进程内存配置过大、给机器合理扩容。另外要确认 swap 是不是建在机械盘或者高 IO 的存储上——换页本身就在消耗磁盘带宽如果 swap 和业务数据在同一个盘等于互相拖累。6. 验证清理是否生效先用日志和 vmstat 盯三天再调参方案上线后不能只看 free 一时变低要验证“清理是否真的有效且无副作用”。我的习惯是在脚本里加一个 dry-run 观察模式先用两周的日志数据做基准再正式触发清理。具体做法是把脚本中真正执行echo 3的那段临时注释掉只保留记录部分然后连续三天用这条命令采样# 每 10 秒记录一次 available、dirty、si/so落盘供分析 watch -n 10 date %F %T; grep -E MemAvailable|Dirty /proc/meminfo; vmstat 1 2 | tail -1 /tmp/mem_trend_$(date %Y%m%d).log观察指标就三个available 是否在业务高峰被压低到 20% 以下、dirty 是否持续超过物理内存的 10%、si/so 是否频繁非零。三天数据里如果每天都有满足触发条件的时段说明脚本有必要如果三天里 available 从未跌破 30%那问题的根源根本不在缓存回收多半要回到进程层排查。验证正式生效的标准是清理触发后 5 分钟内 available 明显回升dirty 归零同时业务接口延迟没有出现陡增。如果清理完成但 available 半小时内又被拉回低水位说明内存是被真增长占掉的脚本需要停用并转向排查应用。还记得第一次给麒麟V10 部署这套方案时我没有先观察就直接把 cron 挂上结果白天业务高峰触发清理数据库热缓存被打穿叫了一整晚。从那以后我每次处理内存问题不管用户催得多急都强制先跑三天监控数据再上定时脚本最后再调阈值——用数据说话不拿生产环境试错。希望帮到你。本文还有配套的精品资源点击获取
返回列表