什麼事讀寫分離
讀寫分離,基本的原理是讓主數據庫處理事務性增、改、刪操作(INSERT、UPDATE、DELETE),而從數據庫處理SELECT查詢操作。數據庫複製被用來把事務性操作導致的變更同步到集群中的從數據庫。
為什麼要實現讀寫分離
增加冗餘
增加了機器的處理能力
對於讀操作為主的應用,使用讀寫分離是最好的場景,因為可以確保寫的服務器壓力更小,而讀又可以接受點時間上的延遲。
實現
本文介紹利用spring aop來動態切換數據源來實現讀寫分離。
先建一個maven項目,導入springBoot依賴。
org.springframework.bootspring-boot-starter-parent1.5.2.RELEASEorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-aoporg.mybatis.spring.bootmybatis-spring-boot-starter1.3.1org.springframework.bootspring-boot-starter-jdbccom.alibabadruid${druid.version}mysqlmysql-connector-java${mysql-connector.version}org.springframework.bootspring-boot-starter-testtest
然後在配置文件application.yml中自定義數據源配置項
server: port: 8080 logging: level: org.springframework: INFO com.qiang: DEBUG spring: output: ansi: enabled: always datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db_area?characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root db: readsize: 2 read0: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db_area?characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root read1: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db_area?characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root aop: auto: true proxy-target-class: true
配置Druid
package com.qiang.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; /** * @author gengqiang * @date 2018/5/3 */ @Configuration public class DruidConfig { private Logger logger = LoggerFactory.getLogger(DruidConfig.class /** * 主據源 * @return */ @Primary @Bean(name = "dataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build(); } /** * 從數據源1 * @return */ @Bean(name = "readDataSource0") @ConfigurationProperties(prefix = "spring.db.read0") public DataSource readDataSource0() { return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build(); } /** * 從數據源2 * @return */ @Bean(name = "readDataSource1") @ConfigurationProperties(prefix = "spring.db.read1") public DataSource readDataSource1() { return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build(); } }
配置Mybaits
package com.qiang.config; import com.qiang.config.db.DataSourceType; import com.qiang.config.db.RoutingDataSource; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.TransactionManagementConfigurer; import javax.sql.DataSource; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @author gengqiang * @date 2018/5/3 */ @Configuration @EnableTransactionManagement(order = 2) @MapperScan(basePackages = {"com.qiang.demo.mapper"}) public class MybatisConfig implements TransactionManagementConfigurer, ApplicationContextAware { private static ApplicationContext context; /** * 寫庫數據源 */ @Autowired private DataSource dataSource; /** * 讀數據源數量 */ @Value("${spring.db.readsize}") private Integer readsize; /** * 數據源路由代理 * * @return */ @Bean public AbstractRoutingDataSource routingDataSouceProxy() { RoutingDataSource proxy = new RoutingDataSource(readsize); Map