全数据库兼容的服务层 Sharding 框架 sharding-method

Apache
Java
跨平台
2017-07-31
skyesx

核心特性

全数据库全SQL兼容、完美RR级别读写分离、与原生一致的ACID特性、轻量简单易扩展

另外一个轮子的意义

很多人会质疑 市面上较为流行的Sharding中间件/应用层Sharing框架已经有很多,他们都已经发展了很久了,功能也很强大,为什么要一个再重复制造这么个轮子呢?

之所以这里有一个新的轮子并不是因为我懒得无事,而是我对目前基于传统RDB上Sharding框架的设计的理念不太赞同,虽然他们或许都很圆,跑的很快,但是使用不当的话,容易翻车。我期望的轮子是既能跑的快,但也能跑的稳。

我们目前国内主流的Sharding框架都是基于SQL来完成,其主要流程:

  1. 是解析上层传入的SQL
  2. 结合对应的分表分库配置,对传入的SQL进行改写并分发到对应的单机数据库上
  3. 获得各个单机数据库的返回结果后,根据原SQL归并结果,返回用户期待的结果

这种实现希望提供一个屏蔽底层Sharding逻辑的解决方案,对上层应用来说,只有一个RDB,这样应用可以透明访问多个数据库。

然而,这仅仅只是一个美丽的目标。因种种原因,这些层次的Sharding方案都无法提供跟原生数据库一样的功能:

  • ACID里的A无法保证
  • ACID里的C可能被打破
  • ACID里的I与原生不一致
  • 由于SQL解析复杂,性能等考虑,很多数据库SQL不支持

正因为存在这些差异,本质上,上层应用必须明确的知道经过此类Sharding方案后得到的查询结果、事务结果与原生的有啥不一致才能写出正确可靠的程序。

因此,基于SQL的Sharding方案对应用层并不透明。

如果要基于SQL层的框架写出正确可靠的代码的话,我们需要遵循一些范式:

  • 所有事务(包括读、写)都不能跨库
  • 跨分片的查询提供的隔离级别与原生不一致
  • 某些聚合查询的性能消耗很大,要慎用
  • ......

这些范式对于实际上就是使用Sharding数据库框架时,不透明的表现。而这些表现都是隐式的隐藏于SQL中,难以REVIEW。

而且这些范式对于很多人来说不一定能够充分理解执行的含义,以至于忽略了。

由上面最重要的一点“所有事务(包括读、写)都不能跨库”决定,一个合理设计的代码里绝大多数的业务代码中数据库访问都不会跨分区,核心业务代码都在同一分区内进行。 因此,我们大多数情况下,需要的只是一个协助我们便捷选择对应分片的一个框架。

因此我的想法很简单,提供一个方便透明选择分片、并辅以自动生成ID的框架。对于需要访问多个分片的少数业务,框架提供手段,便捷地获取所有分片数据库的数据,并由用户自行归并得出所需结果(简单的归并框架可以自动进行)。

基本使用方法

先简略展示以下框架的基本用法(以下代码在UT案例中,但为突出重点,有所裁剪)

Service层

@Service
@ShardingContext(dataSourceSet="orderSet",shardingKeyEls="[user].userId",shardingStrategy="@modUserId",generateIdStrategy="@snowflaker",generateIdEls="[user].userId")
public class UserServceImpl {
	
	@Autowired
	private UserDaoImpl userDao;
	
	@Transactional
	@SelectDataSource
	public void updateUser(User user){
		userDao.updateUser(user);
	}
	
	@Transactional
	@SelectDataSource
	@GenerateId
	public void saveUser(User user){
		userDao.saveUser(user);
	}
	
	@Transactional(readOnly=true)
	@SelectDataSource(keyNameEls="[userId]")
	public User findUser(int userId){
		return userDao.findUser(userId);
	}
	
	public List findAllUsers(){
		return userDao.findAllUsers();
	}
	
	public double calcUserAvgAge(){
		List allUsers = userDao.findAllUsers();
		return allUsers.stream().mapToInt(u->u.getAge()).average().getAsDouble();
	}
}

@ShardingContext表示当前的Service的Sharding上下文,就是说,如果有 选择数据源、Map到各数据库Reduce出结果、生成ID等操作时,如果某些参数没有指定,都从这个ShardingContext里面的配置取

@SelectDataSource表示为该方法内执行的SQL根据Sharding策略选择一个Sharding数据源,在方法结束返回前,不能更改Sharding数据源

