上一篇 下一篇 分享链接 返回 返回顶部

企业ERP系统如何在香港服务器的CentOS 7环境中部署主从数据库,避免跨境访问事务延迟?

发布人:Minchunlin 发布时间:2025-09-13 11:26 阅读量:202


我蹲在香港葵涌机房的冷通道口,背靠着机柜门框陷入了沉思。客户 ERP 当晚要切换到香港节点,白天来自内地的同事抱怨“下单经常卡住”,数据库的事务一旦跨境就像在一根细长的吸管里吹气,延迟能把最简单的扣减库存搞成“幽灵并发”。我知道,这一夜注定无眠。

我把随身的笔记本搭在膝盖上,旁边是刚就绪的两台物理服务器,一台要扛应用和 ProxySQL,一台要扛 MySQL 主库;另外我们在深圳边上租了一台轻量只读副本,用来承接报表查询。目标很明确:把“跨境事务”缩到最短,把“跨境只读”做得最快。

目标与方案一图说清

核心目标

  • ERP 高可用上线:Java(Spring Boot)应用 + Nginx 反代 + Redis 缓存。
  • 数据库主从:MySQL 8.0 GTID,主库在香港,同城高性能;内地放只读副本承接报表/查询。
  • 跨境延迟优化:应用所有写事务在香港落地;读多写少流量可在内地副本就近读取;配合 ProxySQL 读写分离、Redis、异步队列 降低实时跨境调用比例。
  • 观测与回滚:Prometheus + Grafana 全链路监控,XtraBackup 热备 + 蓝绿发布随时回退。

拓扑(文字版)

[China Users] 
     |  BGP/CN2 线路
[HK Edge Nginx/Keepalived VIP]
     |--> [ERP-App-01/02 ... (Java + ProxySQL)] ---> [Redis]
                                  |                     |
                                  └----> [MySQL-Master @HK] <---- GTID ---- [MySQL-Slave @SZ(只读)]

1. 硬件、网络与操作系统基线

1.1 机型与磁盘(我这次的真实参数)

角色 机型 & CPU 内存 系统盘 数据盘 网卡 带宽/线路
App/ProxySQL Dell R650, 2×Xeon Silver 4314(32 vCPU) 128GB 2×480G SSD RAID1 2×1.92TB NVMe RAID1 2×10GbE 100M 独享,国际 BGP + CN2 GIA
MySQL Master Dell R650, 同上 256GB 2×480G SSD RAID1 4×1.92TB NVMe RAID10 2×10GbE 同上
MySQL Slave(SZ) 节省版 16 vCPU 64GB 200G SSD 2×960G SSD RAID1 1×10GbE 国内高质量 BGP

为什么选 RAID10 做主库:InnoDB 随机写多,RAID10 低延迟;只读副本用 RAID1 足够。

1.2 延迟与带宽基线(上线前实测)

路由 TCP RTT(p95) 备注
广州 → 香港 18–25 ms CN2 线路
上海 → 香港 35–50 ms 高峰更波动
北京 → 香港 45–65 ms 夜间更稳定
香港 App → 香港 MySQL 0.2–0.5 ms 同机房

关键认知:跨境 40–60ms 的 RTT 并不可怕,可怕的是把“写事务”跨境。我们要保证写就地(香港),跨境只读+缓存。

1.3 OS 版本与基础策略

CentOS 7.9(客户指定,稳定为先);文件系统 XFS;时区 Asia/Shanghai;chrony 同步时钟;关闭 THP;tuned 选 throughput-performance。

内核 TCP 优化、ulimit、sysctl 调整见下。

2. 系统基线:一把过的命令与参数

# 基础源与工具
yum install -y epel-release
yum install -y chrony tuned wget curl vim net-tools telnet lsof htop jq git

# 时间与 NTP
timedatectl set-timezone Asia/Shanghai
systemctl enable --now chronyd

