数据库主从有延迟怎么处理

数据库主从有延迟怎么处理

面试官提问

"在高并发互联网场景下,数据库主从延迟是高频痛点,会导致用户读到旧数据、业务逻辑异常。请问你在实际项目中是如何系统性解决这个问题的?"

候选人回答 🎯

核心原则是优先业务层优化,成本最低见效最快

先搞懂:主从延迟的本质 💡

主从延迟本质是主库并发写能力 > 从库串行重放能力的矛盾。MySQL 主从复制的核心流程如下:

延迟高发点:第 3 步 SQL 线程重放(MySQL 5.6 之前完全单线程),主库每秒可执行数千并发写,从库只能逐个重放,天然存在延迟。

常见触发炸弹

  • 大事务(批量更新 10 万 + 条数据)
  • 大表 DDL(千万级表加索引)
  • 从库硬件性能弱于主库
  • 跨机房网络抖动

整理了面试真题、每日技术知识点、系统学习路线,都汇总在个人网站www.javadashen.com,有需要的同学自取。

公众号「Rain的Java大神之路」每天拆一个知识点,陪你悄悄变强,有空来坐坐。


分层解决方案(优先级从高到低)✅

我在项目中会严格按照这个优先级落地,避免上来就搞复杂架构。

1. 业务层优化(最优先,成本≈0)

  • 强制走主库:仅用于实时性要求极高的核心场景(如支付后订单状态查询、库存扣减后校验)。⚠️ 绝对不能滥用,否则主库会被打垮。
  • "写后 N 秒读主库" 策略:写操作后的 N 秒内(N 取主从平均延迟,通常 1-3 秒)读主库,N 秒后读从库。对业务侵入极小,覆盖 80% 以上场景。
  • "读自己写" 策略:用户自己写的数据(如发布文章、修改个人信息),10 秒内读主库;其他用户的数据读从库。兼顾实时性和性能。
  • 拆分大事务:把批量更新拆成每次 1000-5000 条的小事务,避免 binlog 过大导致从库重放阻塞。
  • 规避大表 DDL :使用pt-online-schema-changegh-ost工具执行 DDL,MySQL 8.0 + 直接用原生 Online DDL,避免锁表引发雪崩。

2. 数据库层优化(提升复制性能)

  • 升级 MySQL 版本:这是性价比最高的优化

    • MySQL 5.6:基于库的并行复制
    • MySQL 5.7:基于组提交的并行复制(性能提升 10 倍 +)
    • MySQL 8.0:基于写集的并行复制(几乎接近主库并发度)
  • 从库配置调优

    • 关闭从库 binlog(如果不需要从库再做主库)
    • 设置sync_binlog=0innodb_flush_log_at_trx_commit=2,牺牲少量安全性换取写入性能
  • 一主多从:横向扩展读能力,分散单个从库的读压力,间接降低延迟。

  • 半同步复制:确保主库写操作至少同步到 1 个从库,避免数据丢失。⚠️ 会增加主库写入延迟,仅用于金融等强一致性场景。

3. 架构层优化(从根本上解决问题)

  • 引入缓存:把热点数据放入 Redis,读请求优先查缓存。既减少数据库压力,又彻底解决主从延迟问题。⚠️ 需配套解决缓存与数据库的一致性问题。
  • 消息队列异步化:把非实时写操作(如日志记录、积分发放、短信通知)放入 MQ 异步消费,削峰填谷,避免主库瞬间压力过大。
  • 分库分表:将大表拆分为多个小表,分布在不同实例上,降低单个库的写入压力,从根源上减少延迟。
  • 分布式数据库:使用 TiDB、OceanBase 等原生分布式数据库,支持强一致性读写,天生无主从延迟问题。适合对一致性和扩展性要求极高的核心系统。

监控与兜底(必不可少)🚨

