現在大型的電子商務系統,在資料庫層面大都採用讀寫分離技術,就是一個Master資料庫,多個Slave資料庫。 Master庫負責資料更新與即時資料查詢,Slave函式庫當然負責非即時資料查詢。因為在實際的應用中,資料庫都是讀多寫少(讀取資料的頻率高,更新資料的頻率相對較少),而讀取資料通常耗時比較長,佔用資料庫伺服器的CPU較多,從而影響使用者體驗。我們通常的做法就是把查詢從主庫中抽取出來,採用多個從庫,使用負載平衡,減輕每個從庫的查詢壓力。
採用讀寫分離技術的目標:有效減輕Master函式庫的壓力,又可以把使用者查詢資料的請求分送到不同的Slave函式庫,從而確保系統的健壯性。我們看下採用讀寫分離的背景。
隨著網站的業務不斷擴展,資料不斷增加,使用者越來越多,資料庫的壓力也就越來越大,採用傳統的方式,例如:資料庫或SQL的最佳化基本上已達不到要求,這個時候可以採用讀寫分離的策略來改變現狀。
具體到開發中,如何方便的實現讀寫分離呢?目前常用的有兩種方式:
1 第一種方式是我們最常用的方式,就是定義2個資料庫連接,一個是MasterDataSource,另一個是SlaveDataSource。更新資料時我們讀取MasterDataSource,查詢資料時我們讀取SlaveDataSource。這種方式很簡單,我就不贅述了。
2 第二種方式動態資料來源切換,就是在程式運作時,把資料來源動態織入到程式中,從而選擇讀取主函式庫還是從函式庫。主要使用的技術是:annotation,Spring AOP ,反射。以下會詳細的介紹實作方式。
在介紹實現方式之前,我們先準備一些必要的知識,spring 的AbstractRoutingDataSource 類別
AbstractRoutingDataSource這個類別是spring2.0以後增加的,我們先來看下AbstractRoutingDataSource而AbstractDataSource 又是DataSource 的子類別。 DataSource 是javax.sql 的資料來源接口,定義如下:
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {}
DataSource 介面定義了2個方法,都是取得資料庫連接。我們在看下AbstractRoutingDataSource 如何實作了DataSource介面:
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; }
很顯然就是呼叫自己的determineTargetDataSource() 方法取得到connection。 determineTargetDataSource方法定義如下:
public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } public Connection getConnection(String username, String password) throws SQLException { return determineTargetDataSource().getConnection(username, password); }
我們最關心的還是下面2句話:
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; }
. resolvedDataSources 和determineCurrentLookupKey定義如下:
Object lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.resolvedDataSources.get(lookupKey);看到以上定義,我們是不是有點想法了,resolvedDataSources是Map類型,我們可以把所有這些DataSourceSlaveData寫一個類別DynamicDataSource 繼承AbstractRoutingDataSource,實作其determineCurrentLookupKey() 方法,該方法傳回Map的key,master或slave。 好了,說了這麼多,有點煩了,下面我們看下怎麼實現。 上面已經提到了我們要使用的技術,我們先看下annotation的定義:
private Map<Object, DataSource> resolvedDataSources; protected abstract Object determineCurrentLookupKey()

我們還需要實作spring的抽象類型AbstractRoutingDataSource,就是實作.
從DynamicDataSource 的定義看出,他回傳的是DynamicDataSourceHolder.getDataSouce()值,我們需要在程式執行時呼叫DynamicDataSourceHolder.putDataSource()方法,對其賦值。以下是我們實現的核心部分,也就是AOP部分,DataSourceAspect定義如下:@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DataSource { String value(); }為了方便測試,我定義了2個資料庫,shop模擬Master庫,test模擬Slave庫,shop和test的表結構一致,但資料不同,資料庫配置如下:
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(); } }在spring的配置中增加aop配置
public class DataSourceAspect { public void before(JoinPoint point) { Object target = point.getTarget(); String method = point.getSignature().getName(); Class<?>[] classz = target.getClass().getInterfaces(); Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()) .getMethod().getParameterTypes(); 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 (Exception e) { // TODO: handle exception } } }Master庫,用戶列表讀取Slave庫:
<bean id="masterdataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/shop" /> <property name="username" value="root" /> <property name="password" value="yangyanping0615" /> </bean> <bean id="slavedataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test" /> <property name="username" value="root" /> <property name="password" value="yangyanping0615" /> </bean> <beans:bean id="dataSource" class="com.air.shop.common.db.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <!-- write --> <entry key="master" value-ref="masterdataSource"/> <!-- read --> <entry key="slave" value-ref="slavedataSource"/> </map> </property> <property name="defaultTargetDataSource" ref="masterdataSource"/> </beans:bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 配置SqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:config/mybatis-config.xml" /> </bean>好了,運行我們的Eclipse看看效果,輸入用戶名admin 登入看看效果
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持PHP中文網。
更多Spring 實作資料庫讀寫分離的範例相關文章請關注PHP中文網!

Java在企業級應用中被廣泛使用是因為其平台獨立性。 1)平台獨立性通過Java虛擬機(JVM)實現,使代碼可在任何支持Java的平台上運行。 2)它簡化了跨平台部署和開發流程,提供了更大的靈活性和擴展性。 3)然而,需注意性能差異和第三方庫兼容性,並採用最佳實踐如使用純Java代碼和跨平台測試。

JavaplaysigantroleiniotduetoitsplatFormentence.1)itallowscodeTobewrittenOnCeandrunonVariousDevices.2)Java'secosystemprovidesuseusefidesusefidesulylibrariesforiot.3)

ThesolutiontohandlefilepathsacrossWindowsandLinuxinJavaistousePaths.get()fromthejava.nio.filepackage.1)UsePaths.get()withSystem.getProperty("user.dir")andtherelativepathtoconstructthefilepath.2)ConverttheresultingPathobjecttoaFileobjectifne

Java'splatFormIndenceistificantBecapeitAllowSitallowsDevelostWriTecoDeonCeandRunitonAnyPlatFormwithAjvm.this“ writeonce,runanywhere”(era)櫥櫃櫥櫃:1)交叉plat formcomplibility cross-platformcombiblesible,enablingDeploymentMentMentMentMentAcrAptAprospOspOspOssCrossDifferentoSswithOssuse; 2)

Java適合開發跨服務器web應用。 1)Java的“一次編寫,到處運行”哲學使其代碼可在任何支持JVM的平台上運行。 2)Java擁有豐富的生態系統,包括Spring和Hibernate等工具,簡化開發過程。 3)Java在性能和安全性方面表現出色,提供高效的內存管理和強大的安全保障。

JVM通過字節碼解釋、平台無關的API和動態類加載實現Java的WORA特性:1.字節碼被解釋為機器碼,確保跨平台運行;2.標準API抽像操作系統差異;3.類在運行時動態加載,保證一致性。

Java的最新版本通過JVM優化、標準庫改進和第三方庫支持有效解決平台特定問題。 1)JVM優化,如Java11的ZGC提升了垃圾回收性能。 2)標準庫改進,如Java9的模塊系統減少平台相關問題。 3)第三方庫提供平台優化版本,如OpenCV。

JVM的字節碼驗證過程包括四個關鍵步驟:1)檢查類文件格式是否符合規範,2)驗證字節碼指令的有效性和正確性,3)進行數據流分析確保類型安全,4)平衡驗證的徹底性與性能。通過這些步驟,JVM確保只有安全、正確的字節碼被執行,從而保護程序的完整性和安全性。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

禪工作室 13.0.1
強大的PHP整合開發環境

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3漢化版
中文版,非常好用