spring動態資料來源分庫
阿新 • • 發佈:2019-02-10
web專案的瓶頸:服務端(伺服器壓力+資料庫壓力)
伺服器解決辦法:程式優化,提高程式碼執行率
資料庫壓力:快取、分表、分庫
1,分表:適合針對單個的表資料量比較大的情況,分成多張表儲存,比如系統日誌
例如:
系統日誌表以這樣的形式儲存,每個月一張表。
logs_2012_1
logs_2012_2
logs_2012_3
2,分庫
----水平分庫(同構:庫中表結構相同,範圍不同。例如:按地區分,按照省份生成資料庫)
----垂直分庫(異構:庫中表結構不同。例如:按模組分,將某些表單獨儲存到另一個數據庫)
當然,水平分庫與垂直分庫並不是絕對的。也可以混合
例如:
有2個數據庫a、b
a庫中有x、y、z三張表,
b庫中有x、k兩張表
a、b庫中的x表結構完全一致,在某些情況存到a庫,在另外的情況下存到b庫
分庫的原理:動態資料庫路由器+令牌(ThreadLoac)
分庫實現流程:
----------------------------------------------------------------------------
action中資料分發,決定儲存到那個資料庫,繫結令牌到當前執行緒。
比如當前資料決定存放到a庫,繫結一個令牌到當前執行緒,該令牌代表a庫的key
---->
service層
---->
開事務獲取連線
----->
資料庫路由器
----->
獲取當前執行緒中的臨牌
----->
資料庫路由器,通過令牌及其對應的spring檔案中路由的配置獲取到要連線的庫
----->
資料儲存到當前連線到的庫中
要注意的是:
令牌的繫結與解除順序,本次資料儲存成功後應該立即把當前繫結的令牌解除。
最好在獲取令牌的方法中,就解除令牌、以保證下次從新獲取正確的連線。
實現小例子:
伺服器解決辦法:程式優化,提高程式碼執行率
資料庫壓力:快取、分表、分庫
1,分表:適合針對單個的表資料量比較大的情況,分成多張表儲存,比如系統日誌
例如:
系統日誌表以這樣的形式儲存,每個月一張表。
logs_2012_1
logs_2012_2
logs_2012_3
2,分庫
----水平分庫(同構:庫中表結構相同,範圍不同。例如:按地區分,按照省份生成資料庫)
----垂直分庫(異構:庫中表結構不同。例如:按模組分,將某些表單獨儲存到另一個數據庫)
當然,水平分庫與垂直分庫並不是絕對的。也可以混合
例如:
有2個數據庫a、b
a庫中有x、y、z三張表,
b庫中有x、k兩張表
a、b庫中的x表結構完全一致,在某些情況存到a庫,在另外的情況下存到b庫
分庫的原理:動態資料庫路由器+令牌(ThreadLoac)
分庫實現流程:
----------------------------------------------------------------------------
action中資料分發,決定儲存到那個資料庫,繫結令牌到當前執行緒。
比如當前資料決定存放到a庫,繫結一個令牌到當前執行緒,該令牌代表a庫的key
---->
service層
---->
開事務獲取連線
----->
資料庫路由器
----->
獲取當前執行緒中的臨牌
----->
資料庫路由器,通過令牌及其對應的spring檔案中路由的配置獲取到要連線的庫
----->
資料儲存到當前連線到的庫中
要注意的是:
令牌的繫結與解除順序,本次資料儲存成功後應該立即把當前繫結的令牌解除。
最好在獲取令牌的方法中,就解除令牌、以保證下次從新獲取正確的連線。
分庫結構圖:
分庫執行順序:
實現小例子:
1,定義資料庫路由器
package cn.test.datasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 自定義資料來源路由器 */ public class SurveyparkDataSourceRouter extends AbstractRoutingDataSource { protected Object determineCurrentLookupKey() { //資料令牌類,獲取當前繫結的令牌,更具令牌資訊返回一個String的key值, //對應spring配置檔案中,某個資料庫連線的key String token = SurveyToken.getCurrentToken(); if(token != null){ //資料來源的key==token //解除繫結,就這裡解除,以保證其他連結的資訊正確 SurveyToken.unbindToken(); return token; } return null; } }
2,定義資料庫令牌生成器,使用者繫結令牌
package cn.test.datasource; /** * 調查令牌,繫結到當前的執行緒,傳播到資料來源路由器.進行分庫判斷 */ public class SurveyToken { //資料來源的key 代表每個資料庫對應的key public static fnail String ds_1 = "even"; public static fnail String ds_2 = "odd" ; private static ThreadLocal<SurveyToken> t = new ThreadLocal<SurveyToken>(); /** * 將令牌物件繫結到當前執行緒 */ public static void bindingToken(String token){ t.set(token); } /** * 從當前執行緒取得繫結的令牌物件 */ public static String getCurrentToken(){ return t.get() ; } /** * 解除令牌的繫結 */ public static void unbindToken(){ t.remove() ; } }
3,action中決定使用那個資料庫,繫結令牌。
public class TestAction{
//測試資料來源
public String testDS_1(){
//繫結令牌到當前執行緒
//設定令牌:ds_1 = "even"; 代表當前資料將存到"even"對應的資料庫中
token.setCurrentSurvey(token.ds_1);
SurveyToken.bindingToken(SurveyToken.ds_1);
//執行service操作,儲存資料到當前設定的庫中
service.save(User u);
}
//測試資料來源
public String testDS_2(){
//繫結令牌到當前執行緒
//設定令牌:ds_2 = "odd"; 代表當前資料將存到"odd"對應的資料庫中
SurveyToken.bindingToken(SurveyToken.ds_2);
//執行service操作,儲存資料到當前設定的庫中
service.save(User u);
}
}
4,spring檔案的配置
<?xml version="1.0"?>
<beans>
<!-- 資料來源(主庫) -->
<bean id="dataSource_main" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverclass}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxPoolSize" value="${c3p0.pool.size.max}" />
<property name="minPoolSize" value="${c3p0.pool.size.min}" />
<property name="initialPoolSize" value="${c3p0.pool.size.ini}" />
<property name="acquireIncrement" value="${c3p0.pool.size.increment}" />
</bean>
<!-- 資料來源(從庫) id與主庫不一樣,連線與主庫不一樣,其他資訊通過parent="dataSource_main"繼承主庫的資訊-->
<bean id="dataSource_1" parent="dataSource_main">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ds_2" />
</bean>
<!-- 資料來源路由器 -->
<bean id="dataSource_router"
class="cn.test.datasource;.SurveyparkDataSourceRouter">
<property name="targetDataSources">
<map>
<!-- 程式中資料庫路由器SurveyparkDataSourceRouter.determineCurrentLookupKey()方法,
返回的key值對應這裡的key ,代表一個數據源,
所以程式中的令牌繫結的令牌必須按照這裡定義的key返回
-->
<entry key="odd" value-ref="dataSource_main" />
<entry key="even" value-ref="dataSource_1" />
</map>
</property>
<!-- 預設的庫,如果程式中沒有繫結令牌,則預設連線此資料來源,將資訊儲存到此庫中 -->
<property name="defaultTargetDataSource" ref="dataSource_main" />
</bean>
<!-- 本地會話工廠bean,spring整合hibernate的核心入口 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 注入資料來源 這裡注入資料庫路由器,由路由器決定具體的連線那個庫-->
<property name="dataSource" ref="dataSource_router" />
</bean>
<!-- 事務配置 -->
</beans>