SQL的读写分离方案,是一种通用的设计,通常可以较好的提升数据库集群的整体负载能力,当一个mysql实例无法支撑客户端的读写负载时,我们首先会想到对数据库进行“读写分离”
1)在数据库的架构层面,我们使用M-S架构模式,即一主多从,Master主要用于处理write、transaction等核心操作,这些操作必须发生在master上,否则将会导致数据一致性问题。对于slaves,通常用于分流read操作,对于那些对数据实时性要求不高、批量读取、SLOW SQL等操作,我们应该将它们分配到slaves中。
2)能进行读写分离的前提,一个非常重要的指标,就是“集群的R:W比较高”,即较多的read、较少的write,如果读写比很低,读写分离所产生的收益,将是比较微弱的。
3)从架构的视角考虑,Master与slave在整个timeline中,所需要处理的物理write操作是相同的,即在master上发生一个write操作,此操作也必将在slaves上发生(可能时间线不一致,但是最终一定会发生,这取决于binlog和mysql IO线程的处理效率)。因此,粗略来讲,slaves所面对的writes负载与master一致。这也限定我们,“读写比较低”的应用场景下,读写分离收益较低的原因。
4)slaves机器的物理性能,应该与master保持一致,甚至可以更好,毕竟,大量的reads操作需要等待slaves去处理,这些load是master所不会面对的。
本文展示了如何在Spring环境中使用JPA实现dataSource的读写分离(本文没有使用JTA事务),这个东西看起来简单,其实实现起来比较蹩脚,与JDBC有很大区别。
1)使用Spring中的AbstractRoutingDataSource,辅助程序在运行时选择合适的dataSource。
2)可以使用@Master、@Slave注释来强制dao方法调用必须使用master或者slave的数据库源。
3)本例提供的ReadWriteDataSourceRouter可以根据当前Transaction的readOnly特性,将SQL调用按需分发给master或者slaves;可以指定多个slaves,可以简单的负载均衡。
1、persistence.xml
如果我们不适用JTA事务的话,这个文件可以为空即可。
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <!-- <persistence-unit name="trial" transaction-type="RESOURCE_LOCAL"> </persistence-unit> --> </persistence>
2、ReadWriteDataSourceRouter.java
** * Created by liuguanqing on 16/5/10. * 全量读写分离 */ public class ReadWriteDataSourceRouter extends AbstractRoutingDataSource { private Integer slaves;//slaves的个数 private Random random = new Random(); //如果基于JDK 7+,可以使用ThreadLocalRandom public void setSlaves(Integer slaves) { this.slaves = slaves; } @Override protected Object determineCurrentLookupKey() { boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); if(!isReadOnly) { return "WRITE"; } //如果是只读,可以从任意一个slave中执行 return "READ_" + random.nextInt(slaves); //如果基于JDK 7+ //ThreadLocalRandom random = ThreadLocalRandom.current(); } }
java类中使用了一些约定的字符串,比如“WRITE”对应的为masterDataSource,所有的slaves对应的key必须为“READ_” + 数字。(参见下文配置)
3、spring-datasource.xml配置摘要:
<bean id="masterDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.1.100:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=false"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> <property name="maxActive" value="128"></property> <property name="maxIdle" value="6"></property> <property name="minIdle" value="2"></property> <property name="maxWait" value="30000"></property> <property name="defaultAutoCommit" value="true"></property> </bean> <bean id="slaveDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.1.101:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=false"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> <property name="maxActive" value="128"></property> <property name="maxIdle" value="6"></property> <property name="minIdle" value="2"></property> <property name="maxWait" value="30000"></property> <property name="defaultAutoCommit" value="true"></property> </bean> <bean id="rwDataSource" class="com.test.demo.dataSource.TypedReadWriteDataSourceRouter"> <property name="slaves" value="2"/><!-- 允许read操作的节点个数 --> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="WRITE" value-ref="masterDataSource"/> <entry key="READ_0" value-ref="slaveDataSource" /> <entry key="READ_1" value-ref="masterDataSource" /><!-- 允许部分read到slave上 --> </map> </property> <property name="defaultTargetDataSource" ref="slaveDataSource"/><!-- or master --> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceXmlLocation" value="classpath:persistence.xml" /> <property name="dataSource" ref="rwDataSource" /> <!-- model的package--> <property name="packagesToScan" value="com.test.demo.model"/> <property name="jpaVendorAdapter"> <!-- JPA的实现,有多种,请根据实际情况选择 --> <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter"> <property name="showSql" value="false"/> <property name="generateDdl" value="false"/> <property name="database" value="MYSQL"/> <property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform" /> </bean> </property> <property name="jpaProperties"> <props> <prop key="eclipselink.weaving">false</prop> <prop key="eclipselink.cache.shared.default">false</prop> <prop key="eclipselink.read-only">true</prop> </props> </property> </bean> <!-- 声明一个Spring提供的JPA事务管理器,传入的参数是Spring中的实体管理器工厂 --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <context:annotation-config/> <tx:annotation-driven transaction-manager="transactionManager" />
4、TesUser.java(model样例)
@Entity @Table(name="test_user",schema = "vipkid") public class TestUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int id; @Column(name = "name") private String name; @Column(name = "password") private String password; // 创建时间 @Temporal(TemporalType.DATE) @Column(name = "created") private Date created; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
5、TestUserRepository.java(DAO层)
@Repository public class TestUserRepository { @PersistenceContext private EntityManager entityManager; @Transactional(readOnly = true) //To slave public TestUser getFromSlave(int id) { String sql = "select T from TestUser T where T.id = :id"; TypedQuery<TestUser> query = entityManager.createQuery(sql,TestUser.class); query.setParameter("id",id); return query.getSingleResult(); } @Transactional(readOnly = false) //To master public TestUser getFromMaster(int id) { String sql = "select T from TestUser T where T.id = :id"; TypedQuery<TestUser> query = entityManager.createQuery(sql,TestUser.class); query.setParameter("id",id); return query.getSingleResult(); } }
相关推荐
最近项目要支持读写分离, 网上找了很多,但都是不太完整,我自己整理了下供大家参考。 我的项目使用的框架: springMvc+spring+hibernate+springJPA+maven, 数据库连接池用阿里的druid。
主要介绍了SpringBoot集成Spring Data JPA及读写分离的相关知识,需要的朋友可以参考下
1:多租户系统集成 2:集成shardingjdbc分库分表 3:集成shardingjdbc读写分离 4:集成人大金仓数据库 5:重写了JPA-saveandflush方法 6:修复了JPA更新数据为null的问题 7:解压后导入idea,创建数据库即可运行测试
SpringBoot2.x 继承 AbstractRoutingDataSource 动态数据源切换实现 JPA读写分离。 使用MyCat代理MySQL8数据库,添加root账户(读写)和user账户(只读)模拟读写简单分离。
那么这种模式查询的时候性能会变的非常差,这个时候就涉及到了 CQRS ,简单的理解就是读写分离,通过事件触发,将最新状态保存到读库,查询全都走读库,理论上代码层,数据库层,都可以做到分离,当然也可以不分离,...
mysql主从复制,读写分离 3技术要点 后端: spring-boot、spring-session、spring-security等全家桶 dubbo + zookeeper分布式rpc服务 redis缓存 mysql数据库 flowable工作流 mybatis、jdbcTemplate、spring jpa持久...
mysql主从复制,读写分离 3技术要点 后端: spring-boot、spring-session、spring-security等全家桶 dubbo + zookeeper分布式rpc服务 redis缓存 mysql数据库 flowable工作流 mybatis、jdbcTemplate、spring jpa持久...
支持数据库读写分离,Feign增加服务直接的安全调用。v01版本是基础学习,主分支基于目前线上功能拆出来的功能模块,实现开箱即用。 ## 项目结构 ``` lua super-boot ├── client-config -- 项目配置文件信息...
jpa的依赖 (spring-boot-starter-data-jpa) 3.1.2.4 JavaBean对象 3.1.2.5 application.properties配置文件 3.1.2.6 配置类 3.1.2.7 测试类 3.1.3问题与解答 问题: 解答与分析: 案例实操 3.2 作用于Class类及其...
ef-orm A Simple OR-Mapping framework on multiple databases. ... EF-ORM是一个轻量,便捷的Java ORM框架。并且具备若干企业级的... SQL分析,性能统计,分库分表,Oracle RAC支持,读写分离支持 标签:eform
8-20 实现读写分离 8-21 测试与并发 8-22 事件溯源模式与Axon框架总结 第9章 TCC模式和微服务架构的设计模式 本章介绍TCC模式,也对微服务系统的几种设计模式,以及这些模式下分布式事务的实现模式进行了介绍。 9-1...
适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。 支持任意实现JDBC规范的数据库,...
集成mybatis,druid数据库连接池(是否可以动态修改连接数),多数据源(读写分离,主从数据库同步的延迟), 分页:https://github.com/pagehelper/Mybatis-PageHelper(完成) 集成jpa(完成,集成了mybatis工具包后基本...