mysql中的小叔如何表示_springboot实践笔记之一:springboot+sharding-jdbc+mybatis全注解实现增量数据库分片实现...

由于当当发布了最新的Sharding-Sphere,所以本文已经过时,不日将推出新的版本

项目中遇到了分库分表的问题,找到了shrding-jdbc,于是就搞了一个springboot+sharding-jdbc+mybatis的增量分片的应用。今天写博客总结一下遇到的坑。

其实,我自己写了一个increament-jdbc组件的,当我读了sharding-jdbc的源码之后,发现思路和原理差不多,sharding这个各方面要比我的强,毕竟我是一天之内赶出来的东东。

demo没有写日志,也没有各种异常判断,只是说明问题

一、需求背景

我的项目背景就不说了,现在举一个例子吧:A,B两支股票都在上海,深圳上市,需要实时记录这两支股票的交易tick(不懂tick也没有关系)。现在的分片策略是:上海、深圳分别建库,每个库都存各自交易所的两支股票的ticktick,且按照月分表。如图:

db_sh

tick_a_2017_01

tick_b_2017_01

........

tick_a_2017_12

tick_b_2017_12

db_sz

tick_a_2017_01

tick_b_2017_01

........

tick_a_2017_12

tick_b_2017_12

分库分表就是这样的。根据这个建库。

**千万不要讨论这样分库分表是否合适,这里这样分片只是举个栗子,说明分库分表这个事情。**

**Sharding-jdbc是不支持建库的SQL,如果像我这样增量的数据库和数据表,那就要一次性把一段时期的数据库和数据表都要建好。**

二、建库

考虑到表确实多,所以我就只建1,2月份的表。语句见demo文件。

三、springboot集成sharding-jdbc

mvn配置pom如下:

com.spartajet

springboot-sharding-jdbc-demo

0.0.1-SNAPSHOT

jar

springboot-sharding-jdbc-demo

Springboot integrate Sharding-jdbc Demo

UTF-8

UTF-8

UTF-8

zh_CN

1.8

${java.version}

1.4.1.RELEASE

1.0.13

5.1.36

1.4.1

2.8.0

2.9.7

1.4

2.5

1.2.0

org.springframework.boot

spring-boot-starter-jdbc

${spring.boot.version}

org.mybatis.spring.boot

mybatis-spring-boot-starter

${mybatis-spring-boot-starter.version}

commons-dbcp

commons-dbcp

${commons-dbcp.version}

com.dangdang

sharding-jdbc-core

${sharding-jdbc.version}

com.dangdang

sharding-jdbc-config-spring

${sharding-jdbc.version}

com.dangdang

sharding-jdbc-self-id-generator

${sharding-jdbc.version}

com.google.code.gson

gson

${com.google.code.gson.version}

org.springframework.boot

spring-boot-starter-web

${spring.boot.version}

org.springframework.boot

spring-boot-start-logging

org.springframework.boot

spring-boot-starter-test

${spring.boot.version}

test

org.springframework.boot

spring-boot-starter-log4j2

${spring.boot.version}

log4j

log4j

org.springframework.boot

spring-boot-starter

${spring.boot.version}

org.springframework.boot

spring-boot-start-logging

logback-classic

ch.qos.logback

log4j-over-slf4j

org.slf4j

mysql

mysql-connector-java

${mysql-connector-java.version}

org.springframework.boot

spring-boot-maven-plugin

${spring.boot.version}

org.apache.maven.plugins

maven-compiler-plugin

3.1

${project.build.jdk}

${project.build.jdk}

${project.build.sourceEncoding}

org.apache.maven.plugins

maven-jar-plugin

2.4

其实这个和sharding-jdbc的官网差不多。其实我想写一个sharding-jdbc-spring-boot-starter的pom的,等项目业务都做完再说吧。

四、配置数据源

我想将数据库做成可配置的,所以我没有在application.properties文件中直接配置数据库,而是写在了database.json文件中。

