Sharding JDBC sub database and sub table

Posted by promixe on Tue, 08 Feb 2022 02:19:41 +0100

1, What is sharding JDBC

Sharding JDBC is an open source distributed database middleware developed by Dangdang. Sharding JDBC has been included in sharding sphere since 3.0, and the version after 4.0 is Apache.

ShardingSphere is an ecosystem composed of a set of open-source distributed database middleware solutions. It is composed of three independent products: sharding JDBC, sharding proxy and sharding sidecar. They all provide standardized data fragmentation, distributed transactions and database governance capabilities.

Sharding JDBC is positioned as a lightweight Java framework and provides additional services in the JDBC layer of Java. It uses the client to directly connect to the database and provides services in the form of JAR package without additional deployment and dependency. It can be understood as an enhanced jdbc driver and is fully compatible with JDBC and various ORM frameworks.

The core function of sharding JDBC is data fragmentation and read-write separation. Through sharding JDBC, applications can transparently use JDBC to access various data sources that have been divided into databases, tables and read-write separation, without caring about the number of data sources and how the data is distributed.

2, Sub database and sub table

The purpose of sub database and sub table is to solve the problem of large amount of data storage of Internet applications. The sub table is usually divided into: vertical division and horizontal division; Vertical partition usually splits a large multi field table into multiple small tables with few fields according to the business scenario. Horizontal partition is the discrete storage of data. According to a partition strategy, the data of the same table is stored separately in multiple tables with the same structure.

3, What are the sharding algorithms provided by sharding?

`StandardShardingStrategy`         Standard fragmentation strategy.
`InlineShardingStrategy`           Inline Expression fragmentation strategy. use Groovy of Inline expression.
`HintShardingStrategy`             adopt Hint Not SQL The method of analysis and the strategy of segmentation.
`NoneShardingStrategy`             No fragmentation strategy.

4, How to use sharding JDBC to store data discretely?

The use of InlineShardingStrategy;

Step 1: introduce relevant pom dependencies

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

<dependency>
	<groupId>io.shardingsphere</groupId>
	<artifactId>sharding-jdbc-spring-namespace</artifactId>
	<version>3.1.0</version>
</dependency>

The second step is to prepare two databases. There are two tables under db0 and db1. Their structures are the same, but their names are different

Step 3: configure the yml file IN the yml file sharding jdbc. Datasource: connect two database data sources under configuration. If there are more sub databases, the configured connection can also be increased, and then sharding Default database strategy: configure the strategy of sub database and sub table. The strategy column of sub database and sub table is groovy row expression and Inline expression fragmentation strategy. Groovy's Inline expression is used to provide slicing support for = and IN in SQL statements

app:
  id: zt-frank-shardingSphere-service-9960
  name: zt-frank-shardingSphere-service-9960
server:
  port: 9960
spring:
  application:
    name: zt-frank-shardingSphere-service-9960  #Spring of 7001, 70027003 is required for Ribbon load balancing application. The name of name is consistent
  main:
    allow-bean-definition-overriding: true #Allow Bean to inject repeatedly, and the latter overrides the former
#Start configuring connection database
sharding:
  jdbc:
    datasource:
      names: db0,db1
      db0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
        username: root
        password: password
      db1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
        username: root
        password: password
    config:
      props:
        sql.show: true #Print sql
        
#Start configuring the policy column of sub database and sub table  
      sharding:
        default-database-strategy:  #Default sub database policy
          inline:
            sharding-column: age  #Divide the database by age. Note that the data type corresponding to dto needs to be a number, because modular operation is required here. If it is String, the database will not be found and an error will be reported
            algorithm-expression: db$->{age % 2}  #Using Groovy expressions
        tables:
          t_user:
            actual-data-nodes: db$->{0..1}.t_user_$->{0..1} #Actual node (data source: two data sources are configured above, db0 and db1)
            table-strategy: #Table splitting strategy
              inline:
                sharding-column: height #Slice field Height (slice key)
                algorithm-expression: t_user_$->{height % 2}

