[SpringBoot Advanced] SpringBoot は Sharding-JDBC サブデータベースとサブテーブルを統合します

アパッチ・シャーディングスフィア

Apache ShardingSphere (Incubator) は、オープン ソースの分散データベース ミドルウェア ソリューションで構成されたエコシステムであり、独立しているが混合展開が可能な 3 つの製品で構成されています。これらはすべて、標準化されたデータ シャーディング、分散トランザクション、およびデータベース ガバナンス機能を提供し、Java 同型、異種言語、クラウド ネイティブなどのさまざまなアプリケーション シナリオに適用できます。

ShardingSphere は、まったく新しいリレーショナル データベースを実装するのではなく、分散シナリオでリレーショナル データベースのコンピューティングおよびストレージ機能を十分かつ合理的に利用することを目的としたリレーショナル データベース ミドルウェアとして位置づけられています。変わらないものにフォーカスすることで、物事の本質を捉えます。リレーショナルデータベースは現在でも巨大な市場を占めており、各社の基幹事業の礎となっており、今後も揺るぎないものとなっておりますが、現段階では転覆ではなく、本来のベースでの伸びに注目しております。Apache の公式リリースは、バージョン 4.0.0 から始まります。

サブライブラリとサブテーブル

データベース内のデータの量は必ずしも制御可能ではありません. サブデータベースのサブテーブルがなければ, 時間とビジネスの発展に伴い, データベース内のテーブルがますます増え, テーブル内のデータの量も増加します. . 値が大きいほど、それに応じてデータ操作、追加、削除、変更、およびクエリのオーバーヘッドも増加します. また、分散配置ができないため、リソース (CPU、ディスク、メモリ、IO など)最終的には、データベースが処理できるデータ量とデータ処理能力がボトルネックになります。

サブデータベースサブテーブルは、過剰なデータ量によるデータベースのパフォーマンス低下の問題を解決するためのものです.元の独立したデータベースはいくつかのデータベースに分割され、大きなデータテーブルはいくつかのデータテーブルに分割され、単一のデータベースと単一データ データベースのパフォーマンスを向上させる目的を達成するために、テーブル内のデータ量が少なくなります。

サブデータベースサブテーブルのやり方

データベースのセグメンテーションとは、単一のデバイス、つまりサブデータベースの負荷を分散する効果を達成するために、特定の特定の条件を介して、同じデータベースに保存されているデータを複数のデータベース (ホスト) に分散することを指します。サブテーブル。

データセグメンテーションは、セグメンテーションルールの種類に応じて、垂直セグメンテーションと水平セグメンテーションに分けることができます

  • 垂直セグメンテーション: 1 つのテーブルを複数のテーブルに分割し、それらを異なるデータベース (ホスト) に分散します。
  • 水平分割: テーブル内のデータの論理関係に従って、テーブル内のデータは特定の条件に従って複数のデータベースに分割されます

縦割り

データベースは複数のテーブルで構成されており、各テーブルは異なる業務に対応しています.垂直セグメンテーションとは、テーブルを業務別に分類し、異なるデータベースに分散することで、データが異なるデータベース(専用データベース)に分散されるようにすることです.

垂直テーブル

データベースでテーブルを操作し、このテーブルのフィールド データの一部を新しいテーブルに保存してから、このテーブルのフィールド データの他の部分を別のテーブルに保存します。

ここに画像の説明を挿入

垂直サブライブラリ

単一データベースを業務別に分割、専用データベーステーブル

ここに画像の説明を挿入

垂直分割の利点は次のとおりです。

  • 分割後の業務は明確で、システム間の統合や拡張も容易です。

  • プライズ テーブルは、コスト、アプリケーション レベル、アプリケーション タイプなどに応じてさまざまなマシンに配置されるため、管理が容易で、データのメンテナンスも簡単です。

垂直分割の欠点は次のとおりです。

  • 一部のビジネス テーブルは関連付け (結合) できず、インターフェイスを介してのみ解決できるため、システムの複雑さが増します。

  • 業務ごとに異なる制限があるため、単一データベースのパフォーマンスがボトルネックとなり、データの拡張とパフォーマンスの向上が困難になります。

  • トランザクション処理が複雑になります。

水平に分割

