読み取り及び書き込み分離を(ベースMyBatisのは、MySQL)springboot

最近、タスクライト、無料の学習学習技術は、その後、別の読み取りを研究し、実現した場合に書き込みます。ここでは、将来の視聴のために、一方では、次のブログを記録する工程と、だけでなく、あなたに共有すること(情報がオンライン本当に主にコピーするコピーである,,本当に不快見て、フォーマットされていません)。

完全なコード:https://github.com/FleyX/demo-project/tree/master/dxfl

1.背景

  プロジェクトデータベース読み込み、ライブラリに書き込み、最も基本的なだけでなく、最も主流のスタンドアロンデータベース。ユーザーが徐々に増加すると、スタンドアローンのデータベースは、性能要件を満たすことができない、彼らは、読み取りと書き込みの分離変換を(読み取りに適しており、以下の書き込み)し、ライブラリを書き込み、通常は、データベースクラスタを行うからメインを開き、複数のライブラリを読み込みますバックアップ、マスターマルチスレーブは、読み取り性能を向上させます。ユーザー以上の別々の読み取りと書き込みを満たすことができない場合、我々は分散データベース(つまり、後で取得する方法を学習することができる)が必要です。

  別のマルチマスターするだけでなく、データの同期化の必要性から行うには、データベースクラスタの最初のもの、通常の状況下で達成するために読み取りと書き込み。これは、ライトレベルで記録されたコードは、分離を達成するためにどのようにmysqlの、とマスターの構成を確立する回数の記録です。

図2は、クラスタからマルチマスタデータベースを設定しました

  マスター仮想マシンのバックアップは、複数の仮想マシンが必要ですから、私は別のUUIDのために修正する必要があり、同じクローンUUIDに各データベースを導く直接注意し、完全なクローン複数のインスタンスをwmwareました。これを修正するための参考:ジャンプします

  • メインライブラリ構成

    次のようにプライマリ・データベースバイナリデータベース(スレーブ)からの新規ユーザーがログインして読み取るためのマスターデータベース(マスター)は、SQL文は次のとおりです。

    mysql> CREATE USER 'repl'@'%' IDENTIFIED BY '123456';#创建用户
    mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';#分配权限
    mysql>flush privileges;   #刷新权限

    同時に、mysqlの次を追加し、バイナリログを開き、構成ファイルを変更します。

    [mysqld]
    server-id=1
    log-bin=master-bin
    log-bin-index=master-bin.index

    次に使用して、データベースを再起動しshow master status;、次のように文は、メイン州のライブラリを表示します。

メインのライブラリのステータス

  • ライブラリの設定から

    また、コンフィギュレーションの数行を追加します。

    [mysqld]
    server-id=2
    relay-log-index=slave-relay-bin.index
    relay-log=slave-relay-bin

    その後、データベースを再起動し、プライマリ・データベースは、ステートメントを使用して接続されています。

    CHANGE MASTER TO
             MASTER_HOST='192.168.226.5',
             MASTER_USER='root',
             MASTER_PASSWORD='123456',
             MASTER_LOG_FILE='master-bin.000003',
             MASTER_LOG_POS=154;

    そして、実行start slave;Slave_IO_Running Slave_SQL_Runningをし、yesにしている:オープン裏打ちされ、通常は下図のように。

    状態

このステップは、ライブラリから複数開くことができます。

  デフォルトでは、すべてのバックアップ操作がメインのライブラリは、ライブラリにバックアップされますされ、実際にあなたがマスターライブラリに以下の設定を追加することができ、特定のライブラリを無視する必要があります。

# 不同步哪些数据库
binlog-ignore-db = mysql
binlog-ignore-db = test
binlog-ignore-db = information_schema

# 只同步哪些数据库,除此之外,其他不同步
binlog-do-db = game

3、別読み取りおよび書き込みのためのコード・レベル

  コード環境springboot + MyBatisの+ druib接続プール。複数のデータソースは、書き込み動作時に、設定する必要読み取りおよび書き込みを分離することは、書き込みデータソースを選択することで、データソースの選択は、読み出し動作を読み出します。二つの重要なポイントがあります。

  • データソースを切り替える方法
  • 別の方法に従って、正しいデータソースを選択する方法

