Mysql 集群技术

1mysql在服务器中的部署方法

1.1在linux下部署mysql

1.1.1安装依赖性

####下载源码安装需要用到的软件
[root@mysql_1 ~]#  yum  install cmake gcc-c++ openssl-devel -y
[root@mysql_1 ~]# yum install ncurses-devel.x86_64   rpcgen.x86_64  -y

1.1.2下载并解压源码包

########导入libtirpc-devel-0.2.4-0.16.el7.x86_64.rpm
[root@mysql_1 ~]# yum install libtirpc-devel-0.2.4-0.16.el7.x86_64.rpm -y

##########导入mysql-boost-5.7.44.tar.gz
[root@mysql_1 ~]# tar zxf mysql-boost-5.7.44.tar.gz 
[root@mysql_1 ~]# cd mysql-5.7.44/

1.1.3 源码编译安装mysql

[root@mysql_1 mysql-5.7.44]#  cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/data/mysql -DMYSQL_UNIX_ADDR=/data/mysql/mysql.sock -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_EXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_unicode_ci -DWITH_BOOST=/root/mysql-5.7.44/boost/boost_1_59_0/
[root@mysql_1 mysql-5.7.44]# make -j4  && make install

1.2部署mysql

mysql-node1:172.25.250.10

mysql-node2:172.25.250.20

1.2.1部署mysql-node1

#######mysql-node1
[root@mysql-node1 ~]# cd /usr/local/mysql/
[root@mysql-node1 mysql]# ls
bin   include  LICENSE  mysql-test  README-test  support-files
docs  lib      man      README      share

[root@mysql-node1 ~]# useradd -s /sbin/nologin -M mysql
[root@mysql-node1 ~]# mkdir -p /data/mysql
[root@mysql-node1 ~]# chown mysql.mysql -R /data/mysql/

[root@mysql-node1 ~]# cd /usr/local/mysql/
[root@mysql-node1 mysql]# cd support-files/
[root@mysql-node1 support-files]# ls
magic  mysqld_multi.server  mysql-log-rotate  mysql.server  passwd
[root@mysql-node1 support-files]# cp mysql.server /etc/init.d/mysqld 


[root@mysql-node1 support-files]# cat /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0

[root@mysql-node1 support-files]# vim ~/.bash_profile 
[root@mysql-node1 support-files]# cat ~/.bash_profile 
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
	. ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin:/usr/local/mysql/bin        ######添加/usr/local/mysql/bin 

export PATH

######查看/data/mysql发现没有数据
[root@mysql-node1 ~]# ls /data/mysql/

