赞
踩
因为在项目开发需求,本地无法直接连接服务器MYSQL主从,考虑在开发测试时,可以测试方便发现基本问题。考虑在本地虚拟机中搭建,模拟测试环境及线上环境,故选用虚拟机的docker来搭建。选择搭建一主两从
读写分离,顾名思义就是读和写分开,更具体来说,就是:
使用读写分离的根本目的就是为了提高并发性能,如果读写都在同一台MySQL
上实现,相信会不如一台MySQL
写,另外两台MySQL
读这样的配置性能高。另一方面,在很多时候都是读操作的请求要远远高于写操作,这样就显得读写分离非常有必要了。
主从复制,顾名思义就是把主库的数据复制到从库中,因为读写分离之后,写操作都在主库进行,但是读操作是在从库进行的,也就是说,主库上的数据如果不能复制到从库中,那么从库就不会读到主库中的数据。严格意义上说,读写分离并不要求主从复制,只需要在主库写从库读即可,但是如果没有了主从复制,读写分离将失去了它的意义。因此读写分离通常与主从复制配合使用。
因为本示例使用的是MySQL
,这里就说一下MySQL
主从复制的原理,如下图所示:
工作流程如下:
binlog
I/O
线程读取主库的binlog
,并拷贝到从库本地的binlog
中binlog
被SQL
线程读取,执行其中的内容并同步到从库中基础环境安装:
version: '3.6' services: mysql-master: image: mysql:5.7 container_name: mysql-master restart: always privileged: true environment: MYSQL_ROOT_PASSWORD: 123456 TZ: Asia/Shanghai command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --explicit_defaults_for_timestamp=true --lower_case_table_names=1 --max_allowed_packet=128M; ports: - 3316:3306 volumes: - ./config/master.cnf:/etc/mysql/my.cnf mysql-slave1: image: mysql:5.7 container_name: mysql-slave1 restart: always privileged: true environment: MYSQL_ROOT_PASSWORD: 123456 TZ: Asia/Shanghai command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --explicit_defaults_for_timestamp=true --lower_case_table_names=1 --max_allowed_packet=128M; ports: - 3317:3306 volumes: - ./config/slave1.cnf:/etc/mysql/my.cnf mysql-slave2: image: mysql:5.7 container_name: mysql-slave2 restart: always privileged: true environment: MYSQL_ROOT_PASSWORD: 123456 TZ: Asia/Shanghai command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --explicit_defaults_for_timestamp=true --lower_case_table_names=1 --max_allowed_packet=128M; ports: - 3318:3306 volumes: - ./config/slave2.cnf:/etc/mysql/my.cnf
[mysqld] pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock datadir = /var/lib/mysql #log-error = /var/log/mysql/error.log # By default we only accept connections from localhost #bind-address = 127.0.0.1 # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 port = 3306 #全局唯一,取值[1,2^32-1],默认为1 server-id = 1 log-bin = master-bin #只保留7天的二进制日志,以防磁盘被日志占满(可选) expire-logs-days = 7 #不备份的数据库 (可选) binlog-ignore-db=information_schema binlog-ignore-db=performation_schema binlog-ignore-db=sys
[mysqld] pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock datadir = /var/lib/mysql #log-error = /var/log/mysql/error.log # By default we only accept connections from localhost #bind-address = 127.0.0.1 # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 port = 3306 #全局唯一,取值[1,2^32-1],默认为1 server-id = 2 #只保留7天的二进制日志,以防磁盘被日志占满(可选) expire-logs-days = 7 #不备份的数据库 (可选) binlog-ignore-db=information_schema binlog-ignore-db=performation_schema binlog-ignore-db=sys ## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用 log-bin=slave1-bin ## relay_log配置中继日志 relay_log=mysql-relay-bin read_only=1 ## 设置为只读,该项如果不设置,表示slave可读可写
[mysqld] pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock datadir = /var/lib/mysql #log-error = /var/log/mysql/error.log # By default we only accept connections from localhost #bind-address = 127.0.0.1 # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 port = 3306 #全局唯一,取值[1,2^32-1],默认为1 server-id = 3 #只保留7天的二进制日志,以防磁盘被日志占满(可选) expire-logs-days = 7 #不备份的数据库 (可选) binlog-ignore-db=information_schema binlog-ignore-db=performation_schema binlog-ignore-db=sys ## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用 log-bin=slave2-bin ## relay_log配置中继日志 relay_log=mysql-relay-bin read_only=1 ## 设置为只读,该项如果不设置,表示slave可读可写
进入mysql目录,执行启动命令启动docker容器
docker-compose up -d
docker-compose ps
docker-compose down
1、进行master容器,连接mysql
docker exec -it mysql-master bash
muysql -r root -p 123456
2、查看master status
show master status
+-------------------+----------+--------------+--------------------------------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+--------------------------------------------+-------------------+
| master-bin.000003 | 643 | | information_schema,performation_schema,sys | |
+-------------------+----------+--------------+--------------------------------------------+-------------------+
记录下file和position的值
3、分别进行slave1和slave2容器并连接mysql
4、然后执行1):
change master to
master_host='192.168.232.130',--master的IP
master_user='root',--连接master的用户名
master_log_file='master-bin.000003',--第2步骤查询到的结果file字段的值
master_log_pos=154,--第2步骤查询到的结果position字段的值
master_port=3316,--master的端口
master_password='123456';--连接master的密码
执行2 :开启从节点start slave
;
执行3:显示从节点信息show slave status
;
在主节点master上添加数据库、表以及数据看从节点是否同步。
搭建MYSQL主从只是为了使用,重点是在于怎么做读写分离。
在网上也看到了很多种做读写分离的方案,综合考虑,这里我选择了
shardingsphere
来做读写分离。
Maven版本:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
步骤参考:MybatisX插件使用方式
server: port: 8084 spring: shardingsphere: datasource: names: master,slave1,slave2 master: url: jdbc:mysql://192.168.232.130:3316/test?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 slave1: url: jdbc:mysql://192.168.232.130:3317/test?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 slave2: url: jdbc:mysql://192.168.232.130:3318/test?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 rules: sharding: readwrite-splitting: load-balancers: round_robin: type: ROUND_ROBIN data-sources: read_write_db: type: Static props: write-data-source-name: master read-data-source-names: slave1,slave2 load-balancer-name: round_robin props: sql-show: true
MysqlInit.java
/** * 项目初始化执行 */ @Slf4j @Component public class MysqlInit { @Autowired private OperationService operationService; @PostConstruct public void init(){ for (int i = 0; i < 10000; i++) { operationService.save(i); if(i>100){ operationService.query(i); } } } }
OperationService.java
/** * 数据库操作 */ @Slf4j @Service public class OperationService { @Autowired private UserService userService; private static Random random = new Random(); /** * 生成随机内容添加数据 * @param i */ @Async("mysqlThreadPool") public void save(int i) { String sex="男"; if(i%2==0){ sex= "女"; } User user = new User(null, UUID.randomUUID().toString(),sex,random.nextInt(120)); boolean save = userService.save(user); if(save){ log.info("插入数据成功:{}",user); }else{ log.error("插入数据失败"); } } @Async("mysqlThreadPool") public void query(int i) { User user = userService.getById(random.nextInt(i)); log.info("随机查询数据为:{}",user); } }
控制台显示日志:
2022-11-08 10:26:22.082 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty) 2022-11-08 10:26:22.082 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : Actual SQL: master ::: INSERT INTO user ( name,sex,age ) VALUES ( ?,?,? ) ::: [489d32c0-8689-4e05-b68e-96b046ae5046, 女, 22] 2022-11-08 10:26:22.084 INFO 28064 --- [ restartedMain] c.mengl.mysql.service.OperationService : 插入数据成功:User [Hash = 1085628063, id=7396, name=489d32c0-8689-4e05-b68e-96b046ae5046, sex=女, age=22, serialVersionUID=1] 2022-11-08 10:26:22.085 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : Logic SQL: SELECT id,name,sex,age FROM user WHERE id=? 2022-11-08 10:26:22.085 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty) 2022-11-08 10:26:22.085 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : Actual SQL: slave2 ::: SELECT id,name,sex,age FROM user WHERE id=? ::: [5072] 2022-11-08 10:26:22.086 INFO 28064 --- [ restartedMain] c.mengl.mysql.service.OperationService : 随机查询数据为:User [Hash = 1109246400, id=5072, name=b740fae3-0aae-4542-822d-8dd6c6d274ef, sex=女, age=13, serialVersionUID=1] 2022-11-08 10:26:22.086 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : Logic SQL: INSERT INTO user ( name,sex,age ) VALUES ( ?,?,? ) 2022-11-08 10:26:22.086 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty) 2022-11-08 10:26:22.086 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : Actual SQL: master ::: INSERT INTO user ( name,sex,age ) VALUES ( ?,?,? ) ::: [52bebb40-10c9-4117-b2fa-ccaf6104c759, 男, 73] 2022-11-08 10:26:22.089 INFO 28064 --- [ restartedMain] c.mengl.mysql.service.OperationService : 插入数据成功:User [Hash = -74377815, id=7397, name=52bebb40-10c9-4117-b2fa-ccaf6104c759, sex=男, age=73, serialVersionUID=1] 2022-11-08 10:26:22.089 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : Logic SQL: SELECT id,name,sex,age FROM user WHERE id=? 2022-11-08 10:26:22.089 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty) 2022-11-08 10:26:22.089 INFO 28064 --- [ restartedMain] ShardingSphere-SQL : Actual SQL: slave1 ::: SELECT id,name,sex,age FROM user WHERE id=? ::: [2592] 2022-11-08 10:26:22.091 INFO 28064 --- [ restartedMain] c.mengl.mysql.service.OperationService : 随机查询数据为:User [Hash = 1138329258, id=2592, name=8998491e-b737-475b-888f-39dd3b0ae65a, sex=女, age=55, serialVersionUID=1]
可以清晰的看到插入数据使用的是master节点,查询数据使用的是slave1、slave2节点切换。
至此,完成。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。