前言 |
首先爲什麼要做讀寫分離呢,大多數項目都是讀的操作的次數遠遠要比寫操作的次數多,所以讓MySQL的主服務器做寫操作,從服務器做讀操作,下面詳細的說一下如何利用MyCat做讀寫分離,至於MySQL的安裝和搭建MySQL的主從複製請看我另一篇博客 Linux上MySQL安裝和搭建MySQL的主從複製和讀寫分離
MyCat的安裝及啓動 |
環境裝備
虛擬機:192.168.50.152(MyCat)
虛擬機:192.168.50.150(MySQl主)
虛擬機:192.168.50.151(MySQl從)
- 我們從網上下載一個MyCat的壓縮包,放到了/usr/local這個路徑,並解壓,解壓完之後有一個mycat的文件夾
- 我們進入mycat的這個文件夾,編輯conf文件夾下的schema.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- mycat_testdb是mycat的邏輯庫名稱 -->
<schema name="mycat_testdb" checkSQLschema="true" sqlMaxLimit="100" >
</schema>
<!-- database 是MySQL數據庫的庫名 -->
<dataNode name="dn1" dataHost="localhost1" database="test" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- 可以配置多個主從 -->
<writeHost host="hostM1" url="192.168.50.150:3306" user="root" password="root">
<!-- 可以配置多個從庫 -->
<readHost host="hostS2" url="192.168.50.151:3306" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>
- 啓動MyCat,進入MyCat下的bin目錄,執行如下命令
./mycat start
- 查看是否啓動成功,查看MyCat下的logs文件夾下的wrapper.log,看到successfully就算啓動成功
配置SpringBoot項目 |
- 新建yml文件,配置讀和寫的JDBC連接
spring:
datasource:
###可讀數據源
select:
jdbc-url: jdbc:mysql://192.168.50.152:8066/mycat_testdb
driver-class-name: com.mysql.jdbc.Driver
username: user
password: user
####可寫數據源
update:
jdbc-url: jdbc:mysql://192.168.50.152:8066/mycat_testdb
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
- 三個配置文件
@Configuration
public class DataSourceConfig {
// 創建可讀數據源
@Bean(name = "selectDataSource")
@ConfigurationProperties(prefix = "spring.datasource.select") // application.properteis中對應屬性的前綴
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
// 創建可寫數據源
@Bean(name = "updateDataSource")
@ConfigurationProperties(prefix = "spring.datasource.update") // application.properteis中對應屬性的前綴
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
}
@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() {
return contextHolder.get();
}
public static void clearDbType() {
contextHolder.remove();
}
}
@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:::" + DataSourceContextHolder.getDbType());
return DataSourceContextHolder.getDbType();
}
/**
* 配置數據源信息
*/
@Override
public void afterPropertiesSet() {
Map<Object, Object> map = new HashMap<>();
map.put("selectDataSource", selectDataSource);
map.put("updateDataSource", updateDataSource);
setTargetDataSources(map);
setDefaultTargetDataSource(updateDataSource);
super.afterPropertiesSet();
}
}
- 配置aop
// 使用AOP動態切換不同的數據源
@Aspect
@Component
@Lazy(false)
@Order(0) // Order設定AOP執行順序 使之在數據庫事務上先執行
public class SwitchDataSourceAOP {
// 這裏切到你的方法目錄
@Before("execution(* com.example.demo.service.*.*(..))")
public void process(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().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");
}
}
}
- controller、entity、mapper、service的編寫
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/findUser")
public List<UserEntity> findUser() {
return userService.findUser();
}
@RequestMapping("/insertUser")
public List<UserEntity> insertUser(String userName) {
return userService.insertUser(userName);
}
}
public class UserEntity {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
public interface UserMapper {
@Select("SELECT * FROM test ")
public List<UserEntity> findUser();
@Select("insert into test values (#{userName}); ")
public List<UserEntity> insertUser(@Param("userName") String userName);
}
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<UserEntity> findUser() {
return userMapper.findUser();
}
public List<UserEntity> insertUser(String userName) {
return userMapper.insertUser(userName);
}
}
- 啓動項目,進行測試
我們插入一條userName爲123456的數據
IDEA控制檯打出來的日誌爲updateDataSource數據源
然後我們查詢一下剛纔插入的數據
IDEA控制檯打出來的日誌爲selectDataSource數據源
- 至此基於SpringBootl利用MyCat實現對MySQL的讀寫分離就搭建完畢了
總結 |
通過這次顆粒歸倉,發現自己對這個東西還是不熟練,還是要多多學習,積累自己!