在 2核4G 的服务器上部署 LNMP(Linux + Nginx + MySQL + PHP)并共存 Redis,内存资源非常紧张(系统+LNMP+Redis 共享约 3.5–3.8G 可用内存),必须精细化配置,避免 OOM(Out of Memory)或频繁 swap 导致性能崩溃。以下是针对 MySQL(推荐 MySQL 8.0+ 或 Percona Server)和 Redis(7.x) 的实用、安全、可落地的内存优化方案:
✅ 一、总体原则(必读)
- 目标:MySQL + Redis 内存总占用 ≤ 1.6–1.8G(为系统、Nginx、PHP-FPM、OS cache 留足空间)
- 禁用
swap(或设vm.swappiness=1),避免 MySQL/Redis 因 swap 卡死 - 所有配置修改后 必须重启服务,并用
free -h/htop/redis-cli info memory | grep used_memory_human验证 - 生产环境务必启用
slow_query_log和redis slowlog监控异常
🐘 二、MySQL 内存优化(目标:≤ 1.0–1.2G)
✅ 基于
my.cnf(通常/etc/my.cnf或/etc/mysql/my.cnf)
[mysqld]
# === 基础限制 ===
max_connections = 100 # 默认151 → 降为100,减少连接内存开销
table_open_cache = 400 # 原默认2000 → 降低,避免句柄和内存浪费
sort_buffer_size = 256K # 每连接排序缓存,勿超512K(大值只对复杂ORDER BY有用)
read_buffer_size = 128K # 同上,避免全表扫描时暴增
read_rnd_buffer_size = 256K # 随机读缓存,适度下调
join_buffer_size = 256K # 关联查询缓冲,够用即可
# === 关键内存池(重点!)===
innodb_buffer_pool_size = 768M # ⚠️ 核心参数!建议 768–900M(占总内存20–25%)
innodb_buffer_pool_instances = 4 # ≥1G才需分片,768M设4即可(避免内部锁争用)
innodb_log_file_size = 128M # 日志文件大小,≈ buffer_pool_size 的 1/8~1/4(提升写性能且不浪费)
innodb_log_buffer_size = 4M # 日志缓冲区,够用即可(默认1M,升至4M防小事务刷盘)
# === 其他精简项 ===
innodb_flush_method = O_DIRECT # 避免OS double-buffering(Linux下推荐)
innodb_io_capacity = 200 # SSD设200–400,HDD设100
innodb_io_capacity_max = 400
skip_log_bin # ❗关闭binlog(除非需主从/恢复)→ 节省I/O和内存
innodb_doublewrite = OFF # ❗仅限SSD+有UPS场景(否则不建议关!),节省写入开销
query_cache_type = 0 # ❌ MySQL 8.0+ 已移除,5.7请务必关闭(高并发下锁竞争严重)
tmp_table_size = 32M # 内存临时表上限(与 max_heap_table_size 一致)
max_heap_table_size = 32M
# === 安全兜底 ===
wait_timeout = 60 # 空闲连接超时(秒),防连接堆积
interactive_timeout = 60
🔍 验证命令:
# 查看实际buffer pool使用率(健康值:70%–95%,长期<50%说明过大)
mysql -e "SHOW ENGINE INNODB STATUSG" | grep "Buffer pool hit rate"
# 查看内存估算(粗略)
mysql -e "SELECT ( @@innodb_buffer_pool_size + @@key_buffer_size + @@query_cache_size + @@tmp_table_size ) / 1024 / 1024 AS 'MB'"
💡 小贴士:若业务以读为主(如博客、CMS),可将
innodb_buffer_pool_size提至900M;若写多(如API服务),保持768M更稳。
🍭 三、Redis 内存优化(目标:≤ 600–700M)
✅ 基于
redis.conf(通常/etc/redis/redis.conf)
# === 内存限制(强制!)===
maxmemory 600mb # ⚠️ 必须设置!防止OOM killer干掉Redis
maxmemory-policy allkeys-lru # 推荐:LRU淘汰所有key(非volatile-lru,因无TTL场景更公平)
# maxmemory-policy volatile-lru # 若大部分key有TTL,选这个更精准
# === 内存分配优化 ===
# 使用jemalloc(Redis 6+默认),确保已安装:apt install libjemalloc2(Ubuntu/Debian)
# 在redis.conf中确认:
malloc-provider jemalloc
# === 网络与持久化(减负)===
tcp-keepalive 300 # 保活探测,防连接假死
timeout 300 # 客户端空闲超时断开
# 关闭AOF(除非强需求)→ 节省内存+CPU
appendonly no
# 若必须开启AOF,改用everysec(折中):
# appendonly yes
# appendfsync everysec
# RDB快照按需保留(如每天1次)
save 3600 1 # 1小时内至少1次变更才保存(降低频率)
save 300 10 # 5分钟内10次变更(可注释掉,仅保留上面一行)
# === 其他精简 ===
lazyfree-lazy-eviction yes # 淘汰key时异步释放内存(防阻塞)
lazyfree-lazy-expire yes # 过期key异步删除
lazyfree-lazy-server-del yes # DEL/FLUSH等命令异步化
activerehashing yes # 渐进式rehash,避免阻塞
🔍 验证命令:
# 实时查看内存使用
redis-cli info memory | grep -E "(used_memory_human|maxmemory_human|mem_fragmentation_ratio)"
# 检查淘汰策略是否生效(观察evicted_keys增长)
redis-cli info stats | grep evicted_keys
💡 关键提醒:
maxmemory必须显式设置,否则Redis会吃光内存导致系统OOM killer杀进程;- 避免使用
noeviction策略(写失败),allkeys-lru是2核4G最稳妥选择;- 若业务允许,将部分非核心缓存转为本地缓存(如PHP APCu)或降级为文件缓存,进一步减压。
🛠 四、协同优化 & 系统级加固
| 项目 | 推荐配置 | 说明 |
|---|---|---|
| PHP-FPM | pm = static + pm.max_children = 20 |
2核下 static 模式更可控;max_children × memory_per_child ≈ 400–600MB(按每个PHP进程平均20–30MB计) |
| Nginx | worker_processes 2; + worker_connections 1024; |
匹配CPU核心数,避免过度并发 |
| 系统内核 | vm.swappiness = 1 |
echo 'vm.swappiness=1' >> /etc/sysctl.conf && sysctl -p |
| 监控告警 | 安装 netdata 或 prometheus + node_exporter + mysqld_exporter + redis_exporter |
实时盯住 used_memory, buffer_pool_hit_rate, evicted_keys, oom_kill 日志 |
✅ 一键检查脚本(运行前备份配置):
#!/bin/bash
echo "=== 内存概览 ==="
free -h
echo -e "n=== MySQL 缓冲池 ==="
mysql -sN -e "SELECT @@innodb_buffer_pool_size/1024/1024 as 'Buffer Pool (MB)'"
echo -e "n=== Redis 内存 ==="
redis-cli info memory | grep -E "used_memory_human|maxmemory_human|mem_fragmentation_ratio"
echo -e "n=== PHP-FPM 进程数 ==="
ps aux | grep "php-fpm:" | grep -v grep | wc -l
🚫 五、绝对要避免的“坑”
| 错误做法 | 后果 | 正解 |
|---|---|---|
innodb_buffer_pool_size = 2G |
MySQL吃光内存,触发OOM killer杀其他进程 | 严格 ≤ 900M |
Redis 不设 maxmemory |
内存无限增长 → 系统卡死 | 必设!且留余量 |
开启 MySQL query_cache(5.7) |
高并发下锁表严重,性能暴跌 | query_cache_type=0 |
Redis 开启 appendonly yes + appendfsync always |
每次写都刷盘 → CPU/IO瓶颈,内存压力隐性增加 | 用 everysec 或关AOF |
| 大量小对象存 Redis(如1KB×10万) | 内存碎片率 >1.5 → 实际占用翻倍 | 改用压缩(Redis 7+ COMPRESS)或合并存储 |
✅ 最终内存分配建议(总计 ≈ 3.8G)
| 组件 | 推荐内存 | 说明 |
|---|---|---|
| OS + 基础服务 | 600–800 MB | kernel、sshd、cron、log等 |
| Nginx | 80–120 MB | 静态文件+反向X_X |
| PHP-FPM (20子进程) | 400–600 MB | 按20–30MB/进程估算 |
| MySQL | 768–900 MB | 核心缓冲池+连接开销 |
| Redis | 600–700 MB | 含预留碎片空间 |
| 余量(安全缓冲) | ≥ 300 MB | 应对突发流量、日志增长 |
如需我帮你:
- ✅ 生成完整可复制的
my.cnf/redis.conf文件 - ✅ 根据你的具体业务(如 WordPress / Laravel / 自建API)定制参数
- ✅ 提供一键检测+优化脚本(含备份/回滚)
- ✅ 配置 Prometheus 监控告警规则(内存 >90% 触发微信/钉钉通知)
欢迎随时告诉我你的 MySQL 版本、Redis 版本、PHP 类型(FPM/CGI)、以及主要业务场景(如“WordPress 博客”、“小程序后端API”),我会给出精准到行的配置 👇
祝你部署顺利,稳如泰山! 🚀
云服务器