Halo 默认使用 H2 数据库。H2 是一个轻量级的嵌入式数据库,如果遇到服务器突然强制断电,或者后续博客访问量大了,它的数据文件比较容易发生损坏。因此我准备趁着现在还没开始大量写文章,直接在服务器上装一个 MySQL,在 Halo 的 application.yaml 中把数据库源改成 MySQL,以保障数据的安全。
1. 安装MySQL数据库
在 2C2G 的服务器上强跑 MySQL 8.0 非常容易因为内存溢出(OOM)导致数据库频繁崩溃。MySQL 8.0 默认开启了大量的性能监控,很吃内存。
因此,MySQL 5.7 是最稳妥的选择。为了让云服务器运行得更平稳,我们不仅要把它跑起来,还要直接对它进行内存限制优化。
1.1 准备挂载目录与配置文件(核心优化)
在 2C2G 环境下,最关键的优化是关闭 performance_schema 并限制缓冲池大小。
先在服务器终端执行以下命令,创建挂载目录并写入配置文件:
# 1. 创建数据和配置存放目录
mkdir -p /data/mysql/data
mkdir -p /data/mysql/conf
# 2. 写入内存优化配置 (针对 2C2G 服务器的黄金配置)
cat <<EOF > /data/mysql/conf/my.cnf
[mysqld]
# 关闭性能监控,能省下至少 400MB 内存
performance_schema = off
# 限制 InnoDB 缓冲池大小
innodb_buffer_pool_size = 128M
# 限制最大连接数,防止并发耗尽内存
max_connections = 100
# 设置默认字符集为 utf8mb4 (兼容 Emoji)
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
EOF
1.2 使用 Docker 启动 MySQL 5.7
目录和配置准备好后,直接运行以下 Docker 命令(注意将 YourPassword 替换为你自己想设置的强密码):
docker run -d \
--name mysql-halo \
--restart always \
-p 3306:3306 \
-v /data/mysql/data:/var/lib/mysql \
-v /data/mysql/conf/my.cnf:/etc/mysql/mysql.conf.d/my.cnf \
-e MYSQL_ROOT_PASSWORD=YourPassword \
mysql:5.7
参数解析:
-d:后台运行。--restart always:服务器重启或 Docker 重启时,数据库自动拉起。-v:将我们刚才创建的目录和配置文件映射进容器,确保数据持久化。
1.3 创建 Halo 专属数据库
容器跑起来后,大约需要几秒钟初始化。接着我们需要进入容器内部,为博客创建一个数据库。
# 进入 MySQL 容器内部并登录交互界面 (需要输入你刚才设置的密码)
docker exec -it mysql-halo mysql -uroot -p
# 登录成功后,在 mysql> 提示符下执行建库语句:
CREATE DATABASE halodb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 查看是否创建成功:
SHOW DATABASES;
# 退出 MySQL 交互界面
exit
到这里,MySQL 5.7 已经成功部署在云服务器上,并且专门针对 2GB 内存进行了瘦身,非常强壮。

2. 到Halo后台做备份
目前我的 Halo 是连着 H2 数据库跑的。如果此时我直接去修改配置文件,让 Halo 连向刚才建好的 `halodb`,Halo 重启后会发现这是一个空空如也的新库,它就会以为自己是刚被下载下来一样,进入全新的“初始化安装向导”界面。此时在界面上,之前写的那篇文章自然就看不到了。
(注:虽然 H2 的数据文件还在硬盘里,但只要不连它,页面上也就不会显示了)
所以如果 Halo 上有文章,应该先到 Halo 的后台导出数据。
具体步骤如下:
- 用浏览器登录目前的 Halo 博客后台。
- 找到左侧导航栏的 备份 (Backups)。
- 点击"创建备份",等待系统打包完成。
- 将这个备份文件(通常是 .zip 或 .json 格式)下载到自己的电脑上妥善保存。
(注:未发布的草稿也会被完整地备份下来,所以没写完可以不急着发布,如我写到这里就还没有发布,但记得 Ctrl + S 保存一下!)
3. 将H2数据库迁移到 MySQL
备份完成就可以进行迁移操作了。
3.1 停止并删除当前 Halo 容器
docker stop halo
docker rm halo
3.2 注入 MySQL 连接配置
Halo 容器启动时,会自动去读取挂载目录下的 application.yaml 文件。我们直接在宿主机的挂载目录中生成这个配置文件(注意要修改下面的密码!):
cat <<EOF > ~/.halo2/application.yaml
spring:
r2dbc:
url: r2dbc:pool:mysql://172.17.0.1:3306/halodb
username: root
password: 替换为你刚才设置的MySQL密码
sql:
init:
platform: mysql
EOF
注意看,这里填写的 IP 是 172.17.0.1(Docker 默认分配给宿主机的虚拟网桥地址),而不是 127.0.0.1。因为 Halo 和 MySQL 处于两个相互隔离的容器中,如果填 localhost 或 127.0.0.1,会导致 Halo 只在自己的容器内部瞎转悠。通过 172.17.0.1,Halo 就能顺利找到宿主机对外映射出来的 3306 数据库端口。
3.3 用原命令重新拉起 Halo
与之前的启动命令完全一样:
docker run -it -d --name halo -p 8090:8090 -v ~/.halo2:/root/.halo2 halohub/halo:2.20
3.4 导入备份
- 稍等十几秒,让后端完成新数据库的自动建表逻辑。
- 浏览器再次访问
http://<你的云服务器公网IP>:8090,将会看到熟悉的初始化安装向导。 - 随意填写一个临时管理员账号登入后台(因为马上就要被覆盖了)。
- 导航至 系统 -> 备份,选择恢复数据,将刚才下载的备份压缩包传上去。
进度条走完并重新刷新页面后,一切又会回到之前的样子。唯一不同的是,现在的博客底层已经是由稳健强壮的 MySQL 5.7 引擎在驱动了!
Tips
这里可能有人会有疑问,我们之前 MySQL 数据库只执行了建库操作,并没有实际建表,是 Halo 会帮我们自动建表吗?
答:会!这里面涉及到一个核心的后端概念:ORM(对象关系映射)以及数据库版本控制工具(如 Flyway 或 Liquibase)。
- 它是如何工作的:在 Halo 的源码里,定义了各种 Java 实体类(比如
Post、Comment、User)。当 Halo 启动并成功连接到 MySQLhalodb时,Spring 框架底层的组件会自动扫描这些实体类,并自动将它们转化为 MySQL 里的表结构。 - 对于用户的体验就是:重启 Halo 连接 MySQL 时,系统会稍微卡顿几秒钟,这几秒钟就是后端在疯狂向 MySQL 发送自动建表的 SQL 指令。