sharding-method分表分库的新思路——服务层Sharding框架,全SQL、全数据库兼容,ACID特性与原生数据库一致,能实现RR级别读写分离,无SQL解析性能更高
目前国内主流的Sharding框架都是基于SQL来完成,其主要流程:
1)是解析上层传入的SQL
2)结合对应的分表分库配置,对传入的SQL进行改写并分发到对应的单机数据库上
3)获得各个单机数据库的返回结果后,根据原SQL归并结果,返回用户期待的结果
这种实现希望提供一个屏蔽底层Sharding逻辑的解决方案,对上层应用来说,只有一个RDB,这样应用可以透明访问多个数据库。
然而,这仅仅只是一个美丽的目标。因种种原因,这些层次的Sharding方案都无法提供跟原生数据库一样的功能:
1)ACID里的A无法保证
2)ACID里的C可能被打破
3)ACID里的I与原生不一致
4)由于SQL解析复杂,性能等考虑,很多数据库SQL不支持
正因为存在这些差异,本质上,上层应用必须明确的知道经过此类Sharding方案后得到的查询结果、事务结果与原生的有啥不一致才能写出正确可靠的程序。
因此,基于SQL的Sharding方案对应用层并不透明。
如果要基于SQL层的框架写出正确可靠的代码的话,我们需要遵循一些范式:
1)所有事务(包括读、写)都不能跨库
2)跨分片的查询提供的隔离级别与原生不一致
3)某些聚合查询的性能消耗很大,要慎用
这些范式对于实际上就是使用Sharding数据库框架时,不透明的表现。而这些表现都是隐式的隐藏于SQL中,难以REVIEW。
而且这些范式对于很多人来说不一定能够充分理解执行的含义,以至于忽略了。
由上面最重要的一点“所有事务(包括读、写)都不能跨库”决定,一个合理设计的代码里绝大多数的业务代码中数据库访问都不会跨分区,核心业务代码都在同一分区内进行。 因此,我们大多数情况下,需要的只是一个协助我们便捷选择对应分片的一个框架。
基本注解使用方法
@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分片数据的复杂度。
@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
无法实现单库分表
其实,单库分表并不是必须的,这可以用数据原生的表分区来实现,性能一样,使用更便捷
相关推荐
本文将详细介绍如何使用SpringBoot、Mybatis-Plus以及Sharding-JDBC 5.1.1版本来实现单库分表。 #### 二、踩过的坑 在实现过程中遇到了一些常见问题,下面将逐一介绍并提供解决方案: ##### 1. 数据源问题 **...
QNJR-GROUP-sharding-method-00a5aeb可能是一个示例项目或者文档,它可能包含了如何在实际项目中应用ShardingSphere的详细步骤和最佳实践。 总的来说,服务层Sharding框架为Java开发中的数据库扩展提供了一种高效且...
本文将详细介绍如何使用 Sharding JDBC 在 Spring Boot 项目中实现数据分库和读写分离。 排除 DataSourceAutoConfiguration 在使用 Sharding JDBC 之前,需要排除 DataSourceAutoConfiguration,以免多数据源无法...
- **Sending Signals:** A sender can trigger a signal by calling `send()` method on a signal object. This signal can carry data about the event that occurred. - **Receiving Signals:** Receivers are ...
String methodName = joinPoint.getSignature().getName(); if (methodName.startsWith("select")) { // 使用读库 return joinPoint.proceed(new Object[] { secondaryDataSource }); } else { // 使用写库 ...
最初目的是为了解决他们自身数据处理需求而开发的,由于当时市场上缺乏合适的开源数据库解决方案,他们便自行设计了一个基于ISAM(Indexed Sequential Access Method)的存储引擎,用于数据的索引顺序访问。...
* 分布式训练技术的策略:Sharding、Pipeline、数据并行、模型并行等 TePDist架构与特点 * TePDist架构:C/S模式,包括编译和运行时 * TePDist特点: + 以HLO IR作为Server端输入,解耦模型构建语言 + 自动探索...
1. **Java RMI (Remote Method Invocation)**:Java RMI 是一种允许对象在不同JVM之间进行远程调用的技术,它是构建分布式系统的基础。 2. **Java EE (Enterprise Edition)**:Java EE 提供了一套企业级应用开发的...
Redis集群通过将数据分片(sharding)到多个节点来实现水平扩展。在Spring中配置Redis集群,我们需要使用Sentinel或Cluster API来发现和管理节点。 对于Redis Sentinel,配置如下: ```xml <value>...
InnoDB的刷盘策略由`innodb_flush_method`参数控制,有fdatasync、O_DSYNC和O_DIRECT三种模式。每种模式决定了数据何时和如何被写入磁盘和日志文件,以及元数据的同步程度。选择合适的模式可以平衡性能和数据安全性...