@GenerateId表示生成ID,并将其赋值到参数的指定位置

@GenerateId对应的逻辑会先执行,然后到@SelectDataSource然后到@Transaction

@Transactional(readOnly=true)标签指定了事务时只读的,因此框架会根据readOnly标志自动选择读库(如果有的话)

从方法calcUserAvgAge可以看到在JDK8的LAMBADA表达式及Stream功能下,JAVA分析处理集合数据变得极为简单,这会大大减少我们自行加工Sharding分片数据的复杂度。

接下来看DAO层

@Component
public class UserDaoImpl {
	
	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	public void updateUser(User user){
		int update = jdbcTemplate.update("UPDATE `user` SET `name`=? WHERE `user_id`=?;",user.getName(),user.getUserId());
		Assert.isTrue(update == 1,"it should be updated!");
	}
	
	public User findUser(int userId){
		return jdbcTemplate.queryForObject("SELECT * FROM user WHERE user_id = ?", new Object[]{userId}, rowMapper);
	}
	
	@Transactional
	@MapReduce
	public List findAllUsers(){
		return jdbcTemplate.query("SELECT * FROM user", rowMapper);
	}
	
	@Transactional(readOnly=true)
	@MapReduce
	public void findAllUsers(ReduceResultHolder resultHolder){
	    List shardingUsers = jdbcTemplate.query("SELECT * FROM user", rowMapper);
	    resultHolder.setShardingResult(shardingUsers);
	}
}

@MapReduce表示该方法将会在每个数据分片都执行一遍,然后进行数据聚合后返回。 对于聚合前后返回的数据类型一致的方法,调用时可以直接从返回值取得聚合结果。 对于聚合前后返回的数据类型不一致的方法,需要传入一个对象ReduceResultHolder,调用完成后,通过该对象获得聚合结果

默认情况下,框架会提供一个通用Reduce策略,如果是数字则累加返回,如果是Collection及其子类则合并后返回,如果是MAP则也是合并后返回。 如果该策略不适合,那么用户可自行设计指定Reduce策略。

@Transaction表示每一个Sharding执行的SQL都处于一个事务中,并不是表示整个聚合操作是一个整体的事务。所以,MapReduce最好不要进行更新操作(考虑框架层次限制MapReduce只允许ReadOnly事务)。

@MapReduce执行的操作会在@Transaction之前。

优点缺点对比

以上是框架的主要使用形式,我们可以从这种实现中发现服务层的Sharding有以下好处

  • 全数据库、全SQL兼容
    • SQL层Sharding无法做到
  • 能完美实现读写分离
    • 基于SQL层实现的Sharding引入读写分离后,在上层Service感知的事务里,存在混乱的隔离级别的问题,其最多实现RC级别读写分离(若不在Service层介入相关辅助代码的话),而Service层Sharding在Service开始前就能确定该事务是读事务,整个读事务都在一个读库中完成,隔离级别与数据库一致
  • 无额外维护DBProxy可用性的负担
  • 相对于复杂的SQL解析,实现简单,相信花个一天就能看完所有代码,整个框架了如指掌
  • 无SQL解析成本,性能更高
  • 隔离级别及事务原子性等特征与使用的数据库一致,无额外学习负担,易于写出正确的程序
    • 框架限制了所有事务都在单库进行
    • 基于Sql的Sharding即使在非读写分离情况下,因其需要归并多个数据库的结果,其提供的隔离级别也是混乱的,但这个区别并没有显式的提示到程序员。

当然也存在缺点

劣势:

  • 跨库查询需要自行进行结果聚合
    • 是劣势也是优势
    • 劣势:需要完成额外的聚合代码
    • 优势:但其能能更好的调优,使用JDK8的Stream及Lambada表达式,能像写SQL一样简单的完成相关集合处理
  • 跨库事务需要自行保证
    • 是劣势也是优势
    • 劣势:需要额外自行实现跨库事务
    • 优势:目前所有的Sharding框架实现的跨库事务都有缺陷或者说限制,如Sharding-JDBC,Mycat等提供的跨库事务都并非严格意义的ACID,A可能被打破,I也与原生定义的不一样,程序员不熟悉时就很容易写出不可靠的代码。因此自行控制分布式事务,采用显式的事务控制或许是更好的选择。可参考使用本人写的另外一个框架EasyTransaction
  • 无法实现单库分表
    • 其实,单库分表并不是必须的,这可以用数据原生的表分区来实现,性能一样,使用更便捷

具体使用方法

