当前位置: 首页 > news >正文

分库分表一:ShardingSphere介绍和入门实战

文章目录

  • ShardingSphere介绍和实战
    • ShardingSphere介绍
    • ShardingJDBC介绍
    • ShardingJDBC实战
      • 环境搭建和踩坑
      • 案例一:分库分表
      • 分片策略介绍
      • 案例二:使用标准分片策略实现范围查询

ShardingSphere介绍和实战

ShardingSphere介绍

​ShardingSphere是一款起源于当当网内部的应用框架。在2017年开始开源。并逐渐由原本只关注于关系型数据库增强工具的ShardingJDBC升级成为一整套以数据分片为基础的数据生态圈,更名为ShardingSphere。到2020年4月,已经成为了Apache软件基金会的顶级项目。
ShardingSphere包含三个重要的产品,ShardingJDBC、ShardingProxy和ShardingSidecar。其中sidecar是针对service mesh定位的一个分库分表插件,目前在规划中。其中,ShardingJDBC是用来做客户端分库分表的产品,而ShardingProxy是用来做服务端分库分表的产品。这两者定位有什么区别呢?我们看下官方资料中给出的两个重要的图:
在这里插入图片描述
shardingJDBC定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。它使⽤客户端直连数据库,以 jar 包形式提供服务,⽆需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
在这里插入图片描述
ShardingProxy定位为透明化的数据库代理端,提供封装了数据库⼆进制协议的服务端版本,⽤于完成对异构语⾔的⽀持。⽬前提供 MySQL 和 PostgreSQL 版本,它可以使⽤任何兼容 MySQL/PostgreSQL 协议的访问客⼾端。
很显然,ShardingJDBC只是客户端的一个工具包,可以理解为一个特殊的JDBC驱动包,所有分库分表逻辑均由业务方自己控制,所以他的功能相对灵活,支持的数据库也非常多,但是对业务侵入大,需要业务方自己定制所有的分库分表逻辑。而ShardingProxy是一个独立部署的服务,对业务方无侵入,业务方可以像用一个普通的MySQL服务一样进行数据交互,基本上感觉不到后端分库分表逻辑的存在,但是这也意味着功能会比较固定,能够支持的数据库也比较少。这两者各有优劣。

ShardingJDBC介绍

核心概念

逻辑表:水平拆分的数据库的相同逻辑和数据结构表的总称
真实表:在分片的数据库中真实存在的物理表。
数据节点:数据分片的最小单元。由数据源名称和数据表组成
绑定表:分片规则一致的主表和子表。
广播表:也叫公共表,指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中都完全一致。例如字典表。
分片键:用于分片的数据库字段,是将数据库(表)进行水平拆分的关键字段。SQL中若没有分片字段,将会执行全路由,性能会很差。
分片算法:通过分片算法将数据进行分片,支持通过=、BETWEEN和IN分片。分片算法需要由应用开发者自行实现,可实现的灵活度非常高。
分片策略:真正用于进行分片操作的是分片键+分片算法,也就是分片策略。在ShardingJDBC中一般采用基于Groovy表达式的inline分片策略,通过一个包含分片键的算法表达式来制定分片策略,如t_user_$->{u_id%8}标识根据u_id模8,分成8张表,表名称为t_user_0到t_user_7。

ShardingJDBC实战

4.1.1官网文档

环境搭建和踩坑

基础环境
数据库创建逻辑表 user和对应的真实表 user1、user2

# 真实表sql和逻辑表一致
CREATE TABLE `sharding_user` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `source` varchar(255) DEFAULT 'test',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

在这里插入图片描述
实体类和controller

@Data
@TableName("sharding_user")
public class ShardingUser  implements Serializable {
	@TableId(type = IdType.NONE)
	private Long id;

	private String userName;

	private Integer age;

	private String source;

	private Date createTime;

	private Date updateTime;
}

	@GetMapping("/shardingTest")
	public R shardingTest(String name,Integer age){
		ShardingUser user = new ShardingUser();
		user.setUserName(name);
		user.setAge(age);
		shardingUserService.save(user);
		return R.success("成功");
	}

其他的service mapper impl 按照mybatis-plus模式来创建。保证能够实现基础的增删改查
sharding-jdbc环境
依赖

<dependency>
     <groupId>org.apache.shardingsphere</groupId>
     <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
     <version>4.1.1</version>
</dependency>

springboot2.x整合ShardingJDBC 4.1.1 坑很多,配置好依赖后启动看有没有问题。
启动报错:Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required 启动类添加exclude={DruidDataSourceAutoConfigure.class}
我这里用的是Druid的数据源 所以要排除掉。如果是其他数据源,同理排除掉对应数据源的自动配置类

application.yml添加sharding-jdbc配置

