Spring + iBatis 的多庫橫向切分簡易解決思路
筆者最近在做一個網際網路的“類SNS”應用,應用中使用者數量巨大(約4000萬)左右,因此,簡單的使用傳統單一資料庫儲存肯定是不行的。
參考了業內廣泛使用的分庫分表,以及使用DAL資料訪問層等的做法,筆者決定使用一種最簡單的資料來源路由選擇方式來解決問題。
嚴格的說,目前的實現不能算是一個解決方案,只能是一種思路的簡易實現,筆者也僅花了2天時間來完成(其中1.5天是在看資料和Spring/ibatis的原始碼)。這裡也只是為各位看官提供一個思路參考,順便給自己留個筆記
2.系統的設計前提
我們的系統使用了16個數據庫例項(目前分佈在2臺物理機器上,後期將根據系統負荷的增加,逐步移庫到16臺物理機器上)。16個庫是根據使用者的UserID進行簡單的hash分配。這裡值得一說的是,我們既然做了這樣的橫向切分設計,就已經考慮了系統需求的特性,
- 1.不會發生經常性的跨庫訪問。
- 2.主要的業務邏輯都是圍繞UserID為核心的,在一個單庫事務內即可完成。
在系統中,我們使用Spring和iBatis。Spring負責資料庫的事務管理AOP,以及Bean間的IOC。選擇iBatis的最大原因是對Sql的效能優化,以及後期如果有分表要求的時,可以很容易實現對sql表名替換。
3.設計思路
首先,要說明一下筆者的思路,其實很簡單,即“在每次資料庫操作前,確定當前要選擇的資料庫物件”而後就如同訪問單庫一樣的訪問當前選中的資料庫即可。
其次,要在每次DB訪問前選擇資料庫,需要明確幾個問題,1.iBatis在什麼時候從DataSource中取得具體的資料庫Connection的,2.對取得的Connection,iBatis是否進行快取,因為在多庫情況下Connection被快取就意味著無法及時改變資料庫連結選擇。3.由於我們使用了Spring來管理DB事務,因此必須搞清Spring對DB Connction的開關攔截過程是否會影響多DataSource的情況。
幸運的是,研究原始碼的結果發現,iBatis和Spring都是通過標準的DataSource介面來控制
Connection的,這就為我們省去了很多的麻煩,只需要實現一個能夠支援多個數據庫的DataSource,就能達到我們的目標。
4.程式碼與實現
多資料庫的DataSource實現:MultiDataSource.class
Java程式碼
- import java.io.PrintWriter;
- import java.sql.Connection;
- import java.sql.SQLException;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.Map;
-
import javax.sql.DataSource;
- import org.apache.log4j.Logger;
- import com.xxx.sql.DataSourceRouter.RouterStrategy;
- /**
- * 複合多資料來源(Alpha)
- * @author [email protected]
- * Jul 15, 2010
- */
- public class MultiDataSource implements DataSource {
- static Logger logger = Logger.getLogger(MultiDataSource.class);
- //當前執行緒對應的實際DataSource
- private ThreadLocal<DataSource> currentDataSourceHolder = new ThreadLocal<DataSource>();
- //使用Key-Value對映的DataSource
- private Map<String , DataSource> mappedDataSources;
- //使用橫向切分的分散式DataSource
- private ArrayList<DataSource> clusterDataSources;
- public MultiDataSource(){
- mappedDataSources = new HashMap<String , DataSource>(4);
- clusterDataSources = new ArrayList<DataSource>(4);
- }
- /**
- * 資料庫連線池初始化
- * 該方法通常在web 應用啟動時呼叫
- */
- public void initialMultiDataSource(){
- for(DataSource ds : clusterDataSources){
- if(ds != null){
- Connection conn = null;
- try {
- conn = ds.getConnection();
- } catch (SQLException e) {
- e.printStackTrace();
- } finally{
- if(conn != null){
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- conn = null;
- }
- }
- }
- }
- Collection<DataSource> dsCollection = mappedDataSources.values();
- for(DataSource ds : dsCollection){
- if(ds != null){
- Connection conn = null;
- try {
- conn = ds.getConnection();
- } catch (SQLException e) {
- e.printStackTrace();
- } finally{
- if(conn != null){
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- conn = null;
- }
- }
- }
- }
- }
- /**
- * 獲取當前執行緒繫結的DataSource
- * @return
- */
- public DataSource getCurrentDataSource() {
- //如果路由策略存在,且更新過,則根據路由演算法選擇新的DataSource
- RouterStrategy strategy = DataSourceRouter.currentRouterStrategy.get();
- if(strategy == null){
- throw new IllegalArgumentException("DataSource RouterStrategy No found.");
- }
- if(strategy != null && strategy.isRefresh()){
- if(RouterStrategy.SRATEGY_TYPE_MAP.equals(strategy.getType())){
- this.choiceMappedDataSources(strategy.getKey());
- }else if(RouterStrategy.SRATEGY_TYPE_CLUSTER.equals(strategy.getType())){
- this.routeClusterDataSources(strategy.getRouteFactor());
- }
- strategy.setRefresh(false);
- }
- return currentDataSourceHolder.get();
- }
- public Map<String, DataSource> getMappedDataSources() {
- return mappedDataSources;
- }
- public void setMappedDataSources(Map<String, DataSource> mappedDataSources) {
- this.mappedDataSources = mappedDataSources;
- }
- public ArrayList<DataSource> getClusterDataSources() {
- return clusterDataSources;
- }
- public void setClusterDataSources(ArrayList<DataSource> clusterDataSources) {
- this.clusterDataSources = clusterDataSources;
- }
- /**
- * 使用Key選擇當前的資料來源
- * @param key
- */
- public void choiceMappedDataSources(String key){
- DataSource ds = this.mappedDataSources.get(key);
- if(ds == null){
- throw new IllegalStateException("No Mapped DataSources Exist!");
- }
- this.currentDataSourceHolder.set(ds);
- }
- /**
- * 使用取模演算法,在群集資料來源中做路由選擇
- * @param routeFactor
- */
- public void routeClusterDataSources(int routeFactor){
- int size = this.clusterDataSources.size();
- if(size == 0){
- throw new IllegalStateException("No Cluster DataSources Exist!");
- }
- int choosen = routeFactor % size;
- DataSource ds = this.clusterDataSources.get(choosen);
- if(ds == null){
- throw new IllegalStateException("Choosen DataSources is null!");
- }
- logger.debug("Choosen DataSource No." + choosen+ " : " + ds.toString());
- this.currentDataSourceHolder.set(ds);
- }
- /* (non-Javadoc)
- * @see javax.sql.DataSource#getConnection()
- */
- public Connection getConnection() throws SQLException {
- if(getCurrentDataSource() != null){
- return getCurrentDataSource().getConnection();
- }
- return null;
- }
- /* (non-Javadoc)
- * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
- */
- public Connection getConnection(String username, String password)
- throws SQLException {
- if(getCurrentDataSource() != null){
- return getCurrentDataSource().getConnection(username , password);
- }
- return null;
- }
- /* (non-Javadoc)
- * @see javax.sql.CommonDataSource#getLogWriter()
- */
- public PrintWriter getLogWriter() throws SQLException {
- if(getCurrentDataSource() != null){
- return getCurrentDataSource().getLogWriter();
- }
- return null;
-
相關推薦
Spring + iBatis 的多庫橫向切分簡易解決思路
1.引言 筆者最近在做一個網際網路的“類SNS”應用,應用中使用者數量巨大(約4000萬)左右,因此,簡單的使用傳統單一資料庫儲存肯定是不行的。 參考了業內廣泛使用的分庫分表,以及使用DAL資料訪問層等的做法,筆者決定使用一種最簡單的資料來源路由選擇方式來解決問題。
MyBatis-Plus 多庫部署方式;spring mvc 多庫部署方式
1、實現mybatis-plus的多個數據庫的切換方式 原始碼地址:https://github.com/baomidou/mybatisplus-spring-mvc 2、因為其文件都是相互依賴的,所以修改配置,就是在已有的配置中修改
spring框架多個數據庫操作需統一提交事務回滾機制解析以及解決辦法
1、遇到的問題 當我們一個方法裡面有多個數據庫儲存操作的時候,中間的資料庫操作發生的錯誤。虛擬碼如下: public method() { Dao1.save(Person1); Dao1.save(Person2); Dao1.sa
【策略模式】如何結合spring實現一個介面多個實現,如何解決介面選擇問題
1、首先把對映關係放在spring-mvc.xml配置檔案 <bean id="dispatcher" class="com.ms.kai.bms.dispatcher.Abstrac
spring boot+mybatis+druid 多資料來源多庫分散式事務
廢話不多說,首先貼配置檔案,需要引入pomxml <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter&l
Tomcat叢集Spring+Quartz多次執行解決方案記錄
由於在叢集環境下定時器會出現併發和重複執行的問題,我再三考慮記錄有5 一、把定時器模組單獨拿出來放到一臺tomcat或者新建一個Java工程手動啟動定時器,這樣定時器的任務就可以從原來的叢集中抽離開來,原來的tomcat叢集不再執行定時器任務,而是交給定時器應用單獨執
spring mybatis atomikos 多庫分散式事務demo
最近有點時間 , 就準備搭個多庫事務的例子 , 不過中間碰到一些問題 , 這裡記錄下來 . 我的atomikos 版本是 3.7.0 ; Spring4 mybatis3 ; 碰到問題主要有兩類 :
spring+ibatis配置多資料來源
經過上一步對spring配置檔案的配置,接下來我們新建一個介面檔案: public interface IBaseSqlMapClientDaoSupport { public void choseSqlClient(String name); } 同時也新建一個實現類: public class B
iOS專案中引用多個第三方庫引發衝突的解決方法
這個真蛋疼~~~~ 解決方法如下: iOS程式開發過程中引用多個第三方庫時會出現類名重疊,導致衝突,具體的衝突錯誤提示如下: duplicate symbol OBJC_IVAR$_AFHTTPSessionManager._requestSerializer in:
spring整合多個mongodb庫
1. 仿照抽象路由資料來源類建立一個抽象mongodb路由模板源的類。因為操作mongodb的是模板。 package com.caiwufei.common.db.mongo; import java.util.HashMap; import java.util.Map
spring ibatis 整合 abator自動生成的xml檔案報錯及解決方法
程式中的部分程式碼由abator自動生成, dao及其daoimpl package com.our311.demo.dao; import com.our311.demo.dao.model.TbUser; import com.our311.demo.dao.model
ubuntu MySQL數據庫輸入中文亂碼 解決方案
title str itl alt 查詢 ref cte class nbsp 一、登錄MySQL查看用SHOW VARIABLES LIKE ‘character%’;下字符集,顯示如下:+--------------------------+--------------
數據庫水平切分(拆庫拆表)的實現原理解析(轉)
數字 一個數據庫 java ins 結果 都對 不同 com 嚴重 第1章 引言 隨著互聯網應用的廣泛普及,海量數據的存儲和訪問成為了系統設計的瓶頸問題。對於一個大型的互聯網應用,每天幾十億的PV無疑對數據庫造成了相當 高的負載。對於系統的穩定性和擴展性造成了極大的問題。
SQL server觸發器、存儲過程操作遠程數據庫插入數據,解決服務器已存在的問題
定義 ims val rom 記錄 插入記錄 其它 pre 項目 近期弄了一個小項目,也不是非常復雜,須要將一個數據庫的一些數據備份到另外一個庫。不是本地,可能是網絡上其它的數據庫。想了一下,用了存儲過程和觸發器。也不是非常復雜,首先我須要操作遠程數據庫,於是寫了一個存
Maven 搭建spring boot多模塊項目
con pac end ice ces encoding oca 被子 resources Maven 搭建spring boot多模塊項目 備註:所有項目都在idea中創建 1.idea創建maven項目 1-1: 刪除src,target目錄,只保
python3 寫CSV文件多一個空行的解決辦法
bsp eggs line 參數 lov blog mini csv span Python文檔中有提到: open(‘eggs.csv‘, newline=‘‘) 也就是說,打開文件的時候多指定一個參數。Python文檔中也有這樣的示例: import csvwith
ssm框架插入mysql數據庫中文亂碼問題解決
mar word ref def http reat pro xml文件 framework 1. 檢查web.xml <!-- 編碼過濾器 --> <filter> <filter-name>
Spring-更多DI的知識
移除 進行 繼續 -c invoke 這就是 ima 不同的 string 3.3.1 延遲初始化Bean 延遲初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用時才創建及初始化Bean。 配置方式很簡單只需在<bean>標簽上指定 “lazy-
php寫入數據到mysql數據庫中出現亂碼解決方法
names .com http image alt ima utf8 情況 mysql 亂碼情況: 在選擇數據庫前加入一句代碼即可 mysql_query("set names utf8"); 最後效果 php寫入數據到mysql數據庫中出現亂碼解決方法
spring-boot實戰【05】:Spring Boo多環境配置及配置屬性註入到對象
num java red component 配置 cati 定義 fin row 項目工程結構: 配置文件application.properties文件 com.yucong.blog.name=yucong com.yucong.blog.title=Spring