垂直セグメンテーションと比較して、水平セグメンテーションは、テーブルを分類するのではなく、特定のフィールドの特定のルールに従って複数のライブラリに分散させます.各テーブルにはデータの一部が含まれ、すべてのテーブルが合計されます.データ.

簡単に言えば、データの水平分割は、データ行に従って分割すること、つまり、テーブル内のいくつかの行を 1 つのデータベース テーブルに分割し、他の行を別のデータベース テーブルに分割することと理解できます。

水平サブライブラリ

ここに画像の説明を挿入

レベルテーブル

ここに画像の説明を挿入

水平分割の利点:

  • 1 つのデータベースと 1 つのテーブルのデータは一定のレベルに保たれるため、パフォーマンスの向上に役立ちます。

  • 分割されたテーブルの構造は同じで、アプリケーション層の変更はほとんど必要なく、ルーティング ルールを追加するだけで済みます。

  • システムの安定性と負荷容量の向上。

水平分割の欠点は次のとおりです。

  • 分割後、データが散らばり、データベースの結合操作が使いづらく、クロスデータベース結合のパフォーマンスが低下します。

  • 断片化されたトランザクションの一貫性は解決が難しく、データ拡張の難しさと維持は非常に大きいです。

サブデータベースとサブテーブルに起因する問題

  • クロスノード結合に問題があります。
  • クロスノード マージの並べ替えとページングの問題があります。
  • 複数のデータ ソースの管理に問題がある

サブデータベースとサブテーブルのミドルウェア

現在、国内で使用されているサブデータベースとサブテーブル用のミドルウェアは数多くあります。主に次のものが含まれます。

  • アパッチ・シャーディングスフィア
  • 私の猫

シャーディング-JDBC

Sharding-JDBCはDangdang社が開発したオープンソースの分散型データベースミドルウェアで、バージョン3.0からSharding-Sphereに組み込まれ、その後Apacheインキュベーターに入り、バージョン4.0以降がApache版となります。メイヴン座標

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

Sharding-JDBC は、ShardingSphere の最初の製品であり、ShardingSphere の前身です。これは、Java の JDBC 層の上に追加のサービスを提供する軽量の Java フレームワークとして位置づけられています。クライアントを使用してデータベースに直接接続し、追加のデプロイメントや依存関係なしに jar パッケージの形式でサービスを提供します. JDBC ドライバーの拡張バージョンとして理解でき、JDBC およびさまざまな ORM フレームワークと完全に互換性があります.

ここに画像の説明を挿入

Sharding-JDBC のコア機能は、データのシャーディングと読み取りと書き込みの分離です. Sharding-JDBC を介して、アプリケーションは透過的に jdbc を使用して、データベースとテーブルに分割され、読み取りと書き込みから分離された複数のデータ ソースにアクセスできます。データ ソースの数とデータの配布方法。

  • JPA、Hibernate、Mybatis、Spring JDBC テンプレートなどの JDBC ベースの ORM フレームワークに適用するか、JDBC を直接使用します。
  • DBCP、C3P0、BoneCP、Druid、HikariCP などのサードパーティ データベース接続プールをサポートします。
  • JDBC 仕様を実装するすべてのデータベースがサポートされています。現在、MySQL、Oracle、SQLServer、PostgreSQL、および SQL92 標準に準拠する任意のデータベースをサポートしています。

ここに画像の説明を挿入

sharding-jdbc は水平テーブル シャーディングを実装します

データベーススクリプト

デシベル:order_db_1
テーブル: t_order_1、t_order_2

CREATE TABLE `t_order_1` (
  `id` int NOT NULL,
  `order_type` int DEFAULT NULL,
  `customer_id` int DEFAULT NULL,
  `amount` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

ここに画像の説明を挿入

ポンポン

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

   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
   </dependency>
   <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid</artifactId>
       <version>1.1.23</version>
   </dependency>
   <dependency>
       <groupId>org.apache.shardingsphere</groupId>
       <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
       <version>4.1.1</version>
   </dependency>

   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
   </dependency>

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

<build>
   <plugins>
       <plugin>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-maven-plugin</artifactId>
       </plugin>
   </plugins>
</build>

注文

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    
    
    private Integer id;
    private Integer orderType;
    private Integer customerId;
    private Double amount;
}

アプリケーション.yml

