ShardingSphere+Springboot解决单库分表以及读写分离

背景:

Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar,这 3 款相互独立,却又能够混合部署配合使用的产品组成。 它们均提供标准化的数据分片、分布式事务和数据库治理功能,目前,数据分片、读写分离、数据加密、影子库压测等功能,以及 MySQL、PostgreSQL、SQLServer、Oracle 等 SQL 与协议的支持,均通过插件的方式织入项目,ShardingSphere 已于2020年4月16日成为 Apache 软件基金会的顶级项目。

数据库要实现读写分离,比较主流使用的有sharding spheremycatsharding作为一个组件集成在应用内,Sharding是一个Jar形式,在本地应用层重写Jdbc原生的方法,实现数据库分片形式。而mycat则作为一个独立的应用需要单独部署,是一个基于第三方应用中间件数据库代理框架,客户端所有的jdbc请求都必须要先交给MyCat,再有MyCat转发到具体的真实服务器中。MyCat属于服务器端数据库中间件,更倾向于运维层面,而Sharding是一个本地数据库中间件框架。

从设计理念上看,两则确实有一定的相似性。主要流程都是SQL 解析 -> SQL 路由 -> SQL 改写 -> SQL 执行 -> 结果归并。但架构设计上是不同的。Mycat是基于 Proxy,它复写了MySQL 协议,将 Mycat Server伪装成一个 MySQL数据库,而 Sharding是基于 JDBC 的扩展,是以 jar 包的形式提供轻量级服务的。

一、单库分表

1.引入pom文件,并编写yml配置

<!--    依赖shardingsphere    -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.1.0</version>
        </dependency>
spring:
  shardingsphere:
    datasource:
      names: m1   #配置库的名字,随意
      m1:   #配置目前m1库的数据源信息
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.jdbc.Driver
        url: jdbc:mysql://xxxx:3306/xxxxx?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&rewriteBatchedStatements=true
        username: root
        password: xxxxx
        maxActive: 20
        initialSize: 1
        maxWait: 60000
        poolPreparedStqtements: true
        maxPoolPreparedStatementPerConnection: 20
        minIdle: 1
        timeBetweenAEvictionRunsMills: 60000
        minEvictableIdleTimeMills: 300000
        validationQuery: slelect 1 from dual
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        filters: stat, wall, log4j
    sharding:
      tables:
        t_student:  # 指定的数据库名
          actualDataNodes: m1.t_student_$->{1..2}
          tableStrategy:
            inline:   # 指定t_student表的分片策略,分片策略包括分片键和分片算法
              shardingColumn: id
              algorithmExpression: t_student_$->{id % 2 + 1}
          keyGenerator:   # 指定t_student表的主键生成策略为SNOWFLAKE
            type: SNOWFLAKE  #主键生成策略为SNOWFLAKE
            column: id  #指定主键
#        t_student2:  # 指定的数据库名2
#          actualDataNodes: m1.t_student2_$->{1..2}
#          tableStrategy:
#            inline:   # 指定t_student2表的分片策略,分片策略包括分片键和分片算法
#              shardingColumn: id
#              algorithmExpression: t_student2_$->{id % 2 + 1}
#          keyGenerator:   # 指定t_student2表的主键生成策略为SNOWFLAKE
#            type: SNOWFLAKE  #主键生成策略为SNOWFLAKE
#            column: id  #指定主键
    props:
      sql:
        show: true