# 关闭 SELinux(如需强制,后续再写策略;上线夜里求稳)
setenforce 0
sed -ri 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config

# 防火墙(按需放行)
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-port=3306/tcp
firewall-cmd --reload

# 关闭透明大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag
cat >/etc/rc.d/rc.local <<'EOF'
#!/bin/bash
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
EOF
chmod +x /etc/rc.d/rc.local

# tuned
tuned-adm profile throughput-performance

# ulimit
cat >/etc/security/limits.d/99-custom.conf <<'EOF'
* soft nofile 1048576
* hard nofile 1048576
* soft nproc  65536
* hard nproc  65536
EOF

# sysctl(TCP/队列/内存)
cat >/etc/sysctl.d/99-sysctl.conf <<'EOF'
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10000 65000
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600
vm.swappiness = 1
fs.file-max = 2097152
EOF
sysctl --system

3. 组件选择与安装

3.1 Nginx(反向代理/蓝绿切换)

cat >/etc/yum.repos.d/nginx.repo <<'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1
EOF
yum install -y nginx
systemctl enable --now nginx

最小化站点(支持 HTTP/2、长连接):

# /etc/nginx/conf.d/erp.conf
upstream erp_upstream {
    server 10.10.10.11:8080 max_fails=3 fail_timeout=10s;
    server 10.10.10.12:8080 max_fails=3 fail_timeout=10s;
    keepalive 128;
}

server {
    listen 80 default_server;
    # 上线后务必配置 HTTPS,在 HK 可以申请正规证书
    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 120s;
        proxy_pass http://erp_upstream;
    }
}

3.2 Java 运行时与 ERP 应用

yum install -y java-11-openjdk java-11-openjdk-devel
useradd -r -s /sbin/nologin erp

# 假设 ERP 为 Spring Boot 可执行 jar
mkdir -p /opt/erp && chown -R erp:erp /opt/erp
# 上传 erp-app.jar 到 /opt/erp/
cat >/etc/systemd/system/erp.service <<'EOF'
[Unit]
Description=ERP Spring Boot
After=network.target

[Service]
User=erp
WorkingDirectory=/opt/erp
ExecStart=/usr/bin/java -Xms6g -Xmx6g -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/opt/erp/dump.hprof \
  -Dspring.profiles.active=prod \
  -jar /opt/erp/erp-app.jar
Restart=on-failure
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now erp

连接池建议(HikariCP)

  • maximumPoolSize = min( (CPU*2)+1, 50 )(香港 App 与 DB 同机房,RTT 极低,适当小池)
  • 启用 connectionTimeout=3000ms,validationTimeout=1000ms,leakDetectionThreshold=10s。

3.3 Redis(缓存/会话/限流)

yum install -y redis
sed -ri 's/^bind 127.0.0.1/bind 0.0.0.0/' /etc/redis.conf
sed -ri 's/^# maxmemory .*/maxmemory 8gb/' /etc/redis.conf
echo "maxmemory-policy allkeys-lru" >> /etc/redis.conf
systemctl enable --now redis

4. MySQL 8.0 主从(GTID)在 CentOS 7 的落地

4.1 安装与目录布局

rpm -Uvh https://repo.mysql.com//mysql80-community-release-el7-3.noarch.rpm
yum install -y mysql-community-server
mkdir -p /data/mysql/{data,binlog,redo,tmp}
chown -R mysql:mysql /data/mysql

4.2 主库配置(香港)

cat >/etc/my.cnf <<'EOF'
[mysqld]
server_id=101
datadir=/data/mysql/data
tmpdir=/data/mysql/tmp
socket=/var/lib/mysql/mysql.sock
log_error=/var/log/mysqld.log

# InnoDB
innodb_buffer_pool_size=120G
innodb_log_file_size=4G
innodb_flush_log_at_trx_commit=1
innodb_flush_method=O_DIRECT
innodb_io_capacity=20000
innodb_io_capacity_max=40000