更具体使用案例请参考 测试Package:org.easydevelop.business里的案例

的码云指数为
超过 的项目
加载中

评论(6)

森林木大
思路不错,但很久没更新了,望继续维护
jf2
jf2
分页查询如何聚合
相信你自己
相信你自己
为什么我会这样说。你的语法,要求别人要按照你的要求写。而其他的框架 则没有这个要求。
云飞扬11
云飞扬11
在开发之初就把业务按模块垂直切分好,比如用户角色权限之类的基础信息一个库,然后按模块各建一个库,采用sharding-method做增删改操作其实没有分库操作,但是查询呢?特别是复杂的报表统计,SQL复杂还要跨库查询,这会大降低性能,同时新增的时候同时插入多个库的事务怎么保证呢?
s
skyesx 软件作者
作者自评必须满分~
w
weir2016
我看好这个东西,希望能不断完善

Sharding-method 0.3.0 发布,服务层 Sharding 框架

Sharding-method是一个服务层Sharding框架,其以一种全新的Sharding实现思路协助我们更快更好的实现Sharding,其主要特性为:简单易扩展、全数据库全SQL兼容、完美RR级别读写分离、与原生一致...

2017/08/29 00:07

Sharing-method发布Alpha版本,服务层Sharding框架

Sharding-method是一个服务层Sharding框架,其以一种全新的Sharding实现思路协助我们更快更好的实现Sharding,其主要特性为:全数据库全SQL兼容、完美RR级别读写分离、与原生一致的ACID。 这次...

2017/08/06 21:33

没有更多内容

加载失败,请刷新页面

没有更多内容

暂无问答

Spring源码解析(一) Spring事务控制之Hibernate

本文将对Spring在整合Hibernate事务方面的源码作一下初步的解析,特别是Spring对线程、事务、Hibernate Session三者的绑定关系。(注:本文基于目前最新的Spring 3.1.2 RELEASE 版本的源码进...

2014/10/12 11:41
29
0
数据库中间件 Sharding-JDBC 源码分析 —— JDBC实现与读写分离

摘要: 原创出处 http://www.iocoder.cn/Sharding-JDBC/jdbc-implement-and-read-write-splitting/ 「芋道源码」欢迎转载,保留摘要,谢谢! **本文主要基于 Sharding-JDBC 1.5.0 正式版** - ...

2017/10/22 11:11
311
0
sharding-jdbc源码解析全集

本文转自“天河聊技术”微信公众号 sharding-jdbc源码解析之词法解析 sharding源码解析之api分析 sharding-jdbc源码解析之spring集成 sharding-jdbc源码解析之spring集成分片构造实现 shardi...

2018/05/03 13:05
137
0
数据库分库分表(sharding)系列(三) 关于使用框架还是自主开发以及sharding实现层面的考量

当团队对系统业务和数据库进行了细致的梳理,确定了切分方案后,接下来的问题就是如何去实现切分方案了,目前在sharding方面有不少的开源框架和产品可供参考,同时很多团队也会选择自主开发实...

2015/06/05 11:35
23
0
开源数据库 Sharding 技术 (Share Nothing)

开源数据库 Sharding 技术 (Share Nothing)

2015/07/16 17:15
118
0
分库分表利器——sharding-sphere

sharding-sphere 简介 Sharding-Sphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar这3款相互独立的产品组成。他们均提供标准...

2018/10/05 20:36
34
0
揭秘Sharding-Proxy——面向DBA的数据库中间层

大家好,我今天想跟大家分享的是Sharding-Sphere的第二个产品Sharding-Proxy。 在上个月亮相的Sharding-Sphere 3.0.0.M1中首次发布了Sharding-Proxy,希望这次分享能够通过几个优化实践,帮助...

2018/06/21 15:24
19
0
开源数据库 Sharding 技术 (Share Nothing)

转自http://dbanotes.net/database/database_sharding-2.html 从 Shard 到 Sharding “Shard” 这个词英文的意思是”碎片”,而作为数据库相关的技术用语,似乎最早见于大型多人在线角色扮演...

2014/06/23 17:45
38
0
开源数据库 Sharding 技术 (Share Nothing)

注:此文首发于 《程序员》杂志 2008 年 7 月刊。 从 Shard 到 Sharding “Shard” 这个词英文的意思是”碎片”,而作为数据库相关的技术用语,似乎最早见于大型多人在线角色扮演游戏(MMORPG...

2015/08/20 17:04
49
0

没有更多内容

加载失败,请刷新页面

返回顶部
顶部