在 2 核 2G 的 Linux 服务器上同时运行 Tomcat 和轻量级数据库(如 H2、SQLite、HSQLDB,或经过严格调优的 MySQL/PostgreSQL),资源非常紧张。优化核心在于严格控制内存占用、减少上下文切换、合理分配 CPU以及避免 I/O 瓶颈。
以下是针对该场景的具体优化方案:
1. 操作系统层优化 (Linux Kernel Tuning)
在应用启动前,先调整内核参数以适配小内存环境。
- 关闭不必要的服务:
- 禁用
firewalld(改用简单的iptables或ufw),关闭NetworkManager(如果不需要动态 IP),停止bluetooth、avahi-daemon等无用服务。 - 使用
systemctl disable --now <service>清理开机自启项。
- 禁用
- 调整 Swappiness (交换分区):
- 2G 内存极易触发 Swap,导致性能断崖式下跌。建议将
swappiness设为较低值(如 10),或者在内存极度紧张时设为 1,甚至完全禁用(不推荐完全禁用,防止 OOM Killer 直接杀进程)。# 临时生效 sysctl vm.swappiness=10 # 永久生效:编辑 /etc/sysctl.conf,添加 vm.swappiness=10
- 2G 内存极易触发 Swap,导致性能断崖式下跌。建议将
- 调整 VM 缓存策略:
- 确保数据库能利用剩余内存作为 Page Cache,但不要让 Java 堆溢出。
- 设置
vm.vfs_cache_pressure为 50-100,让系统更倾向于回收 inode 和 dentry 而不是文件缓存。
- NUMA 与 CPU 亲和性:
- 如果是虚拟机,确认 CPU 绑核。对于 2 核机器,尽量将 Tomcat 和数据库绑定到不同的物理核上(如果虚拟化支持),减少争抢。
2. 数据库层优化 (轻量级配置)
假设使用 MySQL/MariaDB 或 PostgreSQL(如果是 H2/SQLite 则主要关注 JVM 参数):
- 限制最大连接数:
- 默认配置通常允许几百个连接,这在 2G 内存下是灾难性的。
my.cnf(MySQL):max_connections = 50(甚至更低,视业务量而定)。
- 严格控制 Buffer Pool:
- 关键点:必须预留足够内存给 Tomcat。
- MySQL:
innodb_buffer_pool_size设置为总内存的 30%~40% (即 600MB – 800MB)。不要超过 1GB,否则 Tomcat 必挂。 - PostgreSQL:
shared_buffers设置为 256MB 左右。
- 禁用日志与索引优化:
- 关闭非必要的慢查询日志(Slow Query Log)除非正在调试。
- 对于只读或小表,适当减少索引数量,降低写入时的 IO 开销。
- 使用轻量级引擎:
- 如果数据量极小且对事务要求不高,考虑使用 SQLite 或 H2 (内存模式),它们没有独立的守护进程开销,JVM 内嵌即可。
3. Tomcat/JVM 层优化
这是内存消耗的大头,必须精细裁剪。
- 选择正确的 JDK:
- 优先使用 JDK 8 或 JDK 11 (LTS)。JDK 17+ 虽然性能好,但基础内存占用略高。
- 如果使用 GraalVM Native Image 编译 Tomcat(高级玩法),可将内存占用降至几十 MB,但开发维护成本高。
- JVM 参数调优 (关键):
- 堆大小 (
-Xmx):必须小于数据库预留空间。建议-Xmx设为 512M 或 600M。 - 初始堆大小 (
-Xms):必须等于-Xmx,避免运行时动态扩容带来的 GC 停顿。 - 元空间 (
-XX:MaxMetaspaceSize):限制为 128M。 - GC 算法:
- JDK 8: 使用
-XX:+UseParallelGC(吞吐量优先,停顿短) 或-XX:+UseG1GC(低延迟)。 - 避免使用 CMS(已废弃)或 ZGC(内存开销大)。
- JDK 8: 使用
- 示例参数:
JAVA_OPTS="-server -Xms512m -Xmx512m -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
- 堆大小 (
- Tomcat 配置 (
server.xml):- Connector: 限制
maxThreads(默认 200,建议改为 50-80) 和acceptCount。 - 线程模型: 确保
executor配置正确,避免创建过多线程。 - APR/NIO: 启用 NIO 模式 (
protocol="org.apache.coyote.http11.Http11NioProtocol"),减少文件描述符消耗。 - Disable AJP: 如果不需要外部反向X_X转发 AJP,注释掉 AJP Connector,减少端口监听开销。
- Connector: 限制
4. 架构与部署优化
- 引入反向X_X (Nginx):
- 虽然 Nginx 本身占内存,但它能处理静态资源(图片、CSS、JS)和负载均衡,减轻 Tomcat 负担。
- 开启 Nginx 的 Gzip 压缩,减少网络传输带宽。
- 配置 Nginx 的
proxy_cache缓存后端 API 响应,大幅降低数据库查询压力。
- 应用代码层面:
- 连接池:检查数据库连接池(如 HikariCP, Druid)。
maximum-pool-size: 设置为 10-20 (2G 机器严禁使用 50+ 的连接池)。minimum-idle: 保持最小连接,避免频繁创建销毁。
- 对象复用:避免在循环中创建大量临时对象,减少 GC 压力。
- 异步处理:将耗时操作(发邮件、生成报表)放入消息队列或后台线程,避免阻塞主请求线程。
- 连接池:检查数据库连接池(如 HikariCP, Druid)。
- 监控与告警:
- 安装轻量级监控工具(如
htop,glances或Prometheus + Node Exporter)。 - 重点监控 Load Average 和 Memory Usage。如果 Load > CPU 核数 (2),说明系统过载。
- 安装轻量级监控工具(如
5. 极限生存策略 (如果上述仍不够)
如果业务流量较大,2G 内存依然捉襟见肘:
- 容器化隔离:使用 Docker 并设置严格的
memory_limit和cpu_quota。例如限制 Tomcat 容器只能使用 1.2G 内存,防止它吃光所有资源导致数据库被 OOM Kill。 - 读写分离/分库:如果可能,将数据库迁移到另一台低成本服务器,本服务器仅跑 Web 逻辑。
- 降级策略:在代码中加入熔断机制,当数据库负载过高时,返回缓存数据或友好提示,保护系统不崩溃。
总结配置清单 (参考)
| 组件 | 关键参数/动作 | 目标值/备注 |
|---|---|---|
| OS | vm.swappiness |
10 |
| OS | 无用服务 | 全部关闭 |
| DB | max_connections |
50 |
| DB | innodb_buffer_pool_size |
800M (约 40%) |
| JVM | -Xms / -Xmx |
512M |
| JVM | -XX:MaxMetaspaceSize |
128M |
| Tomcat | maxThreads |
50 |
| DB Pool | maximum-pool-size |
15 |
| Nginx | 静态资源缓存 | 开启 |
警告:在 2G 环境下,任何未经优化的 Java 应用都容易因 Full GC 导致“假死”。务必在生产环境上线前进行压测,观察 GC 频率和暂停时间。
CLOUD云枢