每个主题:架构图 → 配置实战 → 面试速记
① MySQL 主从复制 — 一主多从的数据同步
为什么需要?生产环境单台MySQL扛不住高峰期QPS(读写都打到一台机器),而且一旦宕机数据就全丢了。主从复制解决两个问题:读性能扩展(读请求分散到从库)和数据冗余备份(从库是主库的实时副本)。
🏗️ 架构图:MySQL 主从复制
MySQL 主从复制架构
应用服务
▼ 写操作(INSERT/UPDATE/DELETE)
Master 主库 (10.0.0.1)
binlog(二进制日志)记录所有变更
order_info: INSERT id=101
product: UPDATE price=18
▼ binlog dump(推送binlog)
Slave 从库1 (10.0.0.2)
IO线程 → 接收binlog → 写relay log
SQL线程 → 执行relay log → 数据同步
Slave 从库2 (10.0.0.3)
IO线程 → 接收binlog → 写relay log
SQL线程 → 执行relay log → 数据同步
▲ 读操作(SELECT)可分散到从库
报表/查询服务
核心机制:Master 把所有数据变更写入 binlog(二进制日志文件),Slave 有两个线程协作:IO线程负责从Master拉取binlog写入本地的 relay log(中继日志),SQL线程负责读取relay log并执行SQL来同步数据。两者是异步的,所以主从之间存在延迟。
📋 主从复制的工作流程(3个线程)
Master
→
binlog dump 线程
→
Slave IO线程
→
relay log
→
Slave SQL线程
→
从库数据
第一步:Master上的操作写入 binlog
第二步:Slave 的 IO线程 连接 Master,请求 binlog
第三步:Master 的 binlog dump线程 读取 binlog 发给 Slave
第四步:Slave 的 IO线程 把收到的 binlog 写入 relay log(中继日志)
第五步:Slave 的 SQL线程 读取 relay log 并执行 → 数据同步完成
💻 具体配置
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
expire-logs-days = 7
gtid-mode = ON
enforce-gtid-consistency = ON
mysql> CREATE USER 'repl'@'10.0.0.%' IDENTIFIED BY 'Repl@123';
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.0.%';
mysql> FLUSH PRIVILEGES;
[mysqld]
server-id = 2
relay-log = relay-bin
read-only = ON
gtid-mode = ON
enforce-gtid-consistency = ON
mysql> CHANGE MASTER TO
MASTER_HOST='10.0.0.1',
MASTER_USER='repl',
MASTER_PASSWORD='Repl@123',
MASTER_AUTO_POSITION=1;
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS\G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_IO_Error:
📊 binlog 三种格式对比
| 格式 | 记录内容 | 安全性 | 适用场景 |
| STATEMENT | 记录SQL语句原文 | 低(某些函数如NOW()、UUID()在从库结果不同) | 日志体积小,适合简单SQL |
| ROW | 记录每行的变更前后值 | 最高(精确到行级别) | 生产环境推荐,日志较大 |
| MIXED | 默认STATEMENT,涉及不安全函数时自动切换ROW | 中 | 折中方案 |
GTID 是什么?
GTID = Global Transaction ID(全局事务ID),格式:server_uuid:transaction_id,比如 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5
没有GTID时,从库崩溃重启后需要手动指定"从binlog的哪个文件、哪个位置继续复制"。有了GTID,每个事务有全局唯一编号,从库自动从最后一个已执行的事务之后继续,无需人工干预。
⚠️ 主从延迟怎么处理?
为什么会延迟?主从复制本质是异步的:Master 写完就返回了,不管从库有没有跟上。以下情况会加大延迟:
- 主库并发写入量大,从库单线程回放来不及(MySQL 5.7之前)
- 大事务(一个事务删了100万条数据,从库要逐条回放)
- 从库机器性能差(磁盘慢/CPU弱)
- 网络抖动导致binlog传输慢
生产环境解决方案:
- 多线程复制(MySQL 5.7+ slave_parallel_workers,按库并行回放relay log)
- 读写分离时强制走主库:对一致性要求高的查询(如"下单后立刻查订单详情")直接查主库,不查从库
- 半同步复制:至少一个从库确认收到binlog后才返回成功(比异步安全,比全同步快)
- 业务层延迟双读:写完主库后sleep一小段时间(如200ms)再读从库,给从库追平的时间
📊 异步 vs 半同步 vs 全同步
1️⃣ 异步复制(默认)
Master写入→立即返回✅
Slave→后台慢慢同步
快但不安全(Master挂了未同步的数据会丢)
2️⃣ 半同步复制
Master写入→等1个Slave确认→返回✅
折中方案(至少保证一个从库收到binlog)
3️⃣ 全同步复制
Master写入→等所有Slave完成→返回✅
最安全但最慢(实际很少用)
② 读写分离 — 写走主库、读走从库
为什么需要?主从搭好了,但应用代码里所有SQL都打到同一个数据源。读写分离的核心思路:INSERT/UPDATE/DELETE 路由到Master,SELECT 路由到Slave,从而把读压力分散到多台从库上。
🏗️ 架构图:读写分离
应用代码
▼ INSERT/UPDATE/DELETE
读写分离的核心矛盾:写完主库后立刻去从库读,从库可能还没同步完 → 读到旧数据。典型场景:用户下单后刷新订单列表,发现订单没出现。解决方案:对一致性要求高的查询强行走主库。
💻 实现方式对比
| 方式 | 原理 | 优缺点 |
| 代码层面AOP | 自定义注解 + AOP切面,根据方法名(get*/list*走从库,insert*/update*走主库)动态切换数据源 | 灵活但侵入代码,需要维护ThreadLocal |
| ShardingSphere-JDBC代理 | JAR包嵌入应用,自动识别SQL类型路由(读SQL→Slave,写SQL→Master) | 对代码无侵入,配置即生效,新项目推荐 |
| Spring AbstractRoutingDataSource | Spring原生API,动态切换底层数据源 | 常见方案,需配合ThreadLocal传递路由key |
💻 Spring + AOP 代码示例
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 从ThreadLocal获取当前应该用主库还是从库
return DataSourceContextHolder.get(); // "master" 或 "slave"
}
}
// AOP 切面:读方法走从库,写方法走主库
@Aspect
@Component
public class DataSourceAspect {
@Before("execution(* com.example.service.*.select*(..)) || " +
"execution(* com.example.service.*.get*(..)) || " +
"execution(* com.example.service.*.list*(..))")
public void setSlave() {
DataSourceContextHolder.set("slave"); // 读 → 从库
}
@Before("execution(* com.example.service.*.insert*(..)) || " +
"execution(* com.example.service.*.update*(..)) || " +
"execution(* com.example.service.*.delete*(..))")
public void setMaster() {
DataSourceContextHolder.set("master"); // 写 → 主库
}
}
③ MySQL MHA 高可用 — 主库挂了自动切换
为什么需要?凌晨3点主库宕机,如果没有自动切换机制,所有写操作全部失败,业务停摆。MHA(Master High Availability)是一个监控+自动切换方案:主库一挂,它自动把数据最新的从库提升为新主库,整个过程10-30秒。
🏗️ 架构图:MHA 故障切换
😱 故障前:Master正常运行
💥 故障!Master宕机了
✅ MHA自动切换:Slave1提升为新Master
MHA 工作原理:
1. 持续监控:Manager节点每隔几秒ping Master,连续失败则判定故障
2. 数据补齐:从旧Master的binlog中挽救还没同步到从库的数据(如果旧Master磁盘还活着,SSH过去捞binlog)
3. 选举新主:从所有Slave中选一个数据最新的提升为新Master
4. 重建拓扑:其他Slave改为复制新Master
5. VIP漂移:应用连接的VIP自动漂移到新Master,应用无需改配置
# /etc/mha/app1.cnf — MHA 应用配置
[server default]
manager_workdir=/var/log/mha/app1
manager_log=/var/log/mha/app1/manager.log
user=mha_monitor
password=mha_pass
repl_user=repl
repl_password=repl_pass
ssh_user=root
[server1]
hostname=10.0.0.1
port=3306
candidate_master=1 # 可提升为主库
[server2]
hostname=10.0.0.2
port=3306
candidate_master=1
[server3]
hostname=10.0.0.3
port=3306
no_master=1 # 永远不提升为主库(纯从库)
# 启动 MHA Manager
# masterha_manager --conf=/etc/mha/app1.cnf &
# 检查状态
# masterha_check_status --conf=/etc/mha/app1.cnf
生产建议:MHA是MySQL生态最成熟的高可用方案之一,但现在更多团队转向 Orchestrator(GitHub出品,支持自动拓扑发现)或云厂商自带的RDS高可用。不管用什么工具,核心都是:监控 → 选举 → 切换 → 通知。
④ Redis 部署模式演进 — 从单机到集群
为什么需要演进?单机Redis挂了缓存全丢 → 引入主从做备份;主挂了要手动切 → 引入哨兵自动切换;单机内存放不下/扛不住高并发 → 引入Cluster分片水平扩展。
📊 四种部署模式对比
Redis 部署模式演进路线
① 单机模式
→
② 主从模式
→
③ 哨兵模式
→
④ Cluster模式
① 单机模式
一台Redis搞定
适合:开发/测试
❌ 单点故障
② 主从模式
一主多从,读写分离
适合:读多写少
⚠️ 主挂了要手动切
③ 哨兵模式
自动监控+故障切换
适合:要高可用
✅ 主挂了自动切
④ Cluster模式
分片+高可用
适合:大数据量/高并发
✅ 支持水平扩展
| 模式 | 解决了什么问题 | 遗留问题 | 典型场景 |
| 单机 | — | 单点故障,挂了全丢 | 开发/测试环境 |
| 主从 | 数据备份 + 读扩展 | Master挂了需人工干预 | 读多写少的小型项目 |
| 哨兵 | 自动故障转移 | 单Master写性能瓶颈,无法水平扩展 | 需要高可用但数据量不大的场景 |
| Cluster | 数据分片存储 + 水平扩展 + 高可用 | 运维复杂度高,跨槽操作受限 | 大数据量/高并发的生产环境 |
⑤ Redis 哨兵模式(Sentinel)— 自动故障转移
🏗️ 架构图:Redis Sentinel
Redis Sentinel 哨兵架构
应用服务
▼ 先问哨兵:"谁是Master?"
▲ 3个哨兵互相监控,少数服从多数
📋 哨兵的工作流程
1. 监控
→
2. 发现故障
→
3. 投票
→
4. 故障转移
→
5. 通知
第一步:监控 — 每个Sentinel每秒向Master/Slave/其他Sentinel发PING命令
第二步:主观下线(SDOWN) — 如果Master超过 down-after-milliseconds 没回应,单个Sentinel认为它挂了
第三步:客观下线(ODOWN) — 超过半数Sentinel都认为Master挂了,投票确认故障
第四步:故障转移 — 选举一个Slave提升为新Master,其他Slave改为复制新Master
第五步:通知 — 告诉应用新Master的地址
为什么需要多个Sentinel?网络抖动可能导致误判(比如Master只是暂时忙到没响应PING)。多个Sentinel投票可以避免"一个哨兵误判就切换"。生产环境至少部署3个Sentinel(奇数,保证投票有结果)。
💻 配置示例
port 26379
sentinel monitor mymaster 10.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
spring:
redis:
sentinel:
master: mymaster
nodes:
- 10.0.0.1:26379
- 10.0.0.2:26379
- 10.0.0.3:26379
⑥ Redis Cluster(集群模式)— 分片存储海量数据
为什么需要?单机Redis内存有限(一般几十GB),如果缓存数据量达到上百万个key、占用上百GB内存,单台Redis放不下。Redis Cluster 把数据分散到多台机器上存储,同时保持每个分片的高可用。
🏗️ 架构图:Redis Cluster
Redis Cluster 分片架构(16384个槽)
Node A (Master)
槽 0 ~ 5460
+ Slave A'
Node B (Master)
槽 5461 ~ 10922
+ Slave B'
Node C (Master)
槽 10923 ~ 16383
+ Slave C'
写入流程:
① 客户端发 SET product:101 "{...}"
② 计算 key 的hash → slot = CRC16("product:101") % 16384 = 8190
③ 8190 落在 Node B 的范围 → 转发到 Node B
④ Node B 存储这个key
读取流程:
① 客户端发 GET product:101
② 同样计算hash → slot 8190 → 去 Node B 取
📋 分片原理详解
Redis Cluster 把所有key空间分成 16384个槽(slot),每个Master节点负责一部分槽。一个key属于哪个槽由哈希函数决定:
slot = CRC16(key) mod 16384
客户端连接任意节点时,如果key不在该节点的槽范围内,节点会返回 MOVED 重定向,告诉客户端去正确的节点。客户端可以缓存slot→node的映射,减少重定向。
每个Master节点配一个Slave节点做高可用:Master挂了,Slave自动提升。但Slave不提供读服务(默认配置下),主要做数据冗余。
💻 配置和创建集群
port 7001
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 5000
appendonly yes
[root@redis ~]# redis-cli --cluster create \
10.0.0.1:7001 10.0.0.1:7002 \
10.0.0.2:7001 10.0.0.2:7002 \
10.0.0.3:7001 10.0.0.3:7002 \
--cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 10.0.0.2:7002 to 10.0.0.1:7001
Adding replica 10.0.0.3:7002 to 10.0.0.2:7001
Adding replica 10.0.0.1:7002 to 10.0.0.3:7001
[OK] All 16384 slots covered.
📊 Redis Cluster vs 哨兵 对比
| 对比项 | Sentinel 哨兵 | Cluster 集群 |
| 数据量 | 单机数据量(受内存限制) | 分布式存储(多机容量叠加) |
| 扩展性 | 不能水平扩展 | 可以加节点扩展 |
| 高可用 | ✅ 自动故障转移 | ✅ 自动故障转移 |
| 复杂度 | 中等(需要额外Sentinel进程) | 较高(需要管理槽分配) |
| 适用场景 | 数据量不大但要高可用 | 数据量大/高并发/需要扩展 |
⑦ 分库分表 — 一张表放不下了怎么办?
为什么需要?订单表已经有5000万条数据,查询越来越慢。加索引也不行了,因为单表数据量太大,B+树变4层,每次查询多一次磁盘IO。
为什么5000万就不行了?MySQL默认使用InnoDB引擎,数据组织为B+树索引。3层B+树大约存2000万行(这是为什么"2000万"是个经验阈值)。超过后B+树从3层变4层,叶子节点更多,查询路径变长,性能急剧下降。
🏗️ 分库分表策略
分库分表的4种方式
1️⃣ 垂直分库
按业务拆分
库1:order_info, order_detail
库2:product, category
库3:user, address
不同业务模块放不同数据库
2️⃣ 垂直分表
按字段拆分
order_info:id, user_id, amount
order_ext:id, remark, extra
把不常用的大字段拆到扩展表
3️⃣ 水平分库
按规则拆到不同库
库1:order (user_id % 2 = 0)
库2:order (user_id % 2 = 1)
同一张表的数据分散到多个库
4️⃣ 水平分表
按规则拆成多张表
order_0:id % 3 = 0
order_1:id % 3 = 1
order_2:id % 3 = 2
同一个库里把数据拆成多张表
🏗️ MyCat / ShardingSphere 架构图
分库分表中间件架构
应用代码
→
ShardingSphere-JDBC
→
自动路由
DB1 (ds0)
order_0 ~ order_4
DB2 (ds1)
order_5 ~ order_9
应用只看到一张 order_info 逻辑表,中间件根据分片规则自动路由到对应的物理库和物理表
ShardingSphere-JDBC vs MyCat:
MyCat 是独立部署的中间件(多一层网络转发),应用无需改代码,但多一跳网络开销,且中间件本身是单点
ShardingSphere-JDBC 是一个JAR包,直接嵌入应用中,无额外网络开销,性能更好。新项目推荐。
💻 ShardingSphere-JDBC 配置
# application.yml — ShardingSphere-JDBC 分片配置
spring:
shardingsphere:
datasource:
names: ds0,ds1 # 两个数据源
ds0:
type: com.zaxxer.hikari.HikariDataSource
jdbc-url: jdbc:mysql://10.0.0.1:3306/order_db_0
username: root
password: root
ds1:
type: com.zaxxer.hikari.HikariDataSource
jdbc-url: jdbc:mysql://10.0.0.2:3306/order_db_1
username: root
password: root
rules:
sharding:
tables:
order_info: # 分片表
actual-data-nodes: ds0.order_info_$->{0..4},ds1.order_info_$->{5..9} # 10张表
table-strategy:
standard:
sharding-column: id # 按id分片
sharding-algorithm-name: order-inline
key-generate-strategy:
column: id
key-generator-name: snowflake # 雪花算法生成ID
sharding-algorithms:
order-inline:
type: INLINE
props:
algorithm-expression: order_info_$->{id % 10} # 取模分片
⚠️ 分库分表后的问题
| 问题 | 描述 | 解决方案 |
| 跨库JOIN | 订单在库A,商品在库B,不能JOIN了 | 应用层组装 / 冗余字段 / 全局表 |
| 分布式事务 | 一个操作涉及多个库,事务不好保证 | XA事务 / Seata / 最终一致性 |
| 跨库排序/分页 | ORDER BY 需要所有库都查出来再合并 | 中间件自动合并 / 限制查询范围 |
| 全局ID | 自增ID在多个库会冲突 | Snowflake雪花算法 / 号段模式 |
| 扩容困难 | 数据已经分好了,要加节点需要迁移 | 一致性哈希 / 提前规划好分片数 |
⑧ Nginx 负载均衡 — 把请求均匀分给多台服务器
为什么需要?一台应用服务器扛不住了(CPU 90%、内存80%)。加服务器后,需要一个组件在前面做流量分发:Nginx 就是这个"流量分配器"。
🏗️ 架构图:Nginx 负载均衡
Nginx 负载均衡架构
用户 / 浏览器
↓
Nginx (10.0.0.100)
根据策略分发请求
应用服务器1
10.0.0.1:8080
order-service.jar
应用服务器2
10.0.0.2:8080
order-service.jar
应用服务器3
10.0.0.3:8080
order-service.jar
共享 Redis Session
↔
共享 MySQL 数据库
📊 负载均衡策略
| 策略 | 分配规则 | 适用场景 |
| 轮询(Round Robin) | 1→2→3→1→2→3...(默认) | 服务器性能相同 |
| 加权轮询 | 按权重分配(weight=5的服务器比weight=1的多5倍请求) | 服务器性能不同 |
| IP Hash | 同一个客户端IP永远路由到同一台服务器 | 需要会话保持(但不推荐,用Redis Session更好) |
| 最少连接(least_conn) | 哪个服务器当前连接数少就分给谁 | 请求处理时间差异大的场景 |
💻 Nginx 配置
upstream order_backend {
# 加权轮询 + 健康检查
server 10.0.0.1:8080 weight=5 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.4:8080 backup;
}
server {
listen 80;
server_name api.example.com;
location /api/ {
proxy_pass http://order_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
⑨ Docker Compose — 多服务编排
为什么需要?一个完整的应用需要 Nginx + 应用 + MySQL + Redis + RabbitMQ 等多个服务。手动一个个 docker run 太痛苦。Docker Compose 用一个 YAML 文件定义所有服务及其依赖关系,一条命令 docker-compose up 全部启动。
🏗️ 完整部署架构图
典型电商系统部署架构
用户 (小程序/H5)
↓ HTTPS
Nginx (反向代理 + 负载均衡 + 静态资源)
处理HTTPS证书、静态文件(CSS/JS/图片)、转发API请求到后端
↓ 轮询分发
MySQL Master
10.0.0.1:3306
MySQL Slave
10.0.0.2:3306
💻 Docker Compose 配置
version: '3.8'
services:
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app1
- app2
app1:
image: order-service:latest
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_HOST=mysql-master
- REDIS_HOST=redis
ports:
- "8081:8080"
app2:
image: order-service:latest
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_HOST=mysql-master
- REDIS_HOST=redis
ports:
- "8082:8080"
mysql-master:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=xxx
volumes:
- mysql-data:/var/lib/mysql
- ./my-master.cnf:/etc/mysql/conf.d/my.cnf
redis:
image: redis:7
command: redis-server --requirepass xxx
volumes:
- redis-data:/data
volumes:
mysql-data:
redis-data:
⑩ 分布式 Session — 多台服务器怎么共享登录状态
为什么需要?用户登录后,Session存在应用服务器A的内存中。下一次请求被Nginx分到了服务器B → 服务器B内存里没有这个Session → 用户被踢回登录页。多台服务器之间需要共享登录状态。
📊 4种Session共享方案
| 方案 | 原理 | 优缺点 |
| Session复制 | 每台服务器都存所有Session(Tomcat内置支持) | 简单但浪费内存,服务器多时同步开销大 |
| Session粘滞(IP Hash) | Nginx IP Hash,同一用户永远路由到同一台服务器 | 简单但不均衡,服务器挂了Session也丢 |
| Redis集中存储 | 所有Session存Redis,每台服务器都从Redis读写 | ✅ 推荐:统一管理,服务器挂了Session不丢 |
| Token(JWT) | 不用Session,用户每次请求自己带Token | 无状态、天然分布式,但Token不好主动撤销 |
💻 Spring Session + Redis 配置
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
spring:
session:
store-type: redis
timeout: 30m
redis:
host: 10.0.0.5
port: 6379
⑪ 配置中心 — 多台机器的配置统一管理
为什么需要?5台应用服务器,每台都有自己的 application.yml。改一个数据库密码要SSH到5台机器改5次,改完还要一台一台重启。配置中心统一管理所有配置,改一次全部生效,还能动态刷新(不改代码不重启)。
📊 常见配置中心对比
| 特性 | Nacos | Apollo | Spring Cloud Config |
| 开发方 | 阿里巴巴 | 携程 | Spring官方 |
| 实时推送 | ✅ 支持 | ✅ 支持 | ❌ 需要手动刷新 |
| 配置回滚 | ✅ 支持 | ✅ 支持 | ⚠️ 依赖Git |
| 服务发现 | ✅ 内置 | ❌ 不支持 | ❌ 需要Eureka |
| 上手难度 | 简单 | 中等 | 中等 |
💻 Nacos 配置示例
<!-- pom.xml -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
# bootstrap.yml
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: 10.0.0.100:8848
namespace: dev
group: DEFAULT_GROUP
file-extension: yaml
shared-configs: # 共享配置(多个服务公用)
- data-id: common-db.yaml
group: DEFAULT_GROUP
refresh: true
// 动态刷新示例:配置变更时自动生效,无需重启
@RestController
@RefreshScope // Nacos配置变更时自动刷新
public class ConfigController {
@Value("${order.max-retry:3}")
private int maxRetry;
@Value("${order.timeout-seconds:30}")
private int timeoutSeconds;
@GetMapping("/config/info")
public Map<String, Object> getConfig() {
return Map.of(
"maxRetry", maxRetry,
"timeoutSeconds", timeoutSeconds
);
}
}
⑫ 全链路架构总览 — 一个请求的完整旅程
面试高频面试官问:"你能描述一下你们系统的整体架构吗?一个用户请求从发起到返回,经过了哪些组件?"这道题考的不是某个组件的细节,而是你对全局的理解。
🏗️ 完整链路图
用户发一个请求 —— 请求的完整旅程
① 用户点击"下单"
↓ DNS解析
② CDN / DNS
→
静态资源(CSS/JS/图片)从CDN取
↓ HTTPS
③ Nginx
→
SSL终结、负载均衡、静态文件
↓ 负载均衡
④ Spring Boot 应用
→
Token验证、业务逻辑
↓ 异步
⑧ 消费者服务
→
扣库存、发通知、记日志
↓ 返回
⑨ Nginx → 用户
→
JSON响应 → 页面显示"下单成功"
📊 每一步的技术职责
| 步骤 | 技术组件 | 职责 |
| ① 用户点击 | 小程序/浏览器 | 发起HTTPS请求 |
| ② CDN/DNS | CDN边缘节点 | 静态资源就近返回,减少后端压力 |
| ③ Nginx | 反向代理+负载均衡 | SSL终结、分发请求到多台应用服务器 |
| ④ Spring Boot | 业务应用 | Token验证、参数校验、业务逻辑处理 |
| ⑤ Redis | 缓存/Session存储 | 查缓存命中则直接返回,不走DB |
| ⑥ MySQL | 持久化存储 | 写入订单数据(走主库) |
| ⑦ RabbitMQ | 消息队列 | 发送异步消息,解耦下游服务 |
| ⑧ 消费者 | 异步处理服务 | 扣库存、发推送通知、记录日志 |
| ⑨ 返回 | Nginx → 用户 | JSON响应返回前端 |
📊 面试总结:架构高频考点
| 考点 | 关键词 | 一句话回答 |
| MySQL高可用 | 主从、MHA、GTID | "我们用MySQL主从+MHA,半同步复制,主库挂了30秒内自动切换" |
| 读写分离 | AOP、ShardingSphere | "写走主库读走从库,用ShardingSphere-JDBC自动路由,强一致性查询走主库" |
| Redis高可用 | 哨兵、Cluster | "缓存用Redis Cluster,3主3从,自动故障转移" |
| 分库分表 | ShardingSphere | "订单量大了做水平分表,按userId取模分片,用ShardingSphere-JDBC" |
| 负载均衡 | Nginx、轮询、权重 | "Nginx做反向代理,加权轮询,后端健康检查" |
| Session共享 | Spring Session、JWT | "用Spring Session+Redis集中存储Session" |
| 配置管理 | Nacos | "用Nacos做配置中心和服务发现,配置实时推送" |
| 消息队列 | RabbitMQ/Kafka | "用MQ做异步解耦,下单成功发消息,消费者异步扣库存" |