无论怎么优化,极端情况(如大促峰值、网络故障)下仍可能出现延迟飙升,必须做好兜底。

  • 精准监控延迟
    • 不要只依赖show slave statusSeconds_Behind_Master(存在误差)
    • 使用pt-heartbeat工具,通过主库定时写入心跳数据计算准确延迟
    • 告警阈值:延迟 > 1 秒预警,>5 秒触发自动降级
  • 自动降级策略:当延迟超过阈值时,自动将核心读请求切换到主库,同时降级非核心功能(如商品推荐、历史订单查询)。
  • 定期故障演练:每季度模拟主从延迟飙升场景,验证降级策略的有效性,确保真实故障时能快速响应。

方案对比速查表

解决方案 适用场景 优点 缺点
强制走主库 支付、库存等核心实时场景 简单直接,零延迟 增加主库压力,滥用会导致宕机
写后 N 秒读主库 大部分普通读写分离场景 成本极低,业务侵入小 N 值需根据监控动态调整
升级 MySQL 版本 所有 MySQL 项目 性能提升显著,无业务侵入 存在升级风险,需提前测试
引入 Redis 缓存 热点数据读多写少场景 彻底解决延迟,提升系统吞吐量 需处理缓存一致性,增加复杂度
分布式数据库 金融、电商核心系统 天生无主从延迟,支持水平扩展 成本高,学习曲线陡峭

总结 📝

处理主从延迟的核心思路是:能用业务手段解决的,绝不改数据库;能改数据库解决的,绝不搞架构重构

在我负责的电商订单系统中,我们采用了 "写后 1 秒读主库 + 核心场景强制走主库 + Redis 缓存热点数据" 的组合方案,同时配合 MySQL 8.0 并行复制和精准监控,将主从延迟稳定控制在 100ms 以内,即使在双 11 大促峰值也没有出现过因延迟导致的业务异常。

核心代码实现(技术亮点)✨

以下是我在项目中实际使用的核心代码,零业务侵入性,通过 AOP 和动态数据源实现。

1. 基于 ThreadLocal+AOP 的 "写后 N 秒读主库" 实现

技术亮点:自动拦截所有写操作,通过 ThreadLocal 传递写操作时间戳,数据源路由时自动判断是否走主库,业务代码完全无感知。

java 复制代码
/**
 * 数据源上下文,用于保存当前线程是否需要走主库
 */
public class DataSourceContextHolder {
    private static final ThreadLocal<Long> WRITE_TIME_HOLDER = new ThreadLocal<>();
    
    // 写操作后3秒内读主库
    private static final long MASTER_READ_DURATION = 3000;
    
    public static void markWrite() {
        WRITE_TIME_HOLDER.set(System.currentTimeMillis());
    }
    
    public static boolean shouldReadMaster() {
        Long writeTime = WRITE_TIME_HOLDER.get();
        if (writeTime == null) {
            return false;
        }
        boolean result = System.currentTimeMillis() - writeTime < MASTER_READ_DURATION;
        // 超过时间后清除,避免内存泄漏
        if (!result) {
            WRITE_TIME_HOLDER.remove();
        }
        return result;
    }
    
    public static void clear() {
        WRITE_TIME_HOLDER.remove();
    }
}
java 复制代码
/**
 * 写操作拦截AOP,自动标记写操作
 */
@Aspect
@Component
public class WriteOperationAspect {
    
    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactionalPointcut() {}
    
    @AfterReturning("transactionalPointcut()")
    public void afterWrite(JoinPoint joinPoint) {
        // 事务提交成功后标记写操作
        DataSourceContextHolder.markWrite();
    }
}
java 复制代码
/**
 * 动态数据源路由
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        if (DataSourceContextHolder.shouldReadMaster()) {
            return "master";
        }
        return "slave";
    }
}

2. 基于 Redis 的 "读自己写" 策略实现

技术亮点:针对用户维度的读写分离,用户自己写的数据读主库,其他用户的数据读从库,兼顾实时性和性能。

java 复制代码
/**
 * 用户维度的数据源路由工具类
 */
