1. 目的この記事は次の目標を達成します:
アーキテクチャ図: テーブル構造は次のとおりです。 テーブル「order_XXX」を作成します( `order_id` bigint(20) 符号なし NOT NULL, `user_id` int(11) デフォルト '0' コメント '注文ID', `status` int(11) デフォルト '0' コメント '注文ステータス', `booking_date` 日時 デフォルト NULL、 `create_time` datetime デフォルト NULL、 `update_time` 日時 デフォルト NULL、 主キー (`order_id`)、 キー `idx_user_id` (`user_id`)、 キー `idx_bdate` (`booking_date`)、 キー `idx_ctime` (`create_time`)、 キー `idx_utime` (`update_time`) )ENGINE=InnoDB デフォルト文字セット=utf8; 注: 000<= XXX <= 255。この記事では、データベースとテーブルのシャーディングの実践に焦点を当てています。代表的なフィールドのみが保持されます。これに基づいて、他のシナリオを改善できます。 世界的にユニークなIDデザイン 要件: 1. グローバルに一意であること 2: 大まかに順序付けられていること 3: 発信番号が可逆であること
単一マシンの最大QPS: 256,000 耐用年数: 17年 2. 環境整備1. 基本情報
2. データベース環境の準備mysql を入力します: #メインデータベース mysql -h 172.30.1.21 -uroot -pbyarch #ライブラリから mysql -h 172.30.1.31 -uroot -pbyarch コンテナに入る # メイン docker exec -it db_1_master /bin/bash #docker exec -it db_1_slave /bin/bashから 実行状態を確認する #メイン docker exec db_1_master sh -c 'mysql -u root -pbyarch -e "SHOW MASTER STATUS \G"' #docker から exec db_1_slave sh -c 'mysql -u root -pbyarch -e "SHOW SLAVE STATUS \G"' 3. データベースを構築し、サブテーブルをインポートする(1)MySQLマスターインスタンスにデータベースを作成する 172.30.1.21(オーダーデータベース1)、172.30.1.22(オーダーデータベース2)、 172.30.1.23(オーダーデータベース3)、172.30.1.24(オーダーデータベース4) (2)テーブルを作成するためのSQLコマンドを順番にインポートします。 ディレクトリは、mysql -uroot -pbyarch -h172.30.1.21 order_db_1<fast-cloud-mysql-sharding/doc/sql/order_db_1.sql; mysql -uroot -pbyarch -h172.30.1.22 order_db_2<fast-cloud-mysql-sharding/doc/sql/order_db_2.sql; ディレクトリは、mysql -uroot -pbyarch -h172.30.1.23 order_db_3<fast-cloud-mysql-sharding/doc/sql/order_db_3.sql; ディレクトリは、mysql -uroot -pbyarch -h172.30.1.24 order_db_4<fast-cloud-mysql-sharding/doc/sql/order_db_4.sql; 3. 構成と実践1. pomファイル<!-- mango データベースおよびテーブル シャーディング ミドルウェア --> <依存関係> <groupId>org.jfaster</groupId> <artifactId>マンゴースプリングブートスターター</artifactId> <バージョン>2.0.1</バージョン> </依存関係> <!-- 分散 ID ジェネレーター --> <依存関係> <グループID>com.byarch</グループID> <artifactId>高速クラウド ID ジェネレータ</artifactId> <バージョン>${バージョン}</バージョン> </依存関係> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <依存関係> <グループID>mysql</グループID> <artifactId>mysql-コネクタ-java</artifactId> <バージョン>6.0.6</バージョン> </依存関係> 2. 一定の構成パッケージ com.byarch.fast.cloud.mysql.sharding.common; /** * データベースとテーブルシャーディング戦略の共通定数*/ パブリッククラスShardingStrategyConstant { /** * データベース論理名、実際のデータベース名は order_db_XXX です */ パブリック静的最終文字列 LOGIC_ORDER_DATABASE_NAME = "order_db"; /** * サブテーブルの数は256で、一度確定すると変更できません*/ パブリック静的最終int SHARDING_TABLE_NUM = 256; /** * サブデータベースの数を変更することは推奨されません。変更することは可能ですが、DBA がデータを移行する必要があります。*/ パブリック静的最終int SHARDING_DATABASE_NODE_NUM = 4; } 3. yml設定4 つのマスター データベース構成と 4 つのスレーブ データベース構成。ここでは、デフォルトの root ユーザー パスワードのみをテストします。実稼働環境では、root ユーザーの使用は推奨されません。 マンゴー: スキャンパッケージ: com.byarch.fast.cloud.mysql.sharding.dao データソース: - 名前: order_db_1 マスター: ドライバークラス名: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://172.30.1.21:3306/order_db_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false ユーザー名: root パスワード: bysearch 最大プールサイズ: 10 接続タイムアウト: 3000 奴隷: - ドライバークラス名: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://172.30.1.31:3306/order_db_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false ユーザー名: root パスワード: bysearch 最大プールサイズ: 10 接続タイムアウト: 3000 - 名前: order_db_2 マスター: ドライバークラス名: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://172.30.1.22:3306/order_db_2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false ユーザー名: root パスワード: bysearch 最大プールサイズ: 10 接続タイムアウト: 3000 奴隷: - ドライバークラス名: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://172.30.1.32:3306/order_db_2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false ユーザー名: root パスワード: bysearch 最大プールサイズ: 10 接続タイムアウト: 3000 - 名前: order_db_3 マスター: ドライバークラス名: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://172.30.1.23:3306/order_db_3?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false ユーザー名: root パスワード: bysearch 最大プールサイズ: 10 接続タイムアウト: 3000 奴隷: - ドライバークラス名: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://172.30.1.33:3306/order_db_3?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false ユーザー名: root パスワード: bysearch 最大プールサイズ: 10 接続タイムアウト: 3000 - 名前: order_db_4 マスター: ドライバークラス名: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://172.30.1.24:3306/order_db_4?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false ユーザー名: root パスワード: bysearch 最大プールサイズ: 10 接続タイムアウト: 3000 奴隷: - ドライバークラス名: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://172.30.1.34:3306/order_db_4?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedState&connectTimeout=1000&socketTimeout=5000&useSSL=false ユーザー名: root パスワード: bysearch 最大プールサイズ: 10 接続タイムアウト: 300 4. データベースとテーブルのシャーディング戦略1). order_idをshardKeyとして使用してデータベースとテーブルを分割する パッケージ com.byarch.fast.cloud.mysql.sharding.strategy; com.byarch.fast.cloud.mysql.sharding.common.ShardingStrategyConstant をインポートします。 com.byarch.id.generator.IdEntity をインポートします。 com.byarch.id.generator.SeqIdUtil をインポートします。 org.jfaster.mango.sharding.ShardingStrategy をインポートします。 /** * 注文番号サブライブラリとサブテーブル戦略*/ パブリッククラス OrderIdShardingStrategy は ShardingStrategy<Long, Long> を実装します { @オーバーライド パブリック文字列 getDataSourceFactoryName(Long orderId) { (注文ID == null || 注文ID < 0L)の場合 { 新しい IllegalArgumentException をスローします ("order_id が無効です!"); } IdEntity idEntity = SeqIdUtil.decodeId(orderId); idEntity.getExtraId() >= ShardingStrategyConstant.SHARDING_TABLE_NUM の場合 { throw new IllegalArgumentException("シャーディング テーブル Num が無効です。tableNum:" + idEntity.getExtraId()); } //1. ステップの長さを計算します int step = ShardingStrategyConstant.SHARDING_TABLE_NUM / ShardingStrategyConstant.SHARDING_DATABASE_NODE_NUM; //2. ライブラリ番号を計算します long dbNo = Math.floorDiv(idEntity.getExtraId(), step) + 1; //3. データ ソース名を返します。 return String.format("%s_%s", ShardingStrategyConstant.LOGIC_ORDER_DATABASE_NAME, dbNo); } @オーバーライド パブリック String getTargetTable(String logicTableName, Long orderId) { (注文ID == null || 注文ID < 0L)の場合 { 新しい IllegalArgumentException をスローします ("order_id が無効です!"); } IdEntity idEntity = SeqIdUtil.decodeId(orderId); idEntity.getExtraId() >= ShardingStrategyConstant.SHARDING_TABLE_NUM の場合 { throw new IllegalArgumentException("シャーディング テーブル Num が無効です。tableNum:" + idEntity.getExtraId()); } // 規則に基づいて、実際のテーブル名は logicTableName_XXX になります。XXX が 3 桁未満の場合は、0 を追加します。 String.format("%s_%03d", logicTableName, idEntity.getExtraId()); を返します。 } } 2). user_idをshardKeyとして使用してデータベースとテーブルを分割する パッケージ com.byarch.fast.cloud.mysql.sharding.strategy; com.byarch.fast.cloud.mysql.sharding.common.ShardingStrategyConstant をインポートします。 org.jfaster.mango.sharding.ShardingStrategy をインポートします。 /** *シャーディング KEY とデータベース/テーブル シャーディング戦略を指定します*/ パブリッククラス UserIdShardingStrategy は ShardingStrategy<Integer, Integer> を実装します { @オーバーライド パブリック文字列 getDataSourceFactoryName(Integer userId) { //1. ステップの長さ、つまり単一のデータベース内のテーブル数を計算します。 int step = ShardingStrategyConstant.SHARDING_TABLE_NUM / ShardingStrategyConstant.SHARDING_DATABASE_NODE_NUM; //2. データベース番号を計算します。long dbNo = Math.floorDiv(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM, step) + 1; //3. データ ソース名を返します。 return String.format("%s_%s", ShardingStrategyConstant.LOGIC_ORDER_DATABASE_NAME, dbNo); } @オーバーライド パブリック文字列 getTargetTable(文字列 logicTableName、整数 userId) { // 規則に基づいて、実際のテーブル名は logicTableName_XXX になります。XXX が 3 桁未満の場合は、0 を追加します。 String.format("%s_%03d", logicTableName, userId % ShardingStrategyConstant.SHARDING_TABLE_NUM) を返します。 } } 5. 道層の書き込み1). OrderPartitionByIdDao パッケージ com.byarch.fast.cloud.mysql.sharding.dao; com.byarch.fast.cloud.mysql.sharding.common.ShardingStrategyConstant をインポートします。 com.byarch.fast.cloud.mysql.sharding.pojo.entity.OrderEntity をインポートします。 com.byarch.fast.cloud.mysql.sharding.strategy.OrderIdShardingStrategy をインポートします。 org.jfaster.mango.annotation.* をインポートします。 @DB(名前 = ShardingStrategyConstant.LOGIC_ORDER_DATABASE_NAME、テーブル = "order") @Sharding(シャーディング戦略 = OrderIdShardingStrategy.class) パブリックインターフェース OrderPartitionByIdDao { @SQL("#table (order_id, user_id, status, booking_date, create_time, update_time) VALUES に INSERT" + 「(:orderId,:userId,:status,:bookingDate,:createTime,:updateTime)」 ) int insertOrder(@TableShardingBy("orderId") @DatabaseShardingBy("orderId") OrderEntity の orderEntity); @SQL("UPDATE #table set update_time = now()" + "#if(:bookingDate != null),booking_date = :bookingDate #end " + "#if (:status != null)、ステータス = :status #end" + "WHERE order_id = :orderId" ) OrderEntity を更新します。 @SQL("SELECT * FROM #table WHERE order_id = :1") OrderEntity の OrderById を取得します (@TableShardingBy @DatabaseShardingBy の Long orderId)。 @SQL("SELECT * FROM #table WHERE order_id = :1") @使用マスター OrderEntity をマスターから取得して OrderById を取得します (@TableShardingBy @DatabaseShardingBy の Long orderId)。 6. ユニットテスト@SpringBootTest(クラス = {Application.class}) SpringJUnit4ClassRunner クラスで実行します。 パブリッククラスShardingTest { オートワイヤード パーティションをIdDaoで並べ替えます。 オートワイヤード ユーザーID によるパーティションの順序付け。 @テスト パブリック void testCreateOrderRandom() { (int i = 0; i < 20; i++) の場合 { int ユーザー ID = ThreadLocalRandom.current().nextInt(1000,1000000); OrderEntity は、新しい OrderEntity() です。 orderEntity.setOrderId(SeqIdUtil.nextId(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM)); 注文エンティティのステータスを設定します(1); orderEntity.setUserId(ユーザーID); orderEntity.setCreateTime(新しい日付()); orderEntity.setUpdateTime(新しい日付()); orderEntity.setBookingDate(新しい日付()); 戻り値: orderPartitionByIdDao.insertOrder(orderEntity); アサートします。 } } @テスト パブリックボイドtestOrderAll() { //入れる int ユーザー ID = ThreadLocalRandom.current().nextInt(1000,1000000); OrderEntity は、新しい OrderEntity() です。 orderEntity.setOrderId(SeqIdUtil.nextId(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM)); 注文エンティティのステータスを設定します(1); orderEntity.setUserId(ユーザーID); orderEntity.setCreateTime(新しい日付()); orderEntity.setUpdateTime(新しい日付()); orderEntity.setBookingDate(新しい日付()); orderPartitionByIdDao に orderEntity を挿入します。 アサートします。 //マスターから取得 OrderEntity の orderInfo = orderPartitionByIdDao.getOrderByIdFromMaster(orderEntity.getOrderId()); アサート。assertNotNull(orderInfo); OrderEntity が OrderInfo の getOrderId() を返す場合、OrderEntity は OrderId() を返します。 //スレーブから取得 OrderEntity のスレーブオーダー情報 = orderPartitionByIdDao.getOrderById(orderEntity.getOrderId()); アサート。assertNotNull(スレーブオーダー情報)。 //アップデート OrderEntity を更新します。 Entity を更新します。 エンティティを更新します。 エンティティを更新します。 int 影響行 = orderPartitionByIdDao.updateOrderByOrderId(updateEntity); Assert.assertTrue(affectRows > 0); } @テスト パブリック void testGetListByUserId() { int ユーザー ID = ThreadLocalRandom.current().nextInt(1000,1000000); (int i = 0; i < 5; i++) の場合 { OrderEntity は、新しい OrderEntity() です。 orderEntity.setOrderId(SeqIdUtil.nextId(userId % ShardingStrategyConstant.SHARDING_TABLE_NUM)); 注文エンティティのステータスを設定します(1); orderEntity.setUserId(ユーザーID); orderEntity.setCreateTime(新しい日付()); orderEntity.setUpdateTime(新しい日付()); orderEntity.setBookingDate(新しい日付()); orderPartitionByIdDao.insertOrder(orderEntity); } 試す { //マスタースレーブ遅延による検証エラーを防ぐ Thread.sleep(1000); } キャッチ (InterruptedException e) { e.printStackTrace(); } リスト<OrderEntity> orderListByUserId = orderPartitionByUserIdDao.getOrderListByUserId(userId); アサート。assertNotNull(orderListByUserId); アサートします。assertTrue(orderListByUserId.size() == 5); } } 完了です: IV. 結論この記事では、Java 版の Mango フレームワークを使用した MySQL シャーディングの実践的な実装を主に紹介します。シャーディング ミドルウェアは ShardingJDBC に類似したものを使用することも、独自に開発することもできます。 上記のサブデータベースとサブテーブルの数は、デモンストレーションの参考用です。実際の作業では、サブテーブルとサブデータベースの数は、企業の実際のビジネスデータの増加率、ピーク QPS、物理マシン構成などの要素に基づいて計算されます。 これで、順序再構築における MySQL シャーディングの実践的な適用に関するこの記事は終了です。MySQL シャーディングの詳細については、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: Nodejs のグローバル変数とグローバルオブジェクトの知識ポイントと使用方法の詳細
>>: テキストエリアをレイアウトしたときにテキストが左下にあり、サイズを変更できない問題の解決策
COALESCE は、各パラメータ式 (expression_1、expression_2、...、...
Web アプリケーションの開発とデバッグを行う際には、テストのためにブラウザのキャッシュをクリアした...
数式 calc() は CSS の関数であり、主に数学演算に使用されます。 calc() を使用する...
目次序文最適化派生的な質問: beforeDestroy はトリガーされませんか?序文タイマーをクリ...
MySQL DATE_ADD(date,INTERVAL expr type) 関数と ADDDA...
プロジェクト(nodejs)では、一度に複数のデータをデータベースに挿入する必要があります。データベ...
以前、私は自分で WordPress を構築していましたが、当時はサードパーティの仮想ホストを使用し...
問題の説明Ele.me UI のフレームワークでは、入力データは el-form であり、出力データ...
序文前回の面接では、実行計画について質問されたとき、多くの人がそれが何なのか知りませんでした。実行計...
目次ジェネリック型での条件型の使用ツールタイプ脱出ポッド矢印関数で条件型を使用する型推論による条件型...
Docker-compose デプロイメント構成 Jenkins 1. Docker-compose...
最近、同社は CCFA 関連のいくつかの作業を行う予定で、その 1 つはカメラのリアルタイム監視を再...
これは Element UI の読み込みコンポーネントのエフェクトです。かっこいいですね。実装してみ...
今日は Docker でのネットワーク設定を試し、後で忘れないようにプロセスを記録しました。 (シス...
パラメータを渡すために href が必要で、パラメータが中国語の場合、文字化けした文字が表示されます...