Sharding-JDBC 实战

Sharding-JDBC简单使用


1.Sharding-JDBC之环境搭建


1.1 创建一个Maven项目 mysql-example,父工程项目pom.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mysql-example</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>sharding-jdbc-example</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.compile.sourceEncoding>UTF-8</project.compile.sourceEncoding>
        <shardingsphere.version>4.1.0</shardingsphere.version>
        <springboot.version>2.2.5.RELEASE</springboot.version>
    </properties>

  <dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-jdbc</artifactId>
              <version>${springboot.version}</version>
          </dependency>

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-data-jpa</artifactId>
              <version>${springboot.version}</version>
          </dependency>

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-test</artifactId>
              <version>${springboot.version}</version>
              <scope>test</scope>
          </dependency>

          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>5.1.48</version>
          </dependency>
          <dependency>
              <groupId>org.apache.shardingsphere</groupId>
              <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
              <version>${shardingsphere.version}</version>
          </dependency>
      </dependencies>
  </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <testSource>1.8</testSource>
                    <testTarget>1.8</testTarget>
                </configuration>
                <version>3.8.1</version>
            </plugin>
        </plugins>
    </build>
</project>


1.2 创建子模块sharding-jdbc-example,子模块项目pom.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mysql-example</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sharding-jdbc-example</artifactId>
  <dependencies>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-jdbc</artifactId>
      </dependency>

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

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

      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
      </dependency>
      <dependency>
          <groupId>org.apache.shardingsphere</groupId>
          <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
      </dependency>

  </dependencies>

</project>


2.Sharding-JDBC之职位分库业务


2.1数据库准备

  • 使用的mysql 5.5.54
  • 由于资源限制,我就在一个服务里建两个数据库lagou1,lagou2,创建相同的职位表,职位详情表

  • 建表语句如下
