【背景】:最近生產環境上一個產品經過大半年運行,報表查詢的速度變慢了,為了避免對寫操作造成影響,決定進行讀寫分離升級,
報表查詢和對主從同步延遲無特殊要求的查詢走從庫,不適用從庫主從同步延遲的查詢繼續走主庫。
【選型】:對比了幾個主流的讀寫分離方案,決定選用shardingjdbc進行讀寫分離。主要考慮其已經被Apache收錄,開源性好,並且對現有業務代碼的侵入性較小,既有程序改動量較小。
【思路】:使用shardingjdbc進行讀寫分離。使用注解+Aspect的方法支持主庫查詢,並且查詢完成后取消強制路由,后續查詢繼續到從庫。
下面對開發時的幾個關鍵步驟進行一下記錄,供大家參看,自己將來回顧時也可以利用利用。
1)引入shardingspere,下面是pom的關鍵依賴:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.30</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>io.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>3.1.0</version> </dependency>
2)通過druid數據庫連接池管理多個數據源,配置主從數據庫。作為實驗,1主2從都在本地機器上。yml的關鍵配置:
sharding: jdbc: dataSource: names: masterdb,slavedb01,slavedb02 masterdb: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mcspcsales?useUnicode=true&character_set_server=utf8mb4&useSSL=false&serverTimezone=GMT%2B8 username: root password: root maxPoolSize: 20 slavedb01: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mcspcsales1?useUnicode=true&character_set_server=utf8mb4&useSSL=false&serverTimezone=GMT%2B8 username: root password: root maxPoolSize: 20 slavedb02: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mcspcsales2?useUnicode=true&character_set_server=utf8mb4&useSSL=false&serverTimezone=GMT%2B8 username: root password: root maxPoolSize: 20 config: masterslave: load-balance-algorithm-type: round_robin name: mcspcsalesMaster1Slave2 master-data-source-name: masterdb slave-data-source-names: slavedb01,slavedb02 props: sql: show: true
3)自定義讀主庫的注解類:
package com.chong.common.annotation; import java.lang.annotation.*; @Target(value = {ElementType.METHOD}) @Retention(value = RetentionPolicy.RUNTIME) @Documented public @interface MasterSelect { }
4)aspect類,有注解MasterSelect方法,在方法執行前設置主庫查詢設置 HintManager.getInstance().setMasterRouteOnly();
業務方法執行后執行HintmanagerHolder.clear(),取消對主庫查詢的強制路由。
@Aspect @Component public class MasterSelectAspect { @Pointcut(value = "execution(* com.chong.mcspcreadwritesplit.service.*.*(..))") public void pointcutOnService() { } @Around(value = "pointcutOnService()") public Object setMasterSelect(ProceedingJoinPoint joinPoint) throws Throwable { Object object = null; Throwable currentThrowable = null; MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); if (methodSignature.getMethod().isAnnotationPresent(MasterSelect.class)) { HintManager.getInstance().setMasterRouteOnly(); } try { object = joinPoint.proceed(); } catch (Throwable throwable) { currentThrowable = throwable; } finally { HintManagerHolder.clear(); if (currentThrowable != null) { throw currentThrowable; } } return object; } }
5)service使用,對於需要主庫查詢的語句,方法上增加注解MasterSelect。
@Service public class MemberRepositoryService { @Autowired private MemberRepository memberRepository; @Autowired private IdWorker idWorker; @MasterSelect public List<BizMember> getMemberList() { List<BizMember> list = null; list = memberRepository.findAll(); return list; } // 下略 }
6)下面是啟動類,有個點需要注意,因為sharding-jdbc-spring-boot-starter和druid-spring-boot-starter都去進行datasource的自動配置,所以啟動類中會提示重復的bean定義。
在啟動類里,把DruidDataSourceAutoConfiure的自動配置去掉,就能正常啟動了。
@SpringBootApplication(exclude={DruidDataSourceAutoConfigure.class}) @EnableDiscoveryClient @EnableConfigurationProperties @EnableTransactionManagement @ComponentScan(basePackages = {"com.chong.common","com.chong.mcspcreadwritesplit"}) public class McspcreadwritesplitApplication { public static void main(String[] args) { SpringApplication.run(McspcreadwritesplitApplication.class, args); } }
Controller就不貼了。有上面這幾個核心部分代碼,就能支持讀寫分離了。親測可用。^^