現在、大規模なeコマースシステムは、マスターデータベースと複数のスレーブデータベースであるデータベースレベルで読み取りおよび書き込み分離テクノロジーを使用しています。マスターライブラリは、データの更新とリアルタイムデータクエリを担当し、スレーブライブラリは非リアルタイムデータクエリを担当します。実際のアプリケーションでは、データベースはより多くの読み取りと書き込みが少なくなり(読み取りデータの頻度が高く、データの更新頻度が比較的少ない)、読み取りデータは通常長く、データベースサーバー上でより多くのCPUを占有し、ユーザーエクスペリエンスに影響します。私たちの通常のアプローチは、メインライブラリからクエリを抽出し、複数のスレーブライブラリを使用し、負荷分散を使用して各スレーブライブラリのクエリ圧力を削減することです。
読み取りと書き込み分離技術を使用する目標は、マスターライブラリへの圧力を効果的に軽減し、ユーザークエリデータリクエストをさまざまなスレーブライブラリに配布し、それによりシステムの堅牢性を確保することです。読み取りワイト分離を使用する背景を見てみましょう。
ウェブサイトのビジネスが拡大し続けるにつれて、データが増加し続け、より多くのユーザーがデータベースへの圧力が大きくなります。データベースやSQL最適化などの従来の方法は、基本的に要件を満たすことができません。現時点では、読み取りと執筆の分離戦略を使用して、現状を変えることができます。
具体的には、開発において、読み取りと書き込みの分離を簡単に実現する方法は?一般的に使用される2つの方法があります。
1最初の方法は、最も一般的に使用される方法です。これは、2つのデータベース接続を定義するためです。1つはMasterDataSource、もう1つはSlaveDataSourceです。データを更新するとき、MasterDataSourceを読み取り、データを照会するときはSlaveDataSourceを読み取ります。この方法は非常に簡単なので、詳細は説明しません。
2動的なデータソーススイッチングの2番目の方法は、マスターライブラリまたはスレーブライブラリを読むことを選択して、プログラムが実行されているときにデータソースをプログラムに動的に織り込むことです。使用される主なテクノロジーは、注釈、スプリングAOP、反射です。実装方法については、以下で詳しく説明します。
実装方法を導入する前に、いくつかの必要な知識、春のabstroutingdatasourceクラスを準備します
AbstractroutingDataSourceクラスは、スプリング2.0以降に追加されました。まず、AbstractroutingDataSourceの定義を見てみましょう。
コードコピーは次のとおりです。
パブリックアブストラクトクラスAbstractroutingDataSource Extends AbstractDataSource IntializingBean {}
AbstractroutingDataSourceは、DataSourceのサブクラスであるAbstractDataSourceを継承します。 DataSourceは、次のように定義されるjavax.sqlのデータソースインターフェイスです。
Public Interface DataSourceは、commondatasourceを拡張し、ラッパー{ /** * <p>この<code> dataSource < /code>オブジェクトが表すデータソースとの接続を確立しようとします。 * * @returnデータソースへの接続 * @exception sqlexception databaseアクセスエラーが発生した場合 */ connection getConnection()スローsqlexception; /** * <p>この<code> dataSource </code>オブジェクトが表すデータソースとの接続を確立しようとします。 * * @param username接続が行われているデータベースユーザー * @paramパスワード * @paramユーザーのパスワード * @returnデータソースへの接続 * @exception sqlexception * @since 1.4 */ connection getConnection(string username、string password)throws sqlexception;} DataSourceインターフェイスは、データベース接続を取得している2つのメソッドを定義します。 DataSourceインターフェイスをAbstractroutingDataSourceがどのように実装するかを見てみましょう。
public connection getConnection()throws sqlexception {return sechineTargetDataSource()。getConnection(); } public connection getConnection(String username、string password)sthows sqlexception {return sechineTargetDataSource()。getConnection(username、password); }明らかに、接続を取得するために独自のseceIneTargetDataSource()メソッドを呼び出すことです。 decienTargetDataSourceメソッドは、次のように定義されています。
保護されたDataSource sechIneTargetDataSource(){assert.notnull(this.ResolvedDataSources、 "DataSource Router Not Initialized"); object lookupkey = sechinecurrentlookupkey(); DataSource DataSource = this.ResolvedDataSources.get(lookupkey); if(dataSource == null &&(this.lenientfallback || lookupkey == null)){dataSource = this.ResolvedDefaultDataSource; } if(dataSource == null){新しいIllegalStateExceptionをスロー( "LookupキーのターゲットDataSourceを決定できません[" + lookupkey + "]"); } dataSourceを返します。 }私たちが最も気にかけているのは、次の2つの文です。
Object lookupkey = sechinecurrentlookupkey(); dataSource dataSource = this.ResolvedDataSources.get(lookupkey);
secienCurrentLookUpKeyメソッドLookUpKeyを返し、ResolvedDataSourcesメソッドは、LookupKeyに基づいてマップからデータソースを取得するためです。 ResolvedDataSourcesとsequidedCurrentlookupkeyは、次のように定義されています。
プライベートマップ<オブジェクト、DataSource> ResolvedDataSources;保護された抽象オブジェクトsecurrentlookupkey()
上記の定義を見た後、いくつかのアイデアはありますか? ResolvedDataSourcesはマップタイプです。次のように、MasterDataSourceとSlavedataSourceをマップに保存できます。
| 鍵 | 価値 |
| マスター | MasterDataSource |
| 奴隷 | Slavedatasource |
AbstractroutingDataSourceを継承し、Mapのキー、マスター、またはスレーブを返すsecionecurrentlookupkey()メソッドを実装するクラスDynamicDataSourceを作成しています。
さて、そんなに言った後、私は少し迷惑です。それを達成する方法を見てみましょう。
私たちが使用したい技術は上記で言及されています。まず、注釈の定義を見てみましょう。
@retention(retentionPolicy.runtime)@target(elementType.Method)public @interface dataSource {string value();}また、Springの抽象クラスAbstractroutingDataSourceを実装する必要があります。
パブリッククラスのdynamicdatasourceは、abstractroutingdatasourceを拡張します{@Override保護されたオブジェクトsecurrentlookupkey(){// todo auto-funerated 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(); }} DynamicDataSourceの定義から、DynamicDataSourceHolder.getDataSouce()値を返します。プログラムが実行されているときにDynamicDataSourceholder.putDataSource()メソッドを呼び出して値を割り当てる必要があります。以下は、実装の中核部分、つまりAOP部分です。 DataSourceaspectは次のように定義されています。
public class dataSourceaspect {public void before(joinpoint point){object target = point.getTarget(); string method = point.getSignature()。getName(); class <? class <? try {method m = classz [0] .getmethod(method、parametertypes); if(m!= null && m.isannotationpresent(dataSource.class)){dataSource data = m .getAnnotation(dataSource.class); dynamicdatasourceholder.putdatasource(data.value()); system.out.println(data.value()); }} catch(例外e){// dodo:例外を処理}}}}テストの便利さのために、2つのデータベース、ショップモックマスターライブラリ、テストモックスレーブライブラリ、ショップ、テストテーブル構造は同じですが、データは異なり、データベース構成は次のとおりです。
<bean id = "masterdatasource"> <プロパティ名= "driverclassname" value = "com.mysql.jdbc.driver" /> <プロパティ名= "url" value = "jdbc:mysql://127.0.0.1:3306 /shop" /> <プロパティ= "username" /> < value="yangyanping0615" /> </bean> <bean id="slavedataSource" > <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test" /> <property name="username"値= "root" /> <プロパティ名= "password" value = "yangyanping0615" /> < /bean> <bean:bean id = "dataSource"> <プロパティ名= "ターゲットダタソース"> <マップキータイプ= "java.lang.string"> < key = "lave" value-ref = "slavedatasource"/>> </map> </property> <property name = "defaulttargetdatasource" ref = "masterdatasource"/> </bean> <bean id = "transactionmanager"> <property name = "dataSource" refer " - > <bean id = "sqlsessionfactory"> <プロパティ名= "dataSource" ref = "dataSource" /> <プロパティ名= "configlocation" value = "classpath:config /mybatis-config.xml" /> < /bean> < /bean>
Spring構成にAOP構成を追加します
<! - データベースアノテーションAOPを構成 - > <aop:aspectj-autoproxy> < /aop:aspectj-autoproxy> <beans:bean id = "manydatasourceaspect" /> <aop:config> <aop:aspect id = "c" ref = "mulydatasourcept"> <aebold = "" tx " com.air.shop.mapper。*
以下は、MyBatis Usermapperの定義です。テストの便利さのために、ログインはマスターライブラリを読み取り、ユーザーリストはスレーブライブラリを読み取ります。
パブリックインターフェイスusermapper {@datasource( "master")public void add(user user); @datasource( "Master")public void update(user user); @datasource( "Master")public void delete(int id); @DataSource( "Slave")PublicユーザーLoadByID(int id); @DataSource( "Master")パブリックユーザーloadbyname(string name); @DataSource( "Slave")Public List <user> list();}わかりました、日食を実行して効果を確認し、ユーザー名管理者を入力してログインして効果を確認します
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。