跳过正文
  1. 系列/
  2. VPS 实战系列/

任务 7:部署限制内存的数据库

其雁过无痕
作者
其雁过无痕
目录

在服务器部署的进阶之路上,部署限制内存的数据库是一个必经的关卡 。对于这种小内存的 VPS(比如 RackNerd),如果直接裸跑默认配置的 MySQL 8.0,它会一口气吞掉 400MB 甚至更多的内存,极易导致服务器 OOM(内存溢出)死机。

这篇文章记录了如何通过 Docker 配合自定义 my.cnf 文件,将 MySQL 的内存占用极限压榨到 200MB 以内 ,同时理清了“环境容器化”的核心思想 。

💡 核心认知:MySQL 是“发电厂”,不是“随用随插的 U 盘”
#

在刚接触全栈部署时,很容易产生一个误区:以为“只要 JSP 或 Vue 项目跑起来了,才需要把 MySQL 拉起来”。

真实情况是:

MySQL 是一个需要 24 小时常驻后台独立运行的基础设施。它就像市里的“发电厂”,而我们的各种 Web 项目(无论是以前写的 JSP,还是以后的 Spring Boot/C#)都只是“用电器”。不管用电器开没开,发电厂都必须在那运转,守着 3306 端口等待连接。

一台服务器上,通常只需要运行这一个 MySQL 容器实例。未来不管开发多少个新项目,只需要在这个实例里 CREATE DATABASE 创建新的库即可,数据完全隔离。

🛠️ 实战步骤:打造精简版 MySQL 容器
#

为了方便日后在 VS Code 里通过 Remote-SSH 直接修改配置,我们将配置文件和数据目录挂载到宿主机。

1. 创建宿主机目录
#

mkdir -p ~/mysql/conf ~/mysql/data
cd ~/mysql

2. 编写极限压缩内存的 my.cnf (INI 格式)
#

这是限制 MySQL 内存消耗的核心图纸。在 ~/mysql/conf/my.cnf 中写入:

[mysqld]
# 核心杀手锏:关闭性能监控,省下 100MB+ 内存
performance_schema = OFF

# 核心参数:限制 InnoDB 缓冲池大小(默认通常 128M,压缩到 64M)
innodb_buffer_pool_size = 64M

# 限制最大连接数(默认 151,压缩到 50 足够个人全栈项目使用)
max_connections = 50

# 跳过 DNS 解析,提升连接速度
skip-name-resolve

# 统一字符集,防止前后端中文乱码
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

注意:将其用作个人博客这个配置还算不错,但要注意“skip-name-resolve”功能,MySQL将不再解析 localhost,连接地址必须硬编码写成 120.0.0.1

3. 编写 docker-compose.yml
#

~/mysql/ 目录下创建 docker-compose.yml,把我们的图纸挂载进去:

services:
  mysql-lite:
    image: mysql:8.0
    container_name: mysql-lite
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: "这里填你的强密码"
    ports:
      - "3306:3306"
    volumes:
      - ./conf/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./data:/var/lib/mysql
    command: --default-time-zone='+08:00'

4. 一键启动与内存验证
#

# 后台启动容器
docker compose up -d

# 查看内存占用情况(检验压榨成果)
docker stats

正常情况下,内存占用会被稳稳控制在 150MB - 180MB 左右。

🔧 常见运维操作回顾
#

1. 修改配置后如何生效?
#

MySQL 只有在启动的那一刻才会读取 my.cnf 文件。所以如果后续修改了配置文件,必须重启容器强迫它重新加载规则:

docker restart mysql-lite

2. 如何优雅清理废弃的旧数据库容器?
#

为了防止旧容器占用端口和内存,必须将其彻底清理:

# 1. 停止运行
docker stop 旧容器名称或ID

# 2. 删除容器
docker rm 旧容器名称或ID

# 3. (可选) 清理未被使用的无用数据卷,释放硬盘空间
docker volume prune

总结: 这套组合拳打完,不仅跳出了以前“只懂写增删改查”的代码舒适区 ,更重要的是在服务器部署层面建立起了“容器化隔离”和“底层参数调优”的思维 。以后无论换什么云服务器,这套方案都可以几分钟内快速复刻。

相关文章

番外:1G 小内存 VPS 部署 Java JSP 项目实战:Docker 本地构建 + 远程运行完美方案

在拥有了一台属于自己的 VPS(如 1核 1G内存,配置了 2G Swap)后,很多新手在尝试部署 Java 项目时,往往会选择直接在服务器上安装 Maven 或运行 docker build。但现实很残酷:Java 编译极其消耗内存,1G 的内存在构建瞬间就会被挤爆,导致系统卡死或触发 OOM (Out Of Memory) 杀掉进程。

任务 3:熟练使用终端复用工具

核心目标 # 操作:安装并学习使用 tmux。 收获:掌握在服务器上跑长耗时任务的能力。即使本地 SSH 突然断开,任务依然会在后台运行,不会前功尽弃。 核心工具:tmux # tmux (Terminal Multiplexer) 是现代服务端开发的必备工具,解决了远程连接中途掉线导致任务中断的痛点。

任务 2:分配虚拟内存 (Swap)

Linux 服务器运维笔记:分配虚拟内存 (Swap) 避坑与实操 # 0. 背景与目标 # 在小内存(如 1GB RAM)的云服务器上运行 Java、MySQL 或进行前端构建时,物理内存极易耗尽导致进程被系统杀掉(OOM)。Swap(交换空间) 充当了“虚拟内存”的角色,是服务器在高负载下的“救命支撑”。

番外:Dockerfile 常用指令详解

基础环境设置 # FROM:指定基础镜像 功能:这是每个 Dockerfile 的第一条指令(除注释外)。它决定了你的应用运行在什么环境之上。 示例:FROM ubuntu:24.04 (基于 Ubuntu 24.04 系统)或 FROM nginx:alpine (基于轻量级的 Nginx 镜像)。 WORKDIR:设置工作目录 功能:相当于 Linux 里的 cd 命令。后续的 RUN、CMD、COPY 等指令都会在这个目录下执行。如果目录不存在,Docker 会自动帮你创建。 示例:WORKDIR /app (将后续操作的默认路径设为容器内的 /app 目录)。 2. 文件复制 # COPY:复制文件/目录到容器中 功能:将宿主机(你的电脑或服务器)上的文件或目录,原封不动地拷贝到容器的指定路径下。 示例:COPY . /app (将当前宿主机目录下的所有文件,复制到容器的 /app 目录下)。 ADD:高级复制 功能:和 COPY 类似,但带有额外功能。如果复制的是一个本地的 .tar.gz 压缩包,ADD 会自动解压到目标路径;它也支持填入一个网络 URL 来下载文件。(新手推荐优先使用 COPY,语义更清晰)。 3. 执行命令与配置 # RUN:在构建镜像时执行命令 功能:这是构建阶段的主力军。通常用来安装软件包、创建文件夹、配置环境等。注意:每次 RUN 都会生成一个新的镜像层,所以通常会把多条命令用 && 连起来写。 示例:RUN apt-get update && apt-get install -y curl。 ENV:设置环境变量 功能:定义环境变量,后续的 RUN 指令可以使用,并且这些变量也会一直保留到容器运行阶段供你的程序读取。 示例:ENV MYSQL_ROOT_PASSWORD=my-secret-pw 或 ENV PORT=8080。 EXPOSE:声明监听端口 功能:这只是一个声明,告诉使用这个镜像的人,该容器内部的程序会使用哪个端口。它不会自动将端口映射到宿主机,映射依然需要在运行 docker run 时加上 -p 参数或在 docker-compose.yml 中配置。 示例:EXPOSE 80 (声明容器内的 Web 服务使用 80 端口)。 4. 容器启动指令 # CMD:容器启动时的默认命令 功能:指定容器跑起来之后要做的第一件事(例如启动 Nginx、运行 Java 后端应用等)。如果用户在 docker run 时手动指定了其他命令,CMD 的内容会被覆盖。 示例:CMD ["nginx", "-g", "daemon off;"]。 ENTRYPOINT:容器启动的主入口 功能:和 CMD 类似,但它不会被轻易覆盖。通常用于让容器表现得像一个独立的可执行程序。如果同时存在 ENTRYPOINT 和 CMD,CMD 的内容会作为参数传递给 ENTRYPOINT。 综合示例 # 为了方便理解,这里提供一个部署简单前端/静态网页的通用 Dockerfile 模板: