實戰:Shardingsphere分庫分表

前言

由於關係型數據庫大多采用B+樹類型的索引,在數據量超過閾值的情況下,索引深度的增加也將使得磁盤訪問的IO次數增加,進而導致查詢性能的下降;同時,高併發訪問請求也使得集中式數據庫成爲系統的最大瓶頸。

項目介紹

Apache ShardingSphere 是一套開源的分佈式數據庫中間件解決方案組成的生態圈,它由 JDBC、Proxy 和 Sidecar(規劃中)這 3 款相互獨立,卻又能夠混合部署配合使用的產品組成。它們均提供標準化的數據分片、分佈式事務和數據庫治理功能,可適用於如 Java 同構、異構語言、雲原生等各種多樣化的應用場景。

Apache ShardingSphere 定位爲關係型數據庫中間件,旨在充分合理地在分佈式的場景下利用關係型數據庫的計算和存儲能力,而並非實現一個全新的關係型數據庫。它通過關注不變,進而抓住事物本質。

官方地址:http://shardingsphere.apache.org

本文目標

本文將以springboot進行集成演示,以訂單表爲例,演示shardingsphere分庫分表的基本原理及配置。

項目產生的demo地址見尾部。

實戰

數據庫腳本

id主鍵不設置爲自增,分別在db0和db1創建t_order_2019、t_order_2020三個表

 CREATE TABLE `t_order` (
  `id` bigint(20) NOT NULL,
  `order_sn` varchar(255) DEFAULT NULL COMMENT '訂單編號',
  `member_id` varchar(255) DEFAULT NULL COMMENT '用戶id',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  `status` int(11) DEFAULT NULL COMMENT '訂單狀態',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

maven依賴

    <properties>
         <sharding-sphere.version>4.0.0-RC2</sharding-sphere.version>
    </properties>
 <dependency>
         <groupId>org.apache.shardingsphere</groupId>
         <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
         <version>${sharding-sphere.version}</version>
    </dependency>

配置文件

配置db0和db1兩個數據庫,定義數據庫路由規則爲member_id除以2取餘。

每個數據庫創建t_order_0、t_order_1、t_order_2三張表,定義表的路由規則爲member_id除以3取餘。

mybatis.mapper-locations=classpath:mybatis/*Mapper.xml
logging.level.com.lzn.shardingsphere.dao=debug

spring.shardingsphere.datasource.names = db0,db1

spring.shardingsphere.datasource.db0.type = com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db0.driver-class-name = com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db0.jdbc-url = jdbc:mysql://192.168.202.128:3306/sharding?characterEncoding=utf8&useSSL=false
spring.shardingsphere.datasource.db0.username = root
spring.shardingsphere.datasource.db0.password = 123qwe

spring.shardingsphere.datasource.db1.type = com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.db1.driver-class-name = com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db1.jdbc-url = jdbc:mysql://192.168.202.128:3307/sharding?characterEncoding=utf8&useSSL=false
spring.shardingsphere.datasource.db1.username = root
spring.shardingsphere.datasource.db1.password = 123qwe

# 分庫策略 根據id取模確定數據進哪個數據庫
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column = member_id
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression = db$->{member_id % 2}

# 分表策略
# 節點 db0.t_order_0,db0.t_order_1,db0.t_order_2,db1.t_order_0,db1.t_order_1,db1.t_order_2
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = db$->{0..1}.t_order_$->{0..2}
# 分表字段member_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = member_id
# 分表策略 根據member_id取模,確定數據最終落在那個表中
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{member_id % 3}

# 使用SNOWFLAKE算法生成主鍵
spring.shardingsphere.sharding.tables.t_order.key-generator.column = id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE

數據庫持久層

使用mybatis-generator生成

實體類

image-20200615171558278

Dao接口

image-20200615171449030

sql文件

image-20200615172144593

啓動項目

image-20200615152237950

測試驗證

插入測試:準備member_id分別爲 100,101,102,103的

預期結果:依次在db0.t_order_1、db1.t_order_2、db0.t_order_0、db1.order_1

單元測試類

@Test
    void TestInsertOrder(){
        List<Order> orderList = new ArrayList<>();
        orderList.add(new Order("111111",100L,new Date(),1));
        orderList.add(new Order("222222",101L,new Date(),1));
        orderList.add(new Order("333333",102L,new Date(),1));
        orderList.add(new Order("444444",103L,new Date(),1));
        for(Order order:orderList){
            orderService.createOrder(order);
        }
    }

![image-20200615171235787](http://lznqn.cxylt.cn/picGO/image-20200615171235787.png

image-20200615172440925

插入驗證

member_id = 100 路由到db0.t_order_1

image-20200615173904768
image-20200615172702673

member_id = 101 路由到 db1.t_order_2

image-20200615174037627
image-20200615172833384

member_id = 102 路由到 db0.t_order_0

image-20200615174754591
image-20200615172639393

member_id = 103 路由到 db1.t_order_1

image-20200615175039272
image-20200615172746566

查詢驗證

不帶分片鍵的查詢

@Test
    void TestListOrder(){
        OrderExample orderExample = new OrderExample();
        List<Order> orderList= orderService.listOrder(orderExample);
        for (Order o:orderList){
            System.out.println(o.toString());
        }
    }

此時會全庫全表查詢,並將結果彙總

2020-06-15 18:00:59.099  INFO 948 --- [           main] ShardingSphere-SQL                       : Actual SQL: db0 ::: select
    id, order_sn, member_id, create_time, status
  from t_order_0
2020-06-15 18:00:59.099  INFO 948 --- [           main] ShardingSphere-SQL                       : Actual SQL: db0 ::: select
    id, order_sn, member_id, create_time, status
    from t_order_1
2020-06-15 18:00:59.099  INFO 948 --- [           main] ShardingSphere-SQL                       : Actual SQL: db0 ::: select
    id, order_sn, member_id, create_time, status
    from t_order_2
2020-06-15 18:00:59.099  INFO 948 --- [           main] ShardingSphere-SQL                       : Actual SQL: db1 ::: select
    id, order_sn, member_id, create_time, status
    from t_order_0
2020-06-15 18:00:59.099  INFO 948 --- [           main] ShardingSphere-SQL                       : Actual SQL: db1 ::: select
    id, order_sn, member_id, create_time, status
    from t_order_1
2020-06-15 18:00:59.100  INFO 948 --- [           main] ShardingSphere-SQL                       : Actual SQL: db1 ::: select
    id, order_sn, member_id, create_time, status
    from t_order_2
Order(id=479339444940308481, orderSn=333333, memberId=102, createTime=Mon Jun 15 17:23:47 CST 2020, status=1)
Order(id=479339438946648065, orderSn=111111, memberId=100, createTime=Mon Jun 15 17:23:45 CST 2020, status=1)
Order(id=479339445393293312, orderSn=444444, memberId=103, createTime=Mon Jun 15 17:23:47 CST 2020, status=1)
Order(id=479339444424409088, orderSn=222222, memberId=101, createTime=Mon Jun 15 17:23:47 CST 2020, status=1)

帶有member_id的分片條件

 @Test
    void TestListOrder2(){
        OrderExample orderExample = new OrderExample();
        OrderExample.Criteria criteria = orderExample.createCriteria();
        criteria.andMemberIdEqualTo(101L);
        List<Order> orderList= orderService.listOrder(orderExample);
        for (Order o:orderList){
            System.out.println(o.toString());
        }
    }

會自動路由到對應表

2020-06-15 18:06:18.346  INFO 19768 --- [           main] ShardingSphere-SQL                       : Actual SQL: db1 ::: select
     
     
    id, order_sn, member_id, create_time, status
   
    from t_order_2
     
       
     WHERE (  member_id = ? ) ::: [101]
Order(id=479339444424409088, orderSn=222222, memberId=101, createTime=Mon Jun 15 17:23:47 CST 2020, status=1)

總結

shardingsphere分庫分表可以解決單庫單表的數據庫的性能瓶頸問題,開發者使用起來也比較簡單,只需定義好分片的規則和算法,shardingsphere會自動路由、改寫sql、將結果合併返回。但分庫分表也會增加系統的複雜度,例如跨庫的join問題,事務問題,成本問題等,需要綜合考慮是否有分庫分表的必要。

項目demo地址

https://github.com/pengziliu/GitHub-code-practice

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章