1)データソースを切り替える方法

  springbootデフォルト設定は、唯一の接続プロパティは、設定ファイル内の行にある定義する必要がある場合には、通常使用されているが、今、私たちは、HashMapの中に複数のデータソース、複数のデータソースをサポートするために、独自の、春設定する必要がTargetDataSourcedertermineCurrentLookupKey使用するように設定され、睡眠への鍵データのソースを取得します。だから、私たちの目標は、どのキーを決定するdertermineCurrentLookupKeyメソッドを書き換えながらTargetDataSourceに複数のデータソースを作成するには、非常に明確です。

2)データソースを選択する方法

  このサービスメソッドの開始時にデータのソースを決定するために呼び出されるように、トランザクションは、サービス層にあるのが一般的であるノートは、操作の一般的な方法は、それを行うための方法の実施を開始する前に、何を行うことができますか?私はあなたがそのことを考えていると考えているセクション二つの方法をカットする方法:

  • 型注釈は、注釈は、データの読み出し処理は、読み取りライブラリを使用してマークされている定義します
  • メソッド名は、そのような読書ライブラリとのgetXXX、書き込みライブラリとのsetXXXとして、カットオフポイントに従った方法の名前を書きます

3)コーディング

2つのデータソースの、コンフィギュレーションファイルを作成し、コンフィギュレーション情報

  必要な情報のみ、他はデフォルト設定があります

mysql:
  datasource:
    #读库数目
    num: 1
    type-aliases-package: com.example.dxfl.dao
    mapper-locations: classpath:/mapper/*.xml
    config-location: classpath:/mybatis-config.xml
    write:
      url: jdbc:mysql://192.168.226.5:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver
    read:
      url: jdbc:mysql://192.168.226.6:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver

B、DbContextHolderクラスを書きます

  このクラスは、各スレッドを保持するためにThreadLocalを持つデータベースのカテゴリを設定するために使用される読み取りまたはライブラリを書くためのライブラリを使用することです。コードは以下の通りであります:

/**
 * Description 这里切换读/写模式
 * 原理是利用ThreadLocal保存当前线程是否处于读模式(通过开始READ_ONLY注解在开始操作前设置模式为读模式,
 * 操作结束后清除该数据,避免内存泄漏,同时也为了后续在该线程进行写操作时任然为读模式
 * @author fxb
 * @date 2018-08-31
 */
public class DbContextHolder {

    private static Logger log = LoggerFactory.getLogger(DbContextHolder.class);
    public static final String WRITE = "write";
    public static final String READ = "read";

    private static ThreadLocal<String> contextHolder= new ThreadLocal<>();

    public static void setDbType(String dbType) {
        if (dbType == null) {
            log.error("dbType为空");
            throw new NullPointerException();
        }
        log.info("设置dbType为:{}",dbType);
        contextHolder.set(dbType);
    }

    public static String getDbType() {
        return contextHolder.get() == null ? WRITE : contextHolder.get();
    }

    public static void clearDbType() {
        contextHolder.remove();
    }
}

C、メソッドのオーバーライドdetermineCurrentLookupKey

  データベース操作の初めに春は、私たちがここにDbContextHolderクラス呼び出すデータベースなので、この方法によって決定されるgetDbType()負荷が読んでライブラリコードのバランスを取りながら、現在の操作のカテゴリを取得する方法を次のとおりです。

public class MyAbstractRoutingDataSource extends AbstractRoutingDataSource {

    @Value("${mysql.datasource.num}")
    private int num;

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    protected Object determineCurrentLookupKey() {
        String typeKey = DbContextHolder.getDbType();
        if (typeKey == DbContextHolder.WRITE) {
            log.info("使用了写库");
            return typeKey;
        }
        //使用随机数决定使用哪个读库
        int sum = NumberUtil.getRandom(1, num);
        log.info("使用了读库{}", sum);
        return DbContextHolder.READ + sum;
    }
}

D、コンフィギュレーション・クラスを作成

  分離を読み書きするため、もはや我々は、手動で設定する必要があり、デフォルトの設定springbootになることはできません。最初のデータソースを生成し、データ・ソースは、@ConfigurPropertiesを使用して自動的に生成されました。

    /**
     * 写数据源
     *
     * @Primary 标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean 优先被考虑。
     * 多数据源配置的时候注意,必须要有一个主数据源,用 @Primary 标志该 Bean
     */
    @Primary
    @Bean
    @ConfigurationProperties(prefix = "mysql.datasource.write")
    public DataSource writeDataSource() {
        return new DruidDataSource();
    }

