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

一、为什么需要分库分表

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

  • 单库数据量过亿:查询性能急剧下降
  • 单表数据量过大:索引效率降低,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 模式

八、总结

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

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

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


个人观点,仅供参考

相关推荐
爬山算法2 小时前
MongoDB(60)如何使用explain命令?
数据库·mongodb
2501_908329852 小时前
NumPy入门:高性能科学计算的基础
jvm·数据库·python
瀚高PG实验室2 小时前
数据库因坏块导致无法VACUUM FREEZE问题处理
数据库·瀚高数据库
gechunlian882 小时前
Redis简介、常用命令及优化
数据库·redis·缓存
ahauedu2 小时前
MySQL 8.0+ 默认使用 caching_sha2_password 插件进行密码加密
数据库·mysql
一水鉴天2 小时前
智能代理体系 之2 20260325 (腾讯元宝)
人工智能·重构·架构·自动化
CoderIsArt2 小时前
编译器构建中涉及的算法
数据库·算法
IvorySQL2 小时前
从代码到舞台:HOW 2026 致敬 PostgreSQL 18 贡献者
数据库·postgresql·开源
mldlds2 小时前
Spring Boot 实战:轻松实现文件上传与下载功能
java·数据库·spring boot