【Sharding-JDBC系列一】Spring Boot整合Sharding-JDBC实现读写分离

目录

Sharding-JDBC简介

搭建mysql主从数据库

Springboot应用搭建

pom依赖

application.properties配置

测试

开启日志(非必须)


Sharding-JDBC简介

ShardingSphere-JDBC 定位为轻量级的分布式数据库中间件解决方案,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

  • 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。
  • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。
  • 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 标准的数据库。

功能特性:

扫描二维码关注公众号,回复: 12142473 查看本文章
  • 分库 & 分表
  • 读写分离
  • 分片策略定制化
  • 无中心化分布式主键
  • 分布式治理
  • 柔性事务
  • 标准化事务接口

搭建mysql主从数据库

可参考之前的文章,《【mycat系列一】基于 Docker 搭建 MySQL 主从复制的详细教程》搭建一主两从的mysql,当然你也可以不使用docker,使用多台linux搭建,详细信息如下:

数据库类型 数据库 docker容器中IP 宿主机IP(对外IP)
主:mysql-master test 172.17.0.2:3306 192.168.239.128:3307
从:mysql-slave1 test 172.17.0.3:3306 192.168.239.128:3308
从:mysql-slave2 test 172.17.0.4:3306 192.168.239.128:3309

配置好主从后,在主库创建一个新的数据库test,可使用Navicat等第三方客户端工具,或者命令行脚本都可以,SQL脚本如下:

USE `test`;

DROP TABLE IF EXISTS `t_user`;

CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT '' COMMENT '名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1307873057269878786 DEFAULT CHARSET=utf8 COMMENT='用户表';

Navicat效果如下:

Springboot应用搭建

本文,将整合 SpringBoot + Mybatis-plus + Druid + Sharding-JDBC + MySQL,具体如下:

pom依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>

        <!-- 数据库连接池 -->
       <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.22</version>
        </dependency>

        <dependency>
            <groupId>io.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>3.1.0.M1</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.2</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.1.2</version>
        </dependency>

application.properties配置

# 服务端口
server.port=8080

