随着系统数据量的日益增长,在说起数据库架构和数据库优化的时候,我们难免会常常听到分库分表这样的名词。

当然,分库分表有很多的方法论,比如垂直拆分、水平拆分;也有很多的中间件产品,比如MyCat、ShardingJDBC。

根据业务场景选择合适的拆分方法,再选择一个熟悉的开源框架,就能帮助我们完成项目中所涉及到的数据拆分工作。MySQL大全:20个高频知识点+21个性能调优实践+经典面试题(含解析)​shimo.im

本文并不打算就这些方法论和开源框架展开深入的探讨,笔者想讨论另外一个场景:如果系统中需要拆分的表并不多,只是1个或者少量的几个,我们是否值得引入一些相对复杂的中间件产品;特别是,如果我们对它们的原理不甚了解,是否有信心驾驭它们 ?

基于此,如果你的系统中有少量的表需要拆分,也没有专门的资源去研究开源组件,那么我们可以自己来实现一个简单的分库分表插件;当然,如果你的系统比较复杂,业务量较大,还是采用开源组件或者团队自研组件来解决这事较为稳妥。

一、原理

分库分表这事说简单也简单,说复杂那也挺复杂...

简单是因为它的核心流程比较明确。就是解析SQL语句,然后根据预先配置的规则,重写或路由到真实的数据库表中去;

复杂在于,SQL语句复杂且灵活,比如分页、去重、排序、分组、聚合、关联查询等操作,如何正确的解析它们。

所以就算是ShardingJDBC,在官网中也明确了支持项和不支持项。

二、注解式配置

相对于复杂的配置文件,我们采用较为轻便的注解式配置,它的定义如下:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Sharding {

String tableName(); //逻辑表名

String field(); //分片键

String mode(); //算法模式

int length() default 0; //分表数量

}

那么,在哪里使用它呢 ? 比如我们的用户表需要分表,那就在User这个实体对象上标注。

@Data

@Sharding(tableName = "user",field = "id",mode = "hash",length = 16)

public class User {

private Long id;

private String name;

private String address;

private String tel;

private String email;

}

这就说明了,我一共有 16 张用户表,根据用户ID,使用Hash算法来计算它的位置。

当然,我们不止有Hash算法,还可以根据日期范围来定义。

@Data

@Sharding(tableName = "car",field = "creatTime",mode = "range")

public class Car {

private long id;

private String number;

private String brand;

private String creatTime;

private long userId;

}

三、分片算法

在这里,笔者实现了两种分片方式,就是HashAlgorithm和RangeAlgorithm 。

1、范围分片

如果你的系统中有使用冷热数据分离,我们可以按照日期将不同月的数据分散到不同的表中。

比如车辆的创建时间是2019-12-10 15:30:00,这条数据将会被分配到car_201912这张表中去。

我们通过截取时间的年月部分,然后再加上逻辑表名即可。

public class RangeAlgorithm implements Algorithm {

@Override

public String doSharding(String tableName, Object value,int length) {

if (value!=null){

try{

DateUtil.parseDateTime(value.toString());

String replace = value.toString().substring(0, 7).replace("-", "");

String newName = tableName+"_"+replace;

return newName;

}catch (DateException ex){

logger.error("时间格式不符合要求!传入参数:{},正确格式:{}",value.toString(),"yyyy-MM-dd HH:mm:ss");

return tableName;

}

}

return tableName;

}

}

2、Hash分片

在Hash分片算法中,我们可以先判断表的数量,是不是2的幂次方。如果不是,就通过算数方式获取下标,如果是呢,就通过位运算的方式获取下标。当然了,这是在HashMap源码中学到的哦。

public class HashAlgorithm implements Algorithm {

@Override

public String doSharding(String tableName, Object value,int length) {

if (this.isEmpty(value)){

return tableName;

}else{

int h;

int hash = (h = value.hashCode()) ^ (h >>> 16);

int index;

if (is2Power(length)){

index = (length - 1) & hash;

}else {

index = Math.floorMod(hash, length);

}

return tableName+"_"+index;

}

}

}

四、拦截器

配置和分片算法都有了,接下来就是重头戏了。在这里,我们使用Mybatis拦截器将它们派上用场。

常年CRUD的我们,都知道一条业务SQL肯定逃不出它们的范围。其中,在业务上我们的删除功能一般都是逻辑删除,所以,基本上不会有DELETE操作。