[

{

"name": "db_sh",

"url": "jdbc:mysql://localhost:3306/db_sh",

"username": "root",

"password": "root",

"driveClassName":"com.mysql.jdbc.Driver"

},

{

"name": "db_sz",

"url": "jdbc:mysql://localhost:3306/db_sz",

"username": "root",

"password": "root",

"driveClassName":"com.mysql.jdbc.Driver"

}

]

然后在springboot读取database文件,加载方式如下:

@Value("classpath:database.json")

private Resource databaseFile;

@Bean

public List databases() throws IOException {

String databasesString = IOUtils.toString(databaseFile.getInputStream(), Charset.forName("UTF-8"));

List databases = new Gson().fromJson(databasesString, new TypeToken>() {

}.getType());

return databases;

}

加载完database信息之后,可以通过工厂方法配置逻辑数据库:

@Bean

public HashMap dataSourceMap(List databases) {

Map dataSourceMap = new HashMap<>();

for (Database database : databases) {

DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();

dataSourceBuilder.url(database.getUrl());

dataSourceBuilder.driverClassName(database.getDriveClassName());

dataSourceBuilder.username(database.getUsername());

dataSourceBuilder.password(database.getPassword());

DataSource dataSource = dataSourceBuilder.build();

dataSourceMap.put(database.getName(), dataSource);

}

return dataSourceMap;

}

这样就把各个逻辑数据库就加载好了。

五、配置分片策略

5.1数据库分片策略

在这个实例中,数据库的分库就是根据上海(sh)和深圳(sz)来分的,在sharding-jdbc中是单键分片。根据官方文档实现接口SingleKeyDatabaseShardingAlgorithm就可以

@service

public class DatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm {

/**

* 根据分片值和SQL的=运算符计算分片结果名称集合.

*

* @param availableTargetNames 所有的可用目标名称集合, 一般是数据源或表名称

* @param shardingValue 分片值

*

* @return 分片后指向的目标名称, 一般是数据源或表名称

*/

@Override

public String doEqualSharding(Collection availableTargetNames, ShardingValue shardingValue) {

String databaseName = "";

for (String targetName : availableTargetNames) {

if (targetName.endsWith(shardingValue.getValue())) {

databaseName = targetName;

break;

}

}

return databaseName;

}

}

此接口还有另外两个方法,doInSharding和doBetweenSharding,因为我暂时不用IN和BETWEEN方法,所以就没有写,直接返回null。

5.2数据表分片策略

数据表的分片策略是根据股票和时间共同决定的,在sharding-jdbc中是多键分片。根据官方文档,实现MultipleKeysTableShardingAlgorithm接口就OK了

@service

public class TableShardingAlgorithm implements MultipleKeysTableShardingAlgorithm {

/**

* 根据分片值计算分片结果名称集合.

*

* @param availableTargetNames 所有的可用目标名称集合, 一般是数据源或表名称

* @param shardingValues 分片值集合

*

* @return 分片后指向的目标名称集合, 一般是数据源或表名称

*/

@Override

public Collection doSharding(Collection availableTargetNames, Collection> shardingValues) {

String name = null;

Date time = null;

for (ShardingValue> shardingValue : shardingValues) {

if (shardingValue.getColumnName().equals("name")) {

name = ((ShardingValue) shardingValue).getValue();

}

if (shardingValue.getColumnName().equals("time")) {

time = ((ShardingValue) shardingValue).getValue();

}

if (name != null && time != null) {

break;

}

}

String timeString = new SimpleDateFormat("yyyy_MM").format(time);

String suffix = name + "_" + timeString;

Collection result = new LinkedHashSet<>();

for (String targetName : availableTargetNames) {

if (targetName.endsWith(suffix)) {

result.add(targetName);

}

}

return result;

}

}

这些方法的使用可以查官方文档。

5.3注入分片策略

以上只是定义了分片算法,还没有形成策略,还没有告诉shrding将哪个字段给分片算法:

@Configuration

