博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringBoot使用Sharding-JDBC读写分离
阅读量:4578 次
发布时间:2019-06-08

本文共 12307 字,大约阅读时间需要 41 分钟。

本文介绍SpringBoot使用当当Sharding-JDBC进行读写分离。

1.有关Sharding-JDBC

本文还是基于当当网Sharding-Jdbc的依赖,与上一篇使用Sharding-Jdbc进行分库分表依赖一致,并且本文大致内容与上一篇文章相似,建议先查看我的另一篇在查看这篇会简单许多,传送门。

这里需要特殊介绍的是,使用Sharding-JDBC进行读写分离的时候,只允许设置一个主库,从库的话可以设置多个,访问策略的话从源码上看只有两种轮询(ROUND_ROBIN)和随机(RANDOM)。

源码代码如下:

package com.dangdang.ddframe.rdb.sharding.api.strategy.slave;public enum MasterSlaveLoadBalanceStrategyType {    ROUND_ROBIN(new RoundRobinMasterSlaveLoadBalanceStrategy()),    RANDOM(new RandomMasterSlaveLoadBalanceStrategy());    private final MasterSlaveLoadBalanceStrategy strategy;    public static MasterSlaveLoadBalanceStrategyType getDefaultStrategyType() {        return ROUND_ROBIN;    }    private MasterSlaveLoadBalanceStrategyType(MasterSlaveLoadBalanceStrategy strategy) {        this.strategy = strategy;    }    public MasterSlaveLoadBalanceStrategy getStrategy() {        return this.strategy;    }}

2.本文场景

由于本地环境并没有使用Mysql主从复制,只是创建了三个库,其中database0作为主库,database1和database2作为从库。主库进行增删改操作,从库进行查询操作,如下图为本文数据库的三个表。

168e05a78e567447?w=1378&h=786&f=png&s=58766

如上图分别是三个数据库中的user表,其中master-user为database0数据库中的user表,salve-user1为database1中的user表,salve-user2为database2中的user表。

3.代码实现

本文使用SpringBoot2.0.3,SpringData-JPA,Druid连接池,和当当的sharding-jdbc。

3.1 建表SQL

创建表和数据库的SQL如下所示,这里默认在从库内分别插入了一条数据,name值分别存放dalaoyang1和dalaoyang2便于区分。