# Binlog & GTID
log_bin=/data/mysql/binlog/mysql-bin
binlog_expire_logs_seconds=604800
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_format=ROW
binlog_row_image=FULL
relay_log_recovery=ON

# 连接与超时
max_connections=2000
wait_timeout=600
interactive_timeout=600

# 字符集
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci

# 只在主上写
read_only=OFF
super_read_only=OFF
EOF

systemctl enable --now mysqld
# 取初始 root 密码
grep 'temporary password' /var/log/mysqld.log
mysql_secure_installation

创建复制账号(强制 SSL):

CREATE USER 'repl'@'%' IDENTIFIED BY 'StrongPass!2025' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;

4.3 只读副本配置(深圳)

cat >/etc/my.cnf <<'EOF'
[mysqld]
server_id=201
datadir=/data/mysql/data
log_error=/var/log/mysqld.log

gtid_mode=ON
enforce_gtid_consistency=ON
relay_log_recovery=ON
read_only=ON
super_read_only=ON

# 读多优化
innodb_buffer_pool_size=48G
character-set-server=utf8mb4
EOF

systemctl enable --now mysqld

建立加密复制(建议双向验证,示例用自签 CA;生产可用正式证书):

-- 在从库
CHANGE MASTER TO 
 MASTER_HOST='HK-MASTER-IP',
 MASTER_USER='repl',
 MASTER_PASSWORD='StrongPass!2025',
 MASTER_AUTO_POSITION=1,
 MASTER_SSL=1,
 MASTER_SSL_VERIFY_SERVER_CERT=1;
START SLAVE;
SHOW SLAVE STATUS\G

要点

  • GTID + MASTER_AUTO_POSITION 避免位点找错。
  • 从库 read_only=ON + super_read_only=ON 防止误写。
  • 复制链路走 CN2/BGP,在边界路由上限速防止拥塞。
  • 遇到长事务导致复制延迟时,优先排查应用未提交的批量写/报表误落主库。

5. ProxySQL 读写分离:把写都“关”在香港

为什么选 ProxySQL:它位于应用侧,就近决定路由,事务内强制走主库,普通 SELECT 优先打到只读副本,减少跨境写路径。

5.1 安装与启动

yum install -y https://github.com/sysown/proxysql/releases/download/v2.6.0/proxysql-2.6.0-1-centos7.x86_64.rpm
systemctl enable --now proxysql
mysql -u admin -padmin -h 127.0.0.1 -P6032

5.2 最小可用配置(我的线上模板)

-- 定义 HostGroup:10=主,20=只读
INSERT INTO mysql_servers(hostgroup_id,hostname,port,weight,max_connections)
VALUES (10,'HK-MASTER-IP',3306,100,2000),
       (20,'SZ-SLAVE-IP',3306,100,1000);

-- 监控与心跳账号
INSERT INTO mysql_users(username,password,default_hostgroup,transaction_persistent)
VALUES ('erp_rw','StrongERP!2025',10,1);

-- 事务内粘性:只要 BEGIN 了,就在主库直到 COMMIT/ROLLBACK
UPDATE global_variables SET variable_value='true' WHERE variable_name='mysql-autocommit_false_is_transaction';

-- 路由规则:写入/DDL/显式事务走主,普通 SELECT 走从
INSERT INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,apply) VALUES
(100,1,'^SELECT.*FOR UPDATE',10,1),
(110,1,'^SELECT',20,1),
(200,1,'^INSERT|^UPDATE|^DELETE|^REPLACE|^CREATE|^ALTER|^DROP',10,1),
(300,1,'^BEGIN',10,1);

LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK;
LOAD MYSQL USERS TO RUNTIME;   SAVE MYSQL USERS TO DISK;
LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;

应用侧改造:JDBC 指向 proxysql:6033,生产把 erp_rw 设置为最小权限账号。

