【架构实战】数据库分库分表实战

一、为什么需要分库分表

当数据量超过单机数据库的承载能力时,分库分表成为必然选择:

  • 单库数据量过亿:查询性能急剧下降
  • 单表数据量过大:索引效率降低,DML操作变慢
  • 连接数耗尽:数据库连接成为稀缺资源
  • 存储空间不足:单机的磁盘容量有上限

二、分库分表策略

1. 垂直拆分

按业务将表或库拆分开:

复制代码
原来:用户库(用户表、订单表、支付表)

拆分后:
  用户库(用户表)
  订单库(订单表)
  支付库(支付表)

2. 水平拆分

按数据规则将表拆分:

sql 复制代码
-- 按用户ID取模分表
users_0: WHERE user_id % 4 = 0
users_1: WHERE user_id % 4 = 1
users_2: WHERE user_id % 4 = 2
users_3: WHERE user_id % 4 = 3

3. 常见的分片键选择

分片键 适用场景 注意事项
用户ID 电商、社交 查询需带用户ID
时间 日志、订单 冷数据归档方便
地区 区域性业务 跨区查询困难
哈希 均衡分布 查询复杂度高

三、分库分表中间件

ShardingSphere

yaml 复制代码
# application.yml
spring:
  shardingsphere:
    datasource:
      ds0:
        url: jdbc:mysql://localhost:3306/db0
        driver-class-name: com.mysql.cj.jdbc.Driver
      ds1:
        url: jdbc:mysql://localhost:3306/db1
        driver-class-name: com.mysql.cj.jdbc.Driver
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order_inline

MyCat

xml 复制代码
    

四、实战代码

Java SDK 使用示例

java 复制代码
// ShardingSphere JDBC
@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource dataSource() {
        Map dataSourceMap = new HashMap<>();
        dataSourceMap.put("ds0", createDataSource("db0"));
        dataSourceMap.put("ds1", createDataSource("db1"));
        
        ShardingRuleConfiguration config = new ShardingRuleConfiguration();
        config.getTables().add(createTableRule());
        
        return DataSourceFactory.createDataSource(dataSourceMap, config);
    }
}

// 分片键获取
public Long getShardingKey(Order order) {
    return order.getUserId() % 4;
}

五、跨库查询解决方案

1. 禁止跨库JOIN

java 复制代码
// 业务层面解决:先查用户,再查订单
User user = userMapper.selectById(userId);
List orders = orderMapper.selectByUserId(userId);

2. 异构表

sql 复制代码
-- 在订单库冗余用户信息
CREATE TABLE t_order (
    order_id BIGINT,
    user_id BIGINT,
    user_name VARCHAR(50),  -- 冗余字段
    amount DECIMAL(10,2),
    created_at DATETIME
);

3. ES搜索引擎

复制代码
MySQL(分库分表) → 数据同步 → Elasticsearch
                            ↓
                      复杂查询由ES处理

六、分布式ID生成

分库分表后,需要分布式ID:

java 复制代码
// Snowflake算法
public class SnowflakeIdGenerator {
    private final long twepoch = 1288834974657L;
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    
    public synchronized long nextId() {
        long timestamp = timeGen();
        long id = ((timestamp - twepoch) << 22)
                | (datacenterId << 17)
                | (workerId << 12)
                | sequence;
        return id;
    }
}

// 使用
SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
long orderId = idGenerator.nextId();

七、常见问题与解决方案

Q1:扩容时如何迁移数据?

  • 方案1:双写(新老库同时写)
  • 方案2:定时任务迁移
  • 方案3:使用一致性哈希减少迁移量

Q2:如何保证分页查询?

sql 复制代码
-- 反例:深分页问题
SELECT * FROM t_order ORDER BY id LIMIT 1000000, 10

-- 正例:游标分页
SELECT * FROM t_order WHERE id > 1000000 ORDER BY id LIMIT 10

Q3:分布式事务如何处理?

使用 Seata AT 模式或 TCC 模式

八、总结

分库分表是系统扩展的必经之路,但增加了系统复杂度:

  • ✅ 突破单机性能瓶颈
  • ✅ 提高系统可用性
  • ❌ 跨库查询困难
  • ❌ 运维复杂度增加

思考题:你的系统中数据量最大的是哪张表?有没有考虑过分库分表?


个人观点,仅供参考

相关推荐
程序员阿鹏8 小时前
为什么MySQL InnoDB选择B+树?
数据结构·数据库·b树·sql·mysql·算法·缓存
AI_Auto8 小时前
架构视角看数字化转型|核心架构:大共享平台+小应用,从按需走向适变
大数据·人工智能·架构·制造
科芯创展8 小时前
XU9238 外置NMOS架构宽压Boost升压恒压驱动器方案设计与工程落地指南
架构
Dawson Zhu8 小时前
AI 辅助调试陷入「反复修改」循环?用证据驱动的四步法破局
人工智能·语言模型·架构·aigc·agi
瀚高PG实验室9 小时前
使用pg_stat_statements抓取数据库TOP SQL
数据库·sql·postgresql·瀚高数据库
她的男孩10 小时前
多租户和数据权限怎么共存?扒完拦截器注册链路,我找到 4 个隐蔽的坑
java·后端·架构
yanwumuxi10 小时前
【数据库系列】opensearch数据备份和恢复
数据库
anxiao_m10 小时前
2026企业AI数字孪生选型攻略,不同场景对应不同解决方案
大数据·网络·数据库
SelectDB10 小时前
从 ClickHouse 迁移到 Doris:SQL 兼容、同步与验证清单(实战笔记)
大数据·数据库·数据分析
这个DBA有点耶11 小时前
非关系型数据库到底有几种?键值、文档、列族、图,一篇讲透
mysql·架构·nosql