java实现mysql数据库读写分离之定义多数据源方式

该示例是基于spring提供的AbstractRoutingDataSource,实现了一个动态数据源的功能,在spring配置中定义多个数据库分为主、从数据库,实现效果为当进行保存和修改记录时则对主表操作,查询则对从表进行操作,从而实现对数据库表的读写分离。这样做有利于提高网站的性能,特别是在数据库这一层。因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验。我们通常的做法就是把查询从主库中抽取出来,采用多个从库,使用负载均衡,减轻每个从库的查询压力。该示例并未对数据库同步进行说明,只对读写操作的分离实现:

在进行操作之前,先简单说一下AbstractRoutingDataSource相关的东西:

 1 AbstractRoutingDataSource继承了AbstractDataSource ,而AbstractDataSource 又是DataSource 的子类。DataSource   是javax.sql 的数据源接口,定义如下:
 2
 3 public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean  {}
 4
 5 public interface DataSource  extends CommonDataSource,Wrapper {
 6
 7   /**
 8    * <p>Attempts to establish a connection with the data source that
 9    * this <code>DataSource</code> object represents.
10    *
11    * @return  a connection to the data source
12    * @exception SQLException if a database access error occurs
13    */
14   Connection getConnection() throws SQLException;
15
16   /**
17    * <p>Attempts to establish a connection with the data source that
18    * this <code>DataSource</code> object represents.
19    *
20    * @param username the database user on whose behalf the connection is
21    *  being made
22    * @param password the user‘s password
23    * @return  a connection to the data source
24    * @exception SQLException if a database access error occurs
25    * @since 1.4
26    */
27   Connection getConnection(String username, String password)
28     throws SQLException;
29
30 }
31
32
33 public Connection getConnection() throws SQLException {
34         return determineTargetDataSource().getConnection();
35     }
36
37     public Connection getConnection(String username, String password) throws SQLException {
38         return determineTargetDataSource().getConnection(username, password);
39     }
40
41 protected DataSource determineTargetDataSource() {
42         Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
43         Object lookupKey = determineCurrentLookupKey();
44         DataSource dataSource = this.resolvedDataSources.get(lookupKey);
45         if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
46             dataSource = this.resolvedDefaultDataSource;
47         }
48         if (dataSource == null) {
49             throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
50         }
51         return dataSource;
52     }

从上面的代码中不难看出,获取数据源首先是通过对determineCurrentLookupKey()的调用获取resolvedDataSources对应key的值,故执行创建一个动态数据源类继承AbstractRoutingDataSource,复写determineCurrentLookupKey()去自定义设置和获取resolvedDataSources的key就可以实现了

具体步骤如下:

