分库分表对老业务功能带来的冲击

本文探讨了业务数据增长导致的单库单表性能瓶颈,并提出了通过分库分表解决这一问题的方法。具体分析了分库分表后面临的三个挑战:根据主键查询签约信息、插入数据时userId缺失、跨库联表查询,并给出了相应的解决方案。

本文为原创,转载请注明出处

分库分表对老业务功能带来的冲击

 

         当业务量发展到一定的程度时,不可避免的需要对数据进行分库分表。以用户的签约数据为例,当用户量很少时,单库单表是可以满足的,但当用户量达到某个级别,譬如亿级,那么单库就会成为瓶颈,需要根据某种维度(譬如userId)来进行分库分表。

分库分表如何实现本文就不阐述了,可以参考一下淘宝的tddl。本文主要阐述分库分表过程中对老业务逻辑带来的冲击以及如何改造,因为有些原来单库单表中很容易实现的功能,一经分库分表后,就变的很棘手,譬如:

      a)根据主键ID(非userId)查询签约信息:

      b)插入数据时,userId为空

      c)联表查询,多个表不在同一个库里

 

1、首先来分析第一个问题:根据主键ID(非userId)查询签约信息。

由于根据userId来进行分库,那么根据ID是无法知道该去哪个库查询,当然可以采取全库扫描,但一般这在性能上是无法接受的,违背了分库分表的初衷。可以采取以下几种方案:

      1.1、在进行查询前,如果能拿到userId,则改造为根据userIdid两个条件去查询。

      1.2、如果拿不到userId,那么对于老数据,需要建立一个前置表,该表存储iduserId的映射关系(即:该表只有两个字段,id和userId)。根据ID查询前,先根据ID去查询前置表,得到userId,然后再根据userIdid两个条件查询。



 

分库分表前



 

分库分表后

需要注意的是,该前置表只需要存储老数据的映射关系(该表的数据由系统发布上线前对老数据迁移得到,发布上线后,不会再有新数据写入),对于新数据,在生成ID时,ID需要包含所属库和所属表的标示,这样根据ID查询新数据时就可以直接路由到具体的库和表了,不需要再查询前置表。那么根据ID如何区分新老数据呢,可以根据ID的长度(一般分库后的ID位数会扩容)

        

2、接着分析第二个问题:插入数据时,userId此时为空

在分库分表前,有些业务流程在执行过程中,插入数据时没有分库分表的维度信息譬如userId,只有执行某个业务操作譬如用户登陆后才能得到userId,然后更新这条记录以补全userId




分库分表前

那么分库分表后,在插入数据的那个时刻,就无法知道该把数据插入到哪个库哪个表。针对这种情况,需要对插入数据的流程进行改造。

先将数据进行临时存储,譬如存储在集中式缓存(tairmemcache等)等用户登陆后拿到userId后,再从缓存中查询出数据,然后再插入数据到DB中。



 

分库分表后

3、再来看第三个问题:联表查询,多个表不在同一个库里

         对联表查询进行拆分,保证被拆分过后的原子查询都落在相同的库里。当然,对于左联结或右联结查询,要特备注意拆分前后结果的一致性,很有可能会出现拆分后结果记录数减少的情况,需要重点测试。

 

 