spring:
  shardingsphere:
    datasource:
      # 配置数据源的名称
      names: ds1
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
    # 打开sql输出日志
    props:
      sql:
        show: true
    sharding:
      tables:
        # 逻辑表,SQL语句中写对应的逻辑表
        # 如:insert into t_order(id,order_type,customer_id,amount) values(?,?,?,?)
        # 虽然写的是添加数据到t_order表,实际会根据分表策略路由到t_order_1或t_order_2
        t_order:
          # 指定t_order表的分布情况,配置表在哪个数据库中,表名称是什么
          actual-data-nodes: ds1.t_order_$->{
    
    1..2}
          # 指定t_order表里主键id生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
          # 指定分片策略。根据id的奇偶性来判断插入到哪个表
          table-strategy:
            inline:
              algorithm-expression: t_order_${
    
    id%2+1}
              sharding-column: id

検定試験

@SpringBootTest(classes = ShardingJdbcApplication.class)
public class OrderTest {
    
    

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void testSave() {
    
    
        String saveSQL = "insert into t_order(id,order_type,customer_id,amount) values(?,?,?,?)";

        ArrayList<Order> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            list.add(new Order(i, i, i,1000.0*i));
        }
        jdbcTemplate.batchUpdate(saveSQL, new BatchPreparedStatementSetter() {
    
    
            @Override
            public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
    
    
                Order order = list.get(i);
                preparedStatement.setInt(1,order.getId());
                preparedStatement.setInt(2,order.getOrderType());
                preparedStatement.setInt(3,order.getCustomerId());
                preparedStatement.setDouble(4,order.getAmount());
            }

            @Override
            public int getBatchSize() {
    
    
                return list.size();
            }
        });
    }

    @Test
    public void getOrder() {
    
    
        String querySQL = "select * from t_order";
        List<Order> orders = jdbcTemplate.query(querySQL, new BeanPropertyRowMapper<>(Order.class));
        orders.forEach(System.out::println);

    }
}

1.testSave()メソッドの実行結果
ここに画像の説明を挿入
2.getOrderメソッドの実行結果
ここに画像の説明を挿入

sharding-jdbc は、水平データベース シャーディングを実装します

デシベル

ここに画像の説明を挿入

アプリケーション.yml

spring:
  shardingsphere:
    datasource:
      # 配置不同的数据源
      names: ds1,ds2
      #配置ds1数据源的基本信息
      ds1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
      #配置ds2数据源的基本信息
      ds2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/order_db_2?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
    #打开sql输出日志
    props:
      sql:
        show: true
    sharding:
      tables:
        # 逻辑表,必须配置正确,如:分表t_order_1,t_order_2,则逻辑表为 t_order
        t_order:
          #指定数据库表的分布情况
          actual-data-nodes: ds$->{
    
    1..2}.t_order_$->{
    
    1..2}
           #指定库分片策略,根据customer_id的奇偶性来添加到不同的库中,customer_id为基数路由到ds2,customer_id为偶数路由到ds1
          database-strategy:
            inline:
              sharding-column: customer_id
              algorithm-expression: ds$->{
    
    customer_id%2+1}
          #指定t_order表的主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
          #指定表分片策略,根据id的奇偶性来添加到不同的表中,id为基数路由到ds2,id为偶数路由到ds1
          table-strategy:
            inline:
              sharding-column: id
              algorithm-expression: t_order_$->{
    
    id%2+1}

ルーティング テストの検証

ルーティング キーが指定されていません

/**
* 不指定路由键,扫描全库全表查询汇总,效率低
*/
@Test
public void getOrder() {
    
    
   String querySQL = "select * from t_order";
   List<Order> orders = jdbcTemplate.query(querySQL, new BeanPropertyRowMapper<>(Order.class));
   orders.forEach(System.out::println);
}

ここに画像の説明を挿入

ライブラリ ルーティング キーを指定する

/**
* 指定库路由键
*/
@Test
public void getOrderByCustomerId() {
    
    
   String querySQL = "select * from t_order where customer_id = ?";
   List<Order> orders = jdbcTemplate.query(querySQL, new BeanPropertyRowMapper<>(Order.class), 2);
   orders.forEach(System.out::println);
}

ここに画像の説明を挿入

テーブル ルーティング キーを指定する