第一步:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"
    default-lazy-init="true">
    <!-- 引入配置文件 -->
    <context:component-scan base-package="com.he" />
     <bean id="masterdataSource"
       class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/test" />
        <property name="username" value="root" />
        <property name="password" value="111111" />
    </bean>

    <bean id="slavedataSource"
      class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/test2" />
        <property name="username" value="root" />
        <property name="password" value="111111" />
    </bean>

    <bean id="dataSource" class="com.he.mysql.test.DynamicDataSource">
        <property name="targetDataSources">
              <map key-type="java.lang.String">
                  <!-- write -->
                 <entry key="masterdataSource" value-ref="masterdataSource"/>
                 <!-- read -->
                 <entry key="slavedataSource" value-ref="slavedataSource"/>
              </map>  

        </property>
        <property name="defaultTargetDataSource" ref="masterdataSource"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:com/he/dao/*.xml"></property>
    </bean>  

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.he.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>  

    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean> 

    <!-- 注解式事务管理,需要在Service类上标注@Transactional -->
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>  

第二步:

1 public class DynamicDataSource extends AbstractRoutingDataSource {
2
3     @Override
4     protected Object determineCurrentLookupKey() {
5
6         return DynamicDataSourceHolder.getDataSouce();
7     }
8
9 }

创建动态数据源类继承AbstractRoutingDataSource

第三步:

 1 public class DynamicDataSourceHolder {
 2     public static final ThreadLocal<String> holder = new ThreadLocal<String>();
 3
 4     public static void putDataSource(String name) {
 5         holder.set(name);
 6     }
 7
 8     public static String getDataSouce() {
 9         return holder.get();
10     }
11 }

设置及获取每个线程访问的哪个数据源

第四步:

 1 @Service("userService")
 2 @Transactional
 3 public class UserServiceImpl implements UserService{
 4
 5     @Autowired
 6     private UserMapper userDao;public void add(User user) {
 7
 8         DynamicDataSourceHolder.putDataSource("masterdataSource");
 9         userDao.add(user);
10     }
11
12     public void update(User user) {
13
14         DynamicDataSourceHolder.putDataSource("masterdataSource");
15         userDao.updates(user);
16
17
18     }
19
20     @Transactional(propagation = Propagation.NOT_SUPPORTED)
21     public List<User> query() {
22
23         DynamicDataSourceHolder.putDataSource("slavedataSource");
24         List<User> user = userDao.query();
25         return user;
26
27     }
28
29
30
31
32 }

对service实现层加入设置数据源代码

上述为实现读写分离的关键部分,只是为了简单的做一个示例,完成上面操作以后,可自行的对数据库进行新增和查询操作,查看效果

时间: 2024-11-14 00:55:38

java实现mysql数据库读写分离之定义多数据源方式的相关文章

【转】mysql数据库读写分离数据同步

转载请注明来源:mysql数据库读写分离数据同步 mysql数据库读写分离数据同步 我是用了两个xp(一个主的,一个从的)的系统测试成功的,linux系统我也做测试了,没有成功,不过我想我所遇到的问题是同一个问题,xp下的可以成功,linux下的应该也可以成功,稍候会测试,然后更新结果! PS:刚测试了下linux 可以同步成功,主服务器是xp,从服务器是centos,可以成功. 例: A机器 192.168.0.2 B机器 192.168.0.3 两个机器可以ping通,互相访问 先配置主服务

mysql数据库读写分离

1.检查程序依赖lua语言环境 [[email protected] ~]# rpm -qa |grep lua lua-5.1.4-4.1.el6.x86_64 2.安装proxy软件包 [[email protected] ~]# tar -zxvf mysql-proxy-0.8.3-linux-rhel5-x86-64bit.tar.gz   //解压软件 [[email protected] ~]# mv mysql-proxy-0.8.3-linux-rhel5-x86-64bit 

161920、使用Spring AOP实现MySQL数据库读写分离案例分析

一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库的主从配置,最简单的是一台Master和一台Slave(大型网站系统的话,当然会很复杂,这里只是分析了最简单的情况).通过主从配置主从数据库保持了相同的数据,我们在进行读操作的时候访问从数据库Slave,在进行写操作的时候访问主数据库Master.这样的话就减轻了一台服务器的压力. 在进行读写分离案

使用Spring AOP实现MySQL数据库读写分离案例分析

一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库的主从配置,最简单的是一台Master和一台Slave(大型网站系统的话,当然会很复杂,这里只是分析了最简单的情况).通过主从配置主从数据库保持了相同的数据,我们在进行读操作的时候访问从数据库Slave,在进行写操作的时候访问主数据库Master.这样的话就减轻了一台服务器的压力. 在进行读写分离案

利用amoeba(变形虫)实现mysql数据库读写分离

关于mysql的读写分离架构有很多,百度的话几乎都是用mysql_proxy实现的.由于proxy是基于lua脚本语言实现的,所以网上不少网友表示proxy效率不高,也不稳定,不建议在生产环境使用:amoeba是阿里开发的一款数据库读写分离的项目(读写分离只是它的一个小功能),由于是基于java编写的,所以运行环境需要安装jdk: 前期准备工作:1.两个数据库,一主一从,主从同步:master: 172.22.10.237:3306 :主库负责写入操作:slave: 10.4.66.58:330

MySQL数据库读写分离之---Atles

        Atlas官方文档:   https://github.com/Qihoo360/Atlas/wiki  Atlas是由 Qihoo 360,  Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目.它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基础上,修改了大量bug,添加了很多功能特性.目前该项目在360公司内部得到了广泛应用,很多MySQL业务已经接入了Atlas平 台,每天承载的读写请求数达几十亿条. 主要功能:* 读写分离* 从库负

搭建基于MySQL的读写分离工具Amoeba

搭建基于MySQL的读写分离工具Amoeba: Amoeba工具是实现MySQL数据库读写分离的一个工具,前提是基于MySQL主从复制来实现的: 实验环境(虚拟机): 主机 角色 10.10.10.20 多实例加主从复制 10.10.10.30 Amoeba服务器 10.10.10.40 客户端(最后测试使用) 1.首先搭建MySQL的主从复制(不在多提):需要注意的是:在主从库上需要创建一个用户,在主库上创建的用户为amoeba,权限是create,update,insert,delete:

Spring+Mybatis实现主从数据库读写分离

Spring+Mybatis实现主从数据库读写分离 采用配置+注解的方式. 自定义@DataSource注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME

C#操作SqlServer MySql Oracle通用帮助类Db_Helper_DG(默认支持数据库读写分离、查询结果实体映射ORM)

[前言] 作为一款成熟的面向对象高级编程语言,C#在ADO.Net的支持上已然是做的很成熟,我们可以方便地调用ADO.Net操作各类关系型数据库,在使用了多年的Sql_Helper_DG后,由于项目需要,于是乎,就准备写一个Mysql_Helper在实现过程中,发现ADO.Net封装之完善,以及面向对象的封装.继承.多态,有了这些特性,何不把数据库操作封装成为一个通用的类呢,此文由此铺展而来... [实现功能] 这篇文章将要介绍的主要内容如下: 1.ADO.NET之SqlServer 2.ADO