# 顶层是 spring: 开头   因为spring:下面还有其他不相关的配置 ,所以后续一律以shardingsphere:开头展示
shardingsphere:
    props:
      sql:
       show: true
    datasource:
      # 配置逻辑库名
      names: m1
      m1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/sharding_test?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
        username: root
        password: root
    sharding:
      tables:
        # 配置逻辑表名
        sharding_user:
          actual-data-nodes:
            # 逻辑表和真实表的对应关系
            m1.sharding_user$->{1..2}
          # 配置主键生成策略 采用雪花算法
          key-generator:
            column: id
            type: SNOWFLAKE
            props:
              worker:
                id: 1
          # 分表策略
          table-strategy:
            inline:
              # 分片键
              sharding-column: id
              # 分片算法
              algorithm-expression: sharding_user$->{id%2+1}

访问controller的shardingTest方法。
如果报错java.lang.NoClassDefFoundError: org/antlr/v4/runtime/CharStreams 添加依赖

<dependency>
            <groupId>org.antlr</groupId>
            <artifactId>antlr4-runtime</artifactId>
            <version>4.9.2</version>
        </dependency>

再次访问
在这里插入图片描述
在这里插入图片描述
可以看到逻辑sql和实际sql都打印出来。数据库也有了数据 到此环境搭建结束,这个也相当于入门案例
在这里插入图片描述

案例一:分库分表

该案例将数据根据id分配到不同的数据库和不同的表里面,以两库两表为案例。在入门案例中在本地创建了一个数据库,现在在其他服务器创建另一个数据库,分表还是按照user1、user2来分。

shardingsphere:
    props:
      # 开启sql打印
      sql:
       show: true
    datasource:
      # 配置逻辑库名
      names: m1,m2
      m1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/sharding_test?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
        username: root
        password: root
      m2:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://xxx/xmkf_zt?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
        username: xxx
        password: xxx
    sharding:
      tables:
        # 配置逻辑表名
        sharding_user:
          actual-data-nodes:
            # 逻辑库逻辑表对应真实库、表的关系
            m$->{1..2}.sharding_user$->{1..2}
          # 配置主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
            props:
              worker:
                id: 1
          # 分库策略
          database-strategy:
            inline:
              # 分片键
              sharding-column: id
              # 分片算法
              algorithm-expression: m$->{id%2+1}
          # 分表策略
          table-strategy:
            inline:
              # 分片键
              sharding-column: id
              # 分片算法
              algorithm-expression: sharding_user$->{(id%4).intdiv(2)+1}

测试代码

	@GetMapping("/shardingTest")
	@ApiOperation(value = "测试分库分表")
	@ApiOperationSupport(order = 9, author = "lsx")
	public R shardingTest(String name,Integer age){
		List<ShardingUser> list = new ArrayList<>();
		for (int i = 0;i<100;i++){
			ShardingUser user = new ShardingUser();
			user.setUserName(name+i);
			user.setAge(i);
			list.add(user);
		}
		shardingUserService.saveBatch(list);
		return R.success("成功");
	}

说一下算法吧,首先是分库id%2+1 通过id 取模2 得到 0和1 然后+1 得到 1和2就对应了 m1和m2所在的库
然后分表(id%4).intdiv(2)+1 。首先通过id取模4 得到 0、1、2、3 这几种可能,然后整除2(默认向下取整)得到 0和1。然后再加上1得到1和2 就对应了表1和表2。

看下查询

@GetMapping("/shardingQuery")
	@ApiOperation(value = "测试分库分表查询")
	@ApiOperationSupport(order = 10, author = "lsx")
	public R shardingQuery(String id){
		ShardingUser byId = shardingUserService.getById(Long.valueOf(id));
		return R.data(byId);
	}

在这里插入图片描述
在这里插入图片描述
成功查询到数据
要是范围查询呢,

@GetMapping("/shardingQuery")
	@ApiOperation(value = "测试分库分表查询")
	@ApiOperationSupport(order = 10, author = "lsx")
	public R shardingQuery(String id){
		QueryWrapper<ShardingUser> query = new QueryWrapper<>();
		query.between("id",776812941189058560L,776812941952421888L);
		List<ShardingUser> list = shardingUserService.list(query);
		return R.data(list);
	}

在这里插入图片描述
直接报错了,因为配置文件中使用的是行表达式分片策略不支持 范围查询,具体来看看官网
在这里插入图片描述
所以如果像满足范围查询需要更换分片策略

分片策略介绍

官网分片策略介绍:https://shardingsphere.apache.org/document/4.1.1/cn/features/sharding/concept/sharding/
以下截取官网
在这里插入图片描述
在这里插入图片描述

案例二:使用标准分片策略实现范围查询

自定义分库算法

// 精确算法 用于 =和in
public class MyDBPreciseSharding implements PreciseShardingAlgorithm<Long> {
	@Override
	public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
		Long id = shardingValue.getValue();
		Long db = id % 2 + 1;
		String dbIndex = "m"+db;
		if(availableTargetNames.contains(dbIndex)){
			return dbIndex;
		}
		throw new UnsupportedOperationException(" route "+dbIndex+" is not supported. please check your config");
	}
}
//范围算法 用于 范围查询
public class MyDBRangeSharding implements RangeShardingAlgorithm<Long> {

	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
		Long lower = shardingValue.getValueRange().lowerEndpoint();
		Long upper = shardingValue.getValueRange().upperEndpoint();
		List<String> dbNames = new ArrayList<>();
		availableTargetNames.forEach(s->{
			if (s.startsWith("m")) dbNames.add(s);
		});
		//对于奇偶分离的场景 大概率两个数据库都是要查
		return dbNames;
	}
}

