現在の大規模電子商取引システムのほとんどは、マスター データベースと複数のスレーブ データベースというデータベース レベルで読み書き分離テクノロジーを使用しています。マスター ライブラリはデータ更新とリアルタイム データ クエリを担当し、スレーブ ライブラリはもちろん非リアルタイム データ クエリを担当します。実際のアプリケーションでは、データベースの読み取りは多くなり、書き込みは少なくなります (データの読み取り頻度が高く、データの更新頻度が比較的低い) ため、通常、データの読み取りには時間がかかり、データベース サーバーの CPU を大量に消費します。 、ユーザーエクスペリエンスに影響します。私たちの通常のアプローチは、メイン データベースからクエリを抽出し、複数のスレーブ データベースを使用し、負荷分散を使用して各スレーブ データベースに対するクエリの負荷を軽減することです。
読み書き分離テクノロジーを使用する目的: マスター ライブラリへの負荷を効果的に軽減し、データ クエリに対するユーザーのリクエストを異なるスレーブ ライブラリに分散することで、システムの堅牢性を確保します。読み書き分離が採用された背景を見てみましょう。
ウェブサイトのビジネスが拡大し続けるにつれて、データは増加し続け、ユーザーもますます増加しており、データベースや SQL の最適化などの従来の方法では基本的に要件を満たすことができません。現状を変えるために、読み取りと書き込みを分離する戦略を使用できる場合があります。
特に開発において、読み取りと書き込みの分離を簡単に実現するにはどうすればよいでしょうか? 現在、一般的に使用されている方法が 2 つあります:
1 1 つ目の方法は、最も一般的に使用される方法で、2 つのデータベース接続を定義します。 1 つは MasterDataSource、もう 1 つは SlaveDataSource です。データを更新するときは MasterDataSource を読み取り、データをクエリするときは SlaveDataSource を読み取ります。この方法は非常に簡単なので、詳細は説明しません。
2 動的データ ソース切り替えの 2 番目の方法は、プログラムの実行中にデータ ソースをプログラムに動的に組み込むことで、メイン ライブラリから読み取るかスレーブ ライブラリから読み取るかを選択します。使用される主なテクノロジーは、アノテーション、Spring AOP、リフレクションです。以下に具体的な実装方法を紹介します。
実装方法を紹介する前に、まず Spring の AbstractRoutingDataSource クラス
AbstractRoutingDataSource の定義を見てみましょう:
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {}
AbstractRoutingDataSource は AbstractDataSource を継承します。 ource は DataSource のサブクラスです。 DataSource は javax.sql のデータ ソース インターフェイスであり、次のように定義されています:
public interface DataSource extends CommonDataSource,Wrapper { /** * <p>Attempts to establish a connection with the data source that * this <code>DataSource</code> object represents. * * @return a connection to the data source * @exception SQLException if a database access error occurs */ Connection getConnection() throws SQLException; /** * <p>Attempts to establish a connection with the data source that * this <code>DataSource</code> object represents. * * @param username the database user on whose behalf the connection is * being made * @param password the user's password * @return a connection to the data source * @exception SQLException if a database access error occurs * @since 1.4 */ Connection getConnection(String username, String password) throws SQLException; }
DataSource インターフェイスは 2 つのメソッドを定義しており、どちらもデータベース接続を取得します。 AbstractRoutingDataSource が DataSource インターフェイスをどのように実装するかを見てみましょう。
public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } public Connection getConnection(String username, String password) throws SQLException { return determineTargetDataSource().getConnection(username, password); }
接続を取得するために独自の detectTargetDataSource() メソッドを呼び出していることは明らかです。 detectTargetDataSource メソッドは次のように定義されています:
protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.resolvedDataSources.get(lookupKey); if (dataSource == null && (this.lenientFallback || lookupKey == null)) { dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) { throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); } return dataSource; }
私たちが最も懸念しているのは次の 2 つの文です:
Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.resolvedDataSources.get(lookupKey);
determineCurrentLookupKey メソッドは lookupKey を返し、resolvedDataSources メソッドはそれに基づいて Map からデータ ソースを取得します。ルックアップキー。
private Map<Object, DataSource> resolvedDataSources; protected abstract Object determineCurrentLookupKey()
上記の定義を見て、次のように、MasterDataSource と SlaveDataSource を Map に保存できます。 AbstractRoutingDataSource を継承し、Map キー、マスター、またはスレーブを返すその detectCurrentLookupKey() メソッドを実装するクラス DynamicDataSource を作成します。
さて、たくさん話したので少し疲れましたが、それを実装する方法を見てみましょう。
私たちが使用したいテクノロジーは上で述べたとおりです。まずアノテーションの定義を見てみましょう:@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DataSource { String value(); }また、determineCurrentLookupKey メソッドを実装するための Spring の抽象クラス AbstractRoutingDataSource を実装する必要があります。 より DynamicDataSource の定義は、DynamicDataSourceHolder.getDataSouce() 値を返すことを示しています。プログラムの実行中に値を割り当てるには、DynamicDataSourceHolder.putDataSource() メソッドを呼び出す必要があります。以下は実装の中心部分であり、AOP 部分である DataSourceAspect は次のように定義されています:
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { // TODO Auto-generated method stub return DynamicDataSourceHolder.getDataSouce(); } } public class DynamicDataSourceHolder { public static final ThreadLocal<String> holder = new ThreadLocal<String>(); public static void putDataSource(String name) { holder.set(name); } public static String getDataSouce() { return holder.get(); } }テストを容易にするために、2 つのデータベースを定義し、shop はマスター ライブラリをシミュレートし、test はスレーブをシミュレートします。ライブラリ、shop と test のテーブル 構造は同じですが、データは異なります。 データベース構成は次のとおりです。以下は MyBatis の UserMapper の定義です。テストを容易にするために、ログインはマスター ライブラリを読み取り、ユーザー リストはスレーブ ライブラリを読み取ります。効果を確認してください
以上がこの記事の全内容です。皆さんの学習に役立つことを願っています。また、皆さんも PHP 中国語 Web サイトをサポートしていただければ幸いです。
データベースの読み書き分離を実装する Spring の例に関連するその他の記事については、PHP 中国語 Web サイトに注目してください。