引言

分库分表是应对海量数据的重要方案。本文介绍分库分表的策略和实现。

一、分库分表策略

1.1 垂直拆分

1
2
3
4
-- 按业务拆分
用户库: users, user_profiles
订单库: orders, order_items
商品库: products, categories

1.2 水平拆分

1
2
3
4
5
6
-- 按范围分表
users_2022, users_2023

-- 按哈希分表
users_0, users_1, users_2, users_3
-- user_id % 4 = 分表号

二、ShardingSphere

1
2
3
4
5
6
7
// 配置数据源
spring.shardingsphere.datasource.names=ds0,ds1

// 分片规则
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=ds$->{0..1}.t_order_$->{0..1}
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=order_id
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=table-inline

三、分页查询优化

1
2
3
4
5
6
7
8
9
-- 避免大偏移量
SELECT * FROM users WHERE id > last_id LIMIT 10;

-- 分表聚合
SELECT * FROM (
SELECT * FROM users_0 WHERE age > 18
UNION ALL
SELECT * FROM users_1 WHERE age > 18
) t ORDER BY id LIMIT 10;

四、全局ID生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Snowflake算法
public class SnowflakeIdGenerator {
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;

public synchronized long nextId() {
long timestamp = System.currentTimeMillis();

if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards");
}

if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & 4095;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}

lastTimestamp = timestamp;
return ((timestamp - 1288834974657L) << 22) | (workerId << 12) | sequence;
}
}

总结

分库分表核心:

  1. 拆分策略:垂直、水平拆分
  2. 路由规则:范围、哈希路由
  3. 全局ID:保证ID唯一性
  4. 分布式事务:保证数据一致性

参考资料