Rails 5.0应用中Puma性能未优于Unicorn是否正常?AWS服务器配置优化求助
排查Puma迁移后内存使用率过高的问题
我明白你把Rails应用从Unicorn迁到Puma后,原本期待内存效率提升,结果却遇到内存使用率居高不下甚至缓慢上涨的情况,这确实挺头疼的。结合你给出的配置和3万日活的业务场景,咱们一步步来拆解可能的问题:
1. 先确认Puma配置是否实际生效
有时候配置文件的设置可能因为加载顺序、环境变量覆盖等原因没按预期运行,先做基础验证:
- 查看Puma进程数量:
ps aux | grep puma,确认是否是1个主进程+3个工作进程的集群模式 - 查看每个工作进程的线程数:用
pumactl stats(需提前在Puma配置中开启控制端),或者ps -eLf | grep puma统计每个进程的线程数,确认是否在1-5的范围内 - 检查Rails线程安全模式:Rails 5.x默认开启线程安全,但可以在
config/application.rb中确认是否有config.threadsafe!配置——如果未开启,多线程反而会触发额外的内存开销。
2. 数据库连接池配置需匹配Puma的并发模型
你当前数据库连接池设为5,这里要注意:Rails的连接池是按进程分配的,每个Puma工作进程会独立维护一个连接池。
- 你现在Puma每个工作进程最大5线程,那么
config/database.yml中的pool值应该等于最大线程数(即5),这样每个工作进程最多占用5个数据库连接,总连接数为35=15,比之前Unicorn的45=20更少,理论上不会增加内存。如果连接池设置不合理,可能出现连接等待、闲置连接占用内存的情况。 - 同时要同步检查Sidekiq的连接池:Sidekiq并发数为5,它的数据库连接池也应设为5,避免和Web进程抢连接导致不必要的内存开销。
3. 优化Puma集群模式的内存共享设置
Puma集群模式下,主进程fork工作进程的逻辑直接影响内存占用:
- 确认是否开启了
preload_app!:在config/puma.rb中添加该配置后,主进程会先加载整个Rails应用,再fork工作进程,这样工作进程可以共享主进程的内存页,大幅降低总内存占用。如果未开启,每个工作进程都会独立加载Rails应用,内存开销会显著增加。 - 补充
on_worker_boot钩子:fork后的工作进程需要重新建立数据库连接,否则会出现连接泄漏或重复初始化的问题,示例配置:on_worker_boot do ActiveRecord::Base.establish_connection end
4. 排查内存缓慢上升的泄漏点
你提到内存有缓慢上升的趋势,这大概率是内存泄漏问题,Puma的多线程模式可能会让泄漏更明显,可通过以下方式排查:
- 使用
memory_profilergem:在核心请求路径中注入内存分析,跟踪对象的分配和留存情况,定位持续增长的对象类型 - 用
objspace工具定期生成内存快照:对比不同时间点的对象数量变化,找出异常累积的对象 - 检查代码中的线程安全问题:比如未清理的全局变量/类变量、未关闭的文件句柄、第三方SDK的连接未释放,或者使用了非线程安全的gem
5. 拆分EC2实例的内存占用明细
AWS t3.xlarge的16GB内存并非全部用于应用,先拆分各组件的内存占用:
- 用
free -h查看系统整体内存使用,区分系统内核、nginx、Sidekiq、Puma各进程的占用 - 如果Sidekiq进程占用了大量内存,那泄漏点可能在异步任务中,和Puma本身无关;如果单个Puma工作进程内存远超预期,再聚焦Web应用代码的问题
6. 确认jemalloc是否真正生效
你用jemalloc编译了Ruby,但需要验证是否实际被Ruby加载:
- 运行
ruby -r rbconfig -e 'puts RbConfig::CONFIG["LIBS"]',查看输出是否包含-ljemalloc - 或者用
ldd $(which ruby)检查是否链接了jemalloc库
如果未生效,可在启动Puma时指定环境变量强制加载:LD_PRELOAD=/path/to/libjemalloc.so puma
回滚前的快速测试建议
如果暂时考虑回滚Unicorn,可先做几个小测试缩小问题范围:
- 将Puma工作进程数调到2、线程数设为2,观察内存是否下降
- 临时关闭Sidekiq,单独运行Web进程,排除异步任务对内存的影响
内容的提问来源于stack exchange,提问作者DelPiero
相关产品推荐
相关产品推荐

