常常使用的分库分表

这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战

背景

当遇到数据库表不断增大,一个接口查询的数据已经超过500ms了,就需要进行分库分表来保证查询效率了

问题

值得注意的是数据库表非常庞大时,而且主键ID不同库表时需要考虑主键ID唯一性问题。还有跨库join的问题

方案

目前分库分表有很多非常好的方案

  • 框架层:通过实现一些拦截器(比如MybatisInterceptor接口),增加一些自定义解析来控制数据的流向
  • 驱动层的包括:TDDLShardingJDBC
  • 代理层:MySQL RouterMyCat

下面用ShardingJDBC的代码实现来看下分库分表在项目中如何使用。

代码实现

引入依赖

首先需要引入ShardingJDBC的maven依赖

1
2
3
4
5
xml复制代码   <dependency>
<groupId>com.dangdang</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>1.5.4</version>
</dependency>

排除默认数据源

由于ShardingJDBC是在数据源上边做分库分表,所以排除原有的默认数据源

1
java复制代码@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})

分库规则设置

设置分库策略为键值大于30000走db0,小与等于30000走db1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码@Component
public class DbSharding implements SingleKeyDatabaseShardingAlgorithm<Integer> {

@Autowired
private Db0Config db0Config;

@Autowired
private Db1Config db1Config;

@Override
public String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) {
Long value = shardingValue.getValue();
if (value > 30000) {
return db0Config.getDatabaseName();
} else {
return db1Config.getDatabaseName();
}
}

}

分表规则设置

设置分表规则,按奇数偶数分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码@Component
public class TbSharding implements SingleKeyTableShardingAlgorithm<Integer> {

@Override
public String doEqualSharding(final Collection<String> tableNames, final ShardingValue<Integer> shardingValue) {
for (String table : tableNames) {
if (table.endsWith(shardingValue % 2 + "")) {
return table;
}
}
throw new IllegalArgumentException();
}

}

分库分表策略配置

采用均匀分布模式,最终数据分布如下:

1
2
3
4
5
6
复制代码db0
├── user_0
└── user_1
db1
├── user_0
└── user_1

最关键的代码!设置数据源的分库分表策略

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
java复制代码@Configuration
public class DataSourceConfig {

@Autowired
private Db0Config db0Config;

@Autowired
private Db1Config db1Config;

@Autowired
private DbSharding dbSharding;

@Autowired
private TbSharding tbSharding;

@Bean
public DataSource getDataSource() throws SQLException {
return buildDataSource();
}

private DataSource buildDataSource() throws SQLException {

Map<String, DataSource> dataSourceMap = new HashMap<>(2);
// 添加数据源 db0和db1
dataSourceMap.put(db0Config.getDatabaseName(), db0Config.dataSource());
dataSourceMap.put(db1Config.getDatabaseName(), db1Config.dataSource());
// 设置db0为默认源
DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap, db0Config.getDatabaseName());

// 设置分表,将查询虚拟表user根据规则映射到真实表中去
TableRule userTableRule = TableRule.builder("user")
.actualTables(Arrays.asList("user_0", "user_1"))
.dataSourceRule(dataSourceRule)
.build();

// 分库分表策略
ShardingRule shardingRule = ShardingRule.builder()
.dataSourceRule(dataSourceRule)
.tableRules(Arrays.asList(userTableRule))
.databaseShardingStrategy(new DatabaseShardingStrategy("user_id", dbSharding))
.tableShardingStrategy(new TableShardingStrategy("id", tbSharding)).build();
DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule);
return dataSource;
}


@Bean
public KeyGenerator keyGenerator() {
return new DefaultKeyGenerator();
}

}

概念解释

TableRule

表规则配置对象,内嵌 TableRuleBuilder 对象进行创建。

数据单元

  • DataNode
    静态分库分表数据单元
    数据分片的最小单元,由数据源名称和数据表组成。
    例:ds_1.t_order_0。配置时默认各个分片数据库的表结构均相同,直接配置逻辑表和真实表对应关系即可。
  • DynamicDataNode
    动态表的分库分表数据单元
    逻辑表和真实表不一定需要在配置规则中静态配置。比如按照日期分片的场景,真实表的名称随着时间的推移会产生变化

TableRuleBuilder

TableRuleBuilder 调用 #build() 方法创建 TableRule

分库/分表策略

  • databaseShardingStrategy :分库策略
  • tableShardingStrategy :分表策略

ShardingRule

分库分表规则配置对象,内嵌 ShardingRuleBuilder 对象进行创建。

dataSourceRule

dataSourceRule,数据源配置对象。ShardingRule需要数据源配置正确。这点和 TableRule 是不同的。TableRuledataSourceRule只使用数据源名字,最终执行SQL 使用数据源名字从 ShardingRule 获取数据源连接

主键生成

  • generateKeyColumn :主键字段
  • keyGenerator :主键生成器

小结

本文简单的解释了为什么使用分库分表,以及分库分表的方案,以ShardingJDBC作为示例给出了一部分代码实现,并解释了其中的概念,文章中的部分内容参考各位大佬对分库分表的理解做出整理。

参考

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%