、同様のデータソースを読んでどのように多くのライブラリの読み取りデータソースの設定を読み込みますどのように多くの注意を払って、豆は+シリアル番号を読み取ると呼ばれます。

  そして、私たちの前に書かれたMyAbstractRoutingDataSourceクラスを使用して、データソースを設定します

    /**
     * 设置数据源路由,通过该类中的determineCurrentLookupKey决定使用哪个数据源
     */
    @Bean
    public AbstractRoutingDataSource routingDataSource() {
        MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put(DbContextHolder.WRITE, writeDataSource());
        targetDataSources.put(DbContextHolder.READ+"1", read1());
        proxy.setDefaultTargetDataSource(writeDataSource());
        proxy.setTargetDataSources(targetDataSources);
        return proxy;
    }

  次に、あなたが設定する必要がsqlSessionFactory

    /**
     * 多数据源需要自己设置sqlSessionFactory
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(routingDataSource());
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // 实体类对应的位置
        bean.setTypeAliasesPackage(typeAliasesPackage);
        // mybatis的XML的配置
        bean.setMapperLocations(resolver.getResources(mapperLocation));
        bean.setConfigLocation(resolver.getResource(configLocation));
        return bean.getObject();
    }

  そして最後にそれ以外の場合はトランザクションを設定しなければならなかった、トランザクションが有効になりません。

    /**
     * 设置事务,事务需要知道当前使用的是哪个数据源才能进行事务处理
     */
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager() {
        return new DataSourceTransactionManager(routingDataSource());
    }

4)データソースを選択

  複数のデータソースが設定されますが、コードレベルでは、どのようにデータソースの選択、それを選択するには?ここでは2つのアプローチについて説明します。

、注釈のスタイル

  まず、読み取り専用のアノテーションの定義は、この注釈の方法は、ライブラリを読み込むために使用され、他のライブラリは、プロジェクトがビジネスのコードを変更せずに、読者はこの方法を用いて分離することができる方法を変換する場合、書き込みに使用し、単に読み取り専用のサービスメソッドのコメントに追加それはすることができます。

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {
}

  このセクションでは、開始とクラスで、トランザクション・セクションの優先度よりも高い優先度をgetOrder書き換えることを保証するために、使用されるデータ・ソースを切り替えるセクションを書き込み@EnableTransactionManagement(order = 10)、次のコードを、。

@Aspect
@Component
public class ReadOnlyInterceptor implements Ordered {
    private static final Logger log= LoggerFactory.getLogger(ReadOnlyInterceptor.class);

    @Around("@annotation(readOnly)")
    public Object setRead(ProceedingJoinPoint joinPoint,ReadOnly readOnly) throws Throwable{
        try{
            DbContextHolder.setDbType(DbContextHolder.READ);
            return joinPoint.proceed();
        }finally {
            //清楚DbType一方面为了避免内存泄漏,更重要的是避免对后续在本线程上执行的操作产生影响
            DbContextHolder.clearDbType();
            log.info("清除threadLocal");
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

式B、メソッド名

  このメソッドは、コメントすることができますが、方法を必要とするサービスの名前で書かれた一定の規則に従って、その後、例えば、分類部によるデータベースを設定されていないsetXXX、書くためにgetXXX、私は書く方法を知っている必要があり、コードを書いていない読み取るように。

4、試験

  コードはどのような結果、以下が実行されたショットをしようと書かれています:

テスト結果

ただ、負荷はさらに増加すると、書き込みのための唯一のライブラリが足りない間違いではなく、一度、すべてのための一時的な解決策のデータベースの拡張を読み書き分離、およびデータベースがキャップされ、単一のテーブルには、レベルの数千人までのmysqlデータは、より良いクエリのパフォーマンスを維持することができます。それは最終的になるだろう- サブライブラリーサブテーブルのアーキテクチャ。:サブライブラリーのサブテーブルは、この1を見ることができますhttps://www.tapme.top/blog/detail/2019-03-20-10-38

掲載された記事:www.tapme.top/blog/detail/2018-09-10-10-38

おすすめ

転載: www.cnblogs.com/wuyoucao/p/10965903.html