2、编写实体类,以及service进行测试(注意:实体类表名使用逻辑表明)

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_student")
@ApiModel(value = "StudentEntity对象", description = "学生表")
public class StudentEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    @ApiModelProperty("名称")
    private String name;

    @ApiModelProperty("地址")
    private String address;

    @ApiModelProperty("年龄")
    private Integer age;

    @ApiModelProperty("数据创建时间")
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

}
@EnableTransactionManagement
@Configuration //配置类
public class MybatisPlusConfig {
    /**
     *在实体类的字段上加上@Version注解
     * @return 注册乐观锁插件,  分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
         分页插件   DbType:数据库类型(根据类型获取应使用的分页方言)
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    //逻辑删除
//    @Bean
//    public ISqlInjector sqlInjector(){
//        return new LogicSqlInjector();
//    }

//    /**
//     * SQL执行效率插件
//     */
//    @Bean
//    @Profile({"dev","test"})// 设置 dev test 环境开启
//    public PerformanceInterceptor performanceInterceptor() {
//        PerformanceInterceptor interceptor = new PerformanceInterceptor();
//        //在工作中不允许用户等待
//        interceptor.setMaxTime(100);// ms单位毫秒 设置sql可以执行的最大时间,如果超过了这个时间则不执行
//        interceptor.setFormat(true);//
//        return interceptor;
//    }


}

@Slf4j
@Component //把处理器加到IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill.....");
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
        this.strictInsertFill(metaObject, "version", Integer.class, 1);
        this.strictInsertFill(metaObject, "deleted", Integer.class, 0);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill......");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}
public interface StudentService extends IService<StudentEntity> {

}


@Service
public class StudentServiceImp extends ServiceImpl<StudentMapper, StudentEntity> implements StudentService {

}

@Slf4j
@RestController
@RequestMapping("/student")
public class StudentController {

    @Resource
    private StudentMapper studentMapper;
    @Resource
    private StudentService studentService;

    @GetMapping("/save")
    @ApiOperation("保存学生数据")
    public MyResponse save(){
        StudentEntity studentEntity = StudentEntity.builder().name("肖战").age(26).address("北京海淀中关村").build();
        studentMapper.insert(studentEntity);
        return MyResponse.success(studentEntity);
    }

    @GetMapping("/save1")
    @ApiOperation("保存学生数据")
    public MyResponse save1(){
        StudentEntity studentEntity = StudentEntity.builder().name("肖战1").age(16).address("北京海淀中关村111").build();
        studentService.save(studentEntity);
        return MyResponse.success(studentEntity);
    }

    @ApiOperation("Mysql中条件检索学生信息")
    @PostMapping("/search")
    public MyResponse selectByMysql(@RequestBody StudentEntity studentEntity) throws Exception {
        Preconditions.checkNotNull(studentEntity, "分页数据不能为空");
        log.info("信息检索参数:{}", JSONUtil.toJsonPrettyStr(JSON.toJSONString(studentEntity)));
        try {
            //封装分页和排序条件
            Page<StudentEntity> page = PageUtil.createPage(0, 10, "create_time");
            LambdaQueryWrapper<StudentEntity> wrapper = Wrappers.lambdaQuery(StudentEntity.class)
                    .like(StringUtils.isNotBlank(studentEntity.getName()), StudentEntity::getName, studentEntity.getName())
                    .ge((Objects.nonNull(studentEntity.getAge())), StudentEntity::getAge, studentEntity.getAge())
                    .like(StringUtils.isNotBlank(studentEntity.getAddress()), StudentEntity::getAddress, studentEntity.getAddress());
            page = studentMapper.selectPage(page, wrapper);
            return MyResponse.success(page);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }
}

二、读写分离

spring:
  shardingsphere:
    datasource:
#      names: master,slave0,slave1
      names: master,slave0
      # 数据源 主库
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://xxxxx:3306/xxxxx?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&rewriteBatchedStatements=true
        username: xxxxx
        password: xxxxx
      # 数据源 从库0
      slave0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://xxxxx:3306/xxxxx?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&rewriteBatchedStatements=true
        username: xxxxx
        password: xxxxx
#      # 数据源 从库1
#      slave1:
#        type: com.alibaba.druid.pool.DruidDataSource
#        driver-class-name: com.mysql.jdbc.Driver
#        url: jdbc:mysql://xxxxx/xxxxx?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&rewriteBatchedStatements=true
#        username: xxxxx
#        password: xxxxx
  # 读写分离
    masterslave:
      load-balance-algorithm-type: round_robin
      name: ms
      master-data-source-name: master
      slave-data-source-names: slave0
#      slave-data-source-names: slave0,slave1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值