jdbc shadring 扩容_shadring-jdbc解决查询数据库分库分表的问题
问题背景
业务组在最开始的数据库规划中,通过对未来数据规模的预估后,一开始就进行了分库分表的设计。简单的说,一个业务分为16个库,其中每个库有64张业务表…过去经常使用的和数据库交互的经验就完全派不上用场了,难道真的丑陋的在代码中组装16个连接然后每个语句都去拼接吗?对不起做不到。
将自己的需求梳理清楚后可以看清楚,我需要的是一个不涉及原数据库的改动(不通过增加中间件),可以简单封装我的查询请求发送到所有分库分表中,让代码简洁好看(虽然实际上可能真的要做n * 16个数据库连接)直接一次能查询 16个库 * 64张表 的一个依赖,通过谷歌后,发现了一个叫做sharding-jdbc的开源完全贴合我的需求。
工具介绍
首先放上github的链接sharding-jdbc
这边抄袭一段介绍来凑字数:
Sharding-JDBC直接封装JDBC API,可以理解为增强版的JDBC驱动,旧代码迁移成本几乎为零:
可适用于任何基于java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
可基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid等。
理论上可支持任意实现JDBC规范的数据库。虽然目前仅支持MySQL,但已有支持Oracle,SQLServer,DB2等数据库的计划
事实上给我的使用体验还不错,当然也是因为我的查询语句是最简单的普通查询语句,并没有涉及到太复杂的操作。翻阅issues的时候还是能看到这种第三方的依赖对于一些复杂查询的支持不足。但现在已经完全满足我的需求了。
操作步骤
正常的一个ORM框架查询数据库的应用是比较固定和规范的,由DAO层来负责数据库的沟通,本次我的应用简单的使用了Mybatis来做和Mysql的沟通。DAO层代码简单如下
public interface MtxxTagMapper {
@Select("select tag_id,tag_name,uid,`desc`,status from tb_tag where tag_id = #{tag_id}")
public Map findTagByTagId(@Param("tag_id") String tag_id);
}
是的就是这么简单操作。但是最大的麻烦就在于,并没有tb_tab表,或者人人都是tb_tag表。根据tag_id进行hash过后,tb_tag表被横向分布在16个库和64张表中,这条语句其实是无法执行的。所以重点不在DAO层而在于配置层,这也应了上面的介绍的话
Sharding-JDBC直接封装JDBC API,可以理解为增强版的JDBC驱动,旧代码迁移成本几乎为零
确实在查询层几乎不需要怎么动,不过复杂应用另说吧。
重点其实在于,数据源的配置,这里我使用了javaconfig的方式来配置,代码如下
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.feed")
@MapperScan(basePackages = "com.meitu.dump.mapper.mtxx.tag", sqlSessionFactoryRef = "mtxxTagSqlSessionFactory")
public class MtxxTagDataSourceConfig {
private String url;
private String username;
private String password;
@Bean(name = "mtxxTagDataSource")
public DataSource getDataSource() throws SQLException {
//设置分库映射
Map dataSourceMap = new HashMap<>(16);
dataSourceMap.put("xiuxiu_feed_0", mybatisDataSource("xiuxiu_feed_0"));
dataSourceMap.put("xiuxiu_feed_1", mybatisDataSource("xiuxiu_feed_1"));
dataSourceMap.put("xiuxiu_feed_2", mybatisDataSource("xiuxiu_feed_2"));
dataSourceMap.put("xiuxiu_feed_3", mybatisDataSource("xiuxiu_feed_3"));
dataSourceMap.put("xiuxiu_feed_4", mybatisDataSource("xiuxiu_feed_4"));
dataSourceMap.put("xiuxiu_feed_5", mybatisDataSource("xiuxiu_feed_5"));
dataSourceMap.put("xiuxiu_feed_6", mybatisDataSource("xiuxiu_feed_6"));
dataSourceMap.put("xiuxiu_feed_7", mybatisDataSource("xiuxiu_feed_7"));
dataSourceMap.put("xiuxiu_feed_8", mybatisDataSource("xiuxiu_feed_8"));
dataSourceMap.put("xiuxiu_feed_9", mybatisDataSource("xiuxiu_feed_9"));
dataSourceMap.put("xiuxiu_feed_10", mybatisDataSource("xiuxiu_feed_10"));
dataSourceMap.put("xiuxiu_feed_11", mybatisDataSource("xiuxiu_feed_11"));
dataSourceMap.put("xiuxiu_feed_12", mybatisDataSource("xiuxiu_feed_12"));
dataSourceMap.put("xiuxiu_feed_13", mybatisDataSource("xiuxiu_feed_13"));
dataSourceMap.put("xiuxiu_feed_14", mybatisDataSource("xiuxiu_feed_14"));
dataSourceMap.put("xiuxiu_feed_15", mybatisDataSource("xiuxiu_feed_15"));
List actualTables = new ArrayList<>();
for(int i = 0; i < XiuXiuTableItemHelper.tableNums; i++){
actualTables.add(String.format("tb_tag_%s", Integer.toString(i)));
}
//设置默认库,两个库以上时必须设置默认库。默认库的数据源名称必须是dataSourceMap的key之一
DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap, "xiuxiu_feed_0");
//设置分表映射
TableRule userTableRule = TableRule.builder("tb_tag")
.generateKeyColumn("tag_id") //将user_id作为分布式主键
.actualTables(actualTables)
.dataSourceRule(dataSourceRule)
.build();
//具体分库分表策略
ShardingRule shardingRule = ShardingRule.builder()
.dataSourceRule(dataSourceRule)
.tableRules(Collections.singletonList(userTableRule))
.databaseShardingStrategy(new DatabaseShardingStrategy("tag_id", new ModuloDatabaseShardingAlgorithm()))
.tableShardingStrategy(new TableShardingStrategy("tag_id", new ModuloTableShardingAlgorithm()))
.build();
DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule);
//return new ShardingDataSource(shardingRule);
return dataSource;
}
private DataSource mybatisDataSource(final String dataSourceName) throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl(String.format(url, dataSourceName));
dataSource.setUsername(username);
dataSource.setPassword(password);
/* 配置初始化大小、最小、最大 */
dataSource.setInitialSize(1);
dataSource.setMinIdle(1);
dataSource.setMaxActive(20);
/* 配置获取连接等待超时的时间 */
dataSource.setMaxWait(60000);
/* 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
dataSource.setTimeBetweenEvictionRunsMillis(60000);
/* 配置一个连接在池中最小生存的时间,单位是毫秒 */
dataSource.setMinEvictableIdleTimeMillis(300000);
dataSource.setValidationQuery("SELECT 'x'");
dataSource.setTestWhileIdle(true);
dataSource.setTestOnBorrow(false);
dataSource.setTestOnReturn(false);
/* 打开PSCache,并且指定每个连接上PSCache的大小。
如果用Oracle,则把poolPreparedStatements配置为true,
mysql可以配置为false。分库分表较多的数据库,建议配置为false */
dataSource.setPoolPreparedStatements(false);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
/* 配置监控统计拦截的filters */
// dataSource.setFilters("stat,wall,log4j");
return dataSource;
}
/**
* Sharding-jdbc的事务支持
*
* @return
*/
@Bean(name = "mtxxTagTransactionManager")
public DataSourceTransactionManager mybatisTransactionManager(@Qualifier("mtxxTagDataSource") DataSource dataSource) throws SQLException {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "mtxxTagSqlSessionFactory")
public SqlSessionFactory mybatisSqlSessionFactory(@Qualifier("mtxxTagDataSource") DataSource mybatisDataSource)
throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(mybatisDataSource);
return sessionFactory.getObject();
}
}
在这里我确实配置了16个数据库的链接,不过是通过工厂方式生成的。对于shadring-jdbc来说,他引申出的概念叫做逻辑库和逻辑表。在配置中描述逻辑库和逻辑表的生成规则,然后以后用查询逻辑库与逻辑表来代替。我们看看是在哪里描述这个逻辑
//设置分表映射
TableRule userTableRule = TableRule.builder("tb_tag")
.generateKeyColumn("tag_id") //将user_id作为分布式主键
.actualTables(actualTables)
.dataSourceRule(dataSourceRule)
.build();
//具体分库分表策略
ShardingRule shardingRule = ShardingRule.builder()
.dataSourceRule(dataSourceRule)
.tableRules(Collections.singletonList(userTableRule))
.databaseShardingStrategy(new DatabaseShardingStrategy("tag_id", new ModuloDatabaseShardingAlgorithm()))
.tableShardingStrategy(new TableShardingStrategy("tag_id", new ModuloTableShardingAlgorithm()))
.build();
分表映射规则采用了链式生产对象的方式来描述,generateKeyColumn指定了虚拟表的主键,actualTables用来存储所有真实表的名字。
分库映射规则除了包含分表规则以外,更重要的是指定了如何计算分库和分表尾缀的逻辑。方法databaseShardingStrategy(new DatabaseShardingStrategy("tag_id", new ModuloDatabaseShardingAlgorithm()))指定了分库的逻辑类,方法.tableShardingStrategy(new TableShardingStrategy("tag_id", new ModuloTableShardingAlgorithm()))指定了分表的逻辑类。而这两个类的逻辑由我们自己继承接口实现而得。
再看分库实现类
@Slf4j
public class ModuloDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm {
@Override
public String doEqualSharding(Collection collection, ShardingValue shardingValue) {
int databaseNum = XiuXiuTableItemHelper.getDatabase(shardingValue.getValue().toString());
for (Object database : collection) {
if (database.toString().endsWith("_" + Integer.toString(databaseNum))) {
return database.toString();
}
}
return null;
}
@Override
public Collection doInSharding(Collection collection, ShardingValue shardingValue) {
return null;
}
@Override
public Collection doBetweenSharding(Collection collection, ShardingValue shardingValue) {
return null;
}
}
类只要实现了SingleKeyDatabaseShardingAlgorithm即可作为逻辑类使用,3个实现方法分别是对应sql的一些语句使用,目前我只实现了doEqualSharding方法,我的语句where xx =就会执行这个方法来获得具体库名,在逻辑更复杂的情况下旧需要考虑另外2个方法的实现。
public String doEqualSharding(Collection collection, ShardingValue shardingValue)方法中,collection是所有真实存在的库名,也就是刚刚配置代码中我们注册进去的16个库名的集合,而shardingValue指的是我们设置用来分片的字段,generateKeyColumn("tag_id")。根据业务,我需要通过对tag_id进行hash后得到在[0-15]中对应的数据做为库名尾缀,所以我的逻辑就变成这样:
循环所有真实库名
真实库名是否包含 "_n"(n为计算分库算法得到的尾缀)
返回真实库名
到此就完成了一个简单的分库查询
jdbc shadring 扩容_shadring-jdbc解决查询数据库分库分表的问题相关推荐
- 数据库分库分表和带来的唯一ID、分页查询问题的解决
数据库分库分表和带来的唯一ID.分页查询问题的解决 参考文章: (1)数据库分库分表和带来的唯一ID.分页查询问题的解决 (2)https://www.cnblogs.com/hanzhong/p/1 ...
- 数据库分库分表(sharding)系列(五) 一种支持自由规划无须数据迁移和修改路由代码的Sharding扩容方案...
为什么80%的码农都做不了架构师?>>> 版权声明:本文由本人撰写并发表于2012年9月份的<程序员>杂志,原文题目<一种支持自由规划的Sharding扩容方 ...
- Mycat - 数据库分库分表中间件,国内最活跃的、性能最好的开源数据库中间件
转载自 Mycat - 数据库分库分表中间件,国内最活跃的.性能最好的开源数据库中间件 Mycat是什么 Mycat - 数据库分库分表中间件,国内最活跃的.性能最好的开源数据库中间件! 一个彻底开源 ...
- 数据库分库分表中间件对比(很全)
数据库(分库分表)中间件对比 分区:对业务透明,分区只不过把存放数据的文件分成了许多小块,例如mysql中的一张表对应三个文件.MYD,MYI,frm. 根据一定的规则把数据文件(MYD)和索引文件( ...
- 亿级流量网站架构核心技术之“数据库分库分表策略”
本文节选自<亿级流量网站架构核心技术--跟开涛学搭建高可用高并发系统>一书 张开涛 著 电子工业出版社出版 小编会从留言中选择获赞最多的前五名用户免费送出此书哦!规则见文末. 数据库分库分 ...
- 一文搞懂MySQL数据库分库分表
如果数据量过大,大家一般会分库分表.分库需要注意的内容比较少,但分表需要注意的内容就多了. 工作这几年没遇过数据量特别大的业务,那些过亿的数据,因为索引设置合理,单表性能没有影响,所以实战中一直没用过 ...
- 数据库分库分表(sharding)系列(三) 关于使用框架还是自主开发以及sharding实现层面的考量...
当团队对系统业务和数据库进行了细致的梳理,确定了切分方案后,接下来的问题就是如何去实现切分方案了,目前在sharding方面有不少的开源框架和产 品可供参考,同时很多团队也会选择自主开发实现,而不管是 ...
- 别再问什么是数据库分库分表了,看这里!
编者语:为了避免被误解为:「手里有把锤子,看什么都是钉子!」,说明一下不是什么业务都适合分布式数据库,更不是用了分布式数据库性能就一定能得到扩展. 其次:本文为纯干货,建议先转发.收藏再观看. 分布式 ...
- 数据库分库分表,分片配置轻松入门!
上次和大伙聊了 MyCat 的安装,今天来说一个新的话题,就是数据库的分片. 当我们把 MyCat + MySQL 的架构搭建完成之后,接下来面临的一个问题就是,数据库的分片规则:有那么多 MySQL ...
最新文章
- spdlog源码阅读 (1): sinks
- .net firamework 框架里面的控件的继承关系。
- nginx配合modsecurity实现WAF功能
- oracle12c分页,ArcSDE10.2.1使用Oracle12c新特性分页
- 一篇很全面的freemarker 前端web教程
- sqlserver使用存储过程发送http请求
- android 手动 打包,android 手动打包apk
- 下载Java Jar包的网站(托管厂库)
- 12-基于selenium实现12306模拟登录,及京东登录滑动缺口验证模拟登录
- C++中vector作为参数的三种传参方式
- linux每天一小步---sed命令详解
- 鸿蒙系统的升级名单,定档6月2日!鸿蒙“首批”升级名单公布,共计11款华为机型!...
- react-native 解决“Could not get BatchedBridge...” 的问题
- 《概念与类比》:侯世达的双翼
- hsqldb mysql_HSQLDB的研究与性能测试(与Mysql对比)
- 线性代数知识点(行列式篇)
- VBA 获取某列最后非空单元格的行数
- python闰年统计_利用Python写一个闰年计算器和每月天数计算器
- 深度学习经典试题29道
- ViewPager(一屏多页、无限滑动、自动切换)