1.搭建环境
 
1.1 搭建Mysql主从服务
 
1.1.1 下载Mysql镜像
 
docker pull mysql
:5.7
 
1.1.2 创建Mysql挂载目录
 
mkdir 
-p 
/opt
/mysql
/master
/{conf
,data
,logs
}
 
mkdir 
-p 
/opt
/mysql
/slave
/{conf
,data
,logs
}
 
1.1.3 进入conf目录中编辑my.cnf
 
主配置文件
 
[client
]     
port
=3306   
default-character
-set
=utf8
[mysql
]   
default-character
-set
=utf8
[mysqld
]
character_set_server
=utf8
sql_mode
=NO_ENGINE_SUBSTITUTION
,STRICT_TRANS_TABLES
# 配置大小写不敏感
lower_case_table_names
=1
server_id
=66  ###服务器id
log
-bin
=mysql
-bin   ###开启日志文件
 
从配置文件
 
port
=3306   
default-character
-set
=utf8
[mysql
]   
default-character
-set
=utf8
[mysqld
]
character_set_server
=utf8
sql_mode
=NO_ENGINE_SUBSTITUTION
,STRICT_TRANS_TABLES
# 配置大小写不敏感
lower_case_table_names
=1
server_id
=88  ###服务器id 
log
-bin
=mysql
-bin   ###开启日志文件
binlog_do_db
=test
 
1.1.4 运行Mysql容器
 
docker run 
-it 
--name mysql
-master 
-p 
3308:3306 -e MYSQL_ROOT_PASSWORD
=root 
--privileged
=true -v 
/opt
/mysql
/master
/conf
/my
.cnf
:/etc
/mysql
/my
.cnf 
-v 
/opt
/mysql
/master
/data
:/var
/lib
/mysql 
-v 
/opt
/mysql
/master
/logs
:/var
/log
/mysql 
-d 
--restart
=always mysql
:5.7 
 
docker run 
-it 
--name mysql
-slave 
-p 
3309:3306 -e MYSQL_ROOT_PASSWORD
=root 
--privileged
=true -v 
/opt
/mysql
/slave
/conf
/my
.cnf
:/etc
/mysql
/my
.cnf 
-v 
/opt
/mysql
/slave
/data
:/var
/lib
/mysql 
-v 
/opt
/mysql
/slave
/logs
:/var
/log
/mysql 
-d 
--restart
=always mysql
:5.7 
 
1.1.5 查看容器
 
 
1.1.6 连接到主服务器
 
查询主对应配置文件中的server_id,查询到了说明已经以上步骤ok。
 
show variables like 
'%server_id%';
 
 查询主状态信息
 
show master status
;
 
 
1.1.6 连接从服务器
 
同步主服务器配置
 
change master to master_host
='192.168.0.171',master_user
='root',master_password
='root',
         master_log_file
='mysql-bin.000005',master_log_pos
=1188;
 
开始同步
 
start slave
;
 
检查从服务器复制功能状态
 
SHOW SLAVE STATUS
;
 
 Slave_IO_Running 、Slave_SQL_Running 都为Yes说明配置成功,验证效果就不截图了。
 
1.2 搭建Mycat环境
 
参考博客:https://blog.csdn.net/weixin_39781526/article/details/84867397
 
1.2.1 获取资源
 
#先获取mycat包
wget Mycat
-server
-1.6.5-release
-20180122220033-linux
.tar
.gz
;
 
1.2.2 编写Dockerfile
 
FROM java
:8
USER root
COPY Mycat
-server
-1.6.5-release
-20180122220033-linux
.tar
.gz 
/
RUN tar 
-zxf 
/Mycat
-server
-1.6.5-release
-20180122220033-linux
.tar
.gz
ENV MYCAT_HOME
=/mycat
ENV PATH
=$PATH
:$
{MYCAT_HOME
}/bin
WORKDIR $MYCAT_HOME
/bin
RUN chmod u
+x 
./mycat
EXPOSE 
8066 9066
CMD 
["./mycat", "console"]
 
1.2.3 获取配置文件
 
tar 
-zxvf Mycat
-server
-1.6.5-release
-20180122220033-linux
.tar
.gz 
mv mycat
/conf
/ conf
 
1.2.4 修改mycat配置文件
 
