java 读写分离
源码地址:http://git.oschina.net/xiaochangwei
先回答下 1.为啥要读写分离? 大家都知道最初开始,一个项目对应一个数据库,基本是一对一的,但是由于后来用户及数据还有访问的急剧增多, 系统在数据的读写上出现了瓶颈,为了让提高效率,想读和写不相互影响,读写分离就诞生了...... 2.什么样的项目需要读写分离? 并不是所有项目都适合读写分离,如果我把我自己的博客网站也搞成读写分离的,菜鸟觉得哇好高大上;砖家就会说 SB 读写分离仅适合用在读写尤其频繁的项目,如淘宝这类访问量多,读写都很频繁的情况下, 一般来说想中小型企业的erp,网站啥的都不需要,用了反而影响性能,因为读写时的切换也是要耗费资源的 3.读写分离的前提条件是什么? 如2所说,如果确定了项目需要用到读写分离,那就得配置数据库同步了,数据不同步读写有啥意思, 同步方式很多种,最简单的主从同步可以参考我的博客另一篇文章 http://www.cnblogs.com/xiaochangwei/p/4824355.html 当然还有很多同步方式,抛砖引玉 请自由发挥 大家别急,不要说我写了半天还不如正题, 我文学功底差,语文老师死得早,想到啥说啥,毕竟语文考试很少及格,你们要理解下 下面我讲的j2ee主从同步,用到的是技术框架: 基于注解的SpringMVC + Mybatis + Mysql, Spring mvc就很熟悉了,自己搭个框架一边玩吧,讲讲整合mybatis时候怎么读写分离的, 直接贴代码: <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> <context:component-scan base-package="com.xiao" />
<aop:aspectj-autoproxy/> <!-- 引入属性文件 -->
<context:property-placeholder location="classpath:application.properties" /> <!-- 数据源配置,使用应用内的proxool数据库连接池 -->
<bean id="business_write" class="org.logicalcobwebs.proxool.ProxoolDataSource">
<!-- Connection Info destroy-method="close" -->
<property name="driver" value="${jdbc.driver}" />
<property name="driverUrl" value="${business.write.url}" />
<property name="user" value="${business.write.username}" />
<property name="password" value="${business.write.password}" />
<property name="alias" value="master_db" /> <!-- 测试的SQL执行语句 -->
<property name="houseKeepingTestSql" value="select count(*) from dual" />
<!-- 最少保持的空闲连接数 (默认2个) -->
<property name="prototypeCount" value="${jdbc.prototypeCount}" />
<!-- proxool自动侦察各个连接状态的时间间隔(毫秒),侦察到空闲的连接就马上回收,超时的销毁 默认30秒) -->
<property name="houseKeepingSleepTime" value="${jdbc.hourseKeepingSleepTime}" />
<!-- 最大活动时间(毫秒)(超过此时间线程将被kill,默认为5分钟) -->
<property name="maximumActiveTime" value="${jdbc.maximumActiveTime}" />
<!-- 连接最长时间(毫秒)(默认为4个小时) -->
<property name="maximumConnectionLifetime" value="${jdbc.maximumConnectionLifetime}" />
<!-- 最小连接数 (默认2个) -->
<property name="minimumConnectionCount" value="${jdbc.minimumConnectionCount}" />
<!-- 最大连接数 (默认5个) -->
<property name="maximumConnectionCount" value="${jdbc.maximumConnectionCount}" />
</bean> <bean id="business_read" class="org.logicalcobwebs.proxool.ProxoolDataSource">
<!-- Connection Info destroy-method="close" -->
<property name="driver" value="${jdbc.driver}" />
<property name="driverUrl" value="${business.read.url}" />
<property name="user" value="${business.read.username}" />
<property name="password" value="${business.read.password}" />
<property name="alias" value="slave_db" />
<!-- 测试的SQL执行语句 -->
<property name="houseKeepingTestSql" value="select count(*) from dual" />
<!-- 最少保持的空闲连接数 (默认2个) -->
<property name="prototypeCount" value="${jdbc.prototypeCount}" />
<!-- proxool自动侦察各个连接状态的时间间隔(毫秒),侦察到空闲的连接就马上回收,超时的销毁 默认30秒) -->
<property name="houseKeepingSleepTime" value="${jdbc.hourseKeepingSleepTime}" />
<!-- 最大活动时间(毫秒)(超过此时间线程将被kill,默认为5分钟) -->
<property name="maximumActiveTime" value="${jdbc.maximumActiveTime}" />
<!-- 连接最长时间(毫秒)(默认为4个小时) -->
<property name="maximumConnectionLifetime" value="${jdbc.maximumConnectionLifetime}" />
<!-- 最小连接数 (默认2个) -->
<property name="minimumConnectionCount" value="${jdbc.minimumConnectionCount}" />
<!-- 最大连接数 (默认5个) -->
<property name="maximumConnectionCount" value="${jdbc.maximumConnectionCount}" />
</bean> <bean id="dataSource" class="com.xiao.weixin.common.jdbc.DynamicDataSource">
<property name="targetDataSources">
<map>
<entry key="business_write" value-ref="business_write" />
<entry key="business_read" value-ref="business_read" />
</map>
</property>
<property name="defaultTargetDataSource" ref="business_write" />
</bean> <!-- 线程池配置 -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 线程池活跃的线程数 -->
<property name="corePoolSize" value="5" />
<!-- 线程池最大活跃的线程数 -->
<property name="maxPoolSize" value="10" />
<!-- 队列的最大容量 -->
<property name="queueCapacity" value="25" />
</bean> <!-- myBatis文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
<property name="mapperLocations" value="classpath:com/xiao/**/*Mapper.xml" /> <!-- <property name="configLocation" value="classpath:mybatisMapConfig.xml" />-->
</bean> <!-- myBatis Mapper文件 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xiao.**.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean> <!-- 事务管理器配置 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> <!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager" order="2"/> <!-- Spring jdbcTemplate配置 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean> <bean id="xiaoFilter" class="com.xiao.weixin.common.filter.XiaoFilter" /> <import resource="spring-mybatis_quartz.xml"/>
<!-- 引入shiro 缓存配置 -->
<import resource="applicationContext-shiro.xml"/>
<!-- 引入国际化 配置-->
<import resource="spring-i18n.xml"/>
</beans>首先你得有配置好了的 读数据库和写数据库 不会的戳这里哈 http://www.cnblogs.com/xiaochangwei/p/4824355.html 配置好了读和写的数据源了后,仔细看 <bean id="dataSource" class="com.xiao.weixin.common.jdbc.DynamicDataSource">
<property name="targetDataSources">
<map>
<entry key="business_write" value-ref="business_write" />
<entry key="business_read" value-ref="business_read" />
</map>
</property>
<property name="defaultTargetDataSource" ref="business_write" />
</bean>这里指定了两个数据源,并且默认是用的写数据库,
数据源之所以能切换是因为Spring提供了 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource接口 默认不是写么 package com.xiao.weixin.common.jdbc; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 设置数据源
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {
String value() default "business_read";
} 如果我们在serviceimpl中,某个方法上加上 @DataSource 如下: @Override
@DataSource
public String getAccessToken() {
String access_token = null;
try {
AccessToken token = weixinApiMapper.findToken();// 数据库查找没过期的token并返回
if (token != null) {
access_token = token.getAccessToken();
}
} catch (Exception e) {
log.error("获取accessToken出错");
e.printStackTrace();
}
log.debug("获取到的accessToken是:" + access_token);
return access_token;
}
就会连接到读数据库,不写则用默认的读数据库 不过 只是上面的代码是不行的 ,具体切换还得看Spring提供的接口 配置中关联的实现类代码如下 package com.xiao.weixin.common.jdbc; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override
protected Object determineCurrentLookupKey() {
return JdbcContextHolder.getJdbcType();
}
}JdbcContextHolder 又是下面这样实现滴 package com.xiao.weixin.common.jdbc; /**
* 连接哪个数据源的环境变量
*/
public class JdbcContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setJdbcType(String jdbcType) {
contextHolder.set(jdbcType);
} /**
* 在选用business_write数据源前,执行此方法
*/
public static void setBusinessWrite(){
setJdbcType("business_write");
}
/**
* 在选用business_read数据源前,执行此方法
*/
public static void setBusinessRead(){
setJdbcType("business_read");
}
public static String getJdbcType(){
return (String) contextHolder.get();
}
/**
* 恢复成默认的数据源,即defaultTargetDataSource,执行此方法
*/
public static void clearJdbcType() {
contextHolder.remove();
}
} 就这样就能轻松搞定读写分离了,搞不定的朋友请和我交流 不要问我为啥知道这些,都是面试的时候不懂回来自己研究的, 正如我股灾期间我告诉大家一定要坚持到16号,16号有大量资金入场,大家回复我这么牛逼,知道啥子内幕么,我呵呵一笑 因为我们公司15号发工资 o(^▽^)o
java 读写分离的更多相关文章
- java读写分离的实现
1. 背景 我们一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案, 其中一个是主库,负责写入数据,我们称之为:写库: 其它都是从库,负责 ...
- Java读写分离实现
1.查看源码 AbstractRoutingDataSource类中有个determineTargetDataSource方法 protected DataSource determineTarget ...
- 从零开始学 Java - Spring AOP 实现主从读写分离
深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...
- java环境下的数据库读写分离
方案很多:阿里的中间件cobar.aop注解方式.com.mysql.jdbc.ReplicationDriver读写分离驱动MySQL数据库的同步. MySQL是开源的关系型数据库系统.主从同步复制 ...
- java 使用spring实现读写分离
最近上线的项目中数据库数据已经临近饱和,最大的一张表数据已经接近3000W,百万数据的表也有几张,项目要求读数据(select)时间不能超过0.05秒,但实际情况已经不符合要求,explain建立索引 ...
- Java多线程之~~~ReadWriteLock 读写分离的多线程实现
在多线程开发中,常常会出现一种情况,我们希望读写分离. 就是对于读取这个动作来说,能够同一时候有多个线程同 时去读取这个资源,可是对于写这个动作来说,仅仅能同一时候有一个线程来操作.并且同一时候,当有 ...
- Java实现数据库的读写分离
引言 1.读写分离:可以通过Spring提供的AbstractRoutingDataSource类,重写determineCurrentLookupKey方法,实现动态切换数据源的功能:读写分离可以有 ...
- Java知识点梳理——读写分离
1.读写分离:可以通过Spring提供的AbstractRoutingDataSource类,重写determineCurrentLookupKey方法,实现动态切换数据源的功能:读写分离可以有效减轻 ...
- mybatis plugins实现项目【全局】读写分离
在之前的文章中讲述过数据库主从同步和通过注解来为部分方法切换数据源实现读写分离 注解实现读写分离: http://www.cnblogs.com/xiaochangwei/p/4961807.html ...
随机推荐
- html img加载不同大小图像速度
最近要想法提高网页的性能,在查看图片加载时,产生了试验的想法.一直以来都没有太去深究,还是挖掘一下的好. 很简单的试验,<img>加载两个图像,一个2.3MB,5000*5000,一个22 ...
- VFS文件系统结构分析 与socket
本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载.但转载请保持文档的完整性,并注明原作者及原链接.内容可任意使用,但对因使用该内容引起的后果不做任何保证. 作者:fireaxe_hq@ho ...
- Kattis -Safe Passage(撑伞问题)
Safe Passage Photo by Ian Burt A group of friends snuck away from their school campus, but now they ...
- centos7常见的操作
centos7的网络IP地址配置文件在 /etc/sysconfig/network-scripts 文件夹下, 查看当前网卡名称 ip ad li ens33网卡对应的配置文件为ifcfg-ens ...
- 如何使用Matlab做数字信号处理的仿真1
例如 第三版数字信号处理P51 -1.14习题时域离散信号的相关性研究x(n)=Asin(ωn)+u(n),其中ω=π/16,u(n)是白噪声,现要求 ⑴.产生均值为0,功率P=0.1的均匀分布白噪声 ...
- python二级登陆菜单
""" 1.三级菜单 注册 登陆 注销 2.进入每一个一级菜单,都会有下一级的菜单"""user_item = dict()try: whi ...
- Python编码显示中文乱码
爬虫时出现问题: import requests data=requests.get('http://roll.news.sina.com.cn/')print(data.text) 输出结果中文显示 ...
- Project Euler 43 Sub-string divisibility
题意: 1406357289是一个0至9全数字数,因为它由0到9这十个数字排列而成:但除此之外,它还有一个有趣的性质:子串的可整除性.记d1是它的第一个数字,d2是第二个数字,依此类推,我们注意到: ...
- BZOJ 3144 [HNOI2013]切糕 (最大流+巧妙的建图)
题面:洛谷传送门 BZOJ传送门 最大流神题 把点权转化为边权,切糕里每个点$(i,j,k)$向$(i,j,k+1)$连一条流量为$v(i,j,k)$的边 源点$S$向第$1$层的点连边,第$R+1$ ...
- [洛谷 P2365] 任务安排 (线性dp)
3月14日第二题!! 题目描述 N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务.从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间 ...