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

一、为什么需要分库分表

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

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

八、总结

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

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

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


个人观点,仅供参考

相关推荐
江晓鱼未暖几秒前
十七、Redis 核心原理与架构详解
大数据·数据库·数据仓库·redis·缓存·架构
copyer_xyf1 分钟前
PostgreSQL 做向量检索:一条单库路线
数据库·agent·nestjs
Access开发易登软件5 分钟前
Access 怎么做前后端分离?用 Web API 读写 SQL Server
前端·数据库·人工智能·microsoft·excel·access
Juicedata9 分钟前
腾讯云 x JuiceFS:基于 FoundationDB 的企业级统一存储实践
数据库·人工智能·科技·云计算·腾讯云
小罗水20 分钟前
第13章 Redis 缓存、幂等锁与任务状态
数据库·redis·缓存
小张同学a.28 分钟前
LAMP架构2
linux·运维·网络·架构·负载均衡
SelectDB技术团队29 分钟前
当 PostgreSQL 面临性能瓶颈:80TB 电商业务迁移至 Apache Doris 的实践思考
数据库·postgresql·apache
心念枕惊33 分钟前
新写了个直播录制工具,可录制抖音快手斗鱼直播
运维·服务器·数据库
逐米时代1 小时前
向量数据库选型——Chroma、Qdrant、Milvus到底怎么选
数据库·milvus
面对疾风叭!哈撒给1 小时前
Linux ARM架构的docker和docker-compose离线安装
linux·docker·架构