Step 4: write code to insert data
t_ DO of user logical table,
Mybatis plus is used here. Note that the table name of @ TableName("t_user") is a logical table, and there is no t in the database_ For the user table, sharding JDBC will parse - route - rewrite the original sql according to the fragmentation rules configured in the yml file to match the real table of the database. If you write sql, the xml file is also written t_ user

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user") //Although there is no t in the database_ User table, is t_user_0,t_user_1. However, there can be no suffix here, including the mapper file for writing sql, and the table name is also t_user
public class UserDO {
	@TableId(value = "id", type = IdType.AUTO)
	private Integer id;
	private String userName;
	private Integer age;
	private String sex;
	private Integer height;
	private Integer weight;
	private Date birthday;
	private String isDelete;
	private String create;
	private Date gmtCreate;
	private String modified;
	private Date gmtModified;
}

service layer

@Override
    public Boolean saveUser(UserReqDTO req) {
        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(req,userDO);
        System.out.println("UserDO:"+userDO.toString());
        int i = userDao.insert(userDO);
        return i>0;
    }

controller layer

@PostMapping("/save/user")
public Boolean saveUser(@Valid @RequestBody UserReqDTO req){
    logger.info("Save user start:"+req.toString());
    Boolean bool = userService.saveUser(req);
    logger.info("Save user end:"+req);
    return bool;
}

Step 5; Testing;

5, The above rules of sub database and sub table are configured in the yml file. When the business scenario of some sub database and sub table is complex, the logic of sub database and sub table can be replaced by code, and the standard (standard sharding strategy) standard fragmentation strategy

StandardShardingStrategy ` use of standard fragmentation strategy

Step 1: change the yml file and adopt the standard sharding strategy

sharding:
  jdbc:
    datasource:
      names: db0,db1
      db0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db0?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
        username: root
        password: password
      db1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
        username: root
        password: password
    config:
      props:
        sql.show: true #Print sql
      sharding:
        default-database-strategy:  #Default sub database policy
#          inline:
#            sharding-column: age  #Divide the database by age. Note that the data type corresponding to dto needs to be a number, because modular operation is required here. If it is String, the database will not be found and an error will be reported
#            algorithm-expression: db$->{age % 2}  #Using Groovy expressions
          standard:
            sharding-column: age
            precise-algorithm-class-name: com.zt.frank.config.SubDBConfig  #The sub database rules are configured in the class
        tables:
          t_user:
            actual-data-nodes: db$->{0..1}.t_user_$->{0..1} #Actual node (data source: two data sources are configured above, db0 and db1)
            table-strategy: #Table splitting strategy
#              inline:
#                sharding-column: height #Slice field Height (slice key)
#                algorithm-expression: t_user_$->{height % 2}
              standard:
                sharding-column: height
                precise-algorithm-class-name: com.zt.frank.config.SubTableConfig   #Table splitting rules are configured in classes

Part II: writing configuration classes

Sub database configuration class:
/**
 * Sub database configuration
 *
 * When the sub library rules configured by yml cannot be met, the code can be used to configure the sub rule, and the PreciseShardingAlgorithm interface needs to be implemented,
 * yml The configuration file sub library configuration will reference this class
 */
public class SubDBConfig implements PreciseShardingAlgorithm<Integer> {
    private static Logger logger = Logger.getLogger(SubDBConfig.class);

    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Integer> preciseShardingValue) {
        Integer value = preciseShardingValue.getValue();
        Object[] objects = collection.toArray();
        if((value % 2) == 0){
            logger.info("Library name:"+objects[0]);
            return objects[0]+"";
        }else{
            logger.info("Library name:"+objects[1]);
            return objects[1]+"";
        }
    }
}

Table configuration class

/**
 * Sub table configuration
 *
 * When the table splitting rules configured by yml cannot be met, the code can be used to configure the segmentation rules, and the PreciseShardingAlgorithm interface needs to be implemented
 * yml The configuration file table configuration will refer to this class
 */
public class SubTableConfig implements PreciseShardingAlgorithm<Integer> {
    private static Logger logger = Logger.getLogger(SubTableConfig.class);


    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<Integer> preciseShardingValue) {
        Integer value = preciseShardingValue.getValue();
        //actual-data-nodes: db$->{0..1}.t_user_$->{0..1}
        Object[] objects = collection.toArray();
        if((value % 2) == 0){
            logger.info("Table name:"+objects[0]);
            return objects[0]+"";
        }else{
            logger.info("Table name:"+objects[1]);
            return objects[1]+"";
        }
    }
}

Note that the configuration classes of sub database and sub table need to implement the PreciseShardingAlgorithm interface. These two configuration classes will be referenced in the yml file

Test:

Topics: shardingjdbc