真正需要修改的只有三个文件,具体官方文档写的很详细; (1):schema.xml ,(2):server.xml,(3):rule.xml; 现在搭建的是Mysql读写分离,修改schema.xml、server.xm即可。rule.xml用于分表分库配置文件。
 
<?xml version
="1.0"?>
<!DOCTYPE mycat
:schema SYSTEM 
"schema.dtd">
<mycat
:schema xmlns
:mycat
="http://io.mycat/">
    <!-- TESTDB1 是mycat的逻辑库名称,链接需要用的 
-->
    <schema name
="mycat_testdb" checkSQLschema
="false" sqlMaxLimit
="100" dataNode
="dn1"></schema
>
        <!-- database 是MySQL数据库的库名 
-->
    <dataNode name
="dn1" dataHost
="localhost1" database
="test" />
    <!--
    dataNode节点中各属性说明:
    name:指定逻辑数据节点名称;
    dataHost:指定逻辑数据节点物理主机节点名称;
    database:指定物理主机节点上。如果一个节点上有多个库,可使用表达式db$
0-99,     表示指定
0-99这
100个数据库;
    dataHost 节点中各属性说明:
        name:物理主机节点名称;
        maxCon:指定物理主机服务最大支持
1000个连接;
        minCon:指定物理主机服务最小保持
10个连接;
        writeType:指定写入类型;
            
0,只在writeHost节点写入;
            
1,在所有节点都写入。慎重开启,多节点写入顺序为默认写入根据配置顺序,第一个挂掉切换另一个;
        dbType:指定数据库类型;
        dbDriver:指定数据库驱动;
        balance:指定物理主机服务的负载模式。
            
0,不开启读写分离机制;
            
1,全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式
(M1
->S1,M2
->S2,并且M1与 M2互为主备
),正常情况下,M2
,S1
,S2都参与select语句的负载均衡;
            
2,所有的readHost与writeHost都参与select语句的负载均衡,也就是说,当系统的写操作压力不大的情况下,所有主机都可以承担负载均衡;
-->
    <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.0.171:3308"  user
="root" password
="root">
            <!-- 可以配置多个从库 
-->
            <readHost host
="hostS2" url
="192.168.0.171:3309"   user
="root" password
="root" />
        </writeHost
>
    </dataHost
>
</mycat
:schema
>
 
<?xml version
="1.0" encoding
="UTF-8"?>
<!-- - - Licensed under the Apache License
, Version 
2.0 (the 
"License"); 
	- you may not use 
this file except in compliance with the License
. - You 
	may obtain a copy of the License at 
- - http
://www
.apache
.org
/licenses
/LICENSE
-2.0 
	- - Unless required by applicable law or agreed to in writing
, software 
- 
	distributed under the License is distributed on an 
"AS IS" BASIS
, - WITHOUT 
	WARRANTIES OR CONDITIONS OF ANY KIND
, either express or implied
. - See the 
	License 
for the specific language governing permissions and 
- limitations 
	under the License
. -->
<!DOCTYPE mycat
:server SYSTEM 
"server.dtd">
<mycat
:server xmlns
:mycat
="http://io.mycat/">
   
   <!-- 读写都可用的用户 
-->
    <user name
="root" defaultAccount
="true">
        <property name
="password">123456</property
>
        <property name
="schemas">mycat_testdb
</property
>
        <!-- 表级 DML 权限设置 
-->
        <!--        
        <privileges check
="false">
            <schema name
="TESTDB" dml
="0110" >
                <table name
="tb01" dml
="0000"></table
>
                <table name
="tb02" dml
="1111"></table
>
            </schema
>
        </privileges
>       
         -->
    </user
>
    <!-- 只读用户 
-->
    <user name
="user">
        <property name
="password">user
</property
>
        <property name
="schemas">mycat_testdb
</property
>
        <property name
="readOnly">true</property
>
    </user
>
</mycat
:server
>
 
1.2.5 执行Dockerfile
 
docker build 
-t mycat
:1.6.5 . 
 
1.2.6 编写mycat启动脚本
 
docker run 
-d 
--rm  
-p
:8066:8066 --name mycat
-mysql 
-p 
9066:9066 -v $PWD
/logs
:/mycat
/logs 
-v $PWD
/conf
:/mycat
/conf mycat
:1.6.5
 
 
1.2.7 验证效果
 