6. 应用层避免跨境“写”的几件小事

  • 把订单创建、库存扣减、支付回调等强一致写操作固定在香港主库(ProxySQL/事务粘性保障)。
  • 报表、查询、导出尽量从内地只读副本取(ProxySQL 规则 + 读库 DataSource)。
  • Redis 缓存热读页面(商品详情、价格、品类树),设置合理过期(60–300s)+ 主动失效。
  • 异步队列(如订单日志、消息推送)落到 Redis Stream/RabbitMQ,跨境只传消息,不传事务。
  • 接口级熔断与降级:跨境链路抖动时,读从库失败回落到主库,但对用户界面做兜底(提示“稍后刷新”)。

7. 实测对比(上线夜的三轮压测结果)

指标 上线前(直连主库,跨境读写混杂) 优化后(ProxySQL + 只读副本 + 缓存)
下单接口 p95 1.8 s 420 ms
报表导出(10w 行) 120 s 38 s(全部走只读副本)
主库 QPS 峰值 8k 4.5k(读分流)
复制延迟 p95 1.2 s < 300 ms(高峰 700ms)
广州用户页面首屏 1.4 s 650 ms

这张表是我那晚导出的真实数据快照,最大的变化来自读写隔离 + 缓存,其次是链路质量(CN2)。

8. 监控、告警与可观测

Node Exporter / mysqld_exporter / redis_exporter → Prometheus

重点面板:

  • MySQL:Threads_running、Innodb_buffer_pool_reads、Slave_SQL_Running_State、Seconds_Behind_Master
  • ProxySQL:mysql_query_processor_time_ms、hostgroup 命中率、Errors
  • Nginx:4xx/5xx、upstream_response_time

告警门槛(建议):

  • 复制延迟 > 3s 持续 2 分钟
  • 主库 Threads_running > 256 持续 1 分钟
  • 应用 5xx > 1% 持续 5 分钟

9. 备份与演练(别只备不还)

9.1 XtraBackup 每日热备

yum install -y percona-xtrabackup-80
mkdir -p /backup/mysql
cat >/etc/cron.d/mysql_backup <<'EOF'
# 每天 02:00 全量,保留 7 天
0 2 * * * root innobackupex --user=backup --password='Backup!2025' /backup/mysql/full-$(date +\%F)
EOF

9.2 恢复演练(摘录)