# mysql-plus 配置
mybatis-plus.mapper-locations=classpath:/mapper/*.xml
mybatis-plus.type-aliases-package=com.stwen.shardingjdbc.entity

spring.main.allow-bean-definition-overriding=true

# sharding-jdbc 配置主从
sharding.jdbc.dataSource.names=master,slave1,slave2

# sharding-jdbc 主数据库
sharding.jdbc.dataSource.master.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.dataSource.master.driverClassName=com.mysql.jdbc.Driver
sharding.jdbc.dataSource.master.url=jdbc:mysql://192.168.239.128:3307/test?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
sharding.jdbc.dataSource.master.username=root
sharding.jdbc.dataSource.master.password=123456
sharding.jdbc.dataSource.master.maxPoolSize=20

# sharding-jdbc 从数据库一
sharding.jdbc.dataSource.slave1.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.dataSource.slave1.driverClassName=com.mysql.jdbc.Driver
sharding.jdbc.dataSource.slave1.url=jdbc:mysql://192.168.239.128:3308/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT
sharding.jdbc.dataSource.slave1.username=root
sharding.jdbc.dataSource.slave1.password=123456
sharding.jdbc.dataSource.slave1.maxPoolSize=20

# sharding-jdbc 从数据库二
sharding.jdbc.dataSource.slave2.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.dataSource.slave2.driverClassName=com.mysql.jdbc.Driver
sharding.jdbc.dataSource.slave2.url=jdbc:mysql://192.168.239.128:3309/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT
sharding.jdbc.dataSource.slave2.username=root
sharding.jdbc.dataSource.slave2.password=123456
sharding.jdbc.dataSource.slave2.maxPoolSize=20

# 配置从库选择策略,提供轮询与随机,这里选择用轮询,random-随机
sharding.jdbc.config.masterslave.load-balance-algorithm-type=round_robin
# 配置主从读写分离
sharding.jdbc.config.masterslave.name=master-slave 
sharding.jdbc.config.masterslave.master-data-source-name=master
sharding.jdbc.config.masterslave.slave-data-source-names=slave1,slave2
# 开启SQL显示,默认值: false,注意:仅配置读写分离时不会打印日志
sharding.jdbc.props.sql.show=true

配置参数说明:

  • mybatis-plus.mapper-locations : XXXmapper.xml 映射文件的路径
  • mybatis-plus.type-aliases-package :实体类包名
  • sharding.jdbc.dataSource.names:配置的是数据库的名称,就是上面搭建的多个数据源的名称
  • sharding.jdbc.dataSource:配置多个数据源

其他的详细参数就不一一解释了,简单看一下就明白。

其中,spring.main.allow-bean-definition-overriding=true 是防止报如下错误而添加的:

在Springboot启动类,增加配置,mybatis的dao接口包扫描,如下

微信截图_20190626145550.png

测试

编写测试类,增加两个接口:新增、查询列表

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private IUserService userService;

    @PutMapping("/save")
    public Object save() {
        User user = new User();
        user.setName("新增");
        return userService.save(user);
    }
    
    @GetMapping("/list")
    public Object list() {
        // 强制路由主库
        //HintManager.getInstance().setMasterRouteOnly();
        return userService.list();
    }
    
}

启动应用,访问接口:localhost:8080/user/save ,可以查看数据,发现新增了一条数据

再访问列表查询接口:localhost:8080/user/list

开启日志(非必须)

(非必须)开启sql语句的日志,生产环境不建议开启,这里只是为了验证是否达到读写分离的效果。

查看日志目录,并开启sql语句的日志:

mysql>  show variables like '%general_log%';
mysql>  set global general_log=on;

注:

  • 分别连接进入到上面配置的3个数据库中,执行上述命令,开启sql日志记录(无需重启,即可生效)。
  • 当重启Mysql ,上述开启日志配置将失效。

3个数据库都设置之后,再次访问两个测试接口,再来查看sql日志:

如下,在mysql-master 容器中,查看sql日志,可以看到插入的sql在主数据库执行

如下,查看mysql-slave2的sql日志,读取列表数据在从数据库执行,说明我们配置的读写分离是成功的。

主从同步延迟问题

  读写分离架构中经常出现,那就是读延迟的问题如何解决

  刚插入一条数据,然后马上就要去读取,这个时候有可能会读取不到?归根到底是因为主节点写入完之后数据是要复制给从节点的,读不到的原因是复制的时间比较长,也就是说数据还没复制到从节点,你就已经去从节点读取了,肯定读不到。mysql5.7 的主从复制是多线程了,意味着速度会变快,但是不一定能保证百分百马上读取到,这个问题我们可以有两种方式解决:

  (1)业务层面妥协,是否操作完之后马上要进行读取

  (2)对于操作完马上要读出来的,且业务上不能妥协的,我们可以对于这类的读取直接走主库,当然Sharding-JDBC也是考虑到这个问题的存在,所以给我们提供了一个功能,可以让用户在使用的时候指定要不要走主库进行读取。在读取前使用下面的方式进行设置就可以了:

    @GetMapping("/list")
    public Object list() {
        // 强制路由主库
        HintManager.getInstance().setMasterRouteOnly();
        return userService.list();
    }

查看主库配置的sql日志,会看到打印如下,说明读操作强制走的主库:

SELECT  id,name  FROM t_user

史上最强Tomcat8性能优化

阿里巴巴为什么能抗住90秒100亿?--服务端高并发分布式架构演进之路

B2B电商平台--ChinaPay银联电子支付功能

学会Zookeeper分布式锁,让面试官对你刮目相看

SpringCloud电商秒杀微服务-Redisson分布式锁方案

查看更多好文,进入公众号--撩我--往期精彩

一只 有深度 有灵魂 的公众号0.0

猜你喜欢

转载自blog.csdn.net/a1036645146/article/details/108705394