CREATE DATABASE database0;USE database0;DROP TABLE IF EXISTS `user`;CREATE TABLE `user`(    id bigint(64) not null,    city varchar(20) not null,    name varchar(20) not null,    PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE DATABASE database1;USE database1;DROP TABLE IF EXISTS `user`;CREATE TABLE `user`(    id bigint(64) not null,    city varchar(20) not null,    name varchar(20) not null,    PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `database1`.`user`(`id`, `city`, `name`) VALUES (101, 'beijing', 'dalaoyang1');CREATE DATABASE database2;USE database2;DROP TABLE IF EXISTS `user`;CREATE TABLE `user`(    id bigint(64) not null,    city varchar(20) not null,    name varchar(20) not null,    PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `database2`.`user`(`id`, `city`, `name`) VALUES (102, 'beijing', 'dalaoyang2');

3.2 依赖文件

新建项目,依赖文件还是当当的sharding-jdbc-core依赖和druid连接池,完整pom文件代码如下所示。

4.0.0
org.springframework.boot
spring-boot-starter-parent
2.0.3.RELEASE
com.dalaoyang
springboot2_shardingjdbc_dxfl
0.0.1-SNAPSHOT
springboot2_shardingjdbc_dxfl
springboot2_shardingjdbc_dxfl
1.8
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
runtime
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
test
org.projectlombok
lombok
true
com.alibaba
druid
1.1.9
com.dangdang
sharding-jdbc-core
1.5.4
org.springframework.boot
spring-boot-maven-plugin

3.3 配置信息

在配置信息中配置了三个数据库的信息和JPA的简单配置。

##Jpa配置spring.jpa.database=mysqlspring.jpa.show-sql=truespring.jpa.hibernate.ddl-auto=none##数据库配置##数据库database0地址database0.url=jdbc:mysql://localhost:3306/database0?characterEncoding=utf8&useSSL=false##数据库database0用户名database0.username=root##数据库database0密码database0.password=root##数据库database0驱动database0.driverClassName=com.mysql.jdbc.Driver##数据库database0名称database0.databaseName=database0##数据库database1地址database1.url=jdbc:mysql://localhost:3306/database1?characterEncoding=utf8&useSSL=false##数据库database1用户名database1.username=root##数据库database1密码database1.password=root##数据库database1驱动database1.driverClassName=com.mysql.jdbc.Driver##数据库database1名称database1.databaseName=database1##数据库database2地址database2.url=jdbc:mysql://localhost:3306/database2?characterEncoding=utf8&useSSL=false##数据库database1用户名database2.username=root##数据库database1密码database2.password=root##数据库database1驱动database2.driverClassName=com.mysql.jdbc.Driver##数据库database1名称database2.databaseName=database2

3.4 启动类

上一篇文章说到在启动类加入了@EnableAutoConfiguration去除数据库自动配置,当时也没太注意,其实可以直接在@SpringBootApplication注解上去除数据库自动配置,剩下的和上一篇一样,使用@EnableTransactionManagement开启事务,使用@EnableConfigurationProperties注解加入配置实体,启动类完整代码如下所示。

package com.dalaoyang;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;import org.springframework.boot.context.properties.EnableConfigurationProperties;import org.springframework.transaction.annotation.EnableTransactionManagement;@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})@EnableTransactionManagement(proxyTargetClass = true)@EnableConfigurationPropertiespublic class Springboot2ShardingjdbcDxflApplication {    public static void main(String[] args) {        SpringApplication.run(Springboot2ShardingjdbcDxflApplication.class, args);    }}

3.5 实体类和数据库操作层

User实体类。

package com.dalaoyang.entity;import lombok.Data;import javax.persistence.*;@Entity@Table(name="user")@Datapublic class User {    @Id    private Long id;    private String city;    private String name;}

UserRepository类。

package com.dalaoyang.repository;import com.dalaoyang.entity.User;import org.springframework.data.jpa.repository.JpaRepository;public interface UserRepository extends JpaRepository
{}

3.6 数据库参数类

数据库配置类,Database0Config。

package com.dalaoyang.database;import com.alibaba.druid.pool.DruidDataSource;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;import javax.sql.DataSource;/** * @author yangyang * @date 2019/1/30 */@Data@ConfigurationProperties(prefix = "database0")@Componentpublic class Database0Config {    private String url;    private String username;    private String password;    private String driverClassName;    private String databaseName;    public DataSource createDataSource() {        DruidDataSource result = new DruidDataSource();        result.setDriverClassName(getDriverClassName());        result.setUrl(getUrl());        result.setUsername(getUsername());        result.setPassword(getPassword());        return result;    }}

数据库配置类,Database1Config。

package com.dalaoyang.database;import com.alibaba.druid.pool.DruidDataSource;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;import javax.sql.DataSource;/** * @author yangyang * @date 2019/1/30 */@Data@ConfigurationProperties(prefix = "database1")@Componentpublic class Database1Config {    private String url;    private String username;    private String password;    private String driverClassName;    private String databaseName;    public DataSource createDataSource() {        DruidDataSource result = new DruidDataSource();        result.setDriverClassName(getDriverClassName());        result.setUrl(getUrl());        result.setUsername(getUsername());        result.setPassword(getPassword());        return result;    }}

数据库配置类,Database2Config。

package com.dalaoyang.database;import com.alibaba.druid.pool.DruidDataSource;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;import javax.sql.DataSource;/** * @author yangyang * @date 2019/1/30 */@Data@ConfigurationProperties(prefix = "database2")@Componentpublic class Database2Config {    private String url;    private String username;    private String password;    private String driverClassName;    private String databaseName;    public DataSource createDataSource() {        DruidDataSource result = new DruidDataSource();        result.setDriverClassName(getDriverClassName());        result.setUrl(getUrl());        result.setUsername(getUsername());        result.setPassword(getPassword());        return result;    }}

3.7 读写分离配置

创建一个DataSourceConfig类来设置读写分离,这里其实也与分库分表类似,也可以在分库分表的基础上进行读写分离,需要创建一个Map集合来接收从库。在创建数据源时需要传入五个参数,分别是:

  • name:数据源名称
  • masterDataSourceName:主库数据源名称
  • masterDataSource:主数据源
  • slaveDataSourceMap:从数据源集合
  • strategyType:访问策略

当然,也可以使用其他方法创建数据源,本文代码如下:

package com.dalaoyang.database;import com.dangdang.ddframe.rdb.sharding.api.MasterSlaveDataSourceFactory;import com.dangdang.ddframe.rdb.sharding.api.strategy.slave.MasterSlaveLoadBalanceStrategyType;import com.dangdang.ddframe.rdb.sharding.keygen.DefaultKeyGenerator;import com.dangdang.ddframe.rdb.sharding.keygen.KeyGenerator;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;import java.sql.SQLException;import java.util.HashMap;import java.util.Map;/** * @author yangyang * @date 2019/1/29 */@Configurationpublic class DataSourceConfig {    @Autowired    private Database0Config database0Config;    @Autowired    private Database1Config database1Config;    @Autowired    private Database2Config database2Config;    @Bean    public DataSource getDataSource() throws SQLException {        return buildDataSource();    }    private DataSource buildDataSource() throws SQLException {        //设置从库数据源集合        Map
slaveDataSourceMap = new HashMap<>(); slaveDataSourceMap.put(database1Config.getDatabaseName(), database1Config.createDataSource()); slaveDataSourceMap.put(database2Config.getDatabaseName(), database2Config.createDataSource()); //获取数据源对象 DataSource dataSource = MasterSlaveDataSourceFactory.createDataSource("masterSlave",database0Config.getDatabaseName() ,database0Config.createDataSource(), slaveDataSourceMap, MasterSlaveLoadBalanceStrategyType.getDefaultStrategyType()); return dataSource; } @Bean public KeyGenerator keyGenerator() { return new DefaultKeyGenerator(); }}

3.8 Controller

Controller做为测试类,创建两个方法,save方法和getAll方法,其中:

  • save方法用于测试主库的插入和修改
  • getAll方法用于测试读数据

UserController类如下所示。

package com.dalaoyang.controller;import com.dalaoyang.entity.User;import com.dalaoyang.repository.UserRepository;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class UserController {    @Autowired    private UserRepository userRepository;    @GetMapping("save")    public void save(){        User user = new User();        user.setId(100L);        user.setName("dalaoyang");        user.setCity("beijing");        userRepository.save(user);    }    @GetMapping("getAll")    public Object getAll(){        return userRepository.findAll();    }}

4.测试

4.1 测试主库

使用postman访问,控制台如图所示。

168e07d5d8cf4418?w=2344&h=214&f=png&s=106800

再次访问,如图。

168e084912d64820?w=2364&h=554&f=png&s=271455

主键冲突了,其实这是由于插入的时候使用的database0,但是查询使用的是database1和database2,但是我在从库内并没有ID是100的数据,所以JPA判定我为插入,但是数据库内缺有这样的数据。

我们接下来测试一下查询。访问

168e0833f609f203?w=1676&h=840&f=png&s=97350

再次访问,如图。

168e083a2ddf8081?w=1680&h=804&f=png&s=96830

证明从库的读取是正常的,接下来修改从库的ID为100。然后访问,查看控制台如图。

168e085989c14f65?w=1952&h=142&f=png&s=21482

因为存在了ID为100的数据,所以SQL为修改语句。

5.源码

源码地址:

转载于:https://www.cnblogs.com/dalaoyang/p/10365355.html

你可能感兴趣的文章
我选择的……
查看>>
akka actor初探
查看>>
linux清理Java环境
查看>>
如何更改webstrom的默认端口63342
查看>>
最短路计数
查看>>
SharedPreferences
查看>>
Android性能优化方法(六)
查看>>
JQ在线引用地址
查看>>
TCP协议
查看>>
高级IO-锁与进程和文件
查看>>
uva 11795 Mega Man's Mission(动态规划-状态压缩DP)
查看>>
gc日志分析
查看>>
数据结构--栈的思想与数组实现
查看>>
javascript的构造函数和原型
查看>>
详解C#break ,continue, return
查看>>
Python如何发布程序
查看>>
java中使用session的一些细节
查看>>
浏览器输入服务器端口号来访问html网页
查看>>
hdu 6435 CSGO(最大曼哈顿距离)
查看>>
logback框架之——日志分割所带来的潜在问题
查看>>