@Component
public class UserReadWriteRouter {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 用户写操作后10秒内读主库
    private static final long USER_MASTER_READ_DURATION = 10000;
    private static final String USER_WRITE_KEY_PREFIX = "user:write:";
    
    /**
     * 用户写操作后调用此方法
     */
    public void markUserWrite(Long userId) {
        String key = USER_WRITE_KEY_PREFIX + userId;
        redisTemplate.opsForValue().set(key, "1", USER_MASTER_READ_DURATION, TimeUnit.MILLISECONDS);
    }
    
    /**
     * 读操作前判断是否需要走主库
     */
    public boolean shouldReadMasterForUser(Long userId) {
        if (userId == null) {
            return false;
        }
        String key = USER_WRITE_KEY_PREFIX + userId;
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
}

3. 基于 Nacos 配置中心的自动降级实现

技术亮点:无需重启服务,动态调整降级策略,支持分级降级,主库流量超过阈值时自动拒绝非核心请求。

java 复制代码
/**
 * 主从延迟降级配置
 */
@Configuration
@RefreshScope
public class MasterSlaveDegradeConfig {
    // 主从延迟阈值(ms),超过则触发降级
    @Value("${master-slave.delay-threshold:5000}")
    private long delayThreshold;
    
    // 是否开启自动降级
    @Value("${master-slave.auto-degrade:true}")
    private boolean autoDegrade;
    
    // 核心接口强制走主库
    @Value("${master-slave.core-force-master:true}")
    private boolean coreForceMaster;
    
    // 非核心接口降级开关
    @Value("${master-slave.non-core-degrade:false}")
    private boolean nonCoreDegrade;
    
    // getter方法
}
java 复制代码
/**
 * 降级拦截器
 */
@Component
public class DegradeInterceptor implements HandlerInterceptor {
    @Autowired
    private MasterSlaveDegradeConfig degradeConfig;
    @Autowired
    private MasterSlaveMonitorService monitorService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!degradeConfig.isAutoDegrade()) {
            return true;
        }
        
        long currentDelay = monitorService.getCurrentDelay();
        
        // 延迟超过阈值,触发降级
        if (currentDelay > degradeConfig.getDelayThreshold()) {
            // 核心接口强制走主库
            if (isCoreInterface(request) && degradeConfig.isCoreForceMaster()) {
                DataSourceContextHolder.forceMaster();
                return true;
            }
            
            // 非核心接口直接降级
            if (!isCoreInterface(request) && degradeConfig.isNonCoreDegrade()) {
                response.getWriter().write("系统繁忙,请稍后再试");
                return false;
            }
        }
        
        return true;
    }
    
    private boolean isCoreInterface(HttpServletRequest request) {
        String uri = request.getRequestURI();
        return uri.startsWith("/api/order") || uri.startsWith("/api/pay") || uri.startsWith("/api/inventory");
    }
}

4. pt-heartbeat 精准监控延迟 SQL

技术亮点 :比show slave statusSeconds_Behind_Master准确 100 倍,可精确到毫秒级。

sql 复制代码
-- 主库创建心跳表
CREATE TABLE heartbeat (
  id INT PRIMARY KEY,
  ts TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
);

-- 主库每秒写入一次心跳数据(可通过crontab或程序实现)
INSERT INTO heartbeat (id, ts) VALUES (1, NOW(3)) ON DUPLICATE KEY UPDATE ts = NOW(3);

-- 从库查询延迟(单位:毫秒)
SELECT ROUND((UNIX_TIMESTAMP(NOW(3)) - UNIX_TIMESTAMP(ts)) * 1000) AS delay_ms FROM heartbeat;

核心技术难点及解决方案 🛠️

