1. 程式人生 > 實用技巧 >ShardingSphere入門實戰(1)-Sharding-JDBC使用

ShardingSphere入門實戰(1)-Sharding-JDBC使用

ShardingSphere是一套開源的分散式資料庫中介軟體解決方案組成的生態圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(計劃中)這3款相互獨立的產品組成。 他們均提供標準化的資料分片、分散式事務和資料庫治理功能,可適用於如Java同構、異構語言、雲原生等各種多樣化的應用場景。具體介紹可以檢視官網https://shardingsphere.apache.org/。本文主要介紹在Spring Boot工程中使用Sharding-JDBC,文中使用到的軟體版本:Java 1.8.0_191、sharding-jdbc-core 4.1.1、MySQL 5.7。

1、引入依賴

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-test</artifactId
> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</
artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency>

2、資料分片

2.1、對應配置

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1 #資料來源名稱,多資料來源以逗號分隔
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.40.94.232:3306/qm?useUnicode=true;characterEncoding=UTF-8
        username: root
        password: ooxx5201314
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.198.0.10:3306/itest?useUnicode=true;characterEncoding=UTF-8
        username: root
        password: Root_123!
    sharding:
      tables:
        t_user: #邏輯表名,在一個庫裡分表:ds0.t_user_0,ds0.t_user_1
          actual-data-nodes: ds0.t_user_$->{0..1}
          table-strategy: #表分片策略
            inline: #行表示式分片策略
              sharding-column: user_id #分片的欄位
              algorithm-expression: t_user_$->{user_id % 2} #分片的演算法
        t_dept: #分庫,兩個庫裡建系統的表:ds0.t_dept,ds1.t_dept
          actual-data-nodes: ds$->{0..1}.t_dept
          database-strategy: #資料庫分片策略
            inline: #行表示式分片策略
              sharding-column: dept_id #分片的欄位
              algorithm-expression: ds$->{dept_id % 2} #分片的演算法

    props:
      sql:
        show: true

2.2、對應程式

/**
 * 資料分片
 * t_user在同一個庫裡分表(t_user_0,t_user_1)
 * t_dept分庫
 */
@Test
public void fragmentation() {
    Connection con = null;
    try {
        con = dataSource.getConnection();
        con.setAutoCommit(false);
        Statement st = con.createStatement();

        //插入ds0.t_user_0表
        st.executeUpdate("insert into t_user(user_id,user_name,age) values(10,'趙雲', 30)");
        //插入ds0.t_user_1表
        st.executeUpdate("insert into t_user(user_id,user_name,age) values(11,'張飛', 31)");

        //插入ds0.t_dept表
        st.executeUpdate("insert into t_dept(dept_id,dept_name) values(10,'dept10')");
        //插入ds1.t_dept表
        st.executeUpdate("insert into t_dept(dept_id,dept_name) values(11,'dept11')");

        ResultSet rs = st.executeQuery("select user_id,user_name from t_user where user_id in(10,11)");
        while (rs.next()) {
            logger.info("user_id={},user_name={}", rs.getString("user_id"), rs.getString("user_name"));
        }

        rs = st.executeQuery("select dept_id,dept_name from t_dept where dept_id in(10,11)");
        while (rs.next()) {
            logger.info("dept_id={},dept_name={}", rs.getString("dept_id"), rs.getString("dept_name"));
        }

        con.commit();
    } catch (Exception e) {
        JdbcUtil.rollback(con);
        e.printStackTrace();
    } finally {
        JdbcUtil.close(con);
    }
}

3、讀寫分離

3.1、對應配置

spring:
  shardingsphere:
    datasource:
      names: master,slave0
      master:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.40.94.232:3306/qm?useUnicode=true;characterEncoding=UTF-8
        username: root
        password: ooxx5201314
      slave0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://10.198.0.10:3306/itest?useUnicode=true;characterEncoding=UTF-8
        username: root
        password: Root_123!
    masterslave:
      name: ms
      master-data-source-name: master
      slave-data-source-names: slave0
    props:
      sql:
        show: true

3.2、對應程式

/**
 * 讀寫分離,主庫寫,從庫讀
 * 同一執行緒且同一資料庫連線內,如有寫入操作,以後的讀操作均從主庫讀取,用於保證資料一致性
 */
