spring boot:shardingsphere+druid+mysql主從復制的讀寫分離(分庫分表)(spring boot 2.3.4)


一,如何實現mysql數據庫的讀寫分離?

1,這個需要先實現mysql數據庫的主從復制(master/slave)
請參考:

https://www.cnblogs.com/architectforest/p/12579847.html

 

2,說明:如果分庫則需要在mysql的主從復制中添加需要復制的各個庫:

           例如本例中的從庫:

replicate-do-db = ebusiness,saleorder01,saleorder02

 

說明:劉宏締的架構森林是一個專注架構的博客,地址:https://www.cnblogs.com/architectforest

         對應的源碼可以訪問這里獲取: https://github.com/liuhongdi/

說明:作者:劉宏締 郵箱: 371125307@qq.com

 

二,演示項目的相關信息

1,項目地址:

https://github.com/liuhongdi/masterslavesharding

 

2,項目功能說明:

        演示了帶有分庫與分表的sharding 讀寫分離

        ebusiness:默認訪問的非分表庫

        saleorder01:第一個分表庫

        saleorder02:第二個分表庫

        它們各對應一個讀寫分離用到的從庫

 

3,項目結構:

 

 

 

4,數據庫的結構:

 

 

三,配置文件說明

1,pom.xml

        <!--mybatis begin-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>

        <!--log4j2 begin-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!--mysql begin-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--pagehelper begin-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

        <!--shardingsphere begin-->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.1.1</version>
        </dependency>

        <!--thymeleaf begin-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

 

2,application.properties

#error
server.error.include-stacktrace=always
#error
logging.level.org.springframework.web=trace
#thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

#shardingsphere names 
spring.shardingsphere.datasource.names=ebusinessmaster,ebusinessslave01,saleorder01,saleorder02,saleorder01slave01,saleorder02slave01
#shardingsphere ebusinessmaster
spring.shardingsphere.datasource.ebusinessmaster.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ebusinessmaster.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ebusinessmaster.url=jdbc:mysql://127.0.0.1:3306/ebusiness?characterEncoding=utf-8
spring.shardingsphere.datasource.ebusinessmaster.username=root
spring.shardingsphere.datasource.ebusinessmaster.password=password
spring.shardingsphere.datasource.ebusinessmaster.filters=stat,wall,log4j2
spring.shardingsphere.datasource.ebusinessmaster.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
#shardingsphere ebusinessslave01
spring.shardingsphere.datasource.ebusinessslave01.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ebusinessslave01.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ebusinessslave01.url=jdbc:mysql://127.0.0.1:3307/ebusiness?characterEncoding=utf-8
spring.shardingsphere.datasource.ebusinessslave01.username=root
spring.shardingsphere.datasource.ebusinessslave01.password=password
spring.shardingsphere.datasource.ebusinessslave01.filters=stat,wall,log4j2
spring.shardingsphere.datasource.ebusinessslave01.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
#shardingsphere saleorder01master
spring.shardingsphere.datasource.saleorder01.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.saleorder01.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.saleorder01.url=jdbc:mysql://127.0.0.1:3306/saleorder01?characterEncoding=utf-8
spring.shardingsphere.datasource.saleorder01.username=root
spring.shardingsphere.datasource.saleorder01.password=password
spring.shardingsphere.datasource.saleorder01.filters=stat,wall,log4j2
spring.shardingsphere.datasource.saleorder01.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
#shardingsphere saleorder01slave01
spring.shardingsphere.datasource.saleorder01slave01.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.saleorder01slave01.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.saleorder01slave01.url=jdbc:mysql://127.0.0.1:3307/saleorder01?characterEncoding=utf-8
spring.shardingsphere.datasource.saleorder01slave01.username=root
spring.shardingsphere.datasource.saleorder01slave01.password=password
spring.shardingsphere.datasource.saleorder01slave01.filters=stat,wall,log4j2
spring.shardingsphere.datasource.saleorder01slave01.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
#shardingsphere saleorder02master
spring.shardingsphere.datasource.saleorder02.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.saleorder02.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.saleorder02.url=jdbc:mysql://127.0.0.1:3306/saleorder02?characterEncoding=utf-8
spring.shardingsphere.datasource.saleorder02.username=root
spring.shardingsphere.datasource.saleorder02.password=password
spring.shardingsphere.datasource.saleorder02.filters=stat,wall,log4j2
spring.shardingsphere.datasource.saleorder02.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
#shardingsphere saleorder02slave01
spring.shardingsphere.datasource.saleorder02slave01.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.saleorder02slave01.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.saleorder02slave01.url=jdbc:mysql://127.0.0.1:3307/saleorder02?characterEncoding=utf-8
spring.shardingsphere.datasource.saleorder02slave01.username=root
spring.shardingsphere.datasource.saleorder02slave01.password=password
spring.shardingsphere.datasource.saleorder02slave01.filters=stat,wall,log4j2
spring.shardingsphere.datasource.saleorder02slave01.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
#database default data source
spring.shardingsphere.sharding.default-data-source-name=ebusinessmaster
#database strategy
spring.shardingsphere.sharding.default-database-strategy.standard.sharding-column=orderId
spring.shardingsphere.sharding.default-database-strategy.standard.precise-algorithm-class-name=com.masterslavesharding.demo.algorithm.DatabasePreciseShardingAlgorithm
#database table
spring.shardingsphere.sharding.binding-tables=t_order
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=saleorder0$->{1..1}.t_order_$->{1..2},saleorder0$->{2..2}.t_order_$->{3..4}
#spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=saleorder01master.t_order_$->{1..2},saleorder02master.t_order_$->{3..4}
spring.shardingsphere.sharding.tables.t_order.table-strategy.standard.sharding-column=orderId
spring.shardingsphere.sharding.tables.t_order.table-strategy.standard.precise-algorithm-class-name=com.masterslavesharding.demo.algorithm.OrderTablePreciseShardingAlgorithm
#master slave
spring.shardingsphere.masterslave.load-balance-algorithm-type=round_robin
#指定ebusinessmaster為主庫,slave0為它的從庫
spring.shardingsphere.sharding.master-slave-rules.ebusinessmaster.master-data-source-name=ebusinessmaster
spring.shardingsphere.sharding.master-slave-rules.ebusinessmaster.slave-data-source-names=ebusinessslave01
#指定master1為主庫,ebusinessslave01為它的從庫
spring.shardingsphere.sharding.master-slave-rules.saleorder01.master-data-source-name=saleorder01
spring.shardingsphere.sharding.master-slave-rules.saleorder01.slave-data-source-names=saleorder01slave01
#指定master1為主庫,slave1為它的從庫
spring.shardingsphere.sharding.master-slave-rules.saleorder02.master-data-source-name=saleorder02
spring.shardingsphere.sharding.master-slave-rules.saleorder02.slave-data-source-names=saleorder02slave01
#debug
spring.shardingsphere.props.sql.show=true

