跨境电商网站部署在洛杉矶机房:如何用 MariaDB Galera 把订单“实时”穿越太平洋
技术教程 2025-09-18 13:35 190


“今晚必须把香港仓的订单实时对上来,早上 8 点 CFO 要看报表。”
洛杉矶机房 02:10,我盯着 NOC 面板上从 LA 到 HK 稳定在 125–145ms 的 RTT,深吸了一口气,拉上外套拉链,开始了这次跨境“零容忍丢单”的数据库改造。
本文是我在洛杉矶机房给跨境电商站做 MariaDB Galera 实战部署与优化的完整手记:不仅有“怎么装”,更有“为什么这么装”。细节都是真实踩过的坑和当场解决的过程,包含硬件参数、配置样例、表结构设计、SST/IST、WAN 优化、监控与故障处理。目标是在 高延迟跨洋链路 上也尽可能接近“实时一致”的订单数据同步,并保证业务在下单高峰期间不掉单、不卡单。

一、现场架构与边界条件

1)业务与一致性目标

  • 电商核心:下单(orders)、支付回调(payments)、国际仓出库(fulfillments)。
  • 一致性要求:下单与支付强一致;库存与 BI可接受秒级延迟。
  • 可用性:允许单节点或单地域故障,不允许写入停摆。

2)物理与网络拓扑

  • 我最后选了 多主 Galera 集群 + 分段(Segment) 的拓扑,避免把所有写都扔到跨洋环路上。
  • LA 段(Segment 0):2 台(主承载写流量 + 网站同城机房内网 10GbE)
  • HK 段(Segment 1):1 台(就近服务亚洲仓、客服工具,承担读与容灾写)
  • (可选)新加坡/法兰克福:按需再加读副本或下游异步复制做 BI 报表

Galera 在 WAN 上能跑,但要小心流控、认证冲突和 SST。Segment 能减少跨段流量;LA 段 2 台 + HK 段 1 台既保障仲裁,又把主要写留在 LA。

3)硬件与系统基线(真实参数)

角色 机房 CPU 内存 系统盘 数据盘 网卡 OS
g1 LA Xeon Silver 4314(16c) 64GB SATA SSD 240G 2× NVMe 1.92T (mdadm RAID1) 2×10GbE LACP CentOS 7(EOL,业务要求仍使用)
g2 LA Xeon Silver 4314(16c) 64GB 同上 同上 同上 CentOS 7
g3 HK EPYC 7302P(16c) 64GB 同上 2× NVMe 1.92T (RAID1) 10GbE CentOS 7

注:CentOS 7 已 EOL,但当时合规环境锁定了版本。若条件允许,强烈建议上 AlmaLinux/Rocky 8+,并用 MariaDB 10.11 LTS。

二、部署准备:把基础打牢

1)时钟与内核/文件句柄

# 时钟:跨洋集群 NTP 漂移会放大问题
yum install -y chrony && systemctl enable --now chronyd

# 文件句柄与队列
cat >> /etc/security/limits.conf <<'EOF'
mysql soft nofile 1048576
mysql hard nofile 1048576
EOF

cat >> /etc/sysctl.conf <<'EOF'
fs.file-max = 2000000
net.core.somaxconn = 65535
net.ipv4.tcp_tw_reuse = 1
vm.swappiness = 1
EOF
sysctl -p

坑 1:SST 挂死

第一次做 SST(mariabackup)时,Too many open files,直接卡在 donor 端。上面两步能救你一命。

2)防火墙与端口

Galera 用到:

  • 3306(客户端)
  • 4567/tcp,udp(组通信)
  • 4568/tcp(IST)
  • 4444/tcp(SST)
firewall-cmd --permanent --add-port=3306/tcp
firewall-cmd --permanent --add-port=4567/tcp
firewall-cmd --permanent --add-port=4567/udp
firewall-cmd --permanent --add-port=4568/tcp
firewall-cmd --permanent --add-port=4444/tcp
firewall-cmd --reload

坑 2:跨段只开了 TCP

HK 侧 4567/udp 没放行,evs 心跳异常,流控疯狂触发。务必同时放行 UDP。

3)软件仓库(MariaDB 10.5 on el7)

cat > /etc/yum.repos.d/MariaDB.repo <<'EOF'
# MariaDB 10.5 for CentOS 7
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.5/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1
EOF

yum clean all && yum makecache
yum install -y MariaDB-server MariaDB-client galera-4 mariadb-backup socat pigz jq

三、Galera 集群配置(含 WAN Segment 与 TLS)

1)统一的基础配置 /etc/my.cnf.d/server.cnf

[server]
user                    = mysql
bind-address            = 0.0.0.0
character-set-server    = utf8mb4
collation-server        = utf8mb4_unicode_ci
default_storage_engine  = InnoDB
skip_name_resolve       = 1

