spring动态数据源分库

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,定义数据库路由器

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>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值