相较而言,新增和修改SQL都比较简单且格式固定,查询SQL往往比较灵活且复杂。所以,在这里笔者定义了两个拦截器。

不过,在介绍拦截器之前,我们有理由要了解另外两个东西:SQL语法解析器和分片算法处理器。

1、JSqlParser

JSqlParser负责解析SQL语句,并转化为Java类的层次结构。我们可以先看个简单的例子来认识它。

public static void main(String[] args) throws JSQLParserException {

String insertSql = "insert into user (id,name,age) value(1001,'范闲',20)";

Statement parse = CCJSqlParserUtil.parse(insertSql);

Insert insert = (Insert) parse;

String tableName = insert.getTable().getName();

List columns = insert.getColumns();

ItemsList itemsList = insert.getItemsList();

System.out.println("表名:"+tableName+" 列名:"+columns+" 属性:"+itemsList);

}

输出: 表名:user 列名:[id, name, age] 属性:(1001, '范闲', 20)

我们可以看到,JSqlParser可以解析出SQL的语法信息。相应的,我们也可以更改对象内容,从而达到修改SQL语句的目的。

2、算法处理器

我们的分片算法有多个,具体应该调用哪一个是在程序运行期来决定的。所以,我们使用一个Map先将算法注册起来,然后根据分片模式来调用它。这也是策略模式的体现。

@Component

public class AlgorithmHandler {

private Map algorithm = new HashMap<>();

@PostConstruct

public void init(){

algorithm.put("range",new RangeAlgorithm());

algorithm.put("hash",new HashAlgorithm());

}

public String handler(String mode,String name,Object value,int length){

return algorithm.get(mode).doSharding(name, value,length);

}

}

3、拦截器

我们知道,MyBatis允许你在已映射语句执行过程中的某一点进行拦截调用。

如果你对它的原理还不熟悉,那么可以先看看笔者的文章:Mybatis拦截器的原理。

整体来看,它的流程如下:通过Mybatis拦截待执行的SQL;

通过JSqlParser解析SQL,获取逻辑表名等;

调用分片算法获取真实表名;

修改SQL,并修改BoundSql;

Mybatis执行修改后的SQL,达成目的。

比如,对于insert语句和update语句,它的核心代码如下:

五、查询及分页

事实上,新增和修改都比较简单,较为复杂的是查询语句。

但是,我们的插件并不在于要满足所有的查询语句,而是可以根据真实的业务场景来扩展修改。

不过分页功能基本上是逃不开的。拿PageHelper为例,它的原理也是通过Mybatis拦截器来实现的。如果它和我们的分表插件在一起,可能会产生冲突。

所以在分表插件中,笔者也集成了分页功能,基本上和PageHelper一样,但并未直接使用它。另外,对于查询来说,在查询条件中是否带有分片键,也是很关键的地方。

1、查询

在范围算法中,在业务上我们要求只查询特定某一个月或者近几个月的数据即可;在Hash算法中,我们则要求每次都带有主键。

但第二个条件往往不能成立,业务方也满足不了每次都必须带有主键。

针对这种情况,我们只能遍历所有的表,查询符合条件的数据,然后再汇总返回;

这种方式的缺点显而易见,性能较差。还有一种方式就是可以将常用的查询条件与分片键建立映射关系,在查询时先根据查询条件找到分片键的字段值,然后再根据分片键查询。

2、分页

如上所言,插件中集成了分页功能,实现流程与PageHelper一样,但考虑到冲突,并未直接使用。

