您好,登錄后才能下訂單哦!
隨著系統(tǒng)用戶(hù)訪問(wèn)量的不斷增加,數(shù)據(jù)庫(kù)的頻繁訪問(wèn)將成為我們系統(tǒng)的一大瓶頸之一。由于項(xiàng)目前期用戶(hù)量不大,我們實(shí)現(xiàn)單一的數(shù)據(jù)庫(kù)就能完成。但是后期單一的數(shù)據(jù)庫(kù)根本無(wú)法支撐龐大的項(xiàng)目去訪問(wèn)數(shù)據(jù)庫(kù),那么如何解決這個(gè)問(wèn)題呢?
實(shí)際的應(yīng)用中,數(shù)據(jù)庫(kù)都是讀多寫(xiě)少(讀取數(shù)據(jù)的頻率高,更新數(shù)據(jù)的頻率相對(duì)較少),而讀取數(shù)據(jù)通常耗時(shí)比較長(zhǎng),占用數(shù)據(jù)庫(kù)服務(wù)器的CPU較多,從而影響用戶(hù)體驗(yàn)。我們通常的做法就是把查詢(xún)從主庫(kù)中抽取出來(lái),采用多個(gè)從庫(kù),使用負(fù)載均衡,減輕每個(gè)從庫(kù)的查詢(xún)壓力。
采用讀寫(xiě)分離技術(shù)的目標(biāo):有效減輕Master庫(kù)的壓力,又可以把用戶(hù)查詢(xún)數(shù)據(jù)的請(qǐng)求分發(fā)到不同的Slave庫(kù),從而保證系統(tǒng)的健壯性。我們看下采用讀寫(xiě)分離的背景。
我們?cè)陧?xiàng)目開(kāi)發(fā)初期的時(shí)候就設(shè)計(jì)了一個(gè)簡(jiǎn)單的讀寫(xiě)分離,現(xiàn)在我把demo分享給大家。
首先定義一個(gè)annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
public String value();
}
再定義一個(gè)HandleDataSource
public class HandleDataSource {
public static final ThreadLocal holder = new ThreadLocal();
public static void putDataSource(String datasource) {
holder.set(datasource);
}
public static String getDataSource() {
return holder.get();
}
}
定義一個(gè)切面
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
public class DataSourceAspect {
public void pointCut() {
};
public void before(JoinPoint point) {
Object target = point.getTarget();// 攔截的實(shí)體類(lèi)
String method = point.getSignature().getName();// 攔截的方法名稱(chēng)
Class[] classz = target.getClass().getInterfaces();
Class[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();// 攔截的方法參數(shù)類(lèi)型
try {
Method m = classz[0].getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
HandleDataSource.putDataSource(data.value());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
獲取數(shù)據(jù)源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class ChooseDataSource extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey() {
return HandleDataSource.getDataSource();
}
}
配置XMl
classpath*:mysql.propertiescom.mysql.jdbc.Driver${jdbc.url}${jdbc.user}${jdbc.password}SELECT 1 FROM DUAL32510010000true60com.mysql.jdbc.Driver${jdbc.url.read}${jdbc.user.read}${jdbc.password.read}SELECT 1 FROM DUAL32510010000true60
注解到service接口上面
數(shù)據(jù)庫(kù)表就一張 根據(jù)mybatis的xml大家自己建一下
另外這里還有一個(gè)瑕疵就是,當(dāng)你使用注解事務(wù)的時(shí)候系統(tǒng)只能讀取默認(rèn)的數(shù)據(jù)源,這個(gè)問(wèn)題主要是因?yàn)閟pring的事務(wù)和自定義的aop存在一個(gè)先后順序的問(wèn)題
Spring中的事務(wù)是通過(guò)aop來(lái)實(shí)現(xiàn)的,當(dāng)我們自己寫(xiě)aop攔截的時(shí)候,會(huì)遇到跟spring的事務(wù)aop執(zhí)行的先后順序問(wèn)題,比如說(shuō)動(dòng)態(tài)切換數(shù)據(jù)源的問(wèn)題,如果事務(wù)在前,數(shù)據(jù)源切換在后,會(huì)導(dǎo)致數(shù)據(jù)源切換失效,所以就用到了Order(排序)這個(gè)關(guān)鍵字.
我們可以通過(guò)在@AspectJ的方法中實(shí)現(xiàn)org.springframework.core.Ordered 這個(gè)接口來(lái)定義order的順序,order 的值越小,說(shuō)明越先被執(zhí)行。
大家對(duì)技術(shù)感興趣的朋友也可以來(lái) 關(guān)注我的微信公眾號(hào) : Java填坑之路 也會(huì)分享一些 架構(gòu)技術(shù)資料。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。