连接这四个服务  在root服务修改数据,其他三个服务的数据自动同步相应的修改。  在user服务修改数据报错,只允许读数据。 
 
2 SpringBoot整合Mycat实现读写分离
 
2.1 导入Maven依赖
 
<project xmlns
="http://maven.apache.org/POM/4.0.0" xmlns
:xsi
="http://www.w3.org/2001/XMLSchema-instance" xsi
:schemaLocation
="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion
>
  <groupId>com
.wanzh
</groupId
>
  <artifactId>mycat_demo
</artifactId
>
  <version>0.0.1-SNAPSHOT
</version
>
  <parent>
		<groupId>org
.springframework
.boot
</groupId
>
		<artifactId>spring
-boot
-starter
-parent
</artifactId
>
		<version>2.0.4.RELEASE
</version
>
		<relativePath 
/> <!-- lookup parent from repository 
-->
	</parent
>
	<dependencies>
		<dependency>
			<groupId>org
.springframework
.boot
</groupId
>
			<artifactId>spring
-boot
-starter
-aop
</artifactId
>
		</dependency
>
		<dependency>
			<groupId>org
.springframework
.boot
</groupId
>
			<artifactId>spring
-boot
-starter
-web
</artifactId
>
		</dependency
>
		<dependency>
			<groupId>org
.mybatis
.spring
.boot
</groupId
>
			<artifactId>mybatis
-spring
-boot
-starter
</artifactId
>
			<version>1.3.2</version
>
		</dependency
>
		<dependency>
			<groupId>mysql
</groupId
>
			<artifactId>mysql
-connector
-java
</artifactId
>
			<scope>runtime
</scope
>
		</dependency
>
		<dependency>
			<groupId>org
.springframework
.boot
</groupId
>
			<artifactId>spring
-boot
-starter
-test
</artifactId
>
			<scope>test
</scope
>
		</dependency
>
		<dependency>
			<groupId>com
.alibaba
</groupId
>
			<artifactId>druid
</artifactId
>
			<version>1.0.23</version
>
		</dependency
>
	</dependencies
>
	<build>
		<plugins>
			<plugin>
				<groupId>org
.springframework
.boot
</groupId
>
				<artifactId>spring
-boot
-maven
-plugin
</artifactId
>
			</plugin
>
		</plugins
>
	</build
>
</project
>
 
2.2 配置application.yml
 
spring
:
  datasource
:
    ###可读数据源
    select
:
      jdbc
-url
: jdbc
:mysql
://192.168.0.171:8066/mycat_testdb
      driver
-class-name
: com
.mysql
.jdbc
.Driver
      username
: user
      password
: user
    ####可写数据源  
    update
:
      jdbc
-url
: jdbc
:mysql
://192.168.0.171:8066/mycat_testdb
      driver
-class-name
: com
.mysql
.jdbc
.Driver
      username
: root
      password
: 123456
    type
: com
.alibaba
.druid
.pool
.DruidDataSource
 
2.3 配置多数据源环境
 
package com
.wanzh
.config
;
import org
.springframework
.context
.annotation
.Lazy
;
import org
.springframework
.stereotype
.Component
;
@Component
@Lazy(false)
public class DataSourceContextHolder {
	
	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();
	}
}
 
package com
.wanzh
.config
;
import javax
.sql
.DataSource
;
import org
.springframework
.boot
.context
.properties
.ConfigurationProperties
;
import org
.springframework
.boot
.jdbc
.DataSourceBuilder
;
import org
.springframework
.context
.annotation
.Bean
;
import org
.springframework
.context
.annotation
.Configuration
;
@Configuration
public class DataSourceConfig {
	
	@Bean(name 
= "selectDataSource")
	@ConfigurationProperties(prefix 
= "spring.datasource.select") 
	public DataSource 
dataSource1() {
		return DataSourceBuilder
.create().build();
	}
	
	@Bean(name 
= "updateDataSource")
	@ConfigurationProperties(prefix 
= "spring.datasource.update") 
	public DataSource 
dataSource2() {
		return DataSourceBuilder
.create().build();
	}
}
 