以下是针对 **架构设计 20 道高频面试题** 的标准面试答案详解,符合中高级 Java 开发、后端架构师及技术负责人在真实技术面试中的达规范:逻辑清晰、原理深入、关键词突出、语言专业。每道题均包含 **核心概念 + 实现机制 + 应用场景 + 注意事项 + 最佳实践**,便于精准作答。 --- ### 1. 高并发系统的架构设计? 高并发系统需在单位时间内处理大量请求,保障性能与稳定性。 #### 核心设计原则: - **横向扩展(Scale Out)**:通过增加服务器实例提升吞吐 - **异步化**:将非关键路径任务异步执行(如日志、通知) - **缓存前置**:使用 Redis/Memcached 减少数据库压力 - **服务拆分**:微服务化降低单负载 - **限流降级**:防止雪崩,保护核心链路 #### 典型架构层级: ``` 客户端 → CDN → Nginx(负载均衡) → API 网关 → 微服务集群 ← 缓存/消息队列 ← 数据库集群 ``` > ✅ 关键指标:QPS、RT、TP99、错误率;目标是“稳、快、可扩展” --- ### 2. 分布式系统的CAP理论? CAP 理论指出,在分布式系统中,**一致性(Consistency)、可用性(Availability)、分区容忍性(Partition Tolerance)** 三者不可兼得,最多满足其二。 | 组合 | 含义 | 代系统 | |------|------|----------| | **CA** | 一致性和可用性(牺牲 P) | 单机数据库(不适用分布式) | | **CP** | 一致性和分区容忍性(牺牲 A) | ZooKeeper、etcd、HBase | | **AP** | 可用性和分区容忍性(牺牲 C) | Eureka、Cassandra、DNS | > 📌 实际网络无法避免分区(P 必须满足),因此只能在 CP 和 AP 间权衡 --- ### 3. 分布式事务的解决方案? 分布式事务用于保证跨服务或跨数据库的操作原子性。 #### 常见方案: | 方案 | 原理 | 特 | |------|------|------| | **2PC(两阶段提交)** | 协调者统一提交/回滚 | 强一致性,但阻塞、单故障 | | **3PC(三阶段提交)** | 加入超时机制避免永久阻塞 | 改进 2PC,仍难落地 | | **TCC(Try-Confirm-Cancel)** | 手动实现三个阶段补偿 | 高性能,开发成本高 | | **本地消息(可靠消息)** | 消息与业务同库事务提交 | 最终一致性,适用于订单场景 | | **MQ 事务消息(RocketMQ)** | 半消息 + 回查机制 | 解耦好,支持最终一致 | | **Saga 模式** | 将大事务拆为小事务,失败时触发补偿流程 | 适合长流程业务(如电商下单) | > ✅ 推荐使用 **MQ 事务消息 + 最终一致性** 替代强一致性事务 --- ### 4. 一致性哈希的原理? 一致性哈希解决传统哈希在节增减时导致大规模数据迁移的问题。 #### 核心思想: - 将整个哈希空间组织成一个环(0 ~ 2^32 - 1) - 所有节和数据 key 都取 hash 后映射到环上 - 数据存储在顺时针方向最近的节上 #### 优: - 节增删仅影响相邻区域,大部分数据无需迁移 - 支持虚拟节缓解数据倾斜 ```java // 示例:使用 TreeMap 实现一致性哈希 public class ConsistentHash<T> { private final TreeMap<Integer, T> circle = new TreeMap<>(); private final HashFunction hash; public void add(T node) { int hash = hash(node.toString()); circle.put(hash, node); } public T get(Object key) { if (circle.isEmpty()) return null; int hash = hash(key.toString()); Integer ceilingKey = circle.ceilingKey(hash); if (ceilingKey == null) ceilingKey = circle.firstKey(); return circle.get(ceilingKey); } } ``` > ✅ 广泛应用于 Redis Cluster、负载均衡、CDN 调度 --- ### 5. 分布式锁的实现? 分布式锁用于控制多个节对共享资源的互斥访问。 #### 常见实现方式: | 方式 | 工具 | 说明 | |------|------|------| | **基于 Redis(SETNX + EXPIRE)** | Redis | 简单高效,注意锁续期(Redlock 争议) | | **Redisson** | Redis 客户端 | 提供可重入、公平锁、看门狗自动续期 | | **ZooKeeper** | ZK 节 | 利用临时顺序节实现强一致性锁 | | **数据库唯一索引** | MySQL | 插入记录作为加锁,删除即释放 | ```java // Redisson 示例 RLock lock = redisson.getLock("order_lock"); try { if (lock.tryLock(10, 10, TimeUnit.SECONDS)) { // 执行业务逻辑 } } finally { lock.unlock(); } ``` > ✅ 生产环境推荐 Redisson 或 ZooKeeper,避免手动实现带来的死锁风险 --- ### 6. 高可用架构的设计? 高可用(High Availability)指系统持续对外提供服务的能力,通常以 SLA 衡量(如 99.99%)。 #### 设计策略: - **冗余部署**:无单,主从、集群、多副本 - **故障转移(Failover)**:检测宕机后自动切换(如 Sentinel) - **健康检查**:定期探测服务状态 - **熔断降级**:异常时快速失败,防止级故障 - **多机房部署**:同城双活、异地容灾 > ✅ HA ≠ 冗余,必须配合监控、告警、自动化恢复机制 --- ### 7. 弹性伸缩架构? 弹性伸缩根据负载动态调整资源规模,提升资源利用率。 #### 实现方式: - **垂直伸缩(Scale Up)**:升级单机配置(CPU、内存) - **水平伸缩(Scale Out)**:增加实例数量(更常用) #### 触发条件: - CPU 使用率 > 80% - 请求延迟升高 - 队列积压严重 #### 技术支撑: - Kubernetes HPA(基于指标自动扩缩 Pod) - AWS Auto Scaling Group - 阿里云弹性伸缩 ECS > ✅ 弹性伸缩需结合 CI/CD、配置中心、服务注册发现才能快速生效 --- ### 8. 负载均衡的实现? 负载均衡将流量分发到多个服务器,提升性能与可用性。 #### 类型: | 层级 | 协议 | 工具 | |------|------|------| | **四层(L4)** | TCP/UDP | LVS、F5、Nginx(stream) | | **七层(L7)** | HTTP/HTTPS | Nginx、HAProxy、Spring Cloud Gateway | #### 算法: - 轮询(Round Robin) - 加权轮询 - 最少连接数 - IP Hash(会话保持) - 一致性哈希 > ✅ 微服务中常用 Ribbon 或 LoadBalancer 实现客户端负载均衡 --- ### 9. 服务熔断与降级? 应对依赖服务故障的防护机制。 | 概念 | 说明 | |------|------| | **熔断(Circuit Breaker)** | 当失败率达到阈值时,直接拒绝请求一段时间(类似保险丝) | | **降级(Degradation)** | 在系统压力大或依赖异常时,关闭非核心功能,保障主干流程 | #### 实现工具: - Hystrix(已停更) - Sentinel(阿里开源,主流选择) - Resilience4j(轻量级,适合 Spring Boot) ```java // Sentinel 示例 @SentinelResource(value = "getUser", blockHandler = "fallback") public User getUser(Long id) { throw new RuntimeException("service down"); } public User fallback(Long id, BlockException ex) { return new User().setName("default"); } ``` > ✅ 熔断是“自动”,降级是“主动”;两者常配合使用 --- ### 10. 缓存穿透、击穿、雪崩的解决方案? | 问题 | 成因 | 解决方案 | |------|------|-----------| | **缓存穿透** | 查询不存在的数据,绕过缓存查 DB | 布隆过滤器拦截非法 key;空结果缓存(带短 TTL) | | **缓存击穿** | 热 key 失效瞬间大量请求打到 DB | 设置永不过期热数据;互斥重建(Redis setnx) | | **缓存雪崩** | 大量 key 同时失效,DB 扛不住 | 过期时间加随机扰动;多级缓存;限流降级 | > ✅ 推荐组合策略:BloomFilter + 热探测 + 多级缓存 + Sentinel 限流 --- ### 11. 数据库分库分表策略? 当单库性能瓶颈时,采用分片策略分散压力。 #### 分类: - **垂直拆分**:按业务拆分库(用户库、订单库) - **水平拆分**:同一按规则拆到多个库/ #### 分片键选择: - 用户 ID、订单 ID、时间等 - 要求:离散性好、查询频繁、避免跨片查询 #### 中间件: - ShardingSphere(Apache) - MyCat(社区活跃度下降) ```yaml # ShardingSphere 示例:按 user_id 分片 tables: t_order: actualDataNodes: ds$->{0..1}.t_order_$->{0..3} tableStrategy: standard: shardingColumn: user_id shardingAlgorithmName: mod-algorithm ``` > ✅ 分库分表后需解决:分布式 ID、跨片查询、事务一致性等问题 --- ### 12. 读写分离的实现? 主库负责写,从库负责读,提升数据库吞吐。 #### 实现方式: - MySQL 主从复制(binlog + relay log) - 中间件路由:ShardingSphere、MyCat 自动识别 SQL 类型并转发 - 应用层手动指定数据源(如 DynamicDataSource) #### 注意事项: - 存在主从延迟(半同步复制可缓解) - 强一致性读仍需走主库 > ✅ 适用于读多写少场景(如内容平台、商品详情页) --- ### 13. 消息队列的应用场景? MQ 实现系统解耦、异步处理、削峰填谷。 #### 典型场景: - **异步处理**:注册送积分、发送短信 - **应用解耦**:订单系统通知库存、物流 - **流量削峰**:秒杀系统将请求写入 MQ,后端消费处理 - **日志收集**:Flume/Kafka 收集日志进行分析 - **事件驱动架构(EDA)**:监听事件触发后续动作 > ✅ 常用 MQ:Kafka(高吞吐)、RabbitMQ(灵活路由)、RocketMQ(金融级可靠) --- ### 14. 异步处理的架构设计? 将非实时、非核心操作异步化,提升响应速度。 #### 实现方式: - **线程池**:本地异步(注意资源隔离) - **消息队列**:跨服务异步通信 - **事件总线**:Spring Event、ApplicationEventPublisher #### 设计要: - 明确哪些操作可以异步(如统计、推送) - 保证消息可靠性(持久化、确认机制) - 支持失败重试与死信队列 > ✅ 异步不是万能,需评估数据一致性要求与用户体验影响 --- ### 15. 服务治理的策略? 服务治理是对微服务生命周期的管理。 #### 核心能力: - **服务注册与发现**:Eureka、Nacos、Consul - **配置中心**:Nacos、Apollo、Spring Cloud Config - **熔断限流**:Sentinel、Hystrix - **网关路由**:Spring Cloud Gateway、Zuul - **链路追踪**:SkyWalking、Zipkin - **监控告警**:Prometheus + Grafana + AlertManager > ✅ 服务治理平台是微服务体系的核心基础设施 --- ### 16. 微服务的拆分策略? 合理拆分是微服务成功的关键。 #### 拆分原则(DDD 指导): - **单一职责**:每个服务聚焦一个业务领域 - **高内聚低耦合**:减少服务间依赖 - **独立部署**:可单独发布不影响其他服务 - **团队自治**:一个团队维护一个或多个服务 #### 拆分维度: - 按业务边界拆(用户、订单、支付) - 按子域拆(核心域、支撑域、通用域) - 避免“分布式单体”:过度拆分导致调用链复杂 > ✅ 推荐先单体演进,再逐步拆分为服务,避免早期过度设计 --- ### 17. 限流与降级的实现? 防止系统被突发流量打垮。 #### 限流算法: | 算法 | 特 | |------|------| | **计数器** | 简单,但存在临界问题 | | **滑动窗口** | 更精确控制单位时间请求数 | | **漏桶算法** | 恒定速率处理请求 | | **令牌桶** | 允许一定程度突发流量(Guava RateLimiter 使用) | #### 实现方式: - Nginx limit_req_zone - Sentinel 流控规则 - Redis + Lua 脚本自定义限流 ```java // Sentinel 流控示例 FlowRule rule = new FlowRule(); rule.setResource("createOrder"); rule.setCount(10); // QPS=10 rule.setGrade(RuleConstant.FLOW_GRADE_QPS); FlowRuleManager.loadRules(Collections.singletonList(rule)); ``` > ✅ 限流应分级:接口级、用户级、IP 级;降级策略预设并可动态开关 --- ### 18. 全链路压测方案? 全链路压测验证系统在高负载下的现。 #### 核心步骤: 1. **流量录制**:采集生产环境真实流量(去敏) 2. **流量回放**:在测试环境重放流量,模拟高峰场景 3. **影子库/**:数据库隔离,不影响生产数据 4. **监控分析**:观察 QPS、RT、错误率、资源使用 5. **容量评估**:确定系统极限与扩容需求 #### 工具支持: - 阿里 TProfiler / ChaosBlade - 字节 ByteMesh - 开源方案:JMeter + 影子库 + Mock 服务 > ✅ 压测前必须做好“防泄密、防污染、防冲击”三重防护 --- ### 19. 容灾与灾备方案? 确保系统在灾难发生时仍能提供服务。 | 类型 | 说明 | |------|------| | **同城双活** | 两个数据中心位于同一城市,互为主备,流量分流 | | **异地多活** | 多个地域部署完整系统,极致可用性(如支付宝) | | **冷备** | 备用系统平时不运行,故障时手动切换 | | **热备** | 备用系统实时同步,可自动接管 | #### 关键技术: - 数据同步(DRDB、MySQL GTID) - DNS 切流或 GSLB 调度 - 数据一致性校验 > ✅ 容灾演练常态化,避免“纸上预案” --- ### 20. 架构评审的要? 架构评审是确保系统质量的重要环节。 #### 评审关注: | 维度 | 审查内容 | |------|----------| | **功能性** | 是否满足业务需求?扩展性如何? | | **性能** | QPS、延迟、TP99 是否达标?有无性能瓶颈? | | **可用性** | 是否有单?是否具备容错能力?SLA 目标是多少? | | **可维护性** | 日志、监控、告警是否完备?是否易于排查问题? | | **安全性** | 认证授权、数据加密、防攻击措施是否到位? | | **成本** | 资源利用率、云费用、运维人力是否合理? | | **合规性** | 是否符合公司技术规范、数据隐私法规? | > ✅ 架构评审应形成文档闭环,明确责任人与改进项 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值