#position表
CREATE TABLE `position` (
	`id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT,
	`name` VARCHAR ( 255 ) DEFAULT NULL,
	`salary` VARCHAR ( 50 ) DEFAULT NULL,
	`city` VARCHAR ( 255 ) DEFAULT NULL,
PRIMARY KEY ( `id` ) 
) ENGINE = INNODB AUTO_INCREMENT = 490171537622564865 DEFAULT CHARSET = utf8;

#position_detail表
CREATE TABLE `position_detail` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `pid` bigint(11) DEFAULT NULL,
  `description` text,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=490171537891000321 DEFAULT CHARSET=utf8;

2.2.2 创建实体类与Repository


# Position
package com.lagou.entity;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "position")
public class Position implements Serializable{
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "name")
    private  String name;
    @Column(name = "salary")
    private  String salary;
    @Column(name = "city")
    private  String city;


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSalary() {
        return salary;
    }

    public void setSalary(String salary) {
        this.salary = salary;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Position{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary='" + salary + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}

#PositionDetail

package com.lagou.entity;

import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "position_detail")
public class PositionDetail implements Serializable {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "pid")
    private Long pid;
    @Column(name = "description")
    private String description;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getPid() {
        return pid;
    }

    public void setPid(Long pid) {
        this.pid = pid;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

## PositionRepository
package com.lagou.repository;

import com.lagou.entity.Position;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface PositionRepository extends JpaRepository<Position,Long> {
    @Query(nativeQuery = true,value = "select p.id,p.name,p.salary,p.city,pd.description from position p join position_detail pd on(p.id =pd.pid) where p.id=:id")
    public Object findPositionsById(@Param("id") long id);
}

## PositionDetailRepository

package com.lagou.repository;

import com.lagou.entity.PositionDetail;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PositionDetailRepository extends JpaRepository<PositionDetail,Long> {
}

## 启动类

package com.lagou;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RunBoot {
}


2.2.3 配置文件

  • application.properties
spring.profiles.active=sharding-database
spring.shardingsphere.props.sql.show=true

  • application-sharding-database.properties
#datasource
spring.shardingsphere.datasource.names=ds0,ds1

spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/lagou1
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=root
#连接池类型jpa默认的是HikariDataSource
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://127.0.0.1:3306/lagou2
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root



#sharding-database
#position表名, database-strategy :按库切分 ,  inline:行表达式格式,  sharding-column:指明分片键
spring.shardingsphere.sharding.tables.position.database-strategy.inline.sharding-column=id

# algorithm-expression:指明表达式什么时候定位到ds0ds1
spring.shardingsphere.sharding.tables.position.database-strategy.inline.algorithm-expression=ds$->{id % 2}

#position_detail的配置
spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.sharding-column=pid
spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.algorithm-expression=ds$->{pid % 2}

# 主键生成策略

#指定主键id的字段名  #指定type类型如UUID ,SNOWFLAKE  注意使用jpa中entity的id主键生成需开启
spring.shardingsphere.sharding.tables.position.key-generator.column=id

spring.shardingsphere.sharding.tables.position.key-generator.type=SNOWFLAKE

#spring.shardingsphere.sharding.tables.position.key-generator.type=LAGOUKEY


#position_detail的主键生成策略
spring.shardingsphere.sharding.tables.position_detail.key-generator.column=id
spring.shardingsphere.sharding.tables.position_detail.key-generator.type=SNOWFLAKE


2.2.4 测试类TestShardingDatabase

import javax.annotation.Resource;
import java.util.Date;
import java.util.Random;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RunBoot.class)
public class TestShardingDatabase {

    @Resource
    private PositionRepository positionRepository;
    @Resource
    private PositionDetailRepository positionDetailRepository;
   
    @Test
    public  void testAdd() {
        for (int i = 1; i <= 20; i++) {
            Position position = new Position();
            position.setName("lagou" + i);
            position.setSalary("10000");
            position.setCity("beijing");
            positionRepository.save(position);

            PositionDetail positionDetail = new PositionDetail();
            positionDetail.setPid(position.getId());
            positionDetail.setDescription("this is a pig" + i);
            positionDetailRepository.save(positionDetail);

        }
    }

}

2.2.5 运行效果





2.2.6 自定义主键生成策略实现 ShardingKeyGenerator接口即可

package com.lagou.id;

import org.apache.shardingsphere.core.strategy.keygen.SnowflakeShardingKeyGenerator;
import org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator;

import java.util.Properties;

/**
 * 自定义主键生成器 
 */
public class MyLagouId implements ShardingKeyGenerator {

    /**
     * 用同一个主键生成器对象 就会分配均匀
     */
    private  SnowflakeShardingKeyGenerator snowflakeShardingKeyGenerator =  new SnowflakeShardingKeyGenerator();

    /**
     * 返回我们生成的主键值 由于这个算法比较复杂,就还是使用SNOWFLAKE,也可以自己写
     * @return
     */
    @Override
    public Comparable<?> generateKey() {
        System.out.println("---执行了自定义主键生成器MyLagouId");
        return snowflakeShardingKeyGenerator.generateKey();
    }

    /**
     * 返回配置文件中那个的type
     * @return
     */
    @Override
    public String getType() {
        return "LAGOUKEY";
    }

    @Override
    public Properties getProperties() {
        return null;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

2.2.6.1 修改配置文件并在resources下建一下文件(文件名需一致)

  • org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator内容如下
com.lagou.id.MyLagouId

  • 注意:resource文件下的目录分级为/

2.2.7修改application-sharding-database.properties的主键生成策略的type为自定义主键生成策略返回的type

spring.shardingsphere.sharding.tables.position.key-generator.type=LAGOUKEY


3.Sharding-JDBC之订单分库分表


3.1创建订单表

  • 分别在两个数据库创建两张表b_order0,和b_order1。
  • 我采用的分库分表策略是对公司IDcompany_id分库,对主键ID进行分表
  • 适用业务场景为简历投递,投递人要看到自己投的简历,投递的公司要看到所有投递者的简历
  • 建表语句如下
CREATE TABLE `b_order0` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `is_del` bit(1) DEFAULT NULL COMMENT '是否被删除',
  `company_id` int(11) DEFAULT NULL COMMENT '公司ID',
  `position_id` int(11) DEFAULT NULL COMMENT '职位ID',
  `user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `publish_user_id` int(11) DEFAULT NULL COMMENT '职位发布者id',
  `resume_type` int(2) DEFAULT NULL COMMENT '简历类型:0 附件 1在线',
  `status` varchar(255) DEFAULT NULL COMMENT '投递状态',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `operate_time` datetime DEFAULT NULL COMMENT '操作时间',
  `work_year` varchar(255) DEFAULT NULL COMMENT '工作年限',
  `name` varchar(255) DEFAULT NULL COMMENT '投递简历人名字',
  `position_name` varchar(255) DEFAULT NULL COMMENT '职位名称',
  `resume_id` int(11) DEFAULT NULL COMMENT '投递的简历id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=490198508255576065 DEFAULT CHARSET=utf8;

CREATE TABLE `b_order1` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `is_del` bit(1) DEFAULT NULL COMMENT '是否被删除',
  `company_id` int(11) DEFAULT NULL COMMENT '公司ID',
  `position_id` int(11) DEFAULT NULL COMMENT '职位ID',
  `user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `publish_user_id` int(11) DEFAULT NULL COMMENT '职位发布者id',
  `resume_type` int(2) DEFAULT NULL COMMENT '简历类型:0 附件 1在线',
  `status` varchar(255) DEFAULT NULL COMMENT '投递状态',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `operate_time` datetime DEFAULT NULL COMMENT '操作时间',
  `work_year` varchar(255) DEFAULT NULL COMMENT '工作年限',
  `name` varchar(255) DEFAULT NULL COMMENT '投递简历人名字',
  `position_name` varchar(255) DEFAULT NULL COMMENT '职位名称',
  `resume_id` int(11) DEFAULT NULL COMMENT '投递的简历id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=490198508716949506 DEFAULT CHARSET=utf8;


3.2创建订单的实体类

package com.lagou.entity;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Entity
@Table(name = "b_order")
public class BOrder implements Serializable {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private  Long  id;
    @Column(name = "is_del")
    private  Boolean isDel;
    @Column(name = "company_id")
    private  Integer companyId;
    @Column(name = "position_id")
    private  long  positionId;
    @Column(name = "user_id")
    private  Integer userId;
    @Column(name = "publish_user_id")
    private  Integer publishUserId;
    @Column(name = "resume_type")
    private  Integer resumeType;
    @Column(name = "status")
    private  String  status;
    @Column(name = "create_time")
    private Date  createTime;
    @Column(name = "operate_time")
    private Date  operateTime;
    @Column(name = "work_year")
    private  String workYear;
    @Column(name = "name")
    private  String  name;
    @Column(name = "position_name")
    private  String  positionName;
    @Column(name = "resume_id")
    private  Integer  resumeId;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Boolean getDel() {
        return isDel;
    }

    public void setDel(Boolean del) {
        isDel = del;
    }

    public Integer getCompanyId() {
        return companyId;
    }

    public void setCompanyId(Integer companyId) {
        this.companyId = companyId;
    }

    public long getPositionId() {
        return positionId;
    }

    public void setPositionId(long positionId) {
        this.positionId = positionId;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getPublishUserId() {
        return publishUserId;
    }

    public void setPublishUserId(Integer publishUserId) {
        this.publishUserId = publishUserId;
    }

    public Integer getResumeType() {
        return resumeType;
    }

    public void setResumeType(Integer resumeType) {
        this.resumeType = resumeType;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getOperateTime() {
        return operateTime;
    }

    public void setOperateTime(Date operateTime) {
        this.operateTime = operateTime;
    }

    public String getWorkYear() {
        return workYear;
    }

    public void setWorkYear(String workYear) {
        this.workYear = workYear;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPositionName() {
        return positionName;
    }

    public void setPositionName(String positionName) {
        this.positionName = positionName;
    }

    public Integer getResumeId() {
        return resumeId;
    }

    public void setResumeId(Integer resumeId) {
        this.resumeId = resumeId;
    }
}

3.3创建订单的仓库类

package com.lagou.repository;

import com.lagou.entity.BOrder;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BOderRepository extends JpaRepository<BOrder,Long> {
}


3.4配置文件application-sharding-database.properties修改

#datasource
spring.shardingsphere.datasource.names=ds0,ds1

spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://127.0.0.1:3306/lagou1
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=root
#连接池类型jpa默认的是HikariDataSource
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://127.0.0.1:3306/lagou2
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=root



#sharding-database
#position表名, database-strategy :按库切分 ,  inline:行表达式格式,  sharding-column:指明分片键
spring.shardingsphere.sharding.tables.position.database-strategy.inline.sharding-column=id

# algorithm-expression:指明表达式什么时候定位到ds0ds1
spring.shardingsphere.sharding.tables.position.database-strategy.inline.algorithm-expression=ds$->{id % 2}

#position_detail的配置
spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.sharding-column=pid
spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.algorithm-expression=ds$->{pid % 2}

# 主键生成策略

#指定主键id的字段名  #指定type类型如UUID ,SNOWFLAKE  注意使用jpa中entity的id主键生成需开启
spring.shardingsphere.sharding.tables.position.key-generator.column=id

spring.shardingsphere.sharding.tables.position.key-generator.type=SNOWFLAKE

#spring.shardingsphere.sharding.tables.position.key-generator.type=LAGOUKEY


#position_detail的主键生成策略
spring.shardingsphere.sharding.tables.position_detail.key-generator.column=id
spring.shardingsphere.sharding.tables.position_detail.key-generator.type=SNOWFLAKE


#sharding-database-table 订单表分库
#根据company_id分库
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.sharding-column=company_id
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.algorithm-expression=ds$->{company_id % 2}
#根据id分表
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.algorithm-expression=b_order${id % 2}
#主键生成策略
spring.shardingsphere.sharding.tables.b_order.key-generator.column=id
spring.shardingsphere.sharding.tables.b_order.key-generator.type=SNOWFLAKE
#指定节点 b_order0,b_order1
spring.shardingsphere.sharding.tables.b_order.actual-data-nodes=ds${0..1}.b_order${0..1}


3.5测试类编写

 @Test
    @Repeat(100)
    public  void testShardingBOrder(){
        Random random = new Random();
        int companyId = random.nextInt(10);
        BOrder bOrder = new BOrder();
        bOrder.setDel(false);
        bOrder.setCompanyId(companyId);
        bOrder.setPositionId(123456);
        bOrder.setUserId(654321);
        bOrder.setPublishUserId(111111);
        bOrder.setResumeType(1);
        bOrder.setStatus("AUTO");
        bOrder.setCreateTime(new Date());
        bOrder.setOperateTime(new Date());
        bOrder.setWorkYear("1");
        bOrder.setName("张三");
        bOrder.setPositionName("java");
        bOrder.setResumeId(123456);
        bOderRepository.save(bOrder);

    }


3.6运行效果展示

  • 效果为根据company_id分库,根据主键id分表



posted @ 2020-07-15 17:54  若水一剑  阅读(2549)  评论(2编辑  收藏  举报