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

一、为什么需要分库分表

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

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

八、总结

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

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

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


个人观点,仅供参考

相关推荐
Elastic 中国社区官方博客7 小时前
将你自己的密钥用于现有 Elastic Cloud 部署
大数据·数据库·elasticsearch·全文检索
红海云7 小时前
Jev:给智能系统做判断的模型
大数据·数据库·人工智能
wjkjpcba7 小时前
PCBA烧录程序是什么:PCBA包工包料厂家解析烧录与测试
linux·数据库·人工智能·smt贴片加工·pcba贴片加工厂
꯭自꯭闭꯭8 小时前
达梦事物特性及MVCC
linux·运维·数据库
小马同学-8 小时前
MySQL主从复制和读写分离
数据库·mysql
谢亮_vipxieliang8 小时前
Spring 事务失效的常见场景
java·开发语言·数据库·spring boot
geovindu9 小时前
sql: JSON and XML Data Handling in SQL using sql server 2025
大数据·数据库·sqlserver·数据库开发·数据库架构
IT大白鼠9 小时前
图数据库系列 · 第 02 篇——架构拆解:原生图存储到因果集群
数据库·架构·nosql
EatFan10 小时前
从“框架混战“到“运行时收敛“:2026 年 AI Agent 开发框架的三条路线之争
java·数据库·人工智能·多智能体·ai agent·mcp·agent 框架