/**
* 指定表路由键
*/
@Test
public void getOrderById() {
    
    
   String querySQL = "select * from t_order where id = ?";
   List<Order> orders = jdbcTemplate.query(querySQL, new BeanPropertyRowMapper<>(Order.class), 2);
   orders.forEach(System.out::println);
}

ここに画像の説明を挿入

ライブラリ ルーティング キーとテーブル ルーティング キーを指定する

/**
* 指定库路由键和表路由键 效率最高
*/
@Test
public void getOrderByCustomerIdAndId() {
    
    
   String querySQL = "select * from t_order where customer_id = ? and id = ?";
   List<Order> orders = jdbcTemplate.query(querySQL, new BeanPropertyRowMapper<>(Order.class), 2,2);
   orders.forEach(System.out::println);
}

ここに画像の説明を挿入

sharding-jdbc は垂直データベース シャーディングを実装します

異なるデータ ノードに同じライブラリ order_db_1 を作成します。
ローカル データベースがあり、仮想マシンは docker を使用してデータベースを起動します

デシベル

docker pull mysql:5.7

docker run -d -p 3306:3306 --privileged=true -e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci

ここに画像の説明を挿入
ここに画像の説明を挿入

アプリケーション.yml

spring:
  shardingsphere:
    datasource:
      # 配置不同的数据源
      names: ds1,ds2
      #配置ds1数据源的基本信息
      ds1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: root
      #配置ds2数据源的基本信息
      ds2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://192.168.56.10:3306/order_db_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
        username: root
        password: 123456
    #打开sql输出日志
    props:
      sql:
        show: true
    sharding:
      tables:
        # 逻辑表,必须配置正确
        customer:
          #配置customer表所在的数据节点
          actual-data-nodes: ds2.customer
          #指定orders表的主键生成策略
          key-generator:
            column: id
            type: SNOWFLAKE
          #指定表分片策略
          table-strategy:
            inline:
              sharding-column: id
              algorithm-expression: customer
        # 逻辑表,SQL语句中写对应的逻辑表,必须配置正确,否则报表不存在异常
        # 如:insert into t_order(id,order_type,customer_id,amount) values(?,?,?,?)
        # 虽然写的是添加数据到t_order表,实际会根据分表策略路由到t_order_1或t_order_2
        t_order:
          #实际t_order表所在的数据库表
          actual-data-nodes: ds1.t_order_1

垂直シャーディング戦略をテストする

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Customer {
    
    
    private Integer id;
    private String name;
}
/**
 * 测试垂直分库
 */
@Test
public void testSaveCustomer() {
    
    
    String saveCustomerSQL = "insert into customer(id,name) values(?,?)";
    String saveOrderSQL = "insert into t_order(id,order_type,customer_id,amount) values(?,?,?,?)";

    Customer customer = new Customer(10001,"李星云");

    ArrayList<Order> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
    
    
        list.add(new Order(i, i, customer.getId(),1000.0*i));
    }
    jdbcTemplate.batchUpdate(saveOrderSQL, new BatchPreparedStatementSetter() {
    
    
        @Override
        public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
    
    
            Order order = list.get(i);
            preparedStatement.setInt(1,order.getId());
            preparedStatement.setInt(2,order.getOrderType());
            preparedStatement.setInt(3,order.getCustomerId());
            preparedStatement.setDouble(4,order.getAmount());
        }

        @Override
        public int getBatchSize() {
    
    
            return list.size();
        }
    });
    jdbcTemplate.update(saveCustomerSQL, preparedStatement -> {
    
    
        preparedStatement.setInt(1,customer.getId());
        preparedStatement.setString(2, customer.getName());
    });
}

ここに画像の説明を挿入

サブデータベースとサブテーブルのまとめ

1.サブデータベースとサブテーブルにsharding-jdbcを使用する場合、ルールに従って対応するライブラリルーティングキーとテーブルルーティングキーを指定するのが最も効率的であるため、ルーティングキーは対応するテーブルのインデックスとして設定できますクエリ効率を改善します。開発中に 2 つのルーティング キーを設定することが本当に不可能な場合は、1 つを設定して、データベースとテーブル全体をスキャンしないようにしてください。低効率

2. spring.shardingsphere.sharding.tables.(logical table).actual-data-nodes=ds1.t_order_1 の構成に関して、
ここに画像の説明を挿入
正しい論理テーブル ルーティング構成は次のようになります。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_45297578/article/details/129280078
おすすめ