MySQL读写分离
先搭建主从复制 主从复制
创建SpringBoot程序
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
创建实体类
@TableName("t_user")
@Data
public class TUser {
@TableId(type = IdType.AUTO)
private Long id;
private String uname;
}
创建Mapper
@Mapper
public interface TUserMapper extends BaseMapper<TUser> {
}
配置读写分离
模式配置
官方配置项说明 模式配置官方
mode (?): # 不配置则默认内存模式
type: # 运行模式类型。可选配置:Memory、Standalone、Cluster
repository (?): # 久化仓库配置。Memory 类型无需持久化
overwrite: # 是否使用本地配置覆盖持久化配置
- 注释(?): 表示这是一个可选项,可以选择性地配置。
数据源配置
dataSources: # 数据源配置,可配置多个 <data-source-name>
<data-source-name>: # 数据源名称
dataSourceClassName: # 数据源完整类名
driverClassName: # 数据库驱动类名,以数据库连接池自身配置为准
jdbcUrl: # 数据库 URL 连接,以数据库连接池自身配置为准
username: # 数据库用户名,以数据库连接池自身配置为准
password: # 数据库密码,以数据库连接池自身配置为准
# ... 数据库连接池的其它属性
读写分离
rules:
- !READWRITE_SPLITTING
dataSources:
<data-source-name> (+): # 读写分离逻辑数据源名称
type: # 读写分离类型,比如:Static,Dynamic
props:
auto-aware-data-source-name: # 自动发现数据源名称(与数据库发现配合使用)
write-data-source-name: # 写库数据源名称
read-data-source-names: # 读库数据源名称,多个从数据源用逗号分隔
loadBalancerName: # 负载均衡算法名称
# 负载均衡算法配置
loadBalancers:
<load-balancer-name> (+): # 负载均衡算法名称
type: # 负载均衡算法类型
props: # 负载均衡算法属性配置
# ...
(+): # 读写分离逻辑数据源名称 详解
下面的实际使用中我们使用myds作为读写分离 逻辑 数据源名称
myds逻辑数据源名称实际有三个数据源master、slave1、slave2。 由他们三个组成了一个大的逻辑数据源就是myds 。
当程序的架构变得更大时。 myds也可能会变成庞大数据源的其中一个节点。
auto-aware-data-source-name:# 自动发现数据源名称(与数据库发现配合使用) 当 type: # 读写分离类型,为Dynamic 才配置
(+) 表示可以配置多个
实际配置
# 应用名称
spring:
application:
name: sharding-jdbc-demo
profiles:
active: dev
shardingsphere:
mode:
type: Memory
props:
sql-show: true
# 配置真实数据源
datasource:
names: master,slave1,slave2
# 配置第 1 个数据源
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3506/db_user?allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 123456
# 配置第 2 个数据源
slave1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3507/db_user?allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 123456
# 配置第 3 个数据源
slave2:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3508/db_user?allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 123456
# 读写分离配置
rules:
readwrite-splitting:
data-sources:
myds:
type: Static
props:
write-data-source-name: master
read-data-source-names: slave1,slave2
sql-show: true
load-balancer-name: alg_round
# 负载均衡算法配置
load-balancers:
alg_round:
# 轮询算法
type: ROUND_ROBIN
负载均衡算法配置可根据官网进行实际配置 负载均衡算法
读写分离测试
@Test
public void contextLoads(){
TUser user = new TUser();
user.setUname("2df");
userMapper.insert(user);
}
Logic SQL: INSERT INTO t_user ( uname ) VALUES ( ? )
Logic SQL 就是指逻辑SQL
向逻辑数据源的t_user 逻辑表中插入数据,
具体t_user 表是哪个数据源 是master还是slave1,slave2呢?应用程序并不关心,因为把他交给了shardingjdbc 由shardingjdbc根据配置文件自动的判断,哪一台是写服务器,哪一台是从服务器.shardingjdbc会自动找到写数据源,从而向写数据源写入数据
Actual SQL: master ::: INSERT INTO t_user ( uname ) VALUES ( ? ) ::: [2df]
Actual SQL实际SQL指出了master数据源
事务测试
@Test
public void testTrans(){
TUser user = new TUser();
user.setUname("韩跑跑");
userMapper.insert(user);
List<TUser> users = userMapper.selectList(null);
}
实际SQL日志
- 先向逻辑数据源插入数据
- 实际上是主机插入数据
- 查询是逻辑数据源
- 实际上是slave1查询数据
添加事务@Transactional//开启事务
/**
* 事务测试
*/
@Transactional//开启事务
@Test
public void testTrans(){
TUser user = new TUser();
user.setUname("添加事务测试");
userMapper.insert(user);
List<TUser> users = userMapper.selectList(null);
}
查询和插入均在master执行。这是问什么呢?
- 不添加@Transactional:insert对主库操作,select对从库操作
- 添加@Transactional:则insert和select均对主库操作
其中第五项Rolled back transaction for test: ● 事务回滚 在JUnit测试环境下的**@Transactional**注解,默认情况下就会对事务进行回滚(即使在没加注解@Rollback,也会对事务回滚)
● 测试环境下查看数据库,并没有数据插入 。
负载均衡
/**
* 读数据测试
*/
@Test
public void testSelectAll(){
List<TUser> users1 = userMapper.selectList(null);
List<TUser> users2 = userMapper.selectList(null);
List<TUser> users3 = userMapper.selectList(null);
List<TUser> users4 = userMapper.selectList(null);
users1.forEach(System.out::println);
}
看出是slave1,slave2是轮询查询的。因为我们现在使用的就是轮询的算法。
切换随机算法
# 负载均衡算法配置
load-balancers:
alg_round:
# 轮询算法
type: ROUND_ROBIN
alg_random:
# 随机访问算法
type: RANDOM
更改引用的算法名称
load-balancer-name: alg_round
改为对相应的算法名称
load-balancer-name: alg_random
执行testSelectAll
可以看出执行了三个slave1 一个slave2
切换权重算法,需要配置 权重值
# 负载均衡算法配置
load-balancers:
alg_round:
# 轮询算法
type: ROUND_ROBIN
alg_random:
# 随机访问算法
type: RANDOM
alg_weight:
# 权重访问算法
type: WEIGHT
props:
slave1 : 1
slave2 : 2
引用的算法名称
load-balancer-name: alg_random
改为对相应的算法名称
load-balancer-name: alg_weight
执行testSelectAll,
/**
* 读数据测试
*/
@Test
public void testSelectAll(){
List<TUser> users1 = userMapper.selectList(null);
List<TUser> users2 = userMapper.selectList(null);
List<TUser> users3 = userMapper.selectList(null);
List<TUser> users4 = userMapper.selectList(null);
List<TUser> users5 = userMapper.selectList(null);
List<TUser> users6 = userMapper.selectList(null);
users1.forEach(System.out::println);
}
可以看出六次查询有五次使用了slave2
读写分离全部配置
# 应用名称
spring:
application:
name: sharding-jdbc-demo
profiles:
active: dev
shardingsphere:
mode:
type: Memory
props:
sql-show: true
# 配置真实数据源
datasource:
names: master,slave1,slave2
# 配置第 1 个数据源
master:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3506/db_user?allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 123456
# 配置第 2 个数据源
slave1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3507/db_user?allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 123456
# 配置第 3 个数据源
slave2:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3508/db_user?allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 123456
# 读写分离配置
rules:
readwrite-splitting:
data-sources:
# 读写分离逻辑数据源名称
myds:
# 读写分离类型,比如:Static,Dynamic
type: Static
props:
# 写库数据源名称
write-data-source-name: master
# 读库数据源名称,多个从数据源用逗号分隔
read-data-source-names: slave1,slave2
sql-show: true
#负载均衡算法名称
load-balancer-name: alg_random
# 负载均衡算法配置
load-balancers:
alg_round:
# 轮询算法
type: ROUND_ROBIN
alg_random:
# 随机访问算法
type: RANDOM
alg_weight:
# 权重访问算法
type: WEIGHT
props:
slave1 : 1
slave2 : 2