innobackupex --apply-log /backup/mysql/full-2025-09-10
systemctl stop mysqld
rm -rf /data/mysql/data/*
innobackupex --copy-back /backup/mysql/full-2025-09-10
chown -R mysql:mysql /data/mysql
systemctl start mysqld

每季度至少做一次恢复演练,不演就是没备份。

10. 蓝绿发布与回滚按钮

香港两台 App 分为 蓝(10.10.10.11)/绿(10.10.10.12),Nginx upstream 权重可控。

上线流程:先全量压测绿 → 1% 流量 → 30% → 全量 → 观察 30 分钟 → 合并。

回滚:一条命令改 upstream 权重;数据库禁止结构变更与大版本升级在大流量窗口。

11. “踩过的坑”和当场解决

  • 复制延迟突增:排查发现某报表在“内地”误走了主库(业务写了强一致读),临时在 ProxySQL 加了精确路由规则把该 SQL 导向从库,延迟立刻下降。
  • lower_case_table_names 不一致:老系统迁移过来时,开发机是 Windows(不敏感),线上 Linux(敏感),导致某些表名大小写错;线上通过视图+重建修复,后面在 CI 加“表名大小写”审核。
  • binlog 膨胀:某批导入忘了 SET sql_log_bin = 0;(在从库),导致复制链路压力上升;后来对导入脚本统一模板化。
  • 连接暴涨:ProxySQL 初始 max_connections 太低,应用突刺时 5xx;提升 App 连接池并把 ProxySQL mysql-max_connections 调整到 10k,节点扩容后平稳。
  • 时钟漂移:内地从库早期 NTP 源不稳,Seconds_Behind_Master 波动;改为多上游 pool.ntp.org + 与香港互为备用。
  • THP 未关:MySQL 抖动,perf top 看到大量 thp_collapse_alloc; 关闭 THP 后抖动消失。
  • 跨境链路偶发丢包:夜里高峰 tcp retrans 增多,路由厂商调度到次优路径;临时 强制走 CN2 GIA,并在负载上启用重试与幂等保护。

12. 参数与配置清单(便携版)

12.1 MySQL 主库关键参数

参数 说明
innodb_buffer_pool_size 120G 内存 256G 的 45–50%
innodb_log_file_size 4G 写密集更抗抖
binlog_format ROW 复制安全
gtid_mode/enforce_gtid_consistency ON 免位点
read_only/super_read_only OFF 仅主库

12.2 从库关键参数

参数 说明
read_only/super_read_only ON/ON 禁止误写
relay_log_recovery ON 宕机后自动修复
innodb_buffer_pool_size 48G 读为主

12.3 ProxySQL 要点

主库 HostGroup 10
从库 HostGroup 20
事务粘性 mysql-autocommit_false_is_transaction=true
规则 SELECT→20;SELECT FOR UPDATE/DDL/DML/BEGIN→10

13. 跨境优化的“三板斧”

  • 路径:选 CN2 GIA/BGP 高质量出口,RTT 与丢包更稳定;数据库与应用同机房。
  • 策略:写就地、读就近、强事务缩短(尽量小交易、避免跨境锁等待)。
  • 手段:ProxySQL 读写分离 + Redis 缓存 + 异步解耦(消息/任务队列)。

14. 安全与合规速配

  • 数据库最小权限、账号分离(repl、backup、erp_rw)。
  • 只读副本脱敏报表(必要字段做脱敏视图)。
  • 防火墙白名单,SSH 禁止密码登录,仅密钥。
  • 日志留存与访问审计,满足财务/内控要求。

15. 收尾清单(上线夜的“贴柜条”)

  •  NTP/时区统一;THP 关闭;tuned 到位
  •  MySQL 主从 GTID 建立,SHOW SLAVE STATUS\G 无错误
  •  ProxySQL 规则生效,事务内命中主库
  •  Nginx upstream 蓝绿可控,健康检查通过
  •  Prometheus 指标齐全,告警通道畅通
  •  XtraBackup 可恢复演练通过
  •  压测三轮:1%→30%→100% 流量观测稳定

机房的灯在清晨 5 点半变得柔和起来,风机的嗡嗡声也不再刺耳。最后一轮压测曲线像一条温顺的河,主库的写稳定在可控的范围,内地的报表飞快地从只读副本里“抽走”数据。
我合上笔记本,给客户发出“可以切”的消息。那一刻我很清楚:不是我们把跨境延迟消灭了,而是我们学会了绕开它——写在香港,读在身边,把复杂留给系统,把顺滑还给用户。
等出了机房,天边已经泛白。我给自己点了杯热咖啡,在心里把这套方案又过了一遍:如果哪天它失效了,我也知道下一步该怎么改。工程的魅力大概就在这里——它从不承诺永远,但愿意朝着更好的方向不断迭代。

附:一键回顾(给新同事/自己看)

  • CentOS 7 基线:chrony / tuned / 关 THP / sysctl
  • MySQL 8.0 GTID:主(HK)/ 从(SZ)+ SSL 复制 + 读写分离
  • ProxySQL:规则化把写关在主库、读放到副本
  • Redis 缓存 + 异步队列:降低跨境强一致调用
  • 观测/备份/蓝绿:随时能看见、随时能回退、随时能恢复

如果你拿着这份手记站在机房里,只要按部就班走一遍,你会得到和我那一夜一样的结果:稳定、可观、可回滚。剩下的,就是不断把它做“更顺滑”。

目录结构
全文