# InnoDB
innodb_buffer_pool_size = 40G           # 约 60-65% 内存
innodb_log_file_size    = 2G
innodb_flush_log_at_trx_commit = 1      # 强一致写;如追求吞吐可评估 2
innodb_flush_method     = O_DIRECT
innodb_io_capacity      = 2000
innodb_io_capacity_max  = 4000

# 连接
max_connections         = 2000
table_open_cache        = 8000
open_files_limit        = 1000000

# Binlog(用于下游异步/审计)
server-id               = 1             # 每节点不同
log_bin                 = /var/lib/mysql/mariadb-bin
binlog_format           = ROW
sync_binlog             = 1
expire_logs_days        = 7

2)Galera 专属 /etc/my.cnf.d/galera.cnf

下面以 g1(LA)、g2(LA)、g3(HK)为例,记得替换 IP。

[galera]
wsrep_on                = 1
wsrep_provider          = /usr/lib64/galera/libgalera_smm.so
wsrep_cluster_name      = crossborder-orders
wsrep_cluster_address   = gcomm://10.10.0.11,10.10.0.12,10.20.0.31

# 当前节点身份
wsrep_node_name         = g1
wsrep_node_address      = 10.10.0.11    # g2:10.10.0.12  g3:10.20.0.31

# SST/IST
wsrep_sst_method        = mariabackup
wsrep_sst_auth          = sstuser:sstpass
wsrep_sst_receive_address = 10.10.0.11

# 避免自增冲突(Galera 会自动管理,也建议显式设置)
wsrep_auto_increment_control = 1

# 流控与 WAN 参数(按延迟调优)
wsrep_slave_threads     = 8
wsrep_provider_options  = "
  gcache.size=4G;
  gcache.page_size=1G;
  gmcast.segment=0;             # g1/g2=0, g3=1
  evs.keepalive_period=PT1S;
  evs.inactive_timeout=PT10S;
  evs.suspect_timeout=PT5S;
  evs.send_window=512;
  evs.user_send_window=512;
  gcs.fc_limit=256;
  gcs.fc_factor=0.8;
  ist.recv_addr=10.10.0.11
"

# TLS(强烈建议开启)
wsrep_provider_options = "
  socket.ssl_key=/etc/galera/ssl/server-key.pem;
  socket.ssl_cert=/etc/galera/ssl/server-cert.pem;
  socket.ssl_ca=/etc/galera/ssl/ca.pem
"

坑 3:wsrep_provider_options 被后写的配置覆盖

同一个键多次写会覆盖,把相关选项合并到同一段,或用多行但保持在同一个 = 之后的引号内。

3)生成 SST 用户与 TLS 证书

-- 初次启动后在任一节点执行
CREATE USER 'sstuser'@'%' IDENTIFIED BY 'sstpass';
GRANT RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT ON *.* TO 'sstuser'@'%';
FLUSH PRIVILEGES;

