Table of contents
1. MySQL read and write separation principle
2. Build a MySQL read-write separation cluster
1. Download the MyCat installation package
2. Configure schema.xml and server.xml respectively
4. MyCat implements MySQL reading and writing separation
1. When connecting to MySQL, only the url configuration changes
1. MySQL read and write separation principle
In MySQL read-write separation: the master node is specially used for data modification operations ( 如create、update、insert、delete
etc.), writing on the main database, and then the main database 主从复制
synchronizes the data changes binlog
to all slave databases.
Then map all query operations to the slave database, so that 分摊读写的压力
all requests do not need to be concentrated on the master database, so that MySQL's concurrent processing capabilities can be greatly improved.
"Before MySQL 8.0:" This process requires a middleware (such as MyCat
, Sharding-JDBC
etc.) to identify and parse all requests from the client, map all write operations to the master node, and read operations are transferred to the slave database.
"After MySQL 8.0:"MySQL自身已支持读写分离
(Supported 一主多从
reading and writing separation 多主多从
still need to introduce middleware implementation)
"Reading and writing separation of one master and multiple slaves:"
注意:
After MySQL 8.0, 一主多从
the separation of reading and writing does not depend on middleware.
2. Build a MySQL read-write separation cluster
Before MySQL8.0
1. Download the MyCat installation package
Download MyCat: http://dl.mycat.org.cn
2. Configure schema.xml
andserver.xml
I configure the virtual library here to map to the real library meet0and1-schema
on the master and slave nodes.meet0and1
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 定义MyCat的虚拟逻辑库,dataNode:映射真实数据节点 -->
<schema name="meet0and1-schema" checkSQLschema="false" sqlMaxLimit="800" dataNode="meet0and1Node" />
<!-- 定义MyCat的数据节点,name必须和dataNode值一致,dataHost映射真实主机,database映射真实的库 -->
<dataNode name="meet0and1Node" dataHost="meet0and1Host" database="meet0and1" />
<!-- 配置数据主机,name必须和dataHost一致 -->
<dataHost name="meet0and1Host" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="-1" slaveThreshold="100">
<!--心跳检测-->
<heartbeat>select user()</heartbeat>
<!--主节点(写)-->
<writeHost host="hostM1" url="192.168.31.161:3306" user="root" password="123456">
<!--从节点(读)-->
<readHost host="hostS1" url="192.168.31.162:3306" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<!-- 这里配置的都是一些系统属性-->
<system>
<!-- 默认的sql解析方式 -->
<property name="defaultSqlParser">druidparser</property>
<property name="charset">utf8mb4</property>
</system>
<!-- 配置登录mycat的用户信息 -->
<user name="root">
<property name="password">123456</property>
<!-- 该用户可以操作哪个逻辑库 -->
<property name="schemas">meet0and1-schema</property>
</user>
</mycat:server>
3. Start MyCat
In the bin directory of mycat, start MyCat
# 以前台窗口启动
./mycat console
# 以后台守护进程启动
./mycat start
./mycat status
./mycat stop
./mycat restart
4. MyCat implements MySQL reading and writing separation
Modify properties to connect to MySQL configuration
# MyCat的server.xml中配置的用户和密码
spring.datasource.username=root
spring.datasource.password=123456
# MyCat服务的端口8066,meet0and1-schema:MyCat中配置映射到meet0and1的虚拟库
spring.datasource.url=jdbc:mysql://192.168.31.161:8066/meet0and1-schema?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
"Note:" Transactions in Spring support propagation. In order to avoid performing an update operation in a query, MyCat transfers the connection request to the slave database, resulting in inconsistent master-slave data. ThereforeMyCat会开启了事务的连接请求都转到master主库
For example, in the following findAll
method, the query operation should go through the slave library. However, since transactions are enabled on the class, if the transaction propagation attribute on the method is not set to SUPPORTS
, executing this method will go through the master library.
@Service
@Transactional(rollbackFor = Exception.class)
public class SysUserService {
@Autowired
private SysUserDao sysUserDao;
/**
* Propagation.SUPPORTS:支持事务的传播
* 有事务则融入:如其他含有事务的方法调用这个方法时,融入调用方的事务
* 没有则不开启事务:它自身的方法不开启事务,且这里覆盖了类上配置的事务
* (方法上的事务优先级大于类上开启的事务)
*/
@Transactional(propagation = Propagation.SUPPORTS)
public List<SysUser> findAll() {
return sysUserDao.findAll();
}
/**
* 执行写操作,会走master主库
*/
public void save(SysUser sysUser) {
sysUserDao.save(sysUser);
}
}
After MySQL8.0
The separation of reading and writing after MySQL 8.0 一主多从
no longer relies on any middleware
1. When connecting to MySQL, only the url configuration changes
# 语法:默认配置的第一个节点为主(写)库,后面的都为从(读)库
jdbc:mysql:replication://主(写)库,从(读)库1,从(读)库2,从(读)库N/库名
Example:
spring.datasource.url=jdbc:mysql:replication://192.168.31.161:3306,192.168.31.162:3306/meet0and1?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
2. Only the transaction of the display setting method is read-only ( readOnly = true
), and it will go from the slave (read) library when it is executed alone.
Only query operations whose transactions are read-only are executed 从(读)库
!
为保证主从节点数据的一致性
: When other service
methods are called, no matter whether they contain transactions or not, even if they do not contain update operations (insert/update/delete), the following methods will be used 主(写)库
, such as the following
@Service
public class SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
/**
* 这里只有设置事务为只读,且单独执行该方法时才走从(读)库
*/
@Transactional(readOnly = true)
public List<SysUser> findAll(){
return sysUserMapper.findAll();
}
@Transactional
public void hasTranFind(){
// 走主(写)库
findAll().forEach(System.out::println);
}
public void notTranFind(){
// 走主(写)库
findAll().forEach(System.out::println);
}
public void notTranSave(SysUser sysUser){
// 走主(写)库
findAll().forEach(System.out::println);
sysUserMapper.save(sysUser);
}
/**
* 该方法执行会报错:Connection is read-only. Queries leading to data modification are not allowed
* (设置只读的事务方法中,不能有非查询操作)
*/
@Transactional(readOnly = true)
public List<SysUser> findAll2(SysUser sysUser){
sysUserMapper.save(sysUser);
return sysUserMapper.findAll();
}
}
Precautions
The current community and project maintenance of MyCat is not very friendly. Although MySQL supports it after 8.0
一主多从的读写分离
However, in large-scale projects with extremely high concurrency
分库分表
, it is recommended to implement "Sharding-JDBC"读写分离
for functions such as and分布式主键