技术难点 问题描述 根本原因 解决方案 实战注意事项
大事务导致的延迟雪崩 一个大事务执行几分钟,从库重放也需要几分钟,期间所有从库读都是旧数据,甚至引发连锁反应 binlog 是按事务提交顺序写入的,大事务的 binlog 会阻塞后面所有事务的重放 1. 强制拆分大事务为每次 1000-5000 条的小事务2. 代码中添加事务大小校验3. 数据库层面设置max_binlog_size限制 拆分时要考虑事务原子性,用最终一致性代替强一致性;避免在大促期间执行批量更新
大表 DDL 导致的长时间延迟 千万级表加索引,主库执行几分钟,从库重放也需要几分钟,甚至锁表导致从库读完全阻塞 传统 DDL 会加排他锁,binlog 重放时也会锁从库表,导致从库所有读请求等待 1. MySQL 8.0 + 使用原生 Online DDL2. 低版本使用gh-ost工具3. DDL 执行期间限速,避免影响主库 DDL 必须在凌晨低峰期执行;提前备份数据;先在测试环境验证;执行期间密切监控主从延迟
缓存与数据库一致性问题 引入缓存解决主从延迟,但缓存和数据库不一致会导致更严重的业务问题(如超卖、订单状态错误) 数据库主从延迟 + 缓存更新顺序不当 + 缓存删除失败 1. 采用 "更新数据库 + 删除缓存" 的最终一致性方案2. 设置缓存过期时间兜底3. 缓存删除失败时通过 MQ 重试 绝对不要用 "更新缓存 + 更新数据库" 的顺序;不要用 "更新数据库 + 更新缓存";删除缓存要放在事务提交之后
自动降级的准确性和安全性 降级误判会导致主库压力过大甚至宕机,降级不及时会导致大量业务异常 延迟监控不准确 + 降级策略不合理 + 缺乏流量控制 1. 使用pt-heartbeat精准监控延迟2. 多维度告警(延迟 + 主库 CPU + 主库连接数)3. 分级降级策略4. 设置主库流量上限 非核心功能优先降级;核心功能只在极端情况降级;必须保留手动降级开关;降级后要有告警通知
跨机房主从延迟不可避免 跨机房网络延迟几十毫秒,主从延迟至少几十毫秒,无法通过任何优化完全消除 物理距离导致的网络传输延迟(光速限制) 1. 核心业务同机房部署2. 采用单元化架构,每个单元独立读写3. 非核心数据允许延迟 单元化要做好数据分片,避免跨单元调用;跨机房同步只用于灾备,不用于日常读写
从库并行复制效果不佳 升级到 MySQL 5.7/8.0 后,并行复制仍然没有达到预期效果 并行复制依赖于事务的组提交,组提交数量不足会导致并行度低 1. 调整主库binlog_group_commit_sync_delaybinlog_group_commit_sync_no_delay_count参数2. 增加主库并发写压力,提高组提交数量 调整参数会增加主库写入延迟,需要在延迟和吞吐量之间做平衡;MySQL 8.0 的写集并行复制效果最好

模拟真实面试

模拟真实面试

面试官 😊

"同学,前面基础题聊得不错,咱们来道场景设计题。

我们的电商系统用 MySQL 一主两从、读写分离。用户下单时写主库,下单后页面立刻跳转订单详情,查询走从库。有用户反馈:明明付款成功了,却提示'订单不存在',但刷新一下又好了。你分析下这是什么问题?如果让你来处理,你会怎么设计?"

候选人 🤔

"好的面试官,这明显是主从复制延迟导致的问题。我画个时序图就清楚了。"

"核心矛盾是写完后立刻读,读的是还没同步完的从库 。解决方案我会从四个层面打组合拳:强制路由、复制加速、缓存补偿、应用兜底,再加一套监控。"

面试官 🧐

"不错,抓得准。那先说说第一个层面------强制路由,你怎么落地?"

候选人 💡

"最关键的一招:关键写后读,直接绕过从库,强制走主库。我们用的是 ShardingSphere 读写分离,代码里这么处理:"