package com
.wanzh
.config
;
import java
.util
.HashMap
;
import java
.util
.Map
;
import javax
.sql
.DataSource
;
import org
.springframework
.beans
.factory
.annotation
.Autowired
;
import org
.springframework
.beans
.factory
.annotation
.Qualifier
;
import org
.springframework
.context
.annotation
.Primary
;
import org
.springframework
.jdbc
.datasource
.lookup
.AbstractRoutingDataSource
;
import org
.springframework
.stereotype
.Component
;
@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();
	}
}
 
2.4 AOP拦截动态切换数据源
 
package com
.wanzh
.aop
;
import org
.aspectj
.lang
.JoinPoint
;
import org
.aspectj
.lang
.annotation
.Aspect
;
import org
.aspectj
.lang
.annotation
.Before
;
import org
.springframework
.context
.annotation
.Lazy
;
import org
.springframework
.core
.annotation
.Order
;
import org
.springframework
.stereotype
.Component
;
import com
.wanzh
.config
.DataSourceContextHolder
;
@Aspect
@Component
@Lazy(false)
@Order(0) 
public class SwitchDataSourceAOP {
	
	@Before("execution(* com.wanzh.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 {
			
			DataSourceContextHolder
.setDbType("updateDataSource");
		}
	}
}
 
2.5 整合Mybatis
 
package com
.wanzh
.entity
;
public class User {
	
	private int id
;
	private String name
;
	public int getId() {
		return id
;
	}
	public void setId(int id
) {
		this.id 
= id
;
	}
	public String 
getName() {
		return name
;
	}
	public void setName(String name
) {
		this.name 
= name
;
	}
	
	
}
 
package com
.wanzh
.mapper
;
import java
.util
.List
;
import org
.apache
.ibatis
.annotations
.Param
;
import org
.apache
.ibatis
.annotations
.Select
;
import com
.wanzh
.entity
.User
;
public interface UserMapper {
	@Select("SELECT id,name FROM  sys_user ")
	public List
<User> findUser();
	@Select("insert into sys_user(name) values (#{name}); ")
	public List
<User> insertUser(@Param("name") String name
);
}
 
package com
.wanzh
.service
;
import java
.util
.List
;
import com
.wanzh
.entity
.User
;
public interface UserService {
	public List
<User> findUser();
	public List
<User> insertUser(String name
);
}
 
package com
.wanzh
.service
.impl
;
import java
.util
.List
;
import org
.springframework
.beans
.factory
.annotation
.Autowired
;
import org
.springframework
.stereotype
.Service
;
import com
.wanzh
.entity
.User
;
import com
.wanzh
.mapper
.UserMapper
;
import com
.wanzh
.service
.UserService
;
@Service("userService")
public class UserServiceImpl implements UserService {
	@Autowired
	private UserMapper userMapper
;
	public List
<User> findUser() {
		return userMapper
.findUser();
	}
	public List
<User> insertUser(String name
) {
		return userMapper
.insertUser(name
);
	}
}
 
package com
.wanzh
.controller
;
import java
.util
.List
;
import org
.springframework
.beans
.factory
.annotation
.Autowired
;
import org
.springframework
.web
.bind
.annotation
.RequestMapping
;
import org
.springframework
.web
.bind
.annotation
.RequestMethod
;
import org
.springframework
.web
.bind
.annotation
.RestController
;
import com
.wanzh
.entity
.User
;
import com
.wanzh
.service
.UserService
;
@RestController
public class UserController {
	@Autowired
	private UserService userService
;
	@RequestMapping("/findUser")
	public List
<User> findUser() {
		return userService
.findUser();
	}
	@RequestMapping(value
="/insertUser",method
=RequestMethod
.POST
)
	public List
<User> insertUser(String name
) {
		return userService
.insertUser(name
);
	}
}
 
package com
.wanzh
;
import org
.mybatis
.spring
.annotation
.MapperScan
;
import org
.springframework
.boot
.SpringApplication
;
import org
.springframework
.boot
.autoconfigure
.SpringBootApplication
;
@SpringBootApplication
@MapperScan(basePackages 
= "com.wanzh.mapper")
public class App {
	public static void main(String
[] args
) {
		SpringApplication
.run(App
.class, args
);
	}
}
 
2.6 Postman测试
 
 
 
参考每特教育Mycat课程内容,但是电脑配置有点低,这边就改用docker进行搭建了。