自定义分表算法

//精确
public class MyTablePreciseSharding implements PreciseShardingAlgorithm<Long> {

	@Override
	public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
		Long id = shardingValue.getValue();
		Long inddex = (id % 4)/2+1;
		String logicTableName = shardingValue.getLogicTableName();
		String tabesName = logicTableName + inddex;
		if (availableTargetNames.contains(tabesName)) return tabesName;
		throw new UnsupportedOperationException(" route "+tabesName+" is not supported. please check your config");
	}
}
//范围
public class MyTableRangeSharding implements RangeShardingAlgorithm<Long> {

	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
		Long lower = shardingValue.getValueRange().lowerEndpoint();
		Long upper = shardingValue.getValueRange().upperEndpoint();
		List<String> tableNames = new ArrayList<>();
		availableTargetNames.forEach(s->{
			if (s.startsWith(shardingValue.getLogicTableName())) tableNames.add(s);
		});
		//对于奇偶分离的场景 大概率两个表都要查
		return tableNames;
	}
}

yml配置

shardingsphere:
    props:
      # 开启sql打印
      sql:
       show: true
    datasource:
      # 配置逻辑库名
      names: m1,m2
      m1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/sharding_test?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
        username: root
        password: root
      m2:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://xxx/xmkf_zt?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
        username: xxx
        password: xxx
    sharding:
      tables:
        # 配置逻辑表名
        sharding_user:
          actual-data-nodes:
            # 逻辑库逻辑表对应真实库、表的关系
            m$->{1..2}.sharding_user$->{1..2}
          # 配置主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
            props:
              worker:
                id: 1
          # 分库策略
          database-strategy:
            standard:
              # 分片键
              sharding-column: id
              # 分片算法
              precise-algorithm-class-name: org.spring.common.shardingJDBC.MyDBPreciseSharding
              range-algorithm-class-name: org.spring.common.shardingJDBC.MyDBRangeSharding
          # 分表策略
          table-strategy:
            standard:
              # 分片键
              sharding-column: id
              # 分片算法
              precise-algorithm-class-name: org.spring.common.shardingJDBC.MyTablePreciseSharding
              range-algorithm-class-name: org.spring.common.shardingJDBC.MyTableRangeSharding

执行之前的范围查询
在这里插入图片描述
在这里插入图片描述
然后把查到的数据汇总返回。
在这里插入图片描述

到此分库分表入门案例结束了,下一篇继续实战剩下的分片策略和进阶用法

相关文章:

  • Linux内存管理(三十一):页面回收总结
  • 微信小程序│ 游戏开发 │连连看游戏
  • Python每日一练(牛客数据分析篇新题库)——第34天:数据清洗
  • mysql为什么使用B+树
  • 爬虫学习笔记 -- 实战某电影网(lxml库版)
  • hive on spark下row_number()问题排查
  • 最新面试题:用友OC,美团三面已挂
  • 开发者API管理神器Eolink,比postman好用
  • Mysql - 分库分表
  • 计算机毕业设计django基于python的高校奖学金管理系统(源码+系统+mysql数据库+Lw文档)
  • 软件测试的学习笔记(1)
  • c++11基础
  • 一个发誓不用Java的程序员,不得不在太空中调试Lisp
  • Android音频子系统(十一)------耳机返听(耳返)原理实现
  • 【探花交友】前后端分离、开发工具、环境搭建
  • “大数据应用场景”之隔壁老王(连载四)
  • 2017-08-04 前端日报
  • canvas 高仿 Apple Watch 表盘
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • Java读取Properties文件的六种方法
  • JSDuck 与 AngularJS 融合技巧
  • Js基础知识(一) - 变量
  • orm2 中文文档 3.1 模型属性
  • Python连接Oracle
  • react 代码优化(一) ——事件处理
  • SAP云平台里Global Account和Sub Account的关系
  • SwizzleMethod 黑魔法
  • Vue ES6 Jade Scss Webpack Gulp
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 前端技术周刊 2019-01-14:客户端存储
  • 使用 Docker 部署 Spring Boot项目
  • 收藏好这篇,别再只说“数据劫持”了
  • 双管齐下,VMware的容器新战略
  • 思否第一天
  • 思考 CSS 架构
  • 用quicker-worker.js轻松跑一个大数据遍历
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • ​低代码平台的核心价值与优势
  • ​决定德拉瓦州地区版图的关键历史事件
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • (MATLAB)第五章-矩阵运算
  • (SpringBoot)第七章:SpringBoot日志文件
  • (第61天)多租户架构(CDB/PDB)
  • (六)激光线扫描-三维重建
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .NET Standard 支持的 .NET Framework 和 .NET Core
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装
  • @ModelAttribute使用详解
  • @德人合科技——天锐绿盾 | 图纸加密软件有哪些功能呢?
  • [ element-ui:table ] 设置table中某些行数据禁止被选中,通过selectable 定义方法解决
  • [52PJ] Java面向对象笔记(转自52 1510988116)