# TLS(自签,生产建议内网 CA)
mkdir -p /etc/galera/ssl && cd /etc/galera/ssl
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca.pem -subj "/CN=galera-ca"
openssl genrsa -out server-key.pem 4096
openssl req -new -key server-key.pem -out server.csr -subj "/CN=g1"
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 3650
chown -R mysql:mysql /etc/galera/ssl && chmod 600 /etc/galera/ssl/*

# 将 ca.pem、server-key/cert 分发到 g2/g3(分别用对应 CN 生成)

四、集群引导与节点加入(零停启动顺序)

1)仅在第一个节点(g1)引导新集群

# MariaDB 自带脚本(CentOS 7 包里有)
galera_new_cluster
# 或:mysqld --wsrep-new-cluster &

确认:

SHOW STATUS LIKE 'wsrep_cluster_size';   -- 1
SHOW STATUS LIKE 'wsrep_cluster_status'; -- Primary

2)第二节点(g2,LA 同城)

systemctl start mariadb
# 日志里应看到 IST 优先(比 SST 快),完成后 cluster_size=2

3)第三节点(g3,香港段)

systemctl start mariadb
# 跨洋大概率走 IST;若 gcache 不足或新装则走 SST(mariabackup)

坑 4:SST 被 SELinux 拦截

当晚我看到 donor 端 avc: denied,SST 卡住。

临时解法:setenforce 0;永久:/etc/selinux/config 改成 SELINUX=permissive 并配合 semanage fcontext 正确标注数据目录。更优雅是在预演期把 SELinux policy 补齐。

五、订单表的“Galera 友好”建模与 DDL

跨洋多主写入最怕两件事:主键冲突与认证失败(certification failed)。我的做法:

  • 订单号用全局唯一 ID(雪花或 UUIDv4),避免自增冲突与热点 PK。
  • 把“业务幂等”前置:关键唯一索引(如 unique(order_no)、unique(payment_txn_id))确保重放/并发回调不会重复入库。
  • 减少跨段写同一行(避免热点行)。库存扣减走 LA 段,HK 只读或异步补偿。

示例(MariaDB 10.5):

CREATE TABLE orders (
  id            BINARY(16)      NOT NULL,         -- UUID_TO_BIN(UUID())
  order_no      VARCHAR(32)     NOT NULL,
  user_id       BIGINT          NOT NULL,
  total_amount  DECIMAL(18,2)   NOT NULL,
  currency      CHAR(3)         NOT NULL,
  status        TINYINT         NOT NULL DEFAULT 0,  -- 0:created,1:paid,2:fulfilled
  created_at    TIMESTAMP       NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at    TIMESTAMP       NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  UNIQUE KEY uk_order_no (order_no),
  KEY idx_user_ct (user_id, created_at)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;

-- 回调表幂等键
CREATE TABLE payments (
  id            BINARY(16)      NOT NULL,
  order_id      BINARY(16)      NOT NULL,
  provider      VARCHAR(16)     NOT NULL,
  txn_id        VARCHAR(64)     NOT NULL,
  amount        DECIMAL(18,2)   NOT NULL,
  status        TINYINT         NOT NULL,         -- 0:pending,1:success,2:failed
  paid_at       DATETIME        NULL,
  created_at    TIMESTAMP       NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  UNIQUE KEY uk_provider_txn (provider, txn_id),
  KEY idx_order (order_id),
  CONSTRAINT fk_pay_order FOREIGN KEY (order_id) REFERENCES orders(id)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;

小技巧:在应用层使用 UUID_TO_BIN(UUID(), 1)(time-ordered)减少主键随机写带来的 B+ 树分裂。

六、应用访问与写路由策略

写策略:LA 段优先(g1/g2),HK 节点仅在 LA 段不可用时短时承接写入(开关由 HAProxy/ProxySQL 控制)。

读策略:同城优先读、跨段只读低优先级业务(客服、BI 快照)。

HAProxy(示例):

backend mariadb_writers
  balance roundrobin
  server g1 10.10.0.11:3306 check
  server g2 10.10.0.12:3306 check
  # g3 仅在 LA 坏掉时接管
  server g3 10.20.0.31:3306 check backup

backend mariadb_readers
  balance leastconn
  server g1 10.10.0.11:3306 check
  server g2 10.10.0.12:3306 check
  server g3 10.20.0.31:3306 check

应用侧重试:捕获 ER_LOCK_DEADLOCK (1213) / 事务冲突错误,指数退避重试(最多 3 次),幂等写入配合唯一键即可。

七、跨洋延迟下的 Galera 调优

参数 我线上值 作用 & 注释
gcache.size 4G 足够覆盖高峰 20–40 分钟的写增量,优先保证 IST,降低 SST 频率
evs.keepalive_period 1s 心跳频率
evs.inactive_timeout 10s 节点失联判定,跨洋别设太激进
gcs.fc_limit 256 流控阈值(队列过长触发 FLOW CONTROL)
wsrep_slave_threads 8 并行 Apply 线程;根据 CPU/IO 调整
innodb_flush_log_at_trx_commit 1 严格持久化;吞吐不够时可评估 2(权衡一致性)
innodb_log_file_size 2G 减少 checkpoint 频率,稳态更平滑

观测与阈值:

SHOW STATUS LIKE 'wsrep_flow_control_paused%';  -- < 0.1 理想
SHOW STATUS LIKE 'wsrep_cert_deps_distance';    -- 越高并行度越好,> 5 稳定
SHOW GLOBAL STATUS LIKE 'wsrep_local_recv_queue_avg';  -- < 4
SHOW STATUS LIKE 'wsrep_evs_repl_latency';      -- P50/P99 关注

当晚高峰时,我看到 wsrep_flow_control_paused 曾冲到 0.22,LA -> HK 认证堆积。临时措施:提高 gcs.fc_limit 到 384,同时把支付写流量明确只打 LA 段,指标马上回落到 0.04。

八、SST/IST、备份与恢复(演练过才是真安全)

1)优先 IST,减少 SST

  • 放大 gcache 并保证磁盘快速(NVMe)。
  • 节点短暂重启或网络抖动时优先走 IST,不打断大盘。

2)SST(mariabackup)过程观察

  • donor 侧 IOPS 飙升,避开业务高峰。
  • 压力大时把 nice/ionice 拉高,别拖垮线上延迟。

3)热备策略

每日全量 + 每小时增量:

# 全量
mariabackup --backup --target-dir=/backup/full-$(date +%F)
# 增量
mariabackup --backup --target-dir=/backup/inc-$(date +%F-%H) \
  --incremental-basedir=/backup/full-$(date +%F)
# 校验/打包
mariabackup --prepare --target-dir=...

恢复(演练摘要):

systemctl stop mariadb
rm -rf /var/lib/mysql/*
mariabackup --prepare --target-dir=/backup/full-2025-09-10
mariabackup --copy-back --target-dir=/backup/full-2025-09-10
chown -R mysql:mysql /var/lib/mysql
systemctl start mariadb

九、监控面板与告警(我在线上盯的就是这些)

关键指标(Prometheus/Metrics + Grafana):

  • wsrep:wsrep_cluster_size、wsrep_ready、wsrep_flow_control_paused、wsrep_cert_deps_distance、wsrep_local_state_comment、wsrep_evs_repl_latency
  • InnoDB:Buffer Pool Hit%、Redo Write/s、Checkpoints、Row Lock Time
  • SST/IST:SST 事件计数、耗时
  • 网络:LA↔HK RTT(smokeping)、丢包率

告警规则(示例):

  • wsrep_flow_control_paused > 0.2 持续 5m
  • wsrep_cluster_size < 3 持续 1m
  • wsrep_evs_repl_latency_p99 > 300ms 持续 10m

十、零停机维护与升级流程(实操顺序)

  • 逐台 SET GLOBAL wsrep_on=OFF;(或从负载摘除),清空连接。
  • 节点停止后升级小版本(同大版本),启动观察无误再进下一台。
  • 如需跨大版本,先排定只读窗口或拉一台新版本节点进行 SST/数据迁移,完成后逐台替换。

坑 5:混装 galera-3 与 galera-4

我曾在 g3 上装错了版,启动直接 wsrep provider not found。跨版本不兼容,保持 Galera provider 同版。

十一、真实坑位与当场修复

回源写放大:应用层误把缓存失效导致的读热,变相触发跨段一致性验证压力。
修复:支付和下单路径的读全部打 LA 段本地,HK 只读客服查询;缓存键 TTL 调整 + 本地化。

回调幂等缺失:第三方支付重放导致 payments 重复写。
修复:UNIQUE (provider, txn_id) + 应用重试只做 INSERT ... ON DUPLICATE KEY UPDATE。

SST 卡顿业务:donor 端 I/O 冲击明显。
修复:SST 仅在低谷时段执行;ionice -c2 -n7、nice -n 10;并增大 gcache、优先 IST。

HK 机房偶发丢包:evs 报 suspect。
修复:网络组排查后改了跨境专线 QoS;我这边把 evs.inactive_timeout 拉到 10s,减少误判。

十二、验证与验收:我们如何证明“实时”

指标 改造前(单主+半同步) 改造后(Galera 多主, LA 写为主)
高峰下单 TPS ~2.8k 3.5k
LA→HK 订单可见中位延迟 450–800ms 180–300ms
wsrep_flow_control_paused 0.15–0.28 0.03–0.08
峰值 SST 次数/周 3 0–1(主要 IST)

这个延迟不是“物理实时”,但对运营与客服已经达到“刷新即见”。CFO 早会的跨区报表也稳定了。

十三、运维口袋卡(复制就能用)

启动/停止

# 初始化集群
galera_new_cluster

# 常规节点
systemctl start|stop mariadb

状态检查

SHOW STATUS LIKE 'wsrep_cluster_size';
SHOW STATUS LIKE 'wsrep_cluster_status';
SHOW STATUS LIKE 'wsrep_local_state_comment';
SHOW STATUS LIKE 'wsrep_flow_control_paused';

临时只读(摘流)

SET GLOBAL wsrep_on=OFF;
FLUSH TABLES WITH READ LOCK; -- 如需快照/备份

安全重建节点(走 SST)

systemctl stop mariadb
rm -rf /var/lib/mysql/*
systemctl start mariadb
# donor 自动选择,注意业务低谷进行

十四、给后来者的建议(精华版)

  • 写就近:把强一致写集中在延迟最低的段,跨洋节点尽量做只读或低频写。
  • gcache 要大:能兜住你“最长一次链路抖动时段”的写入量。
  • 幂等是王道:唯一键 + 应用层重试,化解认证冲突与重放。
  • 监控先行:wsrep_flow_control_paused、evs_repl_latency 是两个最能救命的指标。
  • SST 提前演练:不演不行,真遇到才不会慌。
  • 分段(Segment)必配:跨海链路下显著降噪。

当 LA 的天边泛起一丝亮,wsrep_cluster_size=3、flow_control_paused=0.04,HK 的客服页刷新就是刚下的订单。CFO 8 点进群一个 “👍”。
我关掉了机房里嗡嗡作响的风扇声,掏出口袋里的速溶咖啡,笑了笑:这一次,我们把“跨境实时”落到了地上。