1. 从单机LNMP起步,为什么要拆成分离架构
一套LNMP塞一台服务器,跑两个PHP站点,很多小团队初期都是这么过来的。我这次要梳理的项目,内部归档代号就叫789,初始形态就是一台4核8G的Linux主机,LNMP环境里挂着blog和zh两个站点:blog是对外发布的博客系统,zh是内部用的知识库。这套单机架构跑了大概小半年,站点的访问量上来之后,MySQL的慢查询经常把整台机器CPU打满,blog一卡,zh跟着连坐,最后不得不集中做了一轮架构改造,把单机LNMP拆成入口、应用、数据库三层分离的完整形态。
这篇内容我会把整个过程完整写下来,包括单机LNMP怎么搭、两个站点在一台机器上怎么安全共存、MySQL怎么迁出去、PHP应用层怎么独立出去、入口Nginx怎么配代理,以及分离过程里我踩过的各种坑。对正在做网站架构从零到一、准备从单机往多机走的运维或全栈开发者来说,这份记录可以直接拿来当参照方案用。
1.1 单机形态的业务背景
先说清楚为什么最初选择单机。当时团队预算有限,访问量也没上来,一台4核8G的Linux服务器托管全部业务是最务实的选择。Nginx负责对外接收请求,PHP-FPM跑应用代码,MySQL存数据,三样东西全挤在同一台机器上。这种部署方式没什么丢人的,成本最低、交付最快,一个运维半天就能把整套环境搞定,初期性能完全够用。
789这个编号没有任何玄机,就是项目归档代号,可以理解成第七版规划、第八次部署、第九遍梳理出来的一个方案版本。重要的不是数字怎么来的,而是这条演进路线:单台构建LNMP(blog、zh)——再到LNMP分离——再到网站架构完整化。如果你也在运维类似规模的小站,大概率会走上同一条路。
1.2 单机部署下那些迟早会踩的雷
单机形态短则撑几个月,长则撑一两年。但只要业务真实增长,问题就开始成片地冒出来。我这边最先碰到的是资源争抢。MySQL的InnoDB缓冲池和PHP-FPM进程池本质是在抢同一块内存,PHP请求一多,数据库缓存命中率就开始下降;MySQL跑几个慢查询,CPU立刻被拉高,所有页面的响应时间从几百毫秒直接跳到几秒。
这种形态最麻烦的地方在于:不是"某台机器性能不够",而是所有故障都会被放大。我举一个实际场景:博客偶尔会来一波瞬时热点流量,比如某篇文章被推荐,访问量可能突然翻几倍;而知识库是白天工作时间稳定并发。两个站点在同一个机器上,热点流量一冲进来,知识库的响应也跟着遭殃,相互伤害。单机阶段没有隔离,任何异常都会被"全站共享"。
1.3 分离改造的目标和节奏控制
所以分离不是为了炫技,纯粹是给故障"隔开房间"。我当时定的改造目标有三条:
- 数据库独立出去,独占内存和磁盘I/O,慢查询再狠也只压垮数据库层,至少PHP还有机会吐一些页面;
- PHP应用层独立出去,代码执行环境能够单独重启、扩容、随时加机器;
- 入口Nginx做统一流量统一入口,负责静态资源、TLS终结、反向代理,后面挂几台应用服务器都行。
改造节奏我刻意做成了两步而不是一步到位:先把MySQL迁出去,观察一个完整周期;再把PHP应用层迁出去。每一步都保留回滚方案,确认没问题再走下一步。这篇博文后面就是按这个节奏记录的。
2. 单机LNMP的构建与双站共存配置
2.1 组件选型与基础环境
单机阶段的版本组合:操作系统Ubuntu 22.04 LTS,Nginx 1.24,MySQL 8.0,PHP 8.1(php-fpm)。这套组合放在今天依然是主流,社区资料多,遇到问题随便一搜就有答案。如果你用的是CentOS系列,核心思路完全一样,只是包管理命令换成yum或dnf,配置文件路径略有差别。我个人建议新项目不要再用PHP 5.x或者MySQL 5.6这种老版本,安全更新和生态兼容性都已经跟不上了。
安装方面我直接用软件源里的稳定包,没有走源码编译。编译安装虽然可以定制参数,但对这种业务型项目来说性价比太低,后期升级也麻烦。这里要特别提醒一个细节:在Ubuntu上装PHP的时候,除了php8.1-fpm本体,还要装php8.1-mysql、php8.1-gd、php8.1-curl、php8.1-mbstring、php8.1-xml这些常见扩展。少了其中任何一个,站点后台都有可能出现奇怪的功能缺失,比如图片验证码不显示、curl请求失败、字符截断乱码,排查起来很费时间。
2.2 一台Nginx挂两个站点:vhost配置的四个关键点
站点规划阶段,我定了下面这套对应关系:
- blog.example.com → /var/www/blog
- zh.example.com → /var/www/zh
两个站点共用一台Nginx,靠vhost区分,核心机制就是server_name。每个站点一个server块,放在/etc/nginx/sites-available/目录下,再软链到sites-enabled/。这里需要重点注意四点:
第一,代码目录权限。两个站点的代码目录和运行用户要分开。比如blog用www-blog用户,zh用www-zh用户,PHP-FPM的两个进程池各自以对应用户运行。这样做的好处是,万一某个站点被上传了恶意文件,攻击者也只可能操作对应目录,影响面被限制在单站内部。
第二,root目录和index的配置要准确。blog的入口配置大致是这样:
server { listen 80; server_name blog.example.com; root /var/www/blog; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm-blog.sock; } location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico)$ { expires 30d; access_log off; } }zh的server块同理,换掉server_name、root和fastcgi_pass指向zh自己的socket文件就行。第三,两个站点要用不同的PHP-FPM socket,这意味着后面会配置两个独立的进程池,站与站之间的资源不会混在一起。第四,docroot下面不要把数据库配置、日志文件放在web可访问的目录里,这个我在后面权限章节会再强调。
2.3 PHP-FPM多进程池与资源隔离配置
php-fpm默认只有一个叫www的进程池,所有站点共用。低流量没问题,但两个站放一起立刻就有隐患。我在单机阶段就直接建了两个池:/etc/php/8.1/fpm/pool.d/blog.conf和zh.conf。
[blog] user = www-blog group = www-blog listen = /run/php/php8.1-fpm-blog.sock listen.owner = www-data listen.group = www-data listen.mode = 0660 pm = dynamic pm.max_children = 12 pm.start_servers = 3 pm.min_spare_servers = 2 pm.max_spare_servers = 5 pm.max_requests = 500这个配置里有几个参数得仔细解释。pm.max_children=12表示这个池最多同时跑12个PHP子进程;每个PHP-FPM进程大概占40到60MB内存,12个下来就是600MB上下。zh池我再给8个,两个池加起来约20个进程,峰值约1GB。当时机器8G内存,MySQL的innodb_buffer_pool_size给了2G,系统本身再占一些,高峰期还能稳得住。
pm.max_requests=500的意思是每个子进程处理完500个请求后自动回收再重建,这能有效避免代码里的内存泄漏长期累积。这个参数生产环境必加,否则你会发现PHP-FPM的那台机器运行一周之后,内存占用居高不下,各种重启都没用。
2.4 MySQL单实例多库多账号的权限模型
数据库这层,同一台MySQL上给两个站点分开建库、分开建账号,是最基本的隔离手段。字符集我统一用了utf8mb4,配合utf8mb4_unicode_ci排序规则,防止评论里的emoji、生僻字在写入时丢字变成问号。
CREATE DATABASE blog_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE zh_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'blog_user'@'localhost' IDENTIFIED BY 'BlogFakePass_2024'; CREATE USER 'zh_user'@'localhost' IDENTIFIED BY 'ZhFakePass_2024'; GRANT ALL PRIVILEGES ON blog_db.* TO 'blog_user'@'localhost'; GRANT ALL PRIVILEGES ON zh_db.* TO 'zh_user'@'localhost'; FLUSH PRIVILEGES;两个账号各管各的库,互相之间没有越权访问的可能。这个模型在后面把数据库迁到独立服务器时继续沿用,只是把@'localhost'改成应用服务器的内网IP。
2.5 单机阶段的压测与瓶颈定位
环境搭好之后,不能直接上线,得先压一遍看看瓶颈。我用ab做了个简单测试:ab -n 1000 -c 50 http://blog.example.com/。单机阶段的指标大致是:每秒请求数在100到150之间,平均响应时间500到900毫秒,高峰期变化更夸张。压测的同时开着top和mysql的processlist,能很清楚地看到瓶颈在互相转移:先是PHP-FPM的CPU占用拉高,然后是MySQL的CPU追上来,4核8G的机器只要整体CPU超过80%,体验立刻变差。
这一步的意义不是记录数字,而是要建立一个认知:单机LNMP能扛的流量有上限,而且上限不是某一个组件单独决定的,而是所有组件共享资源后互相挤压出来的综合瓶颈。搞清楚瓶颈在哪个环节,后面做分离时才知道先拆谁。
3. 第一步分离:MySQL独立成库
3.1 数据库迁移方案怎么选
数据库迁移,我当时只对比了两个方案:逻辑备份恢复,或者是主从复制后切换。如果你的数据量在几个GB以内,用mysqldump逻辑备份恢复是效率最高的路径,不需要处理复制关系。如果数据量上了几十GB,或者业务对停机时间容忍度极低,那就得考虑先做主从追平,再切换。789这个项目两个库加起来不到10GB,低峰期做导出导入,整个窗口也就十几二十分钟,直接走逻辑备份最稳。
但我要强调一件事:任何数据库迁移,第一步永远是验证备份能不能正常恢复。我见过有人直接在生产库上导出再导入,结果导入时反复报错才发现备份本身就有问题。所以新库导入完成后,先跑几个关键查询,再让应用连上去读一遍,确认无误再考虑切换的事。
3.2 数据导出导入与远程账号授权
老库导出我用的是这条命令:
mysqldump -uroot -p --single-transaction --quick --routines --triggers \ --databases blog_db zh_db > /backup/full_$(date +%F).sql--single-transaction对InnoDB表非常关键,它让导出过程基于一个一致性快照,不需要锁整库;--routines和--triggers会把存储过程、触发器一起导出,这些经常被漏掉,漏了之后线上就出诡异问题。文件导出来之后用rsync传到新库服务器,再执行导入:
mysql -uroot -p < /backup/full_2024-xx-xx.sql导入完成后,重建应用账号。此时账号的host不能再是localhost,而是允许应用服务器内网IP访问:
CREATE USER 'blog_user'@'192.168.1.20' IDENTIFIED BY 'BlogFakePass_2024'; GRANT ALL PRIVILEGES ON blog_db.* TO 'blog_user'@'192.168.1.20'; CREATE USER 'zh_user'@'192.168.1.20' IDENTIFIED BY 'ZhFakePass_2024'; GRANT ALL PRIVILEGES ON zh_db.* TO 'zh_user'@'192.168.1.20'; FLUSH PRIVILEGES;别图省事直接用'blog_user'@'%',那等于把数据库端口暴露给整个内网所有机器。精确到应用服务器IP,以后应用扩容再加授权,成本很低,但安全收益大得多。
3.3 应用连接串切换与回滚预案
这是最容易出问题的环节。PHP代码里连数据库的配置,需要把DB_HOST从127.0.0.1改成新库的内网IP,例如192.168.1.30,DB_USER和DB_PASSWORD保持不动。以WordPress为例就是wp-config.php,自研框架一般是config/database.php。
改完之后先做语法检查,再重启php-fpm:
php-fpm8.1 -t systemctl restart php8.1-fpm重启后访问站点,重点验证登录、列表页、详情页、评论提交这些依赖数据库的操作。如果一切正常,继续保持观察一个低峰期到高峰期的完整周期。如果发现问题,回滚也很快:把DB_HOST改回127.0.0.1,重启php-fpm,流量就回到本机MySQL。
这里必须提一个高频坑:MySQL 8.0的默认认证插件是caching_sha2_password,某些老版本PHP驱动连不上,报错类似"Authentication plugin 'caching_sha2_password' cannot be loaded"。遇到这个问题,优先升级PHP的MySQL驱动到新版本;如果代码实在改不了,才在MySQL里给账号创建为mysql_native_password。从长计议,还是得升驱动,native_password只是过渡兼容方案。
3.4 数据库独立后的参数调优
数据库独立成一台机器之后,资源配置能放开了。假设新库是8核16G,innodb_buffer_pool_size可以直接设到8G左右,也就是物理内存的50%到60%,让常用数据尽量都进出内存。binlog建议开启,设置expire_logs_days足够保留天数,给以后做从库、做恢复留后路。慢查询日志也必须打开:slow_query_log = ON,long_query_time = 2,方便后续定位拖后腿的SQL。
数据库落到独立服务器之后,最明显的效果是:应用层PHP-FPM不会因为数据库I/O抖动而一起遭殃,响应时间变得稳定了。之前那种"一个慢查询全站卡死"的现象基本绝迹。
4. 第二步分离:PHP应用层独立
4.1 应用服务器部署与代码同步
数据库迁走之后,原服务器的压力减轻了不少,但入口Nginx、PHP-FPM、代码文件还在同一台机器上,热点流量一来,PHP进程照样会把机器拖垮。第二步就是把PHP应用层彻底拆出去。
应用服务器我分配的是192.168.1.20,同样安装Nginx和PHP8.1。代码从原机器整体同步过去:
rsync -avz /var/www/blog root@192.168.1.20:/var/www/ rsync -avz /var/www/zh root@192.168.1.20:/var/www/注意rsync的-a参数里包含了权限、属主、时间戳。站点目录的属主如果不对,PHP-FPM就无法写入logs、uploads这类目录。同步完成之后,在应用服务器上最好再chown确认一遍,比如chown -R www-blog:www-blog /var/www/blog。这一步省了,后面全是上传权限报错。
4.2 入口Nginx代理配置与动态静态分流
现在的架构变成了两层Nginx:入口Nginx只做流量分发和静态资源,真正执行PHP的Nginx在应用服务器上。这时入口Nginx的配置得换成代理模式:
upstream backend_blog { server 192.168.1.20:8080; keepalive 32; } server { listen 80; server_name blog.example.com; root /var/www/blog; location / { proxy_pass http://backend_blog; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?)$ { root /var/www/blog; expires 30d; access_log off; } }应用服务器的Nginx监听8080端口,避免和入口的80冲突,然后fastcgi_pass指向本机php-fpm的socket,基本配置和单机阶段一样,只是listen改成8080:
server { listen 8080; server_name blog.example.com; root /var/www/blog; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm-blog.sock; } }为什么要两层Nginx而不是让入口Nginx直接fastcgi_pass到应用服务器的9000端口?因为后端这台Nginx还承担了PHP代码层面的rewrite、重定向、跳转逻辑。如果入口层直接走fastcgi,业务层的URL规则会跟入口层耦合;以后一旦需要多台应用服务器、多个PHP版本,后端Nginx这层扩展性明显更好。
提示:入口层如果直接伺服静态资源,需要确保入口服务器上的/var/www/blog目录和代码目录保持同步。如果不想维护多份静态文件,也可以让静态请求也走proxy_pass转发到后端,代价是多一点内网开销。我这边是同步了静态资源目录,换来的是静态小文件不再穿透到后端,高峰期省不少资源。
4.3 PHP-FPM池配置与超时参数
应用服务器上的PHP-FPM,依然保持两个池分别跑blog和zh。这次应用服务器是8核16G,我调整了max_children到20左右,但每个池仍然保留pm.max_requests=500。这里要注意,PHP-FPM的fastcgi_read_timeout默认是60秒,如果站点内有导出、定时任务触发同步、批量处理这类耗时操作,默认值往往不够。实际过程中我就遇到过后台批量处理需要三四分钟的情况,不调超时直接504。调整方式:
location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm-blog.sock; fastcgi_read_timeout 300s; }4.4 session共享与上传文件同步
应用层一拆出去,最典型的两个问题立刻浮现:session不一致,上传文件不同步。
session的问题是这样的:用户登录后session随机落在一台应用服务器上,下一次请求被入口转发到另一台,登录态就丢了。我建议直接引入独立的会话存储,最省事的是用redis。安装php8.1-redis扩展后,php.ini里配置:
session.save_handler = redis session.save_path = "tcp://192.168.1.40:6379"所有应用服务器连同一个redis,用户登录状态不会因为被分发到不同机器而掉线。如果暂时不想为session单独加redis,用MySQL存session也能用,但多一个内存级别的存储点不算重,后期早晚用得上。
上传文件不同步的问题:我测试负载均衡时发现,图片传到blog后台之后,刷新页面有时能看到,有时404。排查完才知道是两台应用服务器的uploads目录没有共享,第一批请求落在192.168.1.20,图片上传到了/var/www/blog/uploads;第二批请求被分到192.168.1.21,那边没有这个文件,自然404。
所以只要应用层超过一台机器,上传文件就必须放到共享存储或对象存储。临时方案用rsync做双机同步能顶一阵子,但同步窗口内的数据不一致很难排查。我最终先用NFS把uploads目录挂到两台服务器同一路径:
192.168.1.50:/data/uploads /var/www/blog/uploads nfs defaults,_netdev,nofail 0 0nofail参数很重要,万一NFS服务器暂时不可用,系统启动不会卡在挂载上。NFS导出的目录属主和PHP-FPM运行用户保持一致,否则写文件权限会报错。
4.5 应用层多实例的扩容准备
分离MySQL、分离PHP之后,还有一个容易被忽略的坑:入口Nginx的upstream里如果只写死一台应用服务器,这台机器出故障时站点就彻底不可用了。所以应用层拆出去之后,我建议立刻考虑第二台应用节点。最简单的方式是在upstream里加第二个server:
upstream backend_blog { server 192.168.1.20:8080 weight=1; server 192.168.1.21:8080 weight=1; keepalive 32; }Nginx默认会对不可用的后端做健康探测,某个节点连续失败会自动把它摘除一段时间,给运维留出处理时间。这算是成本最低的基础负载均衡了。
5. 分离完成后的整体架构梳理与效果对比
5.1 最终三层拓扑长什么样
到这里,整个架构变成了标准的三层形态:
- 入口层:192.168.1.10,Nginx。职责是静态资源服务、反向代理、负载均衡、TLS终结。对用户来说,所有请求域名解析到这一台。
- 应用层:192.168.1.20,未来再加192.168.1.21,Nginx配PHP-FPM。跑blog和zh的代码,每个站点独立进程池,PHP-FPM连接独立的数据库服务器和可选的redis会话服务器。
- 数据层:192.168.1.30,MySQL。只做数据存取,对外只开3306给应用服务器IP,备份、慢查询、binlog统一管理。
这个架构的本质说穿了就是四个字:故障分区。任何一层出了问题,其他层至少能保证基础服务。数据库坏了,PHP还能勉强返回一些缓存页面;应用层坏了,入口还能吐出静态资源和友好错误页。入口层是现阶段唯一的单点,后续要上高可用,就把入口层再做双机。
5.2 同一批请求下改造前后的性能对比
我保留了当时的一些测试记录,不同机器和业务数据会有差异,但趋势可以参考:
| 指标 | 单机阶段 | 分离后 |
|---|---|---|
| blog平均响应时间(并发100) | 860ms | 320ms |
| zh平均响应时间(并发100) | 720ms | 280ms |
| 高峰期整机最高CPU | 95%~100% | 入口层30%,应用层40%,数据库45% |
| 慢查询对业务的影响 | 拖垮全站 | 只影响数据库层,PHP仍能返回页面 |
| 应用代码重启 | 要避开高峰期 | 随时可重启,入口层自动摘除 |
单机阶段的问题是所有组件共享一份资源,瓶颈互相传染;分离后的机器各司其职,单点故障影响被隔离。最直观的感受就是,数据库某次慢查询暴增时,站点部分功能变慢,但静态资源和页面框架还能加载,不再是一锅端。
5.3 运维监控的补齐
架构拆开之后,运维复杂度增加是必然的,监控必须跟上。我建议至少盯四类指标:
- 入口层:HTTP状态码分布、5xx数量、4xx数量、接口响应时间;
- 应用层:PHP-FPM当前进程数、空闲进程数、内存占用、是否频繁打满max_children上限;
- 数据层:MySQL连接数、慢查询数量、QPS、buffer pool命中率;
- 系统层:CPU、内存、磁盘I/O、网络带宽,以及所有机器的NTP时间偏差。
工具方面,有Prometheus+Grafana体系的团队直接上node_exporter、mysqld_exporter、nginx_exporter。还没有监控平台的小团队,先用几个crontab脚本把关键日志采样记录下来也行。关键是先有数据,再谈优化。没有监控数据就盲目调参,等于蒙着眼开车。
6. 改造过程中的常见问题排查实录
6.1 远程数据库连不上的排查路径
切换数据库那晚,我遇到的第一个现象是blog站点统一报错:Can't connect to MySQL server on '192.168.1.30'。这个问题很典型,排查路径基本固定。
第一步,应用服务器上先用命令行验证连接:mysql -h192.168.1.30 -ublog_user -p。如果是timeout,网络层不通,检查防火墙和安全组是否放行3306。第二步,如果是access denied,就是授权host不对,回到MySQL改用户表。第三步,命令行能连上但PHP连不上,重点检查配置文件的DB_HOST有没有写对、php-fpm是否重启、PHP的MySQL扩展是否安装。还有一个非常隐蔽的点:MySQL的bind-address默认是127.0.0.1,只监听回环地址,防火墙放行也没用。解决办法是在my.cnf里设置bind-address = 0.0.0.0,或者绑内网网卡IP,然后重启MySQL。
6.2 502与504频繁出现:问题定位和参数调优
切换应用层之后,最常碰到的两个状态码是502和504,含义完全不同。
502 Bad Gateway表示入口Nginx连不上后端。排查顺序是:先看入口Nginx错误日志,路径通常在/var/log/nginx/error.log,常见错误是connect() failed (111: Connection refused)或者no route to host。前者说明后端的Nginx没有监听8080端口,检查应用服务器的Nginx启动状态;后者说明网络不通,检查防火墙。还有一个容易被忽略的点:应用服务器的Nginx如果只监听了127.0.0.1:8080,入口Nginx从内网访问也会被拒。一定要监听0.0.0.0:8080或者内网网卡地址。
504 Gateway Timeout表示入口Nginx在设定时间内没收到后端响应,通常是超时参数不够。入口Nginx的proxy_read_timeout默认60秒,如果页面里有超过60秒执行的逻辑,必出504。解决办法是按业务量调大,我这边统一调到300秒。
6.3 上传文件不同步与权限坑
这是多应用节点最典型的案例。当时测试负载均衡,图片后台传上去之后,刷新页面有时能看到,有时404。原因前面说过,uploads目录没共享。这里再补充一个权限细节:用NFS共享之后,目录属主和PHP-FPM运行用户必须一致。我那天下午就是在执行chown -R www-blog:www-blog /data/uploads之后才恢复正常。如果是rsync双机同步,同样要注意--owner和--group参数,否则同步过去的文件权限不对,应用也写不进去。
6.4 时间同步与日志错位的排查
还有一个非常基础、但特别容易被忽略的检查:所有服务器的系统时间必须同步。我遇到过入口Nginx日志和应用服务器日志相差几十秒的情况,排查故障时事件顺序对不上,白白浪费两个小时。解决办法简单粗暴:所有机器装chrony,指向同一组NTP时间源,定时同步。这件事虽然小,但对日志分析、故障定位、数据追溯都极其重要,千万别省。
6.5 按我自己的习惯留几条经验
最后结合这次改造,说几条我自己的习惯:
每拆一层,先想清楚回滚按钮在哪里。配置改完先别删旧方案,至少保留一个版本周期的观察时间。迁移永远先备份、先演练,新环境验证没问题再切流量。分离过程中改配置尽量用脚本管理而不是手动改,后面多节点重复部署时才知道省了多少事。还有就是,架构分离不是终点,入口Nginx现阶段还是单点,如果对可用性要求更高,下一步是把入口层做成双机高可用,再给数据库加一台从库,整条链路才算真正闭环。