java 复制代码
// 下单服务
public Order placeOrder(Order order) {
    orderDao.insert(order); // 写主库
    // 紧接着查询,强制走主库
    try (HintManager hintManager = HintManager.getInstance()) {
        hintManager.setMasterRouteOnly();
        return orderDao.selectById(order.getId());
    }
}

"这样就能保证下单后立即查询返回的是最新数据。但这里有两个要点:

  • 只用在'写后立即读'的高敏感场景,比如下单、支付回调、库存扣减等;
  • 滥用会打爆主库,所以我们配合了一套决策树,后面会提到。"

面试官 🤔

"如果这个场景不能强制走主库,或者主库压力已经很大了,你怎么办?"

候选人 🛠️

"那就上第二、第三招:复制加速 + 缓存补偿

  • 复制加速主要是调参数,让从库尽量快:
措施 作用 代价
半同步复制 (AFTER_SYNC) 主库提交事务需等至少一个从库收到 binlog 写操作 RT 增加约 1ms
并行复制 (MTS) 从库多线程回放 relay log 需关注 slave_parallel_workers,避免大事务冲突
硬件升级 SSD + 万兆网络 💰 砸钱
  • 缓存补偿 更常用,我们会在主库写入成功后,立即往 Redis 写一份短期缓存,过期时间 3~5 秒,刚好覆盖延迟窗口。

"查询时先读 Redis,读不到再降级到从库。这样既透明,又不会给主库增加读负担。"

面试官 👍

"缓存补偿很实用。但万一 Redis 也挂了,或者缓存那几秒都没命中,从库依然没数据呢?"

候选人 🧯

"这时候就要靠应用层兜底了。我们会在查不到订单时做双重确认:

java 复制代码
public Order getOrder(String orderId) {
    Order order = slaveDao.get(orderId);
    if (order == null) {
        // 可能延迟,再查一次主库
        order = masterDao.get(orderId);
        if (order == null) {
            // 真没有就友好提示
            throw new BizException("订单处理中,请稍后刷新");
        }
    }
    return order;
}

"这样就算从库延迟非常严重,也能从主库拿到真实数据。再配合前端'正在处理中,请稍候'的提示 + 自动刷新,用户体验就不会崩。"

面试官 🧐

"方案挺全的。这些策略什么时候用哪种?给我个决策思路。"

候选人 🧭

"我梳理了一张决策树图,团队按这个选型。"

"最后,光靠方案不行,还得有监控兜底 :我们会定时写心跳表,计算主从延迟秒数,接入 Prometheus + Grafana,一旦延迟超过 3 秒就触发报警,甚至自动降级:把所有读暂时切回主库,直到延迟恢复。"

面试官 😊

"思路很清晰,从业务路由、数据层加速、缓存补偿到应用兜底,最后还有监控降级。整个体系很完整,而且给出了具体落地点,不是纸上谈兵。这个场景设计题你过了。"

候选人 😄

"谢谢面试官,我平时确实在生产环境处理过类似问题,所以印象比较深。"

面试官 👨‍💻

"方案讲得不错,落地能力也得验证一下。那接下来咱们现场说几段核心代码 ,再盘一盘这个场景下的技术难点,你怎么解决的。"

核心代码 & 技术亮点 📝

1. 强制路由走主库(读写分离中间件 ShardingSphere)

场景:下单后立刻查询订单详情,必须读到最新数据。

java 复制代码
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    /**
     * 下单并返回最新订单详情
     * 技术亮点:利用 HintManager 强制走主库,方法级粒度控制
     */
    public OrderVO placeOrderAndGet(OrderDTO dto) {
        // 1. 写主库
        Order order = convertToEntity(dto);
        orderMapper.insert(order);

        // 2. 读强制走主库,保证写后读一致性
        try (HintManager hintManager = HintManager.getInstance()) {
            hintManager.setMasterRouteOnly();  // 🎯 核心:一行代码绕过从库
            Order freshOrder = orderMapper.selectById(order.getId());
            return convertToVO(freshOrder);
        }
    }
}

