首页天道酬勤shardingjdbc读写分离,为什么要用ssm不用ssh

shardingjdbc读写分离,为什么要用ssm不用ssh

张世龙 05-05 20:16 13次浏览

个人资料

MySQL已经是应用最广泛的数据库,在大多数情况下,为了在实际使用期间提供高可用性和高性能,项目通过主群集复制方法实现读写隔离。 MySQL本身支持复制,使用简单的配置配置主从机,请参阅3359 www.cn blogs.com/luck cs/articles/6295992.html (gtid模式) 主要解决的问题往往是从数据库级别分离读写,主库负责读写操作,而库只负责读写操作。 但是,要在APP应用中实现读写分离,还需要中间层的支持。 本文主要探讨用Java实现读写分离。

MySQL实现读写分离的方法有MySQL Proxy、Ameoba等多种,本文从热门的Spring Hibernate或Spring Mybatis本文的实际演示范例]中,采用简单的方法进行基本演示

环境

主节点: 192.168.56.102:3306

节点1 :来自1:192.168.56.101:3306

节点2 :来自2:192.168.56.107:3306

Java开发环境: OpenJDK1.8、Centos、Eclipse

使用框架引用代码的pom

基本原理

使用mysql JDBC驱动程序包中的复制驱动程序隔离读/写和平衡轮询策略负载(读取)

MySQL JDBC驱动程序包中的复制驱动程序主要从复制的MySQL环境中提供JDBC支持。 其基本原理是决定是否对连接目标setreadonly(true )实际采用来自主节点(读写)或从节点)的连接,其中心源代码为)复制封装

1 publicsynchronizedvoidsetreadonly (布尔只读) throws sqlexception (2if ) readonly ) 3if (! isslavesconnection (|this.current connection.is closed ) ) {4 boolean switched=true; 5 sqlexceptionexceptioncaught=null; 6 try {7} switched=switchtoslavesconnection (; 8 ) catch(sqlexceptione ) {9 switched=false; 10 exceptionCaught=e; 11 ) 12if (! switched this.readfrommasterwhennoslavesswitchtomasterconnection () {13 exceptionCaught=null; //theconnectionisok.canceltheexception,if any。

14 ) 15if (执行计数!=null}{16throwexceptioncaught; 17 }18 }19 } else{20 if (! ismasterconnection (|this.current connection.is closed ) ({21 boolean switched=true; 22 sqlexceptionexceptioncaught=null; 23 try { 24 } switched=switchtomasterconnection (; 25 ) catch(sqlexceptione ) {26 switched=false; 27 exceptionCaught=e; 28 ) 29if (! switchedswitchtoslavesconnectionifnecessary () {30 exceptionCaught=null; //theconnectionisok.canceltheexception,if any。

31 ) 32if (执行计数!=null}{33throwexceptioncaught; 34 } 35 } 36 } 37 this.readonly=readonly; 38

39 /*

40 * resetmastersconnectionread-onlystateif ' readfrommasterwhennoslaves=true '.iftherearenoslavesthemastersconectives read-onlystateinitsplace.even if not,itmustberesetfromapossiblepreviousread-only state.42 * /

43if (this.readfrommasterwhennoslavesismasterconnection () ) 44t

his.currentConnection.setReadOnly(this.readOnly);45 }46 }

加粗的两行代码就是在只读情况下和非只读情况下分别使用从节点和主节点的连接。为此我们可以编写以下代码验证读写分离:

1 public static voidmain(String[] args) {2 try{3 ReplicationDriver driver = newReplicationDriver();4

5 Properties p =newProperties();6 p.load(TestMasterSlave.class.getResourceAsStream("/db_masterslave.properties"));7 Connection conn = driver.connect(p.getProperty("url"), p);8 conn.setAutoCommit(false);9 conn.setReadOnly(true);10 ResultSet rs = conn.createStatement().executeQuery("select count(*) from user");11 while(rs.next()) {12 System.out.println(rs.getString(1));13 }14 System.out.println(BeanUtils.describe(conn).get("hostPortPair"));15

16 rs.close();17 conn.close();18 } catch(Exception e) {

19 e.printStackTrace();20 }

对应数据库配置为:

url=jdbc:mysql:replication://192.168.56.102:3306,192.168.56.101:3306,192.168.56.107:3306/test1

user=root

password=root

autoReconnect=true

roundRobinLoadBalance=true

System.out.println(BeanUtils.describe(conn).get("hostPortPair"));这行代码主要用来显示实际连接的主机和端口,通过多次运行我们可以发现其真实连接会在101和107之间切换。

基于以上实验,我们引入SSM框架,项目完整的pom依赖为

junit

junit

3.8.1

test

javax.servlet

javax.servlet-api

3.1.0

javax.servlet

jstl

1.2

mysql

mysql-connector-java

5.1.44

commons-beanutils

commons-beanutils-core

1.8.3

org.springframework

spring-webmvc

4.3.9.RELEASE

org.springframework

spring-tx

4.3.9.RELEASE

org.springframework

spring-jdbc

4.3.9.RELEASE

mysql

mysql-connector-java

5.1.44

org.aspectj

aspectjweaver

1.8.10

org.mybatis

mybatis

3.4.4

org.mybatis

mybatis-spring

1.3.0

com.alibaba

druid

1.1.3

我们的目标是通过Spring的声明式事务,在事务的方法为readonly时,自动切换到从节点以实现读写分离。为直接能操作连接,我们使用了spring的JDBC TransactionManager,spring配置中事务相关代码为:

这个MyTransactionManager就是我们真正改造使SSM能够实现基于Spring本身实现读写分离的地方,具体实现代码为:

/**

* 为实现及直观显示读写分离的具体情况,重写了DataSourceTransactionManager,并在事务定义为只读时调用了连接的setReadOnly方法

*

* @author root

*

*/

public class MyTransactionManager extends DataSourceTransactionManager {

@Override

protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition)

throws SQLException {

// TODO Auto-generated method stub

super.prepareTransactionalConnection(con, definition);

if (definition.isReadOnly()) {

con.setReadOnly(true);//只读事物,设置连接的readOnly促使底层的ReplicationDriver选择正确的节点

try {

System.out.println("a readonly transaction:" +

BeanUtils.describe(((DruidPooledConnection)con).getConnection()).get("hostPortPair"));

} catch (Exception e) {

e.printStackTrace();

}

}else {

try {

System.out.println("a write transaction:" +

BeanUtils.describe(((DruidPooledConnection)con).getConnection()).get("hostPortPair"));

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

经过这么小小的改造,如果是在Spring中声明了read-only为true的事务,我们就可以得到definition.isReadOnly()的结果为true,通过设置con的ready only为true,让底层的MySQL的ReplicationDriver做出选择,从而实现纯读取的方法直接从其他节点实现。运行程序,多在业务层调用几次find开始的方法,我们会发现实际的读取节点一直在改变,

本方案未在生产环境中测试,仅作为一种读写分离的尝试。通过简单的逻辑分析可知,所有包含读写操作的事务,所有数据操作都会发生在主节点上,只有标识为纯读取的业务方法的数据操作才会被均衡到只读节点,理论上是没有问题。需要注意的是,如果我们使用的是HibernateTransactionManager,则实现方式不同。

如果MySQL服务器采用的方案不是主从复制,而是MySQL Cluster,则只需要提供一个读写端口和一个只读端口,负载均衡由MySQL Cluster自动实现,该代码只实现读写分离。

欢迎大家交流讨论。

,