#mybatis
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

 

3,數據庫中的數據表:

CREATE TABLE `goods` (
 `goodsId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `goodsName` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'name',
 `stock` int(11) NOT NULL DEFAULT '0' COMMENT 'stock',
 PRIMARY KEY (`goodsId`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表'

插入一條示例數據:

INSERT INTO `goods` (`goodsId`, `goodsName`, `stock`) VALUES
(3, 'green cup1', 70);

分庫分表的訂單表:

CREATE TABLE `t_order_4` (
 `orderId` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
 `goodsName` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'name',
 PRIMARY KEY (`orderId`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='order4'

因為4個表的結構相同,其他表也可用這個建表sql

 

四,java代碼說明

1,DatabasePreciseShardingAlgorithm.java

@Component
public class DatabasePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
        System.out.println("------------------select database name");
        Long curValue = shardingValue.getValue();
        String curBase = "";
        if (curValue > 0 && curValue<=200) {
            curBase = "saleorder01";
        } else {
            curBase = "saleorder02";
        }
        return curBase;
    }
}

分庫算法類

 

2,OrderTablePreciseShardingAlgorithm.java

@Component
public class OrderTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
        Long curValue = shardingValue.getValue();
        String curTable = "";
        if (curValue > 0 && curValue<=100) {
            curTable = "t_order_1";
        } else if (curValue > 100 && curValue<=200) {
            curTable = "t_order_2";
        } else if (curValue > 200 && curValue<=300) {
            curTable = "t_order_3";
        } else {
            curTable = "t_order_4";
        }
        return curTable;
    }
}

分表算法類

 

3,DruidConfig.java

@Configuration
public class DruidConfig {

    /**
     * Druid監控
     */
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        Map<String,String> initParams = new HashMap<>();//這是配置的druid監控的登錄密碼
        initParams.put("loginUsername","root");
        initParams.put("loginPassword","root");
        //默認就是允許所有訪問
        initParams.put("allow","127.0.0.1,192.168.3.4");
        //黑名單IP
        initParams.put("deny","192.168.15.21");
        bean.setInitParameters(initParams);
        return bean;
    }

    /**
     * web監控的filter
     */
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
        Map<String,String> initParams = new HashMap<>();
        initParams.put("exclusions","/static/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");//過濾掉需要監控的文件
        bean.setInitParameters(initParams);
        bean.setUrlPatterns(Arrays.asList("/*"));
        return  bean;
    }
}

druid配置

 

4,GoodsController.java

@RestController
@RequestMapping("/goods")
public class GoodsController {

    private static final String SUCCESS = "SUCCESS";
    private static final String FAIL = "FAIL";

    @Resource
    private GoodsMapper goodsMapper;

    //更新商品庫存 參數:商品id,增加的庫存數量
    @RequestMapping("/goodsstock/{goodsId}/{count}")
    @ResponseBody
    public String goodsStock(@PathVariable Long goodsId,
                            @PathVariable int count) {
         int res = goodsMapper.updateGoodsStock(goodsId,count);
         System.out.println("res:"+res);
         if (res>0) {
             return SUCCESS;
         } else {
             return FAIL;
         }
    }
    //商品詳情 參數:商品id
    @GetMapping("/goodsinfo")
    @ResponseBody
    public Goods goodsInfo(@RequestParam(value="goodsid",required = true,defaultValue = "0") Long goodsId) {
        Goods goods = goodsMapper.selectOneGoods(goodsId);
        return goods;
    }
}

對非分庫分表的庫的訪問

 

5,OrderController.java

@Controller
@RequestMapping("/order")
public class OrderController {

    private static final String SUCCESS = "SUCCESS";
    private static final String FAIL = "FAIL";

    @Resource
    private OrderMapper orderMapper;

    //訂單列表,列出分庫分表的數據
    @GetMapping("/orderlist")
    public String list(Model model, @RequestParam(value="currentPage",required = false,defaultValue = "1") Integer currentPage){
        PageHelper.startPage(currentPage, 5);
        List<Order> orderList = orderMapper.selectAllOrder();
        model.addAttribute("orderlist",orderList);
        PageInfo<Order> pageInfo = new PageInfo<>(orderList);
        model.addAttribute("pageInfo", pageInfo);
        System.out.println("------------------------size:"+orderList.size());
        return "order/list";
    }


    //添加一個訂單
    @GetMapping("/addorder")
    @ResponseBody
    public String addOrder(@RequestParam(value="orderid",required = true,defaultValue = "0") Long orderId
                           )  throws SQLException, IOException {

        String goodsId = "3";
        String goodsNum = "1";
        String goodsName = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
        Order orderOne = new Order();
        orderOne.setOrderId(orderId);
        orderOne.setGoodsName(goodsName);
        int resIns = orderMapper.insertOneOrder(orderOne);
        System.out.println("orderId:"+orderOne.getOrderId());

        if (resIns>0) {
            return SUCCESS;
        } else {
            return FAIL;
        }
    }
}

對分庫分表的訂單表的訪問

 

6,GoodsMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.masterslavesharding.demo.mapper.GoodsMapper">
    <select id="selectOneGoods" parameterType="long" resultType="com.masterslavesharding.demo.pojo.Goods">
        select * from goods where goodsId=#{goodsId}
    </select>
    <update id="updateGoodsStock">
        UPDATE goods SET
        stock = stock+#{changeAmount,jdbcType=INTEGER}
        WHERE goodsId = #{goodsId,jdbcType=BIGINT}
    </update>
</mapper>

 

7,OrderMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.masterslavesharding.demo.mapper.OrderMapper" >
    <select id="selectAllOrder" resultType="com.masterslavesharding.demo.pojo.Order">
        select * from t_order order by orderId desc
    </select>
    <insert id="insertOneOrder" parameterType="com.masterslavesharding.demo.pojo.Order" useGeneratedKeys="true" keyProperty="orderId" >
        insert into t_order(orderId,goodsName)
        values(
            #{orderId},#{goodsName}
        )
     </insert>
</mapper>

 

8,其他非關鍵的對象類等可訪問github獲取

 

五,測試效果

1,測試非分表庫寫入:

查看主庫的商品表:

 

 查看從庫的商品表:

 

更新數據:訪問:

http://127.0.0.1:8080/goods/goodsstock/3/6

給商品添加了6個庫存:

查看主庫的商品表:

 

 查看從庫的商品表:

 

可以看到修改已生效

查看應用的讀取:

http://127.0.0.1:8080/goods/goodsinfo?goodsid=3

返回:

 

  

2,測試非分表庫讀取

修改從庫中商品的信息:如圖:

再次訪問商品的信息:

http://127.0.0.1:8080/goods/goodsinfo?goodsid=3

返回:

查看此時主庫的商品表:

可見數據由從庫讀取,讀寫分離已生效

 

3,測試分表庫寫入,訪問:

http://127.0.0.1:8080/order/addorder?orderid=50

返回:

SUCCESS

查看主庫的數據表:

查看從庫的數據表:

可以看到主庫和從庫都寫入了

 

4,測試分表庫讀取:

查看上一步寫入的id為50的訂單:

手動修改從庫中orderId=50的商品的名字:注意是從庫:

例如修改為:

 

 再次查看訂單列表的讀取:

 

 修改已生效,證明數據是由從庫中讀取

 

六,查看spring boot的版本:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.4.RELEASE)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM