如何用两台香港服务器分层部署数据库与Web,减少磁盘I/O竞争?
把两台香港服务器分别作为 Web 节点和数据库节点,可以让静态资源、应用日志、上传文件与数据库数据页、redo log、binlog 分别落在不同服务器的磁盘上,减少同一块磁盘上的读写竞争。实施时不仅要修改数据库连接地址,还要完成内网通信、数据库访问控制、数据迁移和切换验证,否则只是把本机 I/O 等待换成了网络等待。
下面以 Ubuntu Server 22.04 LTS、Nginx、PHP 8.1-FPM、MySQL 8.0 为部署环境:Web 服务保留在第一台香港服务器,MySQL 迁移到第二台,应用通过私网连接数据库。示例适用于中小型 PHP 网站的新部署或停写迁移;已有业务可替换应用目录、数据库名和域名,但应保留验证及回滚步骤。

围绕 Web 与数据库分层部署,A5数据提供香港物理服务器租用,覆盖常规建站、Xeon Gold及AMD EPYC等配置,可为两个节点分别配置计算、内存与磁盘资源。SSD、NVMe存储与大内存配置,为网站文件读写、数据库缓存及事务持久化提供硬件基础;不同套餐的CN2或国际带宽则承接网站与接口的公网访问需求,为业务分层和后续资源扩展提供配置空间。
一、准备条件:确认拆分的是磁盘压力,而不是只增加一台机器
1. 节点分工与上线目标
本文使用以下示例地址,执行前必须替换为实际配置。
| 项目 | Web 节点 | 数据库节点 |
|---|---|---|
| 主机名 | hk-web-01 | hk-db-01 |
| 私网地址 | 10.20.0.11 | 10.20.0.12 |
| 运行组件 | Nginx、PHP-FPM、业务程序 | MySQL 8.0 |
| 主要磁盘读写 | 静态文件、上传、应用日志、缓存 | 数据文件、事务日志、临时表、备份 |
| 对外访问 | 网站的 HTTP/HTTPS | 不向公网开放数据库端口 |
| 示例资源 | 4 vCPU、4 GiB 内存 | 4 vCPU、8 GiB 内存 |
资源配置只是演示起点,不是容量承诺。数据库需要多少内存,应根据活跃数据量、连接数、排序和临时表开销确定。
分层部署减少的是不同服务之间的磁盘竞争,不会消除数据库内部的 I/O 压力,也不等于数据库高可用。 数据库节点故障仍可能使网站不可用,两台服务器的备份和恢复能力仍需单独建设。
部署前还要向服务商确认:
- 两台香港服务器是否有互通的私网,以及私网带宽、流量计费和访问限制。
- 私网是否需要额外开通,安全组是否允许两台机器通信。
- 云盘是否存在共享存储后端、单卷 IOPS 或吞吐上限。拆成两台虚拟机,不一定意味着底层存储完全独立。
- 是否具备控制台或带外登录能力,避免防火墙配置错误后失联。
没有可信私网时,不应直接把 MySQL 3306 端口开放给所有公网来源。
2. 记录原环境和性能基线
以下命令适用于 Ubuntu 22.04。安装软件会写入磁盘并可能启动服务,生产节点应在维护窗口执行。
sudo apt update
sudo apt install -y sysstat iputils-ping netcat-openbsd
lsb_release -ds
ip -br address
ip route
lsblk -o NAME,SIZE,TYPE,MOUNTPOINTS
df -h
free -h
在原单机环境中,选择有代表性的业务时段记录:
iostat -xz 1 10
pidstat -d 1 10
重点观察:
- 数据盘的
r_await、w_await与读写吞吐。 - MySQL、PHP、日志写入进程分别产生多少 I/O。
- 同期接口响应时间、慢查询和错误率。
%util 不能单独证明所有类型磁盘都已饱和,尤其是支持并行请求的 SSD 和虚拟磁盘。应结合等待时间、队列和业务延迟判断。
3. 验证私网并保留配置备份
在 Web 节点执行:
ping -c 5 10.20.0.12
ip route get 10.20.0.12
路由输出应指向预期私网接口,源地址应为 Web 私网地址。ICMP 被禁用时,后续还需用 TCP 和实际 SQL 验证,不能仅凭 ping 失败判断网络中断。
两台节点分别建立备份目录:
sudo install -d -m 700 /root/a5-split-backup
数据库节点安装 MySQL 后备份 /etc/mysql;Web 节点修改站点前备份 Nginx、PHP-FPM 和应用连接配置。备份目录包含口令和连接信息,不得放进网站目录或公开代码仓库。
二、部署数据库节点:私网监听、受限账号与 TLS
1. 安装并核对 MySQL 版本
在数据库节点执行:
sudo apt install -y mysql-server openssl
sudo cp -a /etc/mysql /root/a5-split-backup/mysql.before
mysql --version
sudo systemctl status mysql --no-pager
sudo mysql -e "SELECT VERSION();"
本文配置针对 MySQL 8.0,不应直接用于 MariaDB。若查询结果不是 8.0,应先检查软件源和兼容性。
2. 为数据库生成 TLS 证书
私网访问控制用于限制来源,TLS 用于保护连接。下面使用自建 CA 签发服务器证书,客户端后续同时验证 CA 和目标地址。
CA 私钥只保存在数据库节点的受保护目录,不复制到 Web 节点。示例证书有效期为 365 天,上线后应安排到期监控与轮换。
sudo -i
install -d -m 700 /root/db-tls-ca
install -d -o mysql -g mysql -m 750 /etc/mysql/tls
cd /root/db-tls-ca
umask 077
openssl req -x509 -newkey rsa:3072 -nodes \
-keyout ca-key.pem -out ca.pem -days 365 \
-subj "/CN=A5-DB-Internal-CA" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign"
openssl req -newkey rsa:3072 -nodes \
-keyout server-key.pem -out server.csr \
-subj "/CN=10.20.0.12"
cat > server.ext <<'EOF'
basicConstraints=critical,CA:FALSE
keyUsage=critical,digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=IP:10.20.0.12
EOF
openssl x509 -req -in server.csr \
-CA ca.pem -CAkey ca-key.pem -CAcreateserial \
-out server-cert.pem -days 365 -sha256 -extfile server.ext
install -o mysql -g mysql -m 644 ca.pem /etc/mysql/tls/ca.pem
install -o mysql -g mysql -m 644 server-cert.pem /etc/mysql/tls/server-cert.pem
install -o mysql -g mysql -m 600 server-key.pem /etc/mysql/tls/server-key.pem
exit
如果应用改用内部域名连接,应在证书的 SAN 中加入对应 DNS 名称,而不是关闭证书身份验证来绕过错误。
3. 配置监听地址和内存
创建新的配置文件,不覆盖发行版原文件:
sudo tee /etc/mysql/mysql.conf.d/90-a5-split.cnf >/dev/null <<'EOF'
[mysqld]
bind-address = 10.20.0.12
mysqlx-bind-address = 127.0.0.1
ssl-ca = /etc/mysql/tls/ca.pem
ssl-cert = /etc/mysql/tls/server-cert.pem
ssl-key = /etc/mysql/tls/server-key.pem
require_secure_transport = ON
innodb_buffer_pool_size = 4G
max_connections = 100
slow_query_log = ON
long_query_time = 1
EOF
sudo mysqld --validate-config
sudo systemctl restart mysql
sudo mysqladmin --protocol=socket ping
sudo ss -lntp | grep -E ':(3306|33060)\b'
示例中的 4 GiB Buffer Pool 面向 8 GiB 内存的专用数据库节点,仍需为连接缓冲、系统和其他组件留出空间,不能在 4 GiB 内存机器上照抄。
验证应看到:
- 配置校验没有报错。
mysqladmin返回mysqld is alive。- 3306 监听在
10.20.0.12,而不是0.0.0.0。 - 启用 MySQL X Protocol 时,33060 仅监听回环地址。
不要为降低写入量随意关闭事务持久化。先完成隔离,再依据数据可靠性要求评估日志参数。
4. 创建业务库和最小权限账号
在数据库节点通过本机 socket 登录:
sudo env MYSQL_HISTFILE=/dev/null mysql
执行以下 SQL,口令必须替换为独立生成的强口令:
CREATE DATABASE shop
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
CREATE USER 'shop_app'@'10.20.0.11'
IDENTIFIED BY 'REPLACE_WITH_A_UNIQUE_STRONG_PASSWORD'
REQUIRE SSL;
GRANT SELECT, INSERT, UPDATE, DELETE
ON shop.* TO 'shop_app'@'10.20.0.11';
SHOW GRANTS FOR 'shop_app'@'10.20.0.11';
这里以应用只需 CRUD 权限为前提。框架数据库迁移应使用独立管理账号;需要存储过程调用时,再针对实际对象授予 EXECUTE。不要把 root 远程账号用于网站连接。
已有业务应沿用经确认的字符集和排序规则,避免迁移时无意改变排序或唯一索引语义。
5. 限制防火墙来源
数据库节点只允许 Web 私网地址访问 3306:
sudo ufw allow from 10.20.0.11 to 10.20.0.12 port 3306 proto tcp
sudo ufw status verbose
如果 UFW 尚未启用,应先放行实际 SSH 端口和管理来源、确认控制台可用,再执行:
sudo ufw enable
不要盲目添加默认 22 端口规则;已改端口的主机会因此失联。若现有规则允许任意来源访问 3306,应在确认新规则生效后按规则编号删除旧规则,并同步检查服务商安全组。
三、部署 Web 节点:验证真实应用连接
1. 安装环境并复制 CA 公钥证书
在 Web 节点执行:
sudo apt install -y nginx php8.1-fpm php8.1-mysql mysql-client
sudo cp -a /etc/nginx /root/a5-split-backup/nginx.before
sudo cp -a /etc/php/8.1/fpm /root/a5-split-backup/php-fpm.before
php -v
php -m | grep -E 'PDO|pdo_mysql'
sudo systemctl status php8.1-fpm --no-pager
通过已确认主机身份的 SSH/SCP 或管理渠道,把数据库节点的 /etc/mysql/tls/ca.pem 复制到 Web 节点。这里只传输 CA 公钥证书,不传输 CA 私钥或服务器私钥。
假设文件已放在 /tmp/db-ca.pem:
sudo install -d -m 755 /etc/a5demo
sudo install -o root -g root -m 644 \
/tmp/db-ca.pem /etc/a5demo/db-ca.pem
先验证网络,再验证账号和 TLS:
nc -vz -w 3 10.20.0.12 3306
mysql --host=10.20.0.12 --user=shop_app --password \
--ssl-mode=VERIFY_IDENTITY \
--ssl-ca=/etc/a5demo/db-ca.pem \
shop
登录后执行:
SELECT DATABASE(), CURRENT_USER();
SHOW SESSION STATUS LIKE 'Ssl_cipher';
SELECT 1;
预期数据库为 shop、授权身份为 shop_app@10.20.0.11,Ssl_cipher 非空。不要把密码直接写在命令参数中。
2. 配置 PHP 数据库连接
以下最小应用用于验证 Nginx、PHP-FPM 和远程数据库整条链路,不应覆盖已有业务文件。已有网站应修改自身连接配置,并清理框架配置缓存。
sudo install -d -o root -g www-data -m 750 /srv/a5demo/public
sudo install -o root -g www-data -m 640 \
/dev/null /etc/a5demo/database.php
sudoedit /etc/a5demo/database.php
文件内容:
<?php
return [
'dsn' => 'mysql:host=10.20.0.12;port=3306;dbname=shop;charset=utf8mb4',
'user' => 'shop_app',
'password' => 'REPLACE_WITH_A_UNIQUE_STRONG_PASSWORD',
'options' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_TIMEOUT => 3,
PDO::MYSQL_ATTR_SSL_CA => '/etc/a5demo/db-ca.pem',
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
],
];
数据库口令文件位于网站根目录之外,只有 root 和 PHP 所属组可读。不要为了处理权限问题将其改为全员可写。
创建测试入口:
sudo tee /srv/a5demo/public/index.php >/dev/null <<'EOF'
<?php
header('Content-Type: application/json; charset=utf-8');
try {
$config = require '/etc/a5demo/database.php';
$pdo = new PDO(
$config['dsn'],
$config['user'],
$config['password'],
$config['options']
);
$ok = (int) $pdo->query('SELECT 1')->fetchColumn() === 1;
echo json_encode(['web' => 'ready', 'database' => $ok ? 'ready' : 'error']);
} catch (Throwable $e) {
error_log('Database probe failed: ' . $e->getMessage());
http_response_code(503);
echo json_encode(['web' => 'ready', 'database' => 'unavailable']);
}
EOF
sudo chown root:www-data /srv/a5demo/public/index.php
sudo chmod 640 /srv/a5demo/public/index.php
错误详情只写服务器日志,不把数据库地址、账号或堆栈返回给访客。
3. 配置 Nginx 并验证
新建独立测试站点:
server {
listen 80;
server_name app.example.com;
root /srv/a5demo/public;
index index.php;
access_log /var/log/nginx/a5demo.access.log;
error_log /var/log/nginx/a5demo.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
location ~ /\. {
deny all;
}
}
将配置保存为 /etc/nginx/sites-available/a5demo,确认没有其他站点重复使用同一域名后启用:
sudo ln -s /etc/nginx/sites-available/a5demo \
/etc/nginx/sites-enabled/a5demo
sudo nginx -t
sudo systemctl reload nginx
curl --fail --show-error \
-H 'Host: app.example.com' http://127.0.0.1/
成功时可得到如下示例结果:
{"web":"ready","database":"ready"}
此结果只证明连接可用,不代表业务迁移完成。生产网站还应沿用或配置有效的 HTTPS 证书;已有应用的路由、上传限制和静态缓存配置也不能用上述测试配置替代。
PHP-FPM 并发应与数据库连接容量一起规划。每个 PHP 工作进程都可能占用连接,后台任务也会连接数据库,不能一边扩大 pm.max_children,一边忽略 MySQL 的连接上限。
四、迁移业务数据:停写、导出、导入、切换
1. 确定停写范围
新建业务可直接执行应用的初始化流程。已有业务使用下面的停写迁移方式。
停写必须覆盖网站、队列、定时任务、管理后台和外部写入接口。只给首页挂维护提示,不代表数据库已经停止写入。
建议操作顺序:
- 备份原应用数据库配置和框架缓存处理方法。
- 启用业务维护模式,停止生产消费者、定时写入任务。
- 等待在途请求和事务结束。
- 确认没有业务写入后再导出。
- 在目标库完成验证前,保持维护状态。
确有需要时可停止原节点 PHP-FPM:
sudo systemctl stop php8.1-fpm
该命令会中断 PHP 服务,未配置维护页时可能返回 502;回滚时用 systemctl start php8.1-fpm 恢复。其他语言服务、计划任务和独立消费者必须分别停止。
2. 导出并传输数据
以下命令在原数据库所在服务器执行,适用于主要使用 InnoDB 的 MySQL 8.0 数据库:
sudo -i
umask 077
mysqldump --single-transaction --quick \
--routines --triggers --events \
--set-gtid-purged=OFF --no-tablespaces \
--default-character-set=utf8mb4 \
shop > /root/shop-cutover.sql
sha256sum /root/shop-cutover.sql > /root/shop-cutover.sql.sha256
exit
必须检查命令退出状态和错误输出。非事务表不能依靠 --single-transaction 获得一致快照;导出期间也不要执行建表、改表等 DDL。
转储文件含业务数据,应通过受控 SSH/SCP 通道传输到数据库节点的 /root 目录,并在那里核对校验值:
sudo -i
cd /root
sha256sum -c shop-cutover.sql.sha256
mysql shop < shop-cutover.sql
exit
导入可能执行建表或删除同名表,因此目标 shop 应为空库;若已有数据,必须先备份并确认覆盖范围,不能直接执行。
包含视图、触发器、事件和存储过程的业务,还需检查 DEFINER 账号及其权限。事件调度器也应单独核对,避免迁移后遗漏任务或两端重复执行。
3. 对照业务数据后切换连接
在两端分别核对关键业务表:
SELECT COUNT(*) AS order_count, MAX(id) AS latest_order_id
FROM shop.orders;
orders 仅为示例,应替换为实际表名。大表全表计数会产生额外读取,可以改用指定日期区间、主键范围和业务汇总核对。
随后修改真实应用的数据库地址、账号、CA 路径与 TLS 校验选项,清理配置缓存,再启动 PHP-FPM及需要恢复的消费者。
上线前还要检查:
- cron、队列和后台脚本是否仍连接
localhost。 - 持久连接或常驻进程是否仍保持旧连接。
- 数据库事件是否只在目标节点运行。
- 原数据库是否已退出业务写入路径。
保留旧数据库用于回滚,但不要允许新旧两端同时接受独立业务写入。普通单库拆分不具备自动合并两端数据的能力。
五、结果检查与异常处理:确认 I/O 隔离确实生效
1. 从功能到性能逐层验收
先验证登录、列表查询和一次可控写入,再检查事务提交、重复请求和后台任务。写入测试应使用测试账号及可清理的数据,不要随意修改真实订单。
两台节点同时采集:
iostat -xz 1 10
pidstat -d 1 10
数据库节点补充查看:
sudo mysql -e "
SHOW GLOBAL STATUS
WHERE Variable_name IN (
'Threads_connected',
'Threads_running',
'Slow_queries',
'Innodb_buffer_pool_reads',
'Innodb_buffer_pool_read_requests'
);"
这些状态值大多为累计值,应比较同一时间窗口的增量,不能拿不同运行时长的绝对值直接比较。
拆分后的合理变化是:Web 节点不再承载业务 MySQL 数据文件的持续读写,数据库节点承担主要数据读写。最终是否改善,需要在相近流量、相近业务请求组合下比较磁盘等待时间、接口延迟和错误率。
若数据库节点仍处于高 I/O 等待,而 Web 节点磁盘已明显空闲,说明服务间竞争得到隔离,但数据库自身瓶颈尚未解决。 下一步应检查慢查询、索引、Buffer Pool、临时表落盘和存储规格,而不是继续增加 Web 机器。
不要在生产库运行无边界压测或写满磁盘的测试。
2. 注意拆分后新增的网络成本
数据库从本机变成远程服务后,每次串行交互都会经过网络。以每次往返约 1 毫秒、一个页面存在 20 次依次执行的数据库交互为例,仅往返等待就可能增加约 20 毫秒,还不包含 SQL 执行和数据传输时间。这只是解释机制的估算,不是香港服务器的固定延迟。
因此应同时减少逐行查询、过大的结果集和重复建连。香港地域一致不代表两台机器一定处于同一机房或具有低延迟私网,必须验证实际链路。
3. 按低风险到高风险排查故障
| 现象 | 优先检查 | 结果含义与处理 |
|---|---|---|
| TCP 3306 连接超时 | 私网路由、安全组、UFW | 通常是路径或过滤问题,不要先改数据库账号 |
Connection refused | MySQL 状态、监听地址 | 主机通常可达,但目标端口没有正常监听 |
Access denied | 来源地址、账号 Host、口令 | 私网流量可能经过地址转换,应确认 MySQL 实际看到的来源 |
| 证书校验失败 | CA、SAN、系统时间、有效期 | 修正证书或连接目标,不要关闭身份验证 |
| 命令行成功、PHP 失败 | 扩展、文件权限、FPM 配置缓存 | CLI 与 Web 运行环境可能不同 |
| 网站返回 502 | FPM 服务、socket、Nginx 配置 | 先检查 Web 链路,不一定是数据库问题 |
| 拆分后延迟增加 | 私网往返、查询次数、慢 SQL | 可能从磁盘瓶颈转为网络或查询瓶颈 |
对应的日志检查命令:
sudo journalctl -u mysql -n 100 --no-pager
sudo journalctl -u php8.1-fpm -n 100 --no-pager
sudo tail -n 100 /var/log/nginx/a5demo.error.log
数据库节点和 Web 节点分别执行对应命令。日志可能包含账号、SQL 和业务信息,提供排障材料前应脱敏。
六、验收与回滚检查项
1. 上线验收清单
- [ ] 数据库只监听指定私网地址,3306 不向任意公网来源开放。
- [ ] 应用账号限制为实际 Web 来源,权限与业务需求匹配。
- [ ] 命令行与 PHP 均通过 TLS 证书验证,而非仅建立加密连接。
- [ ] 关键表、最新业务记录、汇总数据核对一致。
- [ ] 网站读写、队列、cron、管理后台都已使用目标库。
- [ ] 旧数据库没有继续接受业务写入,两端没有重复定时任务。
- [ ] Web 节点与数据库节点的磁盘指标分别采集,业务延迟和错误率可对照。
- [ ] 备份已安排到独立存储,并验证过恢复;备份任务不会长期挤占业务磁盘。
- [ ] 数据盘空间、连接数、慢查询、证书到期和错误率已有监控。
- [ ] 本机测试入口已移除或限制访问,生产网站使用 HTTPS。
2. 尚未产生新写入时的回滚
如果目标库还没有接受新的业务写入,可以恢复原应用连接配置、清理缓存、重启应用进程,并恢复原定时任务与消费者。
仅回滚本文新建的测试站点时:
sudo unlink /etc/nginx/sites-enabled/a5demo
sudo nginx -t
sudo systemctl reload nginx
此操作只移除测试站点的启用链接,不删除配置和业务数据。已有业务站点应恢复自己的配置备份,不要直接覆盖整套 Nginx 目录。
3. 已经产生新写入时的回滚
一旦目标数据库产生了新订单、用户修改或后台写入,就不能直接切回旧库,否则会让这些变化从业务视角消失。
此时应重新进入维护状态,停止所有写入,从目标库导出最新完整数据,备份旧库后再恢复。若两端版本一致、迁移期间没有结构变更,且已经演练过覆盖恢复流程,可在原节点执行:
sudo -i
umask 077
mysqldump --single-transaction --quick \
--routines --triggers --events \
--set-gtid-purged=OFF --no-tablespaces \
shop > /root/shop-before-rollback.sql
mysql shop < /root/shop-back-from-db.sql
exit
其中 /root/shop-back-from-db.sql 必须是目标数据库停写后导出并校验通过的文件。导入可能删除或覆盖同名表,执行前需确认磁盘空间、数据版本、对象权限及恢复范围;有结构变更时不能把这段命令当成通用回滚脚本。
回滚结束后再次核对:
- [ ] 最近新增和修改的数据已经恢复到原节点。
- [ ] 应用连接地址、口令及本地连接方式已恢复。
- [ ] 目标数据库不再接受应用写入。
- [ ] 消费者和定时任务仅在一套环境运行。
- [ ] 网站读写验证通过,日志未持续出现连接或权限错误。
- [ ] 两端备份和迁移记录继续保留,未因回滚立即删除。
两台香港服务器分层部署的价值,在于把 Web 文件读写和数据库持久化读写分开管理。只有同时验证数据一致性、连接安全、网络开销和磁盘指标,才能确认这次拆分真正减少了 I/O 竞争,而不是把压力换了一个位置。