mysql分表插件_分库分表简单?那我想问如何实现“分库分表插件”?相关推荐

  1. mysql 前沿表设计_史上最简单MySQL教程详解(基础篇)之表的维护和改造

    表结构修改 在我们实际的开发的过程,随着开发的深入,会发现我们事先设计好的表可能已经不再适合,就会设计到对表的修改和改造.这里我就向大家介绍一下一些常用的方法和情况.这里我们使用的是之前在中就已经使用 ...

  2. db2查看表结构_作为后端开发如何设计数据库系列文章(一)设计传统系统表结构(Java开发)...

    本篇为第一篇.讲解传统系统的表结构设计(Java开发). 讲讲如何避免数据库设计的一些坑,方便后期的开发与维护. 以前经常能够看到,数据库范式,现在说数据库三大范式的少了. 三大范式我以前也很严格的弄 ...

  3. cte公用表表达式_在SQL Server中使用CTE进行插入和更新(公用表表达式)

    cte公用表表达式 In CTEs in SQL Server; Querying Common Table Expressions the first article of this series, ...

  4. mysql交叉查询教程_史上最简单的 MySQL 教程(二十六)「连接查询(上)」

    连接查询连接查询:将多张表(大于等于 2 张表)按照某个指定的条件进行数据的拼接,其最终结果记录数可能有变化,但字段数一定会增加. 连接查询的意义:在用户查询数据的时候,需要显示的数据来自多张表. 连 ...

  5. php项目排期表模板,最近在开发后台管理,想问下广告排期表怎么做?

    类似这样的表格,请问是用某个JS插件还是PHP程序画? 如果是用PHP写.请问具体的思路怎么样? 回复讨论(解决方案) 导出为excel 可以设置单元格的颜色 导出为excel 可以设置单元格的颜色 ...

  6. ps cs6 磨皮插件_磨皮就是几秒的事!2020顶级PS一件磨皮插件DR5、Portaiture分享

    今天Lisa为大家带来的是两款PS磨皮的插件,是目前评价最高的两款插件.根据使用习惯不同,大家可以自行选择使用. 1.DR5 DR5是delicious retouch 5中文版简称DR5.是一款简单 ...

  7. mysql分库分表面试_【53期】面试官:谈一下数据库分库分表之后,你是如何解决事务问题?...

    点击上方"Java面试题精选",关注公众号 面试刷图,查缺补漏 >>号外:往期面试题,10篇为一个单位归置到本公众号菜单栏->面试题,有需要的欢迎翻阅. 一.概述 ...

  8. mysql数据库 头像字段_模仿陌陌八张头像的数据库,应该如何建表才合适?

    我想大家都已经看过我以前的问题了.就是吐槽创业好难.自己能力不足,基本上我获得的经验都是segmentfault上面一个个提问得来的.在这里,我要感谢哪些帮助过我的人,不管是多么幼稚的问题都会有人热心 ...

  9. datagridview如何将sqlite实现多表查询_服气!月薪3W的Exceler,居然是这样合并多表数据的...

    私信回复关键词[up],获取VLOOKUP函数用法教程合集! Excel 中最伟大的发明是「数据透视表」. 如果给这个最上再加一个最,那最最伟大的肯定是 Power Query. 可惜,很多同学还在用 ...

最新文章

  1. lncRNA研究利器之TANRIC
  2. Android从无知到有知——NO.7
  3. 你根本想象不到,学霸到底经历过什么
  4. 计算机操作系统(11):负载均衡
  5. 逻辑回归能摆平二分类因变量,那……不止二分类呢?
  6. Hbase+Phoenix+Mybatis+Springboot整合查询数据
  7. 奇异值分解(SVD)详解
  8. arrayvalue php,phparrayvalue
  9. 远程桌面控制VC源码剖析
  10. 汇编语言---计算绝对值
  11. html中居中方法,HTML中5种常见的居中方法
  12. ArduinoUNO实战-第六章-电位器实验
  13. Quartus ii 13.1 数字时钟
  14. kali下钓鱼网站的制作
  15. 熊出没之奇幻空间里面的机器人图片_《熊出没之奇幻空间》里面令人触动的两个角色...
  16. 【用html做个人简历的网页(初级)】
  17. Minimum supported Gradle version is 6.1.1. Current version is 5.4.1.
  18. 【技术问题】浏览器主页修复
  19. 同比与环比——财务小知识点
  20. java后台获取微信分享二维码 并返回给前端

热门文章

  1. 海蜘蛛系统日志怎么保存到服务器,海蜘蛛路由安装与启动中常见问题及解决方法...
  2. 猿辅导服务端开发面试--秋招正式批
  3. 6天5个询盘!新手有效用领英开发外贸客户的六个关键点
  4. ROS调用笔记本摄像头和外界摄像头问题汇总(本人亲身经历)非常好用
  5. MODIS地表温度产品处理
  6. ssh xm 工具_常用连接Linux的SSH工具、SFTP工具
  7. Linux软硬链接和软硬限制
  8. 关于 SolidWorks 2012 激活过期或删不干净的问题解决
  9. MA、EMA、SMA的区别
  10. DevExpress报表设计器自定义工具箱