技术亮点

  • 使用 try-with-resources 确保 HintManager 被自动关闭,避免线程污染;
  • 对业务代码侵入性极小,只在需要的方法上加这段即可。

2. 缓存补偿:写主库同步写 Redis

场景:不能强制走主库时,用短期缓存覆盖延迟窗口。

java 复制代码
public OrderVO getOrderWithCache(String orderId) {
    // 1. 查 Redis
    String cacheKey = "order:detail:" + orderId;
    String cachedJson = redisTemplate.opsForValue().get(cacheKey);
    if (cachedJson != null) {
        return JSON.parseObject(cachedJson, OrderVO.class);  // 命中,直接返回 ✅
    }

    // 2. 查从库
    Order order = slaveOrderMapper.selectById(orderId);
    if (order != null) {
        // 回填缓存(防缓存击穿可加短过期时间)
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(order), 
                                        5, TimeUnit.SECONDS);
        return convertToVO(order);
    }

    // 3. 兜底:从库也没有,再查主库(预防极端延迟)
    order = masterOrderMapper.selectById(orderId);
    if (order != null) {
        // 补上缓存,并返回
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(order), 
                                        5, TimeUnit.SECONDS);
        return convertToVO(order);
    }

    throw new BizException("ORDER_NOT_FOUND", "订单处理中,请稍后刷新");
}
java 复制代码
// 下单成功后同步写缓存
@Transactional
public Order placeOrder(OrderDTO dto) {
    Order order = convertToEntity(dto);
    masterOrderMapper.insert(order);
    
    // 🎯 亮点:写入缓存,TTL 3~5 秒覆盖延迟
    String cacheKey = "order:detail:" + order.getId();
    redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(order), 
                                    5, TimeUnit.SECONDS);
    return order;
}

技术亮点

  • 写入缓存的动作和主库写入放在同一个事务里(或至少是紧耦合的本地操作),保证数据一致性;
  • 多级查询链:Redis → 从库 → 主库,层层兜底,又不会频繁打主库。

3. 主从延迟监控:心跳检测 + 自动降级

场景:监控主从延迟,超过阈值自动把读流量切回主库。

java 复制代码
@Component
public class ReplicationLagMonitor {

    @Autowired
    private JdbcTemplate masterJdbc;
    @Autowired
    private JdbcTemplate slaveJdbc;
    @Autowired
    private DynamicDataSource dataSource;

    private volatile long lagSeconds = 0;

