MySQL:MyCat中间件实现动态数据源、读写分离,分库分表⼀、MyCat介绍
1.1、什么是MyCat
来⾃阿⾥的⽤于⽀持数据库读写分离、分表分库的分布式中间件。
1.2、MyCat原理
主要是通过对SQL的拦截,然后经过⼀定规则(如分⽚解析、路由分析、读写分离分析、缓存分析等),将SQL发动给后端真实的数据块,并将返回的结果做适当处理返回给客户端。
有点类似于Nginx反向代理,它可以隐藏数据库真实IP地址,可以实现读写分离、分表分库(注意:主从复制是MySQL⾃⼰实现的)
1.3、实际使⽤MyCat写SQL怎么执⾏的
注意:上图的select * 实际是不带条件的 ,如果带条件,分析如下:
(1)查询带分⽚字段,⽐如ID(效率⾼,只会查⼀个表):
如果过是select * from user_info where id=1,1%3=1,1在DB2,则MyCat只会发⼀条指令查⼀个DB2(可在Linux查看mycat.log查看⽇志信息)。
(2)查询⾮分⽚字段,假如三个表都有name=“⼩瑞”(效率低,可能会查多个表):
select * from user_info where name="⼩瑞"
(3)查询分页:MyCat会发送三条语句查3个表,查看⽇志就知道,⽽且返回结果随机的,可能有(3,6)、(1,4)、(2,5),性能不好
select * from user_info limit 0,2;
(4)如果分页加排序:则也是会查三个表,然后返回MyCat,看谁最⼩或者最⼤ 返回两个给客户端,⽐如(1,2)、(4,5)
select * from user_info order by id desc limit 0,2 ;
1.4、什么是读写分离
读写分离就是把数据库的读和写操作分开,以对应不同的数据库服务。主数据库提供写操作,从数据库提供读操作。主数据进⾏写操作后,数据及时同步到所读的数据库,尽可能保证读、写数据库⼀致。
1.5、Linux环境安装MyCat实现读写分离
1、上传安装Mycat-server-1.6.5-release-20180122220033-linux.taroracle数据库怎么查询表
2、解压安装包tar –zxvf
3、配置l 和l
4、客户端连接端⼝号: 8066
配置⽂件介绍:
⽂件说明
5、进⼊bin⽬录
启动MyCat ./mycat start
停⽌MyCat ./mycat stop
6、查看/usr/local/mycat/logs wrapper.log⽇志如果是为successfully 则启动成功
关闭防⽕墙:systemctl stop firewalld.service
只可读的账号      user  user  端⼝号8066
可读可写的账号    root  123456  端⼝号8066
⼆、S pringBoot项⽬整合动态数据源
动态数据源与多数数据源区别:
多数据源---以分包形式
动态数据在jvm进⾏不断地进⾏切换
2.1、配置多个数据源⽅式:
配置多个数据源,根据业务需求访问不同的额数据,指定对应的策略:增加、删除、修改操作访问对应数据,查询⼜访问另外数据,不同的数据库做好数据⼀致性处理。
2.2、动态切换数据源⽅式:
根据配置⽂件业务动态切换访问的数据库,此⽅案通过SpringAOP实现,使⽤AspactJ来实现动态织⼊,
通过编程继承实现Spring中
的AbstractRoutingDataSource,来实现数据库访问的动态切换。
原理:
(1)⾸先在项⽬中,有两个数据源,分别是读数据源和写数据源;
(2)使⽤AOP技术拦截业务逻辑层⽅法的前缀,如前缀为select、get、find等,判断⽅法是否做读或者写操作;
(3)如果写操作的时候,传递⼀个key给RoutingDataSource指明使⽤写的数据源。
使⽤AOP技术判断业务逻辑层的⽅法,判断⽅法的前缀是否为读或者写的操作。
(4)如果为写操作的时候,会给RountingDataSource传递key为updateDataSource 原理图:
步骤:
(1)创建读和写数据源
(2)读和写数据源注册到RoutingDataSource
(3)使⽤AOP技术拦截业务层⽅法,判断⽅法的前缀是否需要做读或者写
Maven依赖信息:
spring:
datasource:
###可读数据源
select:
jdbc-url: jdbc:mysql://192.168.212.204:8066/TESTDB1
driver-class-name: sql.jdbc.Driver
username: user
password: user
####可写数据源
update:
jdbc-url: jdbc:mysql://192.168.212.204:8066/TESTDB1
driver-class-name: sql.jdbc.Driver
username: user
password: user
type: com.alibaba.druid.pool.DruidDataSource
DataSourceContextHolder:
@Component
@Lazy(false)
public class DataSourceContextHolder {
// 采⽤ThreadLocal 保存本地多数据源
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源类型
public static void setDbType(String dbType) {
contextHolder.set(dbType);
}
public static String getDbType() {
();
}
public static void clearDbType() {
}
}
DataSourceConfig:
@Configuration
public class DataSourceConfig {
// 创建可读数据源
@Bean(name = "selectDataSource")
@ConfigurationProperties(prefix = "spring.datasource.select") // application.properteis中对应属性的前缀 public DataSource dataSource1() {
ate().build();
}
// 创建可写数据源
@Bean(name = "updateDataSource")
@ConfigurationProperties(prefix = "spring.datasource.update") // application.properteis中对应属性的前缀 public DataSource dataSource2() {
ate().build();
}
}
DynamicDataSource:
//在Spring 2.0.1中引⼊了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运⾏时, 根据某种key值来动态切换到真正的DataSource上。
@Component
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {
@Autowired
@Qualifier("selectDataSource")
private DataSource selectDataSource;
@Autowired
@Qualifier("updateDataSource")
private DataSource updateDataSource;
/**
* 这个是主要的⽅法,返回的是⽣效的数据源名称
*/
@Override
protected Object determineCurrentLookupKey() {
System.out.println("DataSourceContextHolder:::" + DbType());
DbType();
}
/**
* 配置数据源信息
*/
@Override
public void afterPropertiesSet() {
Map<Object, Object> map = new HashMap<>();
map.put("selectDataSource", selectDataSource);
map.put("updateDataSource", updateDataSource);
setTargetDataSources(map);
setDefaultTargetDataSource(updateDataSource);
super.afterPropertiesSet();
}
}
SwitchDataSourceAOP:
@Aspect
@Component
@Lazy(false)
@Order(0) // Order设定AOP执⾏顺序使之在数据库事务上先执⾏
public class SwitchDataSourceAOP {
// 这⾥切到你的⽅法⽬录
@Before("execution(* com.mayikt.service.*.*(..))")
public void process(JoinPoint joinPoint) {
String methodName = Signature().getName();
if (methodName.startsWith("get") || methodName.startsWith("count") || methodName.startsWith("find")
|| methodName.startsWith("list") || methodName.startsWith("select") || methodName.startsWith("check")) {
DataSourceContextHolder.setDbType("selectDataSource");
} else {
// 切换dataSource
DataSourceContextHolder.setDbType("updateDataSource");
}
}
}
2.3、MyCat实现读写分离⽅式: