努力
加油

2G内存环境下如何优化MySQL和Redis的内存使用?

2G 内存跑 MySQL + Redis,这在生产环境里属于“极限操作”,但在个人博客、小型 SaaS 或者学习测试环境中非常常见。这时候的核心逻辑不是“怎么让软件跑得更快”,而是“怎么不让系统因为 OOM(Out Of Memory)直接崩盘”。

在 2G 的总量下,你需要做一个残酷的资源切割。通常建议预留 512MB – 1GB 给操作系统内核和 Swap 缓冲,剩下的 1GB – 1.5GB 分给应用层。

以下是基于国内主流云主机(如阿里云 ECS、腾讯云 CVM)实际部署经验的优化方案,按优先级排序:

一、 总体资源分配策略

不要试图让 MySQL 和 Redis 平分这 1.5G 内存。

  • Redis:通常是缓存层,数据量小但并发高,建议占用 40%-50%(约 600MB-800MB)。
  • MySQL:是持久化存储,需要较多的 Buffer Pool 来减少磁盘 IO,建议占用 30%-40%(约 400MB-500MB)。
  • 系统及其他:预留 20% 作为安全垫。

二、 MySQL 优化(核心:MyISAM 转 InnoDB,极致压缩 Buffer Pool)

默认安装的 MySQL 配置绝对跑不起来,必须手动调整 my.cnf (或 my.ini)。

1. 引擎选择与转换

如果你还在用 MyISAM,立刻迁移到 InnoDB。MyISAM 不支持事务且表锁性能差,InnoDB 虽然吃内存,但其 Buffer Pool 机制能大幅降低物理磁盘读写。

2. 关键参数调优 (/etc/my.cnf)

[mysqld]
# 基础设置
basedir=/usr/local/mysql
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
pid-file=/var/run/mysqld/mysqld.pid

# --- 内存核心配置 ---

# 1. InnoDB 缓冲池大小:这是最关键的指标。
# 对于 2G 机器,不要设太大。一般设为总可用内存的 40%-50%。
# 如果只跑 MySQL+Redis,建议设为 300M-500M。
innodb_buffer_pool_size = 300M

# 如果只有一个数据库实例,可以设为 1 个实例以节省管理开销
innodb_buffer_pool_instances = 1

# 2. 日志文件大小:影响崩溃恢复时间和写入性能
# 默认 48M 太小,容易频繁刷新磁盘。适当增大,但不宜过大以免恢复慢。
innodb_log_file_size = 64M
innodb_log_buffer_size = 8M

# 3. 临时表内存限制:防止查询产生大临时表时溢出到磁盘
tmp_table_size = 16M
max_heap_table_size = 16M

# 4. 连接数控制:2G 内存扛不住高并发连接,每个连接至少消耗几 MB 内存
max_connections = 50 
# 如果业务允许,甚至可以降到 20-30

# 5. 关闭不必要的功能以节省内存
# 如果没有用到二进制日志,强烈建议关闭,否则 binlog 会疯狂占内存和磁盘 IO
skip-log-bin
# 如果没有主从复制需求,关闭 relay log
skip-slave-start

# 6. 字符集
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

3. 架构层面的“偷懒”技巧

  • 使用 Percona Server 或 MariaDB:它们对内存泄漏和优化比官方 MySQL 做得更好,特别是在低内存环境下。
  • 定期重启 MySQL:在极端低配环境下,长时间运行可能导致内存碎片化。写一个 crontab 脚本,每周凌晨重启一次 MySQL 服务,释放碎片内存。
  • 监控 Key_buffer_size:如果你不得不保留少量 MyISAM 表,确保 key_buffer_size 不要超过 10M,否则浪费内存。

三、 Redis 优化(核心:淘汰策略,拒绝持久化)

Redis 是单线程模型,内存管理相对简单,主要靠配置参数来控制行为。

1. 关键参数调优 (redis.conf)

# --- 内存核心配置 ---

# 1. 最大内存限制:必须设置!否则 Redis 会撑爆服务器导致 OOM
maxmemory 600mb

# 2. 淘汰策略:当内存达到 maxmemory 时,如何清理数据?
# 推荐:allkeys-lru (Least Recently Used)
# 含义:所有键中,最近最少使用的会被删除。适合做缓存场景。
# 如果你的数据有明确的过期时间,也可以用 volatile-lru
maxmemory-policy allkeys-lru

# 3. 禁用持久化(RDB/AOF):
# 2G 内存环境下,开启 RDB 快照或 AOF 重写会产生大量 CPU 和 I/O 开销,甚至瞬间耗尽内存。
save ""
appendonly no

# 4. 内存碎片率优化
# 启用内存碎片回收,但要注意这会增加 CPU 负载
activedefrag yes
active-defrag-enabled yes

5. 数据结构优化

  • 使用 Hash 代替 String:如果你存储的是用户信息,尽量用 HSET 而不是多个 SET。Hash 在元素较少时采用压缩列表(ziplist),更省内存。
  • 避免大 Key(Big Key):单个 Value 超过 10KB 就要警惕。大 Key 不仅占用内存多,还会阻塞 Redis 线程。
  • 使用 Integers:如果存的是数字,确保不要存成字符串。Redis 对整型有特殊的编码方式,极省内存。

四、 操作系统层面的“救命稻草”

1. Swap 分区是必须的

千万不要为了追求极致速度而关闭 Swap。在 2G 内存下,一旦突发流量导致内存瞬间飙升,如果没有 Swap,Linux 内核会直接杀死进程(OOM Killer),你的服务就挂了。有了 Swap,虽然会变慢,但至少服务还活着,可以通过自动扩容或人工干预来处理。

  • 创建 Swap:
    # 创建 2G swap 文件
    dd if=/dev/zero of=/swapfile bs=1M count=2048
    chmod 600 /swapfile
    mkswap /swapfile
    swapon /swapfile
    # 添加到 fstab 开机自启
    echo '/swapfile none swap sw 0 0' >> /etc/fstab
  • 调整 Swappiness:
    默认值通常是 10 或 60。对于 2G 内存,建议调低到 10,让系统优先使用物理内存,只有实在不够时才用 Swap,避免频繁的页面交换导致系统卡顿。

    sysctl vm.swappiness=10

2. 关闭不需要的服务

检查并停止 firewalld(如果可以用 iptables 或云安全组替代)、auditd、crond(如果不重要)等后台服务。在 CentOS 7/Ubuntu 上,使用 systemctl disable <service> 禁用非必要服务。

3. 使用 Cloud-init 或云厂商监控

国内云厂商(阿里云、腾讯云)通常提供云监控插件。安装后,你可以看到真实的内存使用曲线。如果发现 MySQL 经常触发 Swap,说明 innodb_buffer_pool_size 还是太大了,需要进一步缩小。


五、 总结与建议

在 2G 内存环境下,你的目标不是“高性能”,而是“高可用性”。

  1. MySQL:innodb_buffer_pool_size 设为 300M-500M,关闭 Binlog,限制连接数。
  2. Redis:maxmemory 设为 600M-800M,开启 allkeys-lru 淘汰策略,关闭持久化。
  3. 系统:开启 2G Swap,调低 swappiness,禁用非必要服务。

最后忠告:
这种配置仅适用于日均 PV 低于 1 万的小型项目或个人学习。一旦业务增长,第一反应应该是拆分。将 MySQL 和 Redis 分别迁移到独立的云服务器上,哪怕只是 1核 2G 的独立实例,体验也会好得多。不要在单机上做复杂的分布式架构模拟,那是给自己挖坑。