public class ShardingStrategyConfig {

@Bean

public DatabaseShardingStrategy databaseShardingStrategy(DatabaseShardingAlgorithm databaseShardingAlgorithm) {

DatabaseShardingStrategy databaseShardingStrategy = new DatabaseShardingStrategy("exchange", databaseShardingAlgorithm);

return databaseShardingStrategy;

}

@Bean

public TableShardingStrategy tableShardingStrategy(TableShardingAlgorithm tableShardingAlgorithm) {

Collection columns = new LinkedList<>();

columns.add("name");

columns.add("time");

TableShardingStrategy tableShardingStrategy = new TableShardingStrategy(columns, tableShardingAlgorithm);

return tableShardingStrategy;

}

}

这样才能形成完成的分片策略。

六、配置Sharding-jdbc的DataSource

sharding-jdbc的原理其实很简单,就是自己做一个DataSource给上层应用使用,这个DataSource包含所有的逻辑库和逻辑表,应用增删改查时,他自己再修改sql,然后选择合适的数据库继续操作。所以这个DataSource创建很重要。

@Bean

@Primary

public DataSource shardingDataSource(HashMap dataSourceMap, DatabaseShardingStrategy databaseShardingStrategy, TableShardingStrategy tableShardingStrategy) {

DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap);

TableRule tableRule = TableRule.builder("tick").actualTables(Arrays.asList("db_sh.tick_a_2017_01", "db_sh.tick_a_2017_02", "db_sh.tick_b_2017_01", "db_sh.tick_b_2017_02", "db_sz.tick_a_2017_01", "db_sz.tick_a_2017_02", "db_sz.tick_b_2017_01", "db_sz.tick_a_2017_02")).dataSourceRule(dataSourceRule).build();

ShardingRule shardingRule = ShardingRule.builder().dataSourceRule(dataSourceRule).tableRules(Arrays.asList(tableRule)).databaseShardingStrategy(databaseShardingStrategy).tableShardingStrategy(tableShardingStrategy).build();

DataSource shardingDataSource = ShardingDataSourceFactory.createDataSource(shardingRule);

return shardingDataSource;

}

这里要着重说一下为什么要用@Primary这个注解,没有这个注解是会报错的,错误大致意思就是DataSource太多了,mybatis不知道用哪个。加上这个mybatis就知道用sharding的DataSource了。这里参考的是jpa的多数据源配置

七、配置mybatis

7.1 Bean

public class Tick {

private long id;

private String name;

private String exchange;

private int ask;

private int bid;

private Date time;

}

7.2 Mapper

很简单,只实现一个插入方法

@Mapper

public interface TickMapper {

@Insert("insert into tick (id,name,exchange,ask,bid,time) values (#{id},#{name},#{exchange},#{ask},#{bid},#{time})")

void insertTick(Tick tick);

}

7.3 SessionFactory配置

还要设置一下tick的SessionFactory:

@Configuration

@MapperScan(basePackages = "com.spartajet.shardingboot.mapper", sqlSessionFactoryRef = "sessionFactory")

public class TickSessionFactoryConfig {

@Bean

public SqlSessionFactory sessionFactory(DataSource shardingDataSource) throws Exception {

final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();

sessionFactory.setDataSource(shardingDataSource);

return sessionFactory.getObject();

}

@Bean

public CommonSelfIdGenerator commonSelfIdGenerator() {

CommonSelfIdGenerator.setClock(AbstractClock.systemClock());

CommonSelfIdGenerator commonSelfIdGenerator = new CommonSelfIdGenerator();

return commonSelfIdGenerator;

}

}

这里添加了一个CommonSelfIdGenerator,sharding自带的id生成器,看了下代码和facebook的snowflake类似。我又不想把数据库的主键设置成自增的,否则数据双向同步会死的很惨的。

八、测试写入

@RunWith(SpringJUnit4ClassRunner.class)

@SpringBootTest

public class SpringbootShardingJdbcDemoApplicationTests {

@Autowired

private TickMapper tickMapper;

@Autowired

private CommonSelfIdGenerator commonSelfIdGenerator;

@Test

public void contextLoads() {

Tick tick = new Tick(commonSelfIdGenerator.generateId().longValue(), "a", "sh", 100, 200, new Date());

this.tickMapper.insertTick(tick);

}

}

成功实现增量分库分表!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值