@Test
public void readWrite() {
    Connection con = null;
    try {
        con = dataSource.getConnection();
        Statement st = con.createStatement();

        //從slave0讀資料
        ResultSet rs = st.executeQuery("select * from wlw_all where id<900");
        while (rs.next()) {
            System.out.println(rs.getString("id") + "|" + rs.getString("zonename"));
        }

        //寫入master
        st.executeUpdate("insert into tree_test(id,name) values(11,'測試')");

        //從master讀資料
        rs = st.executeQuery("select * from a_orm");
        while (rs.next()) {
            System.out.println(rs.getString(1) + "|" + rs.getString(2));
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(con);
    }
}

/**
 * 讀寫分離,強制主庫路由
 */
@Test
public void hintMasterRouteOnly() {
    Connection con = null;
    try {
        con = dataSource.getConnection();
        Statement st = con.createStatement();
        HintManager hintManager = HintManager.getInstance();
        //主庫路由
        hintManager.setMasterRouteOnly();

        //從master讀資料
        ResultSet rs = st.executeQuery("select * from a_orm");
        while (rs.next()) {
            logger.info(rs.getString(1) + "|" + rs.getString(2));
        }
        hintManager.close();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JdbcUtil.close(con);
    }
}

4、完整程式碼

package com.inspur.demo.shardingsphere;

import com.inspur.demo.shardingsphere.util.JdbcUtil;
import org.apache.shardingsphere.api.hint.HintManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

/**
 * ShardingJdbc使用樣例
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class ShardingJdbcCase {
    private static Logger logger = LoggerFactory.getLogger(ShardingJdbcCase.class);

    @Resource
    private DataSource dataSource;

    /**
     * 資料分片
     * t_user在同一個庫裡分表(t_user_0,t_user_1)
     * t_dept分庫
     */
    @Test
    public void fragmentation() {
        Connection con = null;
        try {
            con = dataSource.getConnection();
            con.setAutoCommit(false);
            Statement st = con.createStatement();

            //插入ds0.t_user_0表
            st.executeUpdate("insert into t_user(user_id,user_name,age) values(10,'趙雲', 30)");
            //插入ds0.t_user_1表
            st.executeUpdate("insert into t_user(user_id,user_name,age) values(11,'張飛', 31)");

            //插入ds0.t_dept表
            st.executeUpdate("insert into t_dept(dept_id,dept_name) values(10,'dept10')");
            //插入ds1.t_dept表
            st.executeUpdate("insert into t_dept(dept_id,dept_name) values(11,'dept11')");

            ResultSet rs = st.executeQuery("select user_id,user_name from t_user where user_id in(10,11)");
            while (rs.next()) {
                logger.info("user_id={},user_name={}", rs.getString("user_id"), rs.getString("user_name"));
            }

            rs = st.executeQuery("select dept_id,dept_name from t_dept where dept_id in(10,11)");
            while (rs.next()) {
                logger.info("dept_id={},dept_name={}", rs.getString("dept_id"), rs.getString("dept_name"));
            }

            con.commit();
        } catch (Exception e) {
            JdbcUtil.rollback(con);
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con);
        }
    }


    /**
     * 讀寫分離,主庫寫,從庫讀
     * 同一執行緒且同一資料庫連線內,如有寫入操作,以後的讀操作均從主庫讀取,用於保證資料一致性
     */
    @Test
    public void readWrite() {
        Connection con = null;
        try {
            con = dataSource.getConnection();
            Statement st = con.createStatement();

            //從slave0讀資料
            ResultSet rs = st.executeQuery("select * from wlw_all where id<900");
            while (rs.next()) {
                System.out.println(rs.getString("id") + "|" + rs.getString("zonename"));
            }

            //寫入master
            st.executeUpdate("insert into tree_test(id,name) values(11,'測試')");

            //從master讀資料
            rs = st.executeQuery("select * from a_orm");
            while (rs.next()) {
                System.out.println(rs.getString(1) + "|" + rs.getString(2));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con);
        }
    }

    /**
     * 讀寫分離,強制主庫路由
     */
    @Test
    public void hintMasterRouteOnly() {
        Connection con = null;
        try {
            con = dataSource.getConnection();
            Statement st = con.createStatement();
            HintManager hintManager = HintManager.getInstance();
            //主庫路由
            hintManager.setMasterRouteOnly();

            //從master讀資料
            ResultSet rs = st.executeQuery("select * from a_orm");
            while (rs.next()) {
                logger.info(rs.getString(1) + "|" + rs.getString(2));
            }
            hintManager.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con);
        }
    }

}
ShardingJdbcCase