####mysql初始化
[root@mysql-node1 support-files]# mysqld --user mysql --initialize
2024-08-22T14:34:00.281585Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
2024-08-22T14:34:01.907143Z 0 [Warning] InnoDB: New log files created, LSN=45790
2024-08-22T14:34:01.929731Z 0 [Warning] InnoDB: Creating foreign key constraint system tables.
2024-08-22T14:34:01.987762Z 0 [Warning] No existing UUID has been found, so we assume that this is the first time that this server has been started. Generating a new UUID: 93524a13-6093-11ef-aa71-000c29afe8f8.
2024-08-22T14:34:01.988281Z 0 [Warning] Gtid table is not ready to be used. Table 'mysql.gtid_executed' cannot be opened.
2024-08-22T14:34:02.074248Z 0 [Warning] A deprecated TLS version TLSv1 is enabled. Please use TLSv1.2 or higher.
2024-08-22T14:34:02.074263Z 0 [Warning] A deprecated TLS version TLSv1.1 is enabled. Please use TLSv1.2 or higher.
2024-08-22T14:34:02.074569Z 0 [Warning] CA certificate ca.pem is self signed.
2024-08-22T14:34:02.130248Z 1 [Note] A temporary password is generated for root@localhost: (sP--FA8tDOl     ###(sP--FA8tDOl 是mysql的初始密码,一定要记住,每次初始化的密码都不一样

###如果初始化出问题了
rm -rf /data/mysql/*

####现在查看/data/mysql可以看到数据
[root@mysql-node1 support-files]# ls /data/mysql/
auto.cnf    client-cert.pem  ibdata1      mysql               public_key.pem   sys
ca-key.pem  client-key.pem   ib_logfile0  performance_schema  server-cert.pem
ca.pem      ib_buffer_pool   ib_logfile1  private_key.pem     server-key.pem

#####启动mysql
[root@mysql-node1 support-files]# /etc/init.d/mysqld start
Starting MySQL.Logging to '/data/mysql/mysql-node3.err'.
 SUCCESS! 

###设置开机启动mysql
[root@mysql-node1 support-files]# chkconfig mysqld on
[root@mysql-node1 support-files]# chkconfig --list


######设置数据库的安全初始化----修改初始密码
[root@mysql-node1 support-files]# mysql_secure_installation 

Securing the MySQL server deployment.
Enter password for user root:        ###(sP--FA8tDOl
The existing password for the user account root has expired. Please set a new password.
New password: 						###redhat
Re-enter new password: 				###redhat
VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?

####是否开启密码加强
Press y|Y for Yes, any other key for No: no    ### no
Using existing password for root.
Change the password for root ? ((Press y|Y for Yes, any other key for No) : no               ### no     ##是否改变当前超级用户密码

 ... skipping.
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y           ###  y      ###是否删掉远程/匿名用户登录
Success.


Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y         ### y     ###是否删除超级用户远程登录
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.


Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y         #### y    ##是否删除测试库
 - Dropping test database...
Success.

 - Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y             ### y    ###是否刷新当前数据库
Success.

All done! 

[root@mysql-node1 support-files]# mysql -uroot -predhat

1.2.2部署mysql-node2主机

####mysql-node2创建用户以及目录还有授权
[root@mysql-node2 ~]# useradd -s /sbin/nologin -M mysql
[root@mysql-node2 ~]# mkdir -p /data/mysql
[root@mysql-node2 ~]# chown mysql.mysql -R /data/mysql/

###查看创建的/data/mysql是看不到任何信息
[root@mysql-node2 ~]# cd /data/mysql/
[root@mysql-node2 mysql]# ls

###从mysql-node1同步信息到mysql-node2,是同步/usr/local/mysql这个目录
[root@mysql-node1 ~]# cd /usr/local/
[root@mysql-node1 local]# ls
bin  etc  games  include  lib  lib64  libexec  mysql  sbin  share  src

###因为/usr/local/mysql目录有很多链接等,所以不能用普通的同步信息
[root@mysql-node1 local]# rsync -al -r mysql root@172.25.250.20:/usr/local/
root@172.25.250.20's password:            ###redhat

###然后我们到mysql-node2查看,可以看到已经同步过来了
[root@mysql-node2 mysql]# cd /usr/local/
[root@mysql-node2 local]# ls
bin  etc  games  include  lib  lib64  libexec  mysql  sbin  share  src
[root@mysql-node2 local]# ls mysql/
bin   include  LICENSE  mysql-test  README-test  support-files
docs  lib      man      README      share

###然后我们进入/usr/local/mysql/support-files
[root@mysql-node3 mysql]# cd support-files/
[root@mysql-node3 support-files]# cp mysql.server /etc/init.d/mysqld

[root@mysql-node3 support-files]# cat /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0

[root@mysql-node3 support-files]# vim ~/.bash_profile 
[root@mysql-node3 support-files]# cat ~/.bash_profile 
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
	. ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin:/usr/local/mysql/bin   ###添加/usr/local/mysql/bin

export PATH

###我们查看/data/mysql依然看不到任何信息
[root@mysql-node3 support-files]# ls /data/mysql/

###这条语句会生成初始数据库密码    (sP--FA8tDOl    ###每次执行这条生成的密码会不一样
[root@mysql-node3 support-files]# mysqld --user mysql --initialize

###启动mysql
[root@mysql-node3 support-files]# /etc/init.d/mysqld start
Starting MySQL.Logging to '/data/mysql/mysql-node3.err'.
 SUCCESS! 

####
[root@mysql-node3 support-files]# chkconfig mysqld on
[root@mysql-node3 support-files]# chkconfig --list

[root@mysql-node3 support-files]# mysql_secure_installation 

Securing the MySQL server deployment.
Enter password for user root:        ###(sP--FA8tDOl
The existing password for the user account root has expired. Please set a new password.
New password: 						###redhat
Re-enter new password: 				###redhat
VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?

Press y|Y for Yes, any other key for No: no    ### no
Using existing password for root.
Change the password for root ? ((Press y|Y for Yes, any other key for No) : no               ### no

 ... skipping.
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y           ###  y
Success.


Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y         ### y
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.


Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y         #### y
 - Dropping test database...
Success.

 - Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y             ### y
Success.

All done! 

###进入mysql
[root@mysql-node1 support-files]# mysql -uroot -predhat

1.3.mysql的主从复制

master:172.25.250.10

slave;172.25.250.20

1.3.1配置mastesr

[root@mysql-node1 mysql]# cat /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log-bin=mysql-bin           ##########添加二进制日志,用来记录当前数据库插入或删除的动作....或数据数据,他会记录在/data/mysql
server-id=10				##########添加主机的id

####设置完重启,mysql让其生效
[root@mysql-node1 ~]# /etc/init.d/mysqld restart
Shutting down MySQL. SUCCESS! 
Starting MySQL. SUCCESS! 

####查看server-id
[root@mysql-node1 ~]# mysql -uroot -predhat -e "select @@server_id;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| @@server_id |
+-------------+
|          10 |
+-------------+

####登录mysql
[root@mysql-node1 mysql]# mysql -uroot -predhat

##生成专门用来做复制的用户,此用户是用于slave端做认证用
mysql> CREATE USER 'repl'@'%' IDENTIFIED BY 'ren';
Query OK, 0 rows affected (0.00 sec)
 
##对这个用户进行授权
mysql> GRANT REPLICATION SLAVE ON *.* TO repl@'%';

mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |      595 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

mysql> quit
Bye

######查看二进制日志
[root@mysql-node1 ~]# cd /data/mysql/
[root@mysql-node1 mysql]# mysqlbinlog mysql-bin.000001 -vv

1.3.2配置slave

[root@mysql-node2 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
server-id=20


[root@mysql-node2 ~]# /etc/init.d/mysqld restart
Shutting down MySQL.. SUCCESS! 
Starting MySQL. SUCCESS! 

[root@mysql-node2 ~]# mysql -u root -predhat

#####
mysql> change master to master_host='172.25.250.10',master_user='repl',master_password='ren',master_log_file='mysql-bin.000001',master_log_pos=595;
Query OK, 0 rows affected, 2 warnings (0.01 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.25.250.10
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 595
               Relay_Log_File: mysql-node2-relay-bin.000002
                Relay_Log_Pos: 320
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes      ###数据传输
            Slave_SQL_Running: Yes      ###数据回放
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 595
              Relay_Log_Space: 533
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 10
                  Master_UUID: 0244bc06-6030-11ef-a280-000c296598d3
             Master_Info_File: /data/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)

ERROR: 
No query specified

1.3.3测试

#########master主机
####建立库
mysql> create database ren;
Query OK, 1 row affected (0.00 sec)

####建立表
mysql> create table ren.userlist (
    -> username varchar(20) not null,
    -> password varchar(50) not null
    -> );
Query OK, 0 rows affected (0.00 sec)

####插入数据
mysql> insert into ren.userlist value('ren','123');
Query OK, 1 row affected (0.02 sec)

mysql> select * from ren.userlist;
   
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
+----------+----------+
1 row in set (0.00 sec)

####在slave主机中查看数据是否同步过来
mysql> select * from ren.userlist;
   
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
+----------+----------+
1 row in set (0.00 sec)

1.4.当有数据时添加slave2

mysql-node3:172.25.250.30

就是slave2主机没有mysql,操作依旧是从master主机同步/usr/local/mysql到slave主机的/usr/local/,其他操作还是跟master主机一致

1.4.1完成基础配置

[root@mysql-node3 ~]# cat /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
server-id=30

然后从master(mysql-node1)节点备份,但是需要注意的是:

生产环境中备份时需要锁表,保证备份前后的数据一致

mysql> FLUSH TABLES WITH READ LOCK;

备份后再解锁

mysql> UNLOCK TABLES;

####这里注意看此时master中的mysql中内容
[root@mysql-node1 ~]# mysql -uroot -predhat
mysql> select * from ren.userlist;
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
+----------+----------+
4 rows in set (0.00 sec)


#####从master节点备份数据(mysql-node1)
[root@mysql-node1 ~]# mysqldump -uroot -predhat ren > ren.sql 
mysqldump: [Warning] Using a password on the command line interface can be insecure.   ###只是警告这种写法不安全

#####这里我们可以看到ren.sql已经备份好了
[root@mysql-node1 ~]# ls
anaconda-ks.cfg                           mysql-5.7.44               ren.sql
initial-setup-ks.cfg                      mysql-boost-5.7.44.tar.gz  repl.sql
libtirpc-devel-0.2.4-0.16.el7.x86_64.rpm  passwd

#####我们将ren.sql同步到mysql-node3主机上去
[root@mysql-node1 ~]# pwd
/root
[root@mysql-node1 ~]# rsync -al -r ren.sql root@172.25.250.30:/root/
root@172.25.250.30's password: 
[root@mysql-node1 ~]# 

####到mysql-node3主机查看
[root@mysql-node3 ~]# ls
anaconda-ks.cfg  initial-setup-ks.cfg  ren.sql

#####利用master节点中备份出来的lee.sql在slave2(mysql-node3)中拉平数据
[root@mysql-node3 ~]# mysql -uroot -predhat -e "create database ren;"
mysql: [Warning] Using a password on the command line interface can be insecure.
[root@mysql-node3 ~]# mysql -uroot -predhat ren <ren.sql 
mysql: [Warning] Using a password on the command line interface can be insecure.
[root@mysql-node3 ~]# mysql -uroot -predhat -e "select * from ren.userlist;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
+----------+----------+
####可以看到master内容已经备份到slave2


####配置slave2中的slave功能

#在master中查询日志pos
[root@mysql-node1 ~]# mysql -uroot -predhat -e "show master status;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |     2035 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

####进入slave2
[root@mysql-node3 ~]# mysql -uroot -predhat 
mysql> CHANGE MASTER TO
    -> MASTER_HOST='172.25.250.10',
    -> MASTER_USER='repl',
    -> MASTER_PASSWORD='ren',
    -> MASTER_LOG_FILE='mysql-bin.000001',
    -> MASTER_LOG_POS=2035;
Query OK, 0 rows affected, 2 warnings (0.01 sec)

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW SLAVE STATUS\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.25.250.10
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 2035
               Relay_Log_File: mysql-node3-relay-bin.000002
                Relay_Log_Pos: 320
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 2035
              Relay_Log_Space: 533
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 10
                  Master_UUID: 0244bc06-6030-11ef-a280-000c296598d3
             Master_Info_File: /data/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)

ERROR: 
No query specified

1.4.2测试

###master
[root@mysql-node1 ~]# mysql -uroot -predhat -e "insert into ren.userlist values('ren444','123');"
mysql: [Warning] Using a password on the command line interface can be insecure.
[root@mysql-node1 ~]# mysql -uroot -predhat -e "select * from ren.userlist;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
+----------+----------+

###slave1
[root@mysql-node2 ~]# mysql -uroot -predhat -e "select * from ren.userlist;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
+----------+----------+
[root@mysql-node2 ~]# 

###slave2
[root@mysql-node3 ~]# mysql -uroot -predhat -e "select * from ren.userlist;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
+----------+----------+
[root@mysql-node3 ~]# 

1.5延迟复制

延迟复制时用来控制sql线程的,和i/o线程无关

这个延迟复制不是i/o线程过段时间来复制,i/o是正常工作的

是日志已经保存在slave端了,那个sql要等多久进行回放

延迟复制时为了防止手误删掉数据表什么的,

只需要在一台slave主机部署延迟复制就行

[root@mysql-node3 ~]# mysql -uroot -predhat

#####关闭sql进程
mysql> stop slave sql_thread;
Query OK, 0 rows affected (0.00 sec)

mysql> change master to master_delay=60;        ###延迟60s
Query OK, 0 rows affected (0.00 sec)

mysql> start slave sql_thread;
Query OK, 0 rows affected (0.00 sec)

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.25.250.10
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 2301
               Relay_Log_File: mysql-node3-relay-bin.000002
                Relay_Log_Pos: 586
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 2301
              Relay_Log_Space: 799
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 10
                  Master_UUID: 0244bc06-6030-11ef-a280-000c296598d3
             Master_Info_File: /data/mysql/master.info
                    SQL_Delay: 60          ###延迟60s
 .......

测试

在master中写入或删掉数据后过了延迟时间才能被查询到

####在master中添加数据
[root@mysql-node1 ~]# mysql -uroot -predhat -e "insert into ren.userlist values('ren555','123');"
mysql: [Warning] Using a password on the command line interface can be insecure.
[root@mysql-node1 ~]# mysql -uroot -predhat -e "select * from ren.userlist;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
| ren555   | 123      |
+----------+----------+

###slave2中查看数据
mysql> select * from ren.userlist;
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
+----------+----------+
5 rows in set (0.00 sec)

####还没有过延迟时间,所以查询不到
mysql> select * from ren.userlist;
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
+----------+----------+
5 rows in set (0.00 sec)

######过了延迟时间才被查询到ren555
mysql> select * from ren.userlist;
+----------+----------+
| username | password |
+----------+----------+
| ren      | 123      |
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
| ren555   | 123      |
+----------+----------+
6 rows in set (0.00 sec)

#####如果误删了就可以在slave中把原来的数据dump出来,
mysqldump -uroot -predhat ren > ren.sql     ######这样式儿的

1.6慢查询日志

  • 慢查询,执行很慢的查询

  • 当执行SQL超过long_query_time参数设定的时间阈值(默认10s)时,就被认为是慢查询,这个 SQL语句就是需要优化的

  • 慢查询被记录在慢查询日志里

  • 慢查询日志默认是不开启的

  • 如果需要优化SQL语句,就可以开启这个功能,它可以让你很容易地知道哪些语句是需要优化的。

    #####查看慢查询是否开启
    mysql> show variables like "slow%";
    +---------------------+----------------------------------+
    | Variable_name | Value |
    +---------------------+----------------------------------+
    | slow_launch_time | 2 |
    | slow_query_log | OFF |
    | slow_query_log_file | /data/mysql/mysql-node1-slow.log |
    +---------------------+----------------------------------+
    3 rows in set (0.00 sec)

    #####开启慢查询
    mysql> set global slow_query_log=on;
    Query OK, 0 rows affected (0.01 sec)

    mysql> show variables like "slow%";
    +---------------------+----------------------------------+
    | Variable_name | Value |
    +---------------------+----------------------------------+
    | slow_launch_time | 2 |
    | slow_query_log | ON |
    | slow_query_log_file | /data/mysql/mysql-node1-slow.log |
    +---------------------+----------------------------------+
    3 rows in set (0.00 sec)

    #####慢查询时间,如果超过十秒,那么就会被认定为是一个慢查询
    mysql> SHOW VARIABLES like "long%";
    +-----------------+-----------+
    | Variable_name | Value |
    +-----------------+-----------+
    | long_query_time | 10.000000 |
    +-----------------+-----------+
    1 row in set (0.00 sec)

    ########设定慢查询时间为4s
    mysql> set long_query_time=4;
    Query OK, 0 rows affected (0.00 sec)

    mysql> SHOW VARIABLES like "long%";
    +-----------------+----------+
    | Variable_name | Value |
    +-----------------+----------+
    | long_query_time | 4.000000 |
    +-----------------+----------+
    1 row in set (0.00 sec)

    mysql> show variables like "slow%";
    +---------------------+----------------------------------+
    | Variable_name | Value |
    +---------------------+----------------------------------+
    | slow_launch_time | 2 |
    | slow_query_log | ON |
    | slow_query_log_file | /data/mysql/mysql-node1-slow.log |
    +---------------------+----------------------------------+
    3 rows in set (0.00 sec)

    mysql> quit
    Bye

    #########
    [root@mysql-node1 ~]# cat /data/mysql/mysql-node1-slow.log
    /usr/local/mysql/bin/mysqld, Version: 5.7.44-log (Source distribution). started with:
    Tcp port: 3306 Unix socket: /data/mysql/mysql.sock
    Time Id Command Argument

测试慢查询

mysql> select sleep (10);
+------------+
| sleep (10) |
+------------+
|          0 |
+------------+
1 row in set (10.00 sec)

mysql> quit
Bye
[root@mysql-node1 ~]# cat /data/mysql/mysql-node1-slow.log
/usr/local/mysql/bin/mysqld, Version: 5.7.44-log (Source distribution). started with:
Tcp port: 3306  Unix socket: /data/mysql/mysql.sock
Time                 Id Command    Argument
# Time: 2024-08-23T06:46:12.141457Z
# User@Host: root[root] @ localhost []  Id:    28
# Query_time: 10.001070  Lock_time: 0.000000 Rows_sent: 1  Rows_examined: 0
SET timestamp=1724395572;
select sleep (10);

1.7.mysql的并行复制

如果主从数据复制的比较慢的话,那么会有差异,这个差异是时间差

######查看slave中的线程信息
mysql> show processlist;
+----+------+---------------------+------+-------------+-------+---------------------------------------------------------------+------------------+
| Id | User | Host                | db   | Command     | Time  | State                                                         | Info             |
+----+------+---------------------+------+-------------+-------+---------------------------------------------------------------+------------------+
|  3 | repl | 172.25.250.20:36010 | NULL | Binlog Dump | 98046 | Master has sent all binlog to slave; waiting for more updates | NULL             |
| 20 | repl | 172.25.250.30:46604 | NULL | Binlog Dump | 54046 | Master has sent all binlog to slave; waiting for more updates | NULL             |
| 29 | root | localhost           | NULL | Query       |     0 | starting                                                      | show processlist |
+----+------+---------------------+------+-------------+-------+---------------------------------------------------------------+------------------+
3 rows in set (0.00 sec)
  • 默认情况下slave中使用的是sql单线程回放

  • 在master中时多用户读写,如果使用sql单线程回放那么会造成组从延迟严重

  • 开启MySQL的多线程回放可以解决上述问题

1.7.1在slave中设定

[root@mysql-node2 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
server-id=2
####gtid
gtid_mode=ON				###gtid模式
enforce-gtid-consistency=ON				###强一致性
slave-parallel-type=LOGICAL_CLOCK	 #基于组提交,
slave-parallel-workers=16 			 #开启线程数量
master_info_repository=TABLE		 #master信息在表中记录,默认记录
在/data/mysql//master.info
relay_log_info_repository=TABLE 	 #回放日志信息在表中记录,默认记录在/data/mysql/relay-log.info
relay_log_recovery=ON 					#日志回放恢复功能开启

[root@mysql-node2 ~]# /etc/init.d/mysqld restart
Shutting down MySQL. SUCCESS! 
Starting MySQL.. SUCCESS! 

[root@mysql-node2 ~]# mysql -uroot -p

2.mysql主从原理

三个线程

实际上主从同步的原理就是基于binlog进行数据同步的,在主从复制过程中,会基于3个线程来操作,一个主库线程(Dump线程),两个从库线程(I/O线程和SQL线程)

  • 二进制日志转储线程(Binlog dump thread)是一个主库线程。当从库线程连接的时候, 主库可以 将二进制日志发送给从库,当主库读取事件(Event)的时候,会在 Binlog 上加锁,读取完成之 后,再将锁释放掉

  • 从库 I/O 线程会连接到主库,向主库发送请求更新 Binlog。这时从库的 I/O 线程就可以读取到主库 的二进制日志转储线程发送的 Binlog 更新部分,并且拷贝到本地的中继日志 (Relay log)。

  • 从库 SQL 线程会读取从库中的中继日志,并且执行日志中的事件,将从库中的数据与主库保持同步。

复制三步骤

1.master将写操作记录到二进制日志(binlog)

2.slave将master的binary log events拷贝到它的中继日志(relay log)

3.slave重做中继日志的事件,将改变应用到自己的数据库中,mysql复制时异步的且串行化的,而且重启后从接入点开始复制

具体操作

1.slaves端中设置了master端的ip,用户,日志,和日志的Position,通过这些信息取得master的认证及 信息

2.master端在设定好binlog启动后会开启binlog dump的线程

3.master端的binlog dump把二进制的更新发送到slave端的

4.slave端开启两个线程,一个是I/O线程,一个是sql线程,

  • i/o线程用于接收master端的二进制日志,此线程会在本地打开relaylog中继日志,并且保存到本地 磁盘

  • sql线程读取本地relog中继日志进行回放

5.什么时候我们需要多个slave?

当读取的而操作远远高与写操作时。我们采用一主多从架构 数据库外层接入负载均衡层并搭配高可用机制

2.1 架构缺陷

主从架构采用的是异步机制

master更新完成后直接发送二进制日志到slave,但是slaves是否真正保存了数据master端不会检测

master直接保存二进制日志到磁盘,当master端到slave端的网络出现问题时或master挂掉,二进制日志可能根本没有到达slave,所以master端出现问题slave端就会接管master,这个过程数据就丢失了

这样的问题出现就无法达到数据的强一致性和零数据丢失

3.半同步模式

3.1半同步模式原理

用户线程写入完成后master中的Dump线程会把日志推掉slave端

slave端的io线程接收后保存到relaylog中继日志

保存完后slave想master返回ack

在未接收slave的ack时master暂时不做提交,就会一直处于等待状态,当收到slave返回的ack后提交到存储引擎,然后返回给客户

3.2gtid模式

当启用gtid时需要考虑的问题

在master端的写入时多用户读写,在slave端的复制单线程日志回放,所以slave端一定会延迟于master端

这种延迟在slave端的延迟可能会不一致,当master挂掉后slave接管,一般会挑选一个和master延迟日志最接近的充当新的master

那么其他的slave主机继续充当slave并且指向新的master,作为新master的slave

这时候按照之前的配置我们需要知道新的master上的pos的id,但是我们无法确定新的master和slave之间相差多少

当激活gtid之后

当master出现问题后,slave2和master的数据最接近,会被作为新的master

slave1指向新的master,但是他不会去检测新的master的pos id,只需要继续读取自己gtid_next即可

我们把三台机子都开启gtid

####master主机
[root@mysql-node1 ~]# cat /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log-bin=mysql-bin
server-id=10
gtid_mode=ON
enforce-gtid-consistency=ON

slave1主机
[root@mysql-node2 ~]# cat /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log-bin=mysql-bin
server-id=20
super_read_only=on
gtid_mode=ON
enforce-gtid-consistency=ON
slave-parallel-type=LOGICAL_CLOCK 
slave-parallel-workers=16
master_info_repository=TABLE 
relay_log_info_repository=TABLE 
relay_log_recovery=ON

####slave2主机
[root@mysql-node3 ~]# cat /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
server-id=30
gtid_mode=ON
enforce-gtid-consistency=ON

###然后开始gtid后都要重启mysql
/etc/init.d/mysqld restart

然后我们在slave1和slave2中重新设定slave

###在master中查看master status
mysql>  show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000002 |      154 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

###slave1主机
[root@mysql-node2 ~]# mysql -uroot -predhat

mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)

mysql> change master to master_host='172.25.250.10',master_user='repl',master_password='ren',master_auto_position=1;      ##master_auto_position=1;自动发现master中
Query OK, 0 rows affected, 2 warnings (0.01 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.25.250.10
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002     ####和master中一致
          Read_Master_Log_Pos: 154       ###可以看到和master中一致
               Relay_Log_File: mysql-node2-relay-bin.000002
                Relay_Log_Pos: 367
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 154
              Relay_Log_Space: 580
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 10
                  Master_UUID: 0244bc06-6030-11ef-a280-000c296598d3
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 1     ###自动识别
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)

ERROR: 
No query specified


####slave2主机
[root@mysql-node3 ~]# mysql -uroot -predhat 

mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)

mysql> change master to master_host='172.25.250.10',master_user='repl',master_password='ren',master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.00 sec)

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.25.250.10
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 154
               Relay_Log_File: mysql-node3-relay-bin.000002
                Relay_Log_Pos: 367
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 154
              Relay_Log_Space: 580
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 10
                  Master_UUID: 0244bc06-6030-11ef-a280-000c296598d3
             Master_Info_File: /data/mysql/master.info
                    SQL_Delay: 60
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 1
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)

ERROR: 
No query specified

3.3启用半同步模式

在master端配置启用半同步模式

[root@mysql-node1 ~]# cat /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log-bin=mysql-bin
server-id=10
gtid_mode=ON
enforce-gtid-consistency=ON
rpl_semi_sync_master_enabled=1           ###开启半同步模式

[root@mysql-node1 ~]# mysql -uroot -predhat 

####安装半同步插件
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.00 sec)

###查看插件情况
mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS 
    -> FROM INFORMATION_SCHEMA.PLUGINS
    -> WHERE PLUGIN_NAME LIKE '%semi%';
+----------------------+---------------+
| PLUGIN_NAME          | PLUGIN_STATUS |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE        |
+----------------------+---------------+
1 row in set (0.00 sec)

#打开半同步功能
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;

####查看半同步状态
mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | ON         |
| rpl_semi_sync_master_timeout              | 10000      |
| rpl_semi_sync_master_trace_level          | 32         |
| rpl_semi_sync_master_wait_for_slave_count | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 0     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 0     |
| Rpl_semi_sync_master_no_times              | 0     |
| Rpl_semi_sync_master_no_tx                 | 0     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 0     |
| Rpl_semi_sync_master_tx_wait_time          | 0     |
| Rpl_semi_sync_master_tx_waits              | 0     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 0     |
+--------------------------------------------+-------+
14 rows in set (0.01 sec)

mysql> show plugins

在slave端开启半同步功能

####slave1

[root@mysql-node2 ~]# mysql -uroot -predhat

mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.00 sec)


mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS
    -> FROM INFORMATION_SCHEMA.PLUGINS
    -> WHERE PLUGIN_NAME LIKE '%semi%';
+----------------------+---------------+
| PLUGIN_NAME          | PLUGIN_STATUS |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE        |
+----------------------+---------------+
1 row in set (0.00 sec)

mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | ON         |
| rpl_semi_sync_master_timeout              | 10000      |
| rpl_semi_sync_master_trace_level          | 32         |
| rpl_semi_sync_master_wait_for_slave_count | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.01 sec)

mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 0     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 0     |
| Rpl_semi_sync_master_no_times              | 0     |
| Rpl_semi_sync_master_no_tx                 | 0     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 0     |
| Rpl_semi_sync_master_tx_wait_time          | 0     |
| Rpl_semi_sync_master_tx_waits              | 0     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 0     |
+--------------------------------------------+-------+
14 rows in set (0.01 sec)

####如果半同步没有生效,重启io线程,半同步才能生效
mysql> stop slave io_thread;
Query OK, 0 rows affected (0.00 sec)

mysql> start slave io_thread;
Query OK, 0 rows affected (0.00 sec)


###slave2
[root@mysql-node3 ~]# mysql -uroot -predhat 


mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS
    -> FROM INFORMATION_SCHEMA.PLUGINS
    -> WHERE PLUGIN_NAME LIKE '%semi%';
+----------------------+---------------+
| PLUGIN_NAME          | PLUGIN_STATUS |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE        |
+----------------------+---------------+
1 row in set (0.00 sec)

mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | ON         |
| rpl_semi_sync_master_timeout              | 10000      |
| rpl_semi_sync_master_trace_level          | 32         |
| rpl_semi_sync_master_wait_for_slave_count | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 0     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 0     |
| Rpl_semi_sync_master_no_times              | 0     |
| Rpl_semi_sync_master_no_tx                 | 0     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 0     |
| Rpl_semi_sync_master_tx_wait_time          | 0     |
| Rpl_semi_sync_master_tx_waits              | 0     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 0     |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)

mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | ON         |
| rpl_semi_sync_master_timeout              | 10000      |
| rpl_semi_sync_master_trace_level          | 32         |
| rpl_semi_sync_master_wait_for_slave_count | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.00 sec)

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%';
+----------------------+---------------+
| PLUGIN_NAME          | PLUGIN_STATUS |
+----------------------+---------------+
| rpl_semi_sync_master | ACTIVE        |
+----------------------+---------------+
1 row in set (0.00 sec)

测试

####在slave端
[root@mysql-node2 ~]# mysql -plee
mysql> STOP SLAVE IO_THREAD;
Query OK, 0 rows affected (0.00 sec)
[root@mysql-node3 ~]# mysql -plee
mysql> STOP SLAVE IO_THREAD;
Query OK, 0 rows affected (0.00 sec)

####在master中插入数据
mysql> insert into ren.userlist values ('ren888','123');
Query OK, 1 row affected (0.00 sec)

mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+--------------------------------------------+----------+
| Variable_name                              | Value    |
+--------------------------------------------+----------+
| Rpl_semi_sync_master_clients               | 0        |
| Rpl_semi_sync_master_net_avg_wait_time     | 0        |
| Rpl_semi_sync_master_net_wait_time         | 0        |
| Rpl_semi_sync_master_net_waits             | 0        |
| Rpl_semi_sync_master_no_times              | 1        |
| Rpl_semi_sync_master_no_tx                 | 3        |
| Rpl_semi_sync_master_status                | OFF      |   ###转为异步模式
| Rpl_semi_sync_master_timefunc_failures     | 0        |   ##会自动恢复
| Rpl_semi_sync_master_tx_avg_wait_time      | 8881814  |
| Rpl_semi_sync_master_tx_wait_time          | 17763628 |
| Rpl_semi_sync_master_tx_waits              | 2        |
| Rpl_semi_sync_master_wait_pos_backtraverse | 2        |
| Rpl_semi_sync_master_wait_sessions         | 0        |
| Rpl_semi_sync_master_yes_tx                | 0        |
+--------------------------------------------+----------+
14 rows in set (0.00 sec)

####slave端
###slave1
mysql> select * from ren.userlist;       #####master上的数据没有同步到slave上
+----------+----------+
| username | password |
+----------+----------+
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
| ren555   | 123      |
| ren666   | 123      |
| ren777   | 123      |
+----------+----------+
7 rows in set (0.00 sec)

###slave2
mysql> select * from ren.userlist;
+----------+----------+
| username | password |
+----------+----------+
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
| ren555   | 123      |
| ren666   | 123      |
| ren777   | 123      |
+----------+----------+
7 rows in set (0.00 sec)

####slave1和slave2开启io线程
mysql> start slave io_thread;
Query OK, 0 rows affected (0.01 sec)

####再次查看
mysql> select * from ren.userlist;    #####master上的数据同步到slave上
+----------+----------+
| username | password |
+----------+----------+
| ren11    | 123      |
| ren222   | 123      |
| ren333   | 123      |
| ren444   | 123      |
| ren555   | 123      |
| ren666   | 123      |
| ren777   | 123      |
| ren888   | 123      |
+----------+----------+
8 rows in set (0.00 sec)

使用半同步模式时,在slave都挂掉了,master是不能写入数据,但是为什么这里能写入数据,是因为在slave挂掉后,master自动进入异步模式,默认卡顿时间10s

4.实现mysql组复制

为了避免出错,所有节点都需重新生成新数据库

编辑配置文件

mysql-node1:172.25.250.10

[root@mysql-node1 ~]# /etc/init.d/mysqld stop
Shutting down MySQL........... SUCCESS! 
[root@mysql-node1 ~]# cd /data/mysql/
[root@mysql-node1 mysql]# ls
auto.cnf         ib_buffer_pool  mysql-bin.000001  mysql-node1-slow.log  server-cert.pem
ca-key.pem       ibdata1         mysql-bin.000002  performance_schema    server-key.pem
ca.pem           ib_logfile0     mysql-bin.000003  private_key.pem       sys
client-cert.pem  ib_logfile1     mysql-bin.index   public_key.pem
client-key.pem   mysql           mysql-node1.err   ren
[root@mysql-node1 mysql]# rm -rf *
[root@mysql-node1 mysql]# ls
[root@mysql-node1 mysql]# cd

[root@mysql-node1 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log-bin=mysql-bin
server-id=10
gtid_mode=ON
enforce-gtid-consistency=ON
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
plugin_load_add='group_replication.so'
transaction_write_set_extraction=XXHASH64
group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
group_replication_start_on_boot=off
group_replication_local_address="172.25.250.10:33061"
group_replication_group_seeds="172.25.250.10:33061,172.25.250.20:33061,172.25.250.30:33061"
group_replication_ip_whitelist="172.25.250.0/24,127.0.0.1/8"
group_replication_bootstrap_group=off
group_replication_single_primary_mode=OFF
group_replication_enforce_update_everywhere_checks=ON
group_replication_allow_local_disjoint_gtids_join=1

[root@mysql-node1 ~]# ls /data/mysql/
[root@mysql-node1 ~]# mysqld --user=mysql --initialize   ###生成初始密码t9d,lpy9nw8B
[root@mysql-node1 ~]# /etc/init.d/mysqld start
Starting MySQL.Logging to '/data/mysql/mysql-node1.err'.
 SUCCESS! 
[root@mysql-node1 ~]# mysql -uroot -p't9d,lpy9nw8B'


mysql> alter user root@localhost identified by 'redhat';
Query OK, 0 rows affected (0.00 sec)

mysql> SET SQL_LOG_BIN=0;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'lee';
Query OK, 0 rows affected (0.00 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SQL_LOG_BIN=1;
Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='lee' FOR CHANNEL 
    -> 'group_replication_recovery';
Query OK, 0 rows affected, 2 warnings (0.01 sec)

mysql> SET GLOBAL group_replication_bootstrap_group=ON;
Query OK, 0 rows affected (0.00 sec)

mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected, 1 warning (2.12 sec)

mysql> 
mysql> SET GLOBAL group_replication_bootstrap_group=OFF;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| group_replication_applier | 6945a50e-61e6-11ef-8312-000c296598d3 | mysql-node1 |        3306 | ONLINE       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
1 row in set (0.00 sec)


[root@mysql-node1 ~]# scp /etc/my.cnf root@172.25.250.20:/etc/my.cnf
root@172.25.250.20's password: 
my.cnf                                                       100%  932     1.2MB/s   00:00    
[root@mysql-node1 ~]# scp /etc/my.cnf root@172.25.250.30:/etc/my.cnf
root@172.25.250.30's password: 
my.cnf                                                       100%  932     1.6MB/s   00:00   

########node1、node2、node3都做本地解析
[root@mysql-node1 ~]# vim /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.250.20   mysql-node2
172.25.250.10   mysql-node1
172.25.250.30   mysql-node3

mysql-node2:172.250.20

[root@mysql-node2 ~]# /etc/init.d/mysqld stop 
Shutting down MySQL. SUCCESS! 
[root@mysql-node2 ~]# rm -rf /data/mysql/*
[root@mysql-node2 ~]# ls /data/mysql/
[root@mysql-node2 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log-bin=mysql-bin
server-id=20
gtid_mode=ON
enforce-gtid-consistency=ON
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
plugin_load_add='group_replication.so'
transaction_write_set_extraction=XXHASH64
group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
group_replication_start_on_boot=off
group_replication_local_address="172.25.250.20:33061"
group_replication_group_seeds="172.25.250.10:33061,172.25.250.20:33061,172.25.250.30:33061"
group_replication_ip_whitelist="172.25.250.0/24,127.0.0.1/8"
group_replication_bootstrap_group=off
group_replication_single_primary_mode=OFF
group_replication_enforce_update_everywhere_checks=ON
group_replication_allow_local_disjoint_gtids_join=1


[root@mysql-node2 ~]# mysqld --user=mysql --initialize
#### /GnF4k)Yrt1I

[root@mysql-node2 ~]# /etc/init.d/mysqld start
Starting MySQL.Logging to '/data/mysql/mysql-node2.err'.
 SUCCESS! 
[root@mysql-node2 ~]# mysql -uroot -p'/GnF4k)Yrt1I'

mysql> alter user root@localhost identified by 'redhat';
Query OK, 0 rows affected (0.01 sec)

mysql> SET SQL_LOG_BIN=0;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'lee';
Query OK, 0 rows affected (0.00 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SQL_LOG_BIN=1;
Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='lee' FOR CHANNEL 
    -> 'group_replication_recovery';
Query OK, 0 rows affected, 2 warnings (0.01 sec)

mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected, 1 warning (6.05 sec)

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| group_replication_applier | 6945a50e-61e6-11ef-8312-000c296598d3 | mysql-node1 |        3306 | ONLINE       |
| group_replication_applier | 7596f5ca-61e7-11ef-93ce-000c296cedfd | mysql-node2 |        3306 | ONLINE       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
2 rows in set (0.00 sec)

mysql-node3:172.25.250.30

[root@mysql-node3 ~]# /etc/init.d/mysqld stop
Shutting down MySQL.. SUCCESS! 
[root@mysql-node3 ~]# rm -rf /data/mysql/*
[root@mysql-node3 ~]# ls
anaconda-ks.cfg  initial-setup-ks.cfg  ren.sql
[root@mysql-node3 ~]# ls
anaconda-ks.cfg  initial-setup-ks.cfg  ren.sql
[root@mysql-node3 ~]# ls /data/mysql/
[root@mysql-node3 ~]# 
[root@mysql-node3 ~]# 
[root@mysql-node3 ~]# 
[root@mysql-node3 ~]# vim /etc/my.cnf
[root@mysql-node3 ~]# ls /data/mysql/
[root@mysql-node3 ~]# vim /etc/hosts
[root@mysql-node3 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log-bin=mysql-bin
server-id=30
gtid_mode=ON
enforce-gtid-consistency=ON
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
plugin_load_add='group_replication.so'
transaction_write_set_extraction=XXHASH64
group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
group_replication_start_on_boot=off
group_replication_local_address="172.25.250.30:33061"
group_replication_group_seeds="172.25.250.10:33061,172.25.250.20:33061,172.25.250.30:33061"
group_replication_ip_whitelist="172.25.250.0/24,127.0.0.1/8"
group_replication_bootstrap_group=off
group_replication_single_primary_mode=OFF
group_replication_enforce_update_everywhere_checks=ON
group_replication_allow_local_disjoint_gtids_join=1


[root@mysql-node3 ~]# mysqld --user=mysql --initialize
#######T+4qgepSfklp

[root@mysql-node3 ~]# mysql -uroot -p'T+4qgepSfklp'

mysql> alter user root@localhost identified by 'redhat';
Query OK, 0 rows affected (0.00 sec)

mysql> SET SQL_LOG_BIN=0;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'lee';
Query OK, 0 rows affected (0.00 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
Query OK, 0 rows affected (0.00 sec)

mysql>  FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SQL_LOG_BIN=1;
Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='lee' FOR CHANNEL 
    -> 'group_replication_recovery';
Query OK, 0 rows affected, 2 warnings (0.01 sec)

mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected, 1 warning (3.09 sec)

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
| group_replication_applier | 1f4b2f48-61e9-11ef-ad9f-000c29afe8f8 | mysql-node3 |        3306 | ONLINE       |
| group_replication_applier | 6945a50e-61e6-11ef-8312-000c296598d3 | mysql-node1 |        3306 | ONLINE       |
| group_replication_applier | 7596f5ca-61e7-11ef-93ce-000c296cedfd | mysql-node2 |        3306 | ONLINE       |
+---------------------------+--------------------------------------+-------------+-------------+--------------+
3 rows in set (0.00 sec)

测试

在每个节点都可以完成读写

在mysql-node3中

mysql> create database lee;
Query OK, 1 row affected (0.00 sec)

mysql> create table lee.userlist(
    -> username varchar(10) primary key not null,
    -> password varchar(50) not null
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> insert into lee.userlist values ('user1','111');
Query OK, 1 row affected (0.00 sec)

mysql> select * from lee.userlist;
+----------+----------+
| username | password |
+----------+----------+
| user1    | 111      |
+----------+----------+
1 row in set (0.00 sec)

5.mysql-router(mysql路由)

mysql router

是一个对应用程序透明的InnoDB Cluster连接路由服务,提供负载均衡、应用连接故障转移和客户端路 由。

利用路由器的连接路由特性,用户可以编写应用程序来连接到路由器,并令路由器使用相应的路由策略 来处理连接,使其连接到正确的MySQL数据库服务器

[root@mysql-node1 ~]# ls
anaconda-ks.cfg                           mysql-router-community-8.4.0-1.el7.x86_64.rpm
initial-setup-ks.cfg                      passwd
libtirpc-devel-0.2.4-0.16.el7.x86_64.rpm  ren.sql
mysql-5.7.44                              repl.sql
mysql-boost-5.7.44.tar.gz

####安装mysql-router
[root@mysql-node1 ~]# rpm -ivh mysql-router-community-8.4.0-1.el7.x86_64.rpm 
warning: mysql-router-community-8.4.0-1.el7.x86_64.rpm: Header V4 RSA/SHA256 Signature, key ID a8d3785c: NOKEY
Preparing...                          ################################# [100%]
	package mysql-router-community-8.4.0-1.el7.x86_64 is already installed
[root@mysql-node1 ~]# vim /etc/mysqlrouter/mysqlrouter.conf 
[routing:ro]              ###设置只读,但是不会真实控制mysql只读
bind_address = 0.0.0.0		###监控端口
bind_port = 7001			###访问哪个端口可以访问数据库
destinations=172.25.250.10:3306,172.25.250.20:3306,172.25.250.30:3306                     ###真正访问的是谁
routing_strategy = round-robin          ###轮询

[routing:rw]
bind_address = 0.0.0.0
bind_port = 7002
destinations = 172.25.250.30:3306,172.25.250.20:3306,172.25.250.10:3306
routing_strategy = first-available        ###谁最先选择就连谁

[root@mysql-node1 ~]# systemctl start mysqlrouter.service

测试

在mysql-node2和mysql-node3上建立测试用户 2和3建立用户一样

[root@mysql-node2 ~]# mysql -uroot -predhat

mysql> create user root@'%' identified by 'redhat';
Query OK, 0 rows affected (0.00 sec)

mysql> grant all on *.* to root@'%';
Query OK, 0 rows affected (0.00 sec)

mysql> quit
Bye

然后在所谓的router上登录,需要注意的是,router上是没有开启mysql的

[root@mysql-node1 ~]# /etc/init.d/mysqld stop
Shutting down MySQL............ SUCCESS! 
[root@mysql-node1 ~]# mysql -uroot -predhat -h 172.25.250.10 -P 7001             ####也可以7002

mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          20 |
+-------------+
1 row in set (0.00 sec)

mysql> quit
Bye
[root@mysql-node1 ~]# mysql -uroot -predhat -h 172.25.250.10 -P 7001

mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          30 |
+-------------+
1 row in set (0.01 sec)

#########轮询负载均衡

6.mysql高可用之MHA

一对多

为什么要用MHA?

解决Master的单点故障问题

在master中

####首先还原数据库,重新初始化数据库
[root@mysql-node1 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log-bin=mysql-bin
server-id=10
gtid_mode=ON
enforce-gtid-consistency=ON


[root@mysql-node1 ~]# mysql -uroot -p'itPOG(_)2:;K'
mysql> alter user root@localhost identified by 'redhat';
Query OK, 0 rows affected (0.00 sec)

###这条命令是在数据库中创建一个新用户的 SQL 语句,其具体含义如下:"CREATE USER":表示创建用户的 SQL 关键字。
###"'repl'@'%'":这里 "repl" 是用户名,"@'%'" 表示这个用户可以从任何 IP 地址连接到数据库服务器。其中 "%" 是通配符,表示任意 IP 地址。
###"IDENTIFIED BY 'ren'":表示设置这个用户的密码为 "ren"。
mysql> CREATE USER 'repl'@'%' IDENTIFIED BY 'ren';
Query OK, 0 rows affected (0.00 sec)

####授予用户 "repl" 从任何 IP 地址('%')连接到数据库服务器并拥有复制从服务器权限(REPLICATION SLAVE),且这个权限作用于所有数据库(.)
####"GRANT":是 SQL 中的授权关键字。
####"REPLICATION SLAVE":表示复制从服务器的权限,拥有这个权限的用户可以连接到主数据库服务器并从主服务器接收复制数据,从而实现数据库的复制功能。
####"ON .":表示这个权限作用于所有的数据库和所有的数据库对象。
####"TO repl@'%'":指定将这个权限授予用户 "repl",该用户可以从任何 IP 地址连接到数据库服务器。
mysql> GRANT REPLICATION SLAVE ON *.* TO repl@'%';
Query OK, 0 rows affected (0.00 sec)

###安装"rpl_semi_sync_master"的插件,插件的共享库文件名soname
###指定该插件的共享库文件名为"semisync_master.so"
###insert plugin 用于在数据库中安装插件的关键字
###"rpl_semi_sync_master":插件名称,通常与 MySQL 的半同步复制主服务器功能相关。
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.00 sec)

###将全局变量"rpl_semi_sync_master_enabled"设为1
###SET GLOBAL  表示设置一个全局的数据库服务器变量
###"rpl_semi_sync_master_enabled":这是与 MySQL 半同步复制主服务器相关的变量。当这个变量设置为 1 时,表示启用半同步复制主服务器功能。如果设置为 0,则表示禁用该功能。
###启用半同步复制主服务器功能后,主数据库服务器在提交事务时会等待至少一个从服务器确认收到事务,从而提高数据的一致性和可靠性,但可能会对性能产生一定的影响。
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | ON         |
| rpl_semi_sync_master_timeout              | 10000      |
| rpl_semi_sync_master_trace_level          | 32         |
| rpl_semi_sync_master_wait_for_slave_count | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.01 sec)

mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 0     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 0     |
| Rpl_semi_sync_master_no_times              | 0     |
| Rpl_semi_sync_master_no_tx                 | 0     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 0     |
| Rpl_semi_sync_master_tx_wait_time          | 0     |
| Rpl_semi_sync_master_tx_waits              | 0     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 0     |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)

mysql> quit
Bye

在slave1和slave2中

[root@mysql-node1 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
log-bin=mysql-bin
server-id=20   和30
gtid_mode=ON
enforce-gtid-consistency=ON

####同样还原数据库,重新初始化数据库
[root@mysql-node2 ~]# mysqld --user=mysql --initialize
[root@mysql-node2 ~]# /etc/init.d/mysqld start
[root@mysql-node2 ~]# mysql -uroot -p'/FWosRqiv2Vg'

mysql> alter user root@localhost identified by 'redhat';
Query OK, 0 rows affected (0.00 sec)

mysql> change master to master_host='172.25.250.10',master_user='repl',master_password='ren',master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.01 sec)

mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
Query OK, 0 rows affected (0.00 sec)

mysql> stop slave io_thread;
Query OK, 0 rows affected (0.00 sec)

mysql> start slave io_thread;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW VARIABLES LIKE 'rpl_semi_sync%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | ON         |
| rpl_semi_sync_master_timeout              | 10000      |
| rpl_semi_sync_master_trace_level          | 32         |
| rpl_semi_sync_master_wait_for_slave_count | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
+-------------------------------------------+------------+
6 rows in set (0.01 sec)

mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 0     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 0     |
| Rpl_semi_sync_master_no_times              | 0     |
| Rpl_semi_sync_master_no_tx                 | 0     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 0     |
| Rpl_semi_sync_master_tx_wait_time          | 0     |
| Rpl_semi_sync_master_tx_waits              | 0     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 0     |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)

安装MHA所需要的软件

[root@mysql-mha ~]# ls
anaconda-ks.cfg  initial-setup-ks.cfg  MHA-7.zip
[root@mysql-mha ~]# rz -E
rz waiting to receive.
[root@mysql-mha ~]# rz -E
rz waiting to receive.
[root@mysql-mha ~]# unzip MHA-7.zip 
Archive:  MHA-7.zip
   creating: MHA-7/
  inflating: MHA-7/mha4mysql-manager-0.58-0.el7.centos.noarch.rpm  
  inflating: MHA-7/mha4mysql-manager-0.58.tar.gz  
  inflating: MHA-7/mha4mysql-node-0.58-0.el7.centos.noarch.rpm  
  inflating: MHA-7/perl-Config-Tiny-2.14-7.el7.noarch.rpm  
  inflating: MHA-7/perl-Email-Date-Format-1.002-15.el7.noarch.rpm  
  inflating: MHA-7/perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm  
  inflating: MHA-7/perl-Mail-Sender-0.8.23-1.el7.noarch.rpm  
  inflating: MHA-7/perl-Mail-Sendmail-0.79-21.el7.noarch.rpm  
  inflating: MHA-7/perl-MIME-Lite-3.030-1.el7.noarch.rpm  
  inflating: MHA-7/perl-MIME-Types-1.38-2.el7.noarch.rpm  
  inflating: MHA-7/perl-Net-Telnet-3.03-19.el7.noarch.rpm  
  inflating: MHA-7/perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm  
[root@mysql-mha ~]# ls
anaconda-ks.cfg       master_ip_failover       MHA-7
initial-setup-ks.cfg  master_ip_online_change  MHA-7.zip
[root@mysql-mha ~]# cd MHA-7/
[root@mysql-mha MHA-7]# ls
mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
mha4mysql-manager-0.58.tar.gz
mha4mysql-node-0.58-0.el7.centos.noarch.rpm
perl-Config-Tiny-2.14-7.el7.noarch.rpm
perl-Email-Date-Format-1.002-15.el7.noarch.rpm
perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm
perl-Mail-Sender-0.8.23-1.el7.noarch.rpm
perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
perl-MIME-Lite-3.030-1.el7.noarch.rpm
perl-MIME-Types-1.38-2.el7.noarch.rpm
perl-Net-Telnet-3.03-19.el7.noarch.rpm
perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
[root@mysql-mha MHA-7]# ssh-keygen 

[root@mysql-mha MHA-7]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@172.25.250.10
[root@mysql-mha MHA-7]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@172.25.250.20
[root@mysql-mha MHA-7]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@172.25.250.20

[root@mysql-mha MHA-7]# vim /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.250.40   mysql-mha
172.25.250.20   mysql-node2
172.25.250.10   mysql-node1
172.25.250.30   mysql-node3


[root@mysql-mha MHA-7]# ls
mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
mha4mysql-manager-0.58.tar.gz
mha4mysql-node-0.58-0.el7.centos.noarch.rpm
perl-Config-Tiny-2.14-7.el7.noarch.rpm
perl-Email-Date-Format-1.002-15.el7.noarch.rpm
perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm
perl-Mail-Sender-0.8.23-1.el7.noarch.rpm
perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
perl-MIME-Lite-3.030-1.el7.noarch.rpm
perl-MIME-Types-1.38-2.el7.noarch.rpm
perl-Net-Telnet-3.03-19.el7.noarch.rpm
perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
[root@mysql-mha MHA-7]# yum install *.rpm -y


[root@mysql-mha MHA-7]# scp mha4mysql-node-0.58-0.el7.centos.noarch.rpm root@172.25.250.10:/root
mha4mysql-node-0.58-0.el7.centos.noarch.rpm               100%   35KB  24.6MB/s   00:00    
[root@mysql-mha MHA-7]# scp mha4mysql-node-0.58-0.el7.centos.noarch.rpm root@172.25.250.20:/root
mha4mysql-node-0.58-0.el7.centos.noarch.rpm               100%   35KB  29.6MB/s   00:00    
[root@mysql-mha MHA-7]# scp mha4mysql-node-0.58-0.el7.centos.noarch.rpm root@172.25.250.30:/root
mha4mysql-node-0.58-0.el7.centos.noarch.rpm               100%   35KB  19.7MB/s   00:00    
[root@mysql-mha MHA-7]# masterha_
masterha_check_repl       masterha_conf_host        masterha_master_switch
masterha_check_ssh        masterha_manager          masterha_secondary_check
masterha_check_status     masterha_master_monitor   masterha_stop
[root@mysql-mha MHA-7]# cd
[root@mysql-mha ~]# masterha_manager --help
Usage:
    masterha_manager --global_conf=/etc/masterha_default.cnf
    --conf=/usr/local/masterha/conf/app1.cnf

    See online reference
    (http://code.google.com/p/mysql-master-ha/wiki/masterha_manager) for
    details.

[root@mysql-mha ~]# mkdir /etc/masterha
[root@mysql-mha ~]# cd MHA-7/

[root@mysql-mha MHA-7]# ls
mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
mha4mysql-manager-0.58.tar.gz
mha4mysql-node-0.58-0.el7.centos.noarch.rpm
perl-Config-Tiny-2.14-7.el7.noarch.rpm
perl-Email-Date-Format-1.002-15.el7.noarch.rpm
perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm
perl-Mail-Sender-0.8.23-1.el7.noarch.rpm
perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
perl-MIME-Lite-3.030-1.el7.noarch.rpm
perl-MIME-Types-1.38-2.el7.noarch.rpm
perl-Net-Telnet-3.03-19.el7.noarch.rpm
perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
[root@mysql-mha MHA-7]# tar zxf mha4mysql-manager-0.58.tar.gz 
[root@mysql-mha MHA-7]# cd mha4mysql-manager-0.58/
[root@mysql-mha mha4mysql-manager-0.58]# ls
AUTHORS  COPYING  lib          MANIFEST       README  samples  tests
bin      debian   Makefile.PL  MANIFEST.SKIP  rpm     t
[root@mysql-mha mha4mysql-manager-0.58]# cd samples/conf/
[root@mysql-mha conf]# ls
app1.cnf  masterha_default.cnf
[root@mysql-mha conf]# cat masterha_default.cnf app1.cnf > /etc/masterha/app1.cnf
[root@mysql-mha conf]# cd /etc/masterha/
[root@mysql-mha masterha]# ls
app1.cnf


[root@mysql-mha masterha]# vim app1.cnf 
[server default]
user=root
password=ren
ssh_user=root
master_binlog_dir= /data/mysql
remote_workdir=/tmp
secondary_check_script= masterha_secondary_check -s 172.25.250.10 -s 172.25.250.11
ping_interval=3
# master_ip_failover_script= /script/masterha/master_ip_failover
# shutdown_script= /script/masterha/power_manager
# report_script= /script/masterha/send_report
# master_ip_online_change_script= /script/masterha/master_ip_online_change
[server default]
manager_workdir=/etc/masterha
manager_log=/etc/masterha/manager.log

[server1]
hostname=172.25.250.10
candidate_master=1
check_repl_delay=0

[server2]
hostname=172.25.250.20
candidate_master=1
check_repl_delay=0

[server3]
hostname=172.25.250.30
no_master=1

[root@mysql-mha ~]# scp id_rsa root@172.25.250.10:/root/.ssh/
[root@mysql-mha ~]# scp id_rsa root@172.25.250.20:/root/.ssh/
[root@mysql-mha ~]# scp id_rsa root@172.25.250.30:/root/.ssh/


[root@mysql-mha ~]# masterha_check_ssh --conf=/etc/masterha/app1.cnf

[root@mysql-mha ~]# masterha_check_repl --conf=/etc/masterha/app1.cnf

6.1MHA的故障切换

6.1.1MHA的故障切换过程

1.配置文件检查阶段,这个阶段检查整个集群配置文件配置

2.宕机的master处理,这个阶段包括虚拟ip摘除阶段,主机关机操作

3.复制dead master 和最新slave相差的relay log 并保存到MHA manger具体目录下

4.识别含有最新更新的slave

5,应用从master保存的二进制日志事件(binlog events)

6.提升一个slave为新的master进行复制

7.使其他的slave连接新的master进行复制

6.1.1.1手动故障切换(master未出现故障)
###在master数据节点还在正常工作情况下
[root@mysql-mha ~]# masterha_master_switch \
--conf=/etc/masterha/app1.cnf \ 		#指定配置文件
--master_state=alive \ 					#指定master节点状态
--new_master_host=172.25.254.20 \		 #指定新master节点
--new_master_port=3306 \ 					#执行新master节点端口
--orig_master_is_new_slave \ 			#原始master会变成新的slave
--running_updates_limit=10000 			#切换的超时时间

####上面是原master是172.25.250.10,新master是172.25.250.20
####如果想要切换回来的话
[root@mysql-mha ~]# masterha_master_switch \
--conf=/etc/masterha/app1.cnf \ 		#指定配置文件
--master_state=alive \ 					#指定master节点状态
--new_master_host=172.25.254.10 \		 #指定新master节点
--new_master_port=3306 \ 					#执行新master节点端口
--orig_master_is_new_slave \ 			#原始master会变成新的slave
--running_updates_limit=10000 			#切换的超时时间
6.1.1.2master故障手动切换
#模拟master故障
[root@mysql-node2 mysql]# /etc/init.d/mysqld stop


#在MHA-master中做故障切换
[root@mysql-mha masterha]# masterha_master_switch --master_state=dead --conf=/etc/masterha/app1.cnf --dead_master_host=192.168.56.12 --dead_master_port=3306 --new_master_host=192.168.56.11 -new_master_port=3306 --ignore_last_failover

--ignore_last_failover 表示忽略在/etc/masterha/目录中在切换过程中生成的锁文件


###恢复故障mysql节点
[root@mysql-node20 tmp]# /etc/init.d/mysqld start
Starting MySQL. SUCCESS!
[root@mysql-node20 tmp]# mysql -uroot -predhat
mysql> CHANGE MASTER TO MASTER_HOST='172.25.254.10', MASTER_USER='repl', 
MASTER_PASSWORD='ren', MASTER_AUTO_POSITION=1;

mysql> show slave\G

####测试一主两从是否正常
[root@mysql-mha masterha]# masterha_check_repl --conf=/etc/masterha/app1.cnf
6.1.1.3自动切换

如果要自动切换,需要删掉切换锁文件

[root@mysql-mha masterha]# rm -fr app1.failover.complete #删掉切换锁文件

#监控程序通过指定配置文件监控master状态,当master出问题后自动切换并退出避免重复做故障切换
[root@mysql-mha masterha]# masterha_manager --conf=/etc/masterha/app1.cnf 
[root@mysql-mha masterha]# cat /etc/masterha/manager.log

现在master是172.25.250.10,我们把master停掉模拟master故障

[root@mysql-node1 mysql]# /etc/init.d/mysqld stop

###停掉之后我们监控和看日志
[root@mysql-mha masterha]# masterha_manager --conf=/etc/masterha/app1.cnf 
[root@mysql-mha masterha]# cat /etc/masterha/manager.log

###切换完成新master为172.25.250.20

然后我们恢复故障节点172.25.250.10,这个节点就会变成slave,但是不会自动变

[root@mysql-mha ~]# masterha_check_repl --conf=/etc/masterha/app1.cnf

###[root@mysql-node1 mysql]# /etc/init.d/mysqld start


[root@mysql-node1 mysql]# mysql -u -predhat
mysql> slave stop
mysql> CHANGE MASTER TO MASTER_HOST='172.25.254.20', MASTER_USER='repl', MASTER_PASSWORD='ren',MASTER_AUTO_POSITION=1
mysql> slave start
mysql> show slave status\G;

###检测
[root@mysql-mha ~]# masterha_check_repl --conf=/etc/masterha/app1.cnf

###需要注意的时,自动切换完后都会生成一个锁文件,如果再次启动自动切换的监控,需要把它清除掉
[root@mysql-mha masterha]# rm -rf app1.failover.complete manager.log

6.2为MHA添加VIP功能

[root@mysql-mha ~]# ls
anaconda-ks.cfg       master_ip_failover       MHA-7
initial-setup-ks.cfg  master_ip_online_change  MHA-7.zip
[root@mysql-mha ~]# file master_ip_failover 
master_ip_failover: Perl script, ASCII text executable
[root@mysql-mha ~]# cp master_ip_* /usr/local/bin/master_ip_*
[root@mysql-mha ~]# chmod +x /usr/local/bin/master_ip_*

####修改脚本在脚本中只需要修改下vip
[root@mysql-mha ~]# vim /usr/local/bin/master_ip_failover
my $vip = '172.25.250.100/24';
my $ssh_start_vip = "/sbin/ip addr add $vip dev eth0";
my $ssh_stop_vip = "/sbin/ip addr del $vip dev eth0";

[root@mysql-mha ~]# vim /usr/local/bin/master_ip_online_change
my $vip = '172.25.250.100/24';
my $ssh_start_vip = "/sbin/ip addr add $vip dev eth0";
my $ssh_stop_vip = "/sbin/ip addr del $vip dev eth0";
my $exit_code = 0;

[root@mysql-mha ~]# vim /etc/masterha/app1.cnf 
[server default]
user=root
password=ren
ssh_user=root
master_binlog_dir= /data/mysql
remote_workdir=/tmp
secondary_check_script= masterha_secondary_check -s 172.25.250.10 -s 172.25.250.11
ping_interval=3
 master_ip_failover_script= /usr/local/master_ip_failover
# shutdown_script= /script/masterha/power_manager
# report_script= /script/masterha/send_report
 master_ip_online_change_script= /usr/local/bin/master_ip_online_change
[server default]
manager_workdir=/etc/masterha
manager_log=/etc/masterha/manager.log

[server1]
hostname=172.25.250.10
candidate_master=1
check_repl_delay=0

[server2]
hostname=172.25.250.20
candidate_master=1
check_repl_delay=0

[server3]
hostname=172.25.250.30
no_master=1

6.2.1在master上添加VIP

目前master在172.25.250.20上

ip a a 172.25.250.100/24 dev eth0

###然后我们模拟master故障,关闭master的mysql
/etc/init.d/mysqld stop 

##我们可以观察到VIP由原来的172.25.250.10(原master)转到172.25.250.20(新master)

###我们恢复故障机
/etc/init.d/mysqld start
mysql -uroot -predhat

mysql> stop slave;
mysql> CHANGE MASTER TO MASTER_HOST='172.25.254.20', MASTER_USER='repl', MASTER_PASSWORD='ren',MASTER_AUTO_POSITION=1
mysql> start slave;
mysql> show slave status\G;

手动切换后查看VIP变化

[root@mysql-mha masterha]# masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=172.25.254.10 --new_master_port=3306 --
orig_master_is_new_slave --running_updates_limit=10000

###旧master172.25.250.20 新master172.25.250.10
VIP转到172.25.250.10上
相关推荐
尘浮生7 分钟前
Java项目实战II基于微信小程序的校运会管理系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
偶尔。5359 分钟前
什么是事务?事务有哪些特性?
数据库·oracle
安迁岚11 分钟前
【SQL Server】华中农业大学空间数据库实验报告 实验六 视图
数据库·sql·mysql·oracle·实验报告
xoxo-Rachel20 分钟前
(超级详细!!!)解决“com.mysql.jdbc.Driver is deprecated”警告:详解与优化
java·数据库·mysql
JH30731 小时前
Oracle与MySQL中CONCAT()函数的使用差异
数据库·mysql·oracle
蓝染-惣右介1 小时前
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
java·数据库·tomcat·mybatis
冷心笑看丽美人1 小时前
Spring框架特性及包下载(Java EE 学习笔记04)
数据库
努力的悟空1 小时前
国土变更调查拓扑错误自动化修复工具的研究
运维·自动化
武子康2 小时前
Java-07 深入浅出 MyBatis - 一对多模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据库·sql·mybatis·springboot
代码吐槽菌2 小时前
基于SSM的毕业论文管理系统【附源码】
java·开发语言·数据库·后端·ssm