    // 每秒检测一次
    @Scheduled(fixedDelay = 1000)
    public void checkLag() {
        // 主库写入时间戳
        masterJdbc.update("INSERT INTO heartbeat (id, ts) VALUES (1, NOW()) ON DUPLICATE KEY UPDATE ts = NOW()");
        String masterTime = masterJdbc.queryForObject("SELECT ts FROM heartbeat WHERE id=1", String.class);

        // 从库读取时间戳
        String slaveTime = slaveJdbc.queryForObject("SELECT ts FROM heartbeat WHERE id=1", String.class);

        // 计算延迟秒数
        lagSeconds = ChronoUnit.SECONDS.between(
                LocalDateTime.parse(slaveTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
                LocalDateTime.parse(masterTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
        );

        // 🎯 亮点:超过阈值自动降级,读流量全部切主库
        if (lagSeconds > 3) {
            dataSource.forceMasterRoute();   // 切换数据源,强制走主库
            log.warn("主从延迟 {} 秒,触发自动降级", lagSeconds);
        } else {
            dataSource.restoreNormalRoute(); // 恢复正常读写分离
        }
    }
}

技术亮点

  • 用独立的心跳表精确量化延迟,不依赖数据库自带的状态变量;
  • 自动降级,无需人工干预,延迟恢复后自动切回,保障高可用。

技术难点 & 解决方案拆解 ⚡

难点 1:如何界定"关键业务"与"可降级业务"?

难点分析 解决方案
所有写后读都走主库,主库压力暴增 用前面给的决策树,只对支付、库存、下单回调等强一致性场景强制走主库;普通浏览类业务容忍短暂延迟

难点 2:半同步复制带来的性能损耗与主库阻塞风险

难点分析 解决方案
半同步模式下,主库需要等待至少一个从库应答,如果从库挂掉或网络抖动,写操作会阻塞 ① 使用 AFTER_SYNC 模式(先提交引擎层,再等从库ACK),降低阻塞窗口;② 超时机制:设置 rpl_semi_sync_master_timeout,超时自动降级为异步复制,避免长时间阻塞

难点 3:缓存补偿的一致性边界

难点分析 解决方案
写 Redis 和写 DB 不在一个事务,可能 DB 成功、Redis 失败,导致缓存丢失 ① Redis 写失败记录日志 + 重试;② 兜底的二级查询链(从库 → 主库)会补上数据;③ TTL 设短(3~5秒),即使缓存缺失影响范围极小

难点 4:从库延迟过大时,自动降级可能引发主库过载

难点分析 解决方案
一旦降级,所有读流量瞬间涌向主库 逐步放量:不一次性把全部读切到主库,先切换 50% 流量,观察负载;② 结合限流组件(Sentinel/Guava RateLimiter)对主库的读操作做保护;③ 故障止损优先,恢复后第一时间切回

难点 5:并行复制可能带来的数据冲突

难点分析 解决方案
开启 MTS 后,从库多线程回放,如果多个事务修改同一行,可能出现短暂不一致 ① 调整 binlog_transaction_dependency_tracking 为 WRITESET,让 MySQL 自动分析事务依赖,安全并行;② 尽量让业务写入做到单行、按主键顺序,减少冲突概率

面试官 😊

"代码写得够细致,难点也拆解得透彻,尤其是监控自动降级和缓存多级兜底这两块,能看出实战经验。这轮代码和难点部分也顺利通过,继续保持这个状态。"

别走,交个朋友

我是 Rain ,一个喜欢把复杂技术讲透、让代码落地的实践者。

如果你厌倦了四处收集碎片化的八股文,想看看一线项目里真实的架构决策和踩坑记录,欢迎来我的公众号「Rain的Java大神之路 」坐坐。

在这里,我会把每一次技术复盘、每一个项目的设计源码,毫无保留地分享给你。

🔥技术这条路很酷,我们结伴同行。

🚀Keep Coding, Keep Loving ------ 与所有 Java 同路人共勉。

相关推荐
doiito2 天前
【开源项目】开源智能排产系统深度研究报告(第一部分)
架构设计·系统设计
潘潘的嵌入式日记3 天前
嵌入式安全重构——新老两套同时跑
嵌入式·架构设计·验证·代码重构
在水一缸3 天前
深度解析:OPEN NPO网络大模型架构与开发者实践指南
网络·人工智能·华为·架构·架构设计·open npo·网络大模型
doiito4 天前
【Agent Harness】Gliding Horse 高级认知优化:让 Agent 的“大脑”和“免疫系统”真正协同
ai·rust·架构设计·系统设计·ai agent
Rain的Java大神实战圈4 天前
分库分表会带来哪些问题如何解决
架构设计·场景设计题
Rain的Java大神实战圈5 天前
介绍一下分库分表
架构设计·场景设计题
doiito6 天前
【RUST AI】把 TTS 搬进浏览器:kokoroi-rs 的 WASM 实践
ai·rust·架构设计
Rain的Java大神实战圈6 天前
线上慢SQL的排查思路
经验分享·架构设计·场景设计题
Rain的Java大神实战圈7 天前
MySQL单表有10亿数据如何做迁移
架构设计·面试经验·场景设计题