18.MYSQL数据库(2)

6 MySQL 集群 Cluster

服务性能扩展方式

  • Scale Up,向上扩展,垂直扩展
  • Scale Out,向外扩展,横向扩展

6.1 MySQL 主从复制

6.1.1 主从复制架构和原理

6.1.1.1 MySQL的主从复制

  • 读写分离
  • 复制:每个节点都有相同的数据集,向外扩展,基于二进制日志的单向复制

6.1.1.2 复制的功用

  • 负载均衡读操作
  • 备份
  • 高可用和故障切换
  • 数据分布
  • MySQL升级

6.1.1.3 复制架构

一主一从复制架构
18.MYSQL数据库(2)_第1张图片
一主多从复制架构
18.MYSQL数据库(2)_第2张图片
6.1.1.4 主从复制原理
18.MYSQL数据库(2)_第3张图片
主从复制相关线程

  • 主节点:
    dump Thread:为每个Slave的I/O Thread启动一个dump线程,用于向其发送binary log events

  • 从节点:
    I/O Thread:向Master请求二进制日志事件,并保存于中继日志中
    SQL Thread:从中继日志中读取日志事件,在本地完成重放

跟复制功能相关的文件:

  • master.info:用于保存slave连接至master时的相关信息,例如账号、密码、服务器地址等

  • relay-log.info:保存在当前slave节点上已经复制的当前二进制日志和本地relay log日志的对应关系

  • mysql-relay-bin.00000#: 中继日志,保存从主节点复制过来的二进制日志,本质就是二进制日志

说明:

MySQL8.0 取消master.info和relay-log.info文件

6.1.1.5 主从复制特点

  • 异步复制: 客户端性能良好
  • 主从数据不一致比较常见

6.1.1.6 各种复制架构

18.MYSQL数据库(2)_第4张图片

  • 一Master/一Slave
  • 一主多从
  • 从服务器还可以再有从服务器
  • Master/Master
  • 一从多主:适用于多个不同数据库
  • 环状复制

复制需要考虑二进制日志事件记录格式

  • STATEMENT(5.0之前), Mariadb5.5 默认使用此格式
  • ROW(5.1之后,推荐),MySQL 8.0 默认使用此格式
  • MIXED: Mariadb10.3 默认使用此格式

6.1.2 实现主从复制配置

官网参考

https://dev.mysql.com/doc/refman/8.0/en/replication-configuration.html
https://dev.mysql.com/doc/refman/5.7/en/replication-configuration.html
https://dev.mysql.com/doc/refman/5.5/en/replication-configuration.html
https://mariadb.com/kb/en/library/setting-up-replication/

主节点配置:

(1) 启用二进制日志

[mysqld]
log_bin

(2) 为当前节点设置一个全局惟一的ID号

 [mysqld]
server-id=#
log-basename=master  #可选项,设置datadir中日志名称,确保不依赖主机名

说明:

server-id的取值范围
1 to 4294967295 (>= MariaDB 10.2.2),默认值为1
0 to 4294967295 (<= MariaDB 10.2.1),默认值为0,如果从节点为0,所有master都将拒绝此
slave的连接

(3) 查看从二进制日志的文件和位置开始进行复制

SHOW MASTER STATUS;

(4) 创建有复制权限的用户账号

GRANT REPLICATION SLAVE  ON *.* TO 'repluser'@'HOST' IDENTIFIED BY 'replpass';

从节点配置:
(1) 启动中继日志

[mysqld]
server_id=# #为当前节点设置一个全局惟的ID号
log-bin
read_only=ON #设置数据库只读,针对supper user无效
relay_log=relay-log #relay log的文件路径,默认值hostname-relay-bin
relay_log_index=relay-log.index  #默认值hostname-relay-bin.index

(2) 使用有复制权限的用户账号连接至主服务器,并启动复制线程

CHANGE MASTER TO MASTER_HOST='masterhost',
MASTER_USER='repluser',
MASTER_PASSWORD='replpass',
MASTER_LOG_FILE='mariadb-bin.xxxxxx',
MASTER_LOG_POS=#;
START SLAVE [IO_THREAD|SQL_THREAD];
SHOW SLAVE STATUS;

范例:新建主从复制
18.MYSQL数据库(2)_第5张图片

#主节点
[root@master ~]#dnf -y install mysql-server
[root@master ~]#vim /etc/my.cnf.d/mysql-server.cnf
[mysqld]
server-id=8
log-bin=/mysql/binlog/mysql-bin
[root@master ~]#systemctl start  mysqld.service 

[root@master ~]#mysql
#查看二进制文件和位置
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000002 |      155 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

#创建复制用户是,MySQL 8.0 需要分成下面两步实现
mysql> create user 'user'@'10.0.0.%' identified by '123456';
Query OK, 0 rows affected (0.01 sec)

mysql> grant replication slave on *.* to 'user'@'10.0.0.%';
Query OK, 0 rows affected (0.01 sec)

MariaDB [(none)]> grant replication slave on *.* to user@'10.0.0.%'
identified by '123456';



#从节点
[root@slave ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=18 

[root@slave ~]# systemctl start  mysqld.service 
[root@slave1 ~]#mysql
mysql> help change master to
mysql> CHANGE MASTER TO
    ->   MASTER_HOST='10.0.0.8',
    ->   MASTER_USER='user',
    ->   MASTER_PASSWORD='123456',
    ->   MASTER_PORT=3306,
    ->   MASTER_LOG_FILE='mysql-bin.000002',
    ->   MASTER_LOG_POS=155;
Query OK, 0 rows affected, 2 warnings (0.13 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: 10.0.0.8
                  Master_User: user
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000003
          Read_Master_Log_Pos: 752
               Relay_Log_File: centos8-relay-bin.000002
                Relay_Log_Pos: 920
        Relay_Master_Log_File: mysql-bin.000003
             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: 752
              Relay_Log_Space: 1131
              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: 8
                  Master_UUID: c591d6c8-7c9b-11eb-bcef-000c29c066b6
             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: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
       Master_public_key_path: 
        Get_master_public_key: 0
            Network_Namespace: 
1 row in set (0.00 sec)




mysql> mysql> show processlist;
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
| Id | User            | Host      | db   | Command | Time | State                                                  | Info             |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
|  4 | event_scheduler | localhost | NULL | Daemon  |  595 | Waiting on empty queue                                 | NULL             |
|  8 | root            | localhost | NULL | Query   |    0 | starting                                               | show processlist |
|  9 | system user     |           | NULL | Connect |   61 | Waiting for master to send event                       | NULL             |
| 10 | system user     |           | NULL | Query   |  750 | Slave has read all relay log; waiting for more updates | NULL             |
+----+-----------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
4 rows in set (0.00 sec)

范例:主服务器非新建时,主服务器运行一段时间后,新增从节点服务器
18.MYSQL数据库(2)_第6张图片
如果主节点已经运行了一段时间,且有大量数据时,如何配置并启动slave节点

  • 通过备份恢复数据至从服务器
  • 复制起始位置为备份时,二进制日志文件及其POS
#在主服务器完全备份
[root@master ~]#mysqldump -uroot --single-transaction --master-data=1 -F -A > /backup/all_`date +%F`.sql
[root@master ~]#ll /backup/all_2021-03-04.sql 
-rw-r--r-- 1 root root 1010316 Mar  4 15:35 /backup/all_2021-03-04.sql

#主节点备份的内容复制到从节点
[root@master ~]#scp /backup/all_2021-03-04.sql  10.0.0.118:/backup


#配置从节点,从完全备份的位置之后开始复制
[root@slave ~]# vim /backup/all_2021-03-04.sql
#补全
CHANGE MASTER TO
  MASTER_HOST='10.0.0.8',
  MASTER_USER='user',
  MASTER_PASSWORD='123456',
  MASTER_PORT=3306,
 MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=155;

#将完全备份还原到新的从节点
mysql> set sql_log_bin=0;
mysql> source /backup/all_2021-03-04.sql;

#开启线程
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

mysql> set sql_log_bin=1;

6.1.3 主从复制相关

6.1.3.1 限制从服务器为只读

read_only=ON
#注意:此限制对拥有SUPER权限的用户均无效

注意:以下命令会阻止所有用户, 包括主服务器复制的更新

FLUSH TABLES WITH READ LOCK;

6.1.3.2 在从节点清除信息

注意:以下都需要先 STOP SLAVE

RESET SLAVE #从服务器清除master.info ,relay-log.info, relay log ,开始新的relay log
RESET SLAVE  ALL #清除所有从服务器上设置的主服务器同步信息,如HOST,PORT, USER和PASSWORD 等

6.3.1.3 复制错误解决方法

可以在从服务器忽略几个主服务器的复制事件,此为global变量,或指定跳过事件的ID

注意: Centos 8.1以上版本上的MariaDB10.3主从节点同时建同名的库和表不会冲突,建主键记录会产
生冲突

#系统变量,指定跳过复制事件的个数
SET GLOBAL sql_slave_skip_counter = N
#服务器选项,只读系统变量,指定跳过事件的ID
[mysqld]
slave_skip_errors=1007|ALL

范例:复制冲突的解决

#CentOS7上Mariadb5.5 在slave创建库和表,再在master上创建同名的库和表,会出现复制冲突,而在CentOS8上的Mariadb10.3上不会冲突
#如果添加相同的主键记录都会冲突
mysql> mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 10.0.0.8
                  Master_User: user
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000003
          Read_Master_Log_Pos: 1361
               Relay_Log_File: centos8-relay-bin.000002
                Relay_Log_Pos: 920
        Relay_Master_Log_File: mysql-bin.000003
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 1062		 #错误编码
                   Last_Error: Could not execute Write_rows event on table hellodb.teachers; Duplicate entry '7' for key 'teachers.PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000003, end_log_pos 1026
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 752
              Relay_Log_Space: 1740
              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: NULL
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 1062
               Last_SQL_Error: Could not execute Write_rows event on table hellodb.teachers; Duplicate entry '7' for key 'teachers.PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000003, end_log_pos 1026
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 8
                  Master_UUID: c591d6c8-7c9b-11eb-bcef-000c29c066b6
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: 
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 210305 00:27:43
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
       Master_public_key_path: 
        Get_master_public_key: 0
            Network_Namespace: 
1 row in set (0.00 sec)




#方法1
mysql> stop slave;
mysql> set global sql_slave_skip_counter=1;
mysql> start slave;


#方法2
[root@slave1 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
slave_skip_errors=1062|ALL                                                                                              
[root@slave1 ~]#systemctl restart mariadb

#手动修复数据
mysql> update teachers set name='xiaohong',age=67,gender='M' where tid=7;

6.3.1.4 START SLAVE 语句,指定执到特定的点

START SLAVE [thread_types]
START SLAVE [SQL_THREAD] UNTIL   MASTER_LOG_FILE = 'log_name', MASTER_LOG_POS =
log_pos
START SLAVE [SQL_THREAD] UNTIL   RELAY_LOG_FILE = 'log_name', RELAY_LOG_POS =
log_pos
thread_types:
   [thread_type [, thread_type] ... ]
thread_type: IO_THREAD | SQL_THREAD

6.3.1.5 保证主从复制的事务安全

参看https://mariadb.com/kb/en/library/server-system-variables/
在master节点启用参数:

sync_binlog=1 #每次写后立即同步二进制日志到磁盘,性能差
#如果用到的为InnoDB存储引擎:
innodb_flush_log_at_trx_commit=1 #每次事务提交立即同步日志写磁盘
sync_master_info=# #次事件后master.info同步到磁盘
innodb_support_xa=ON #分布式事务MariaDB10.3.0废除

在slave节点启用服务器选项:

skip-slave-start=ON #不自动启动slave

在slave节点启用参数:

sync_relay_log=# #次写后同步relay log到磁盘
sync_relay_log_info=# #次事务后同步relay-log.info到磁盘

6.3.1.6 实战案例:当master服务器宕机,提升一个slave成为新的master

当master(10.0.0.8)宕机后,提升slave(10.0.0.108)称为新的master

#找到哪个从节点的数据库是最新,让它成为新master
#观察每个从节点
mysql> show slave status\G
 Master_Log_File: mysql-bin.000009
 Read_Master_Log_Pos: 55492996


#提升从节点为主节点,新master修改配置文件,关闭read-only配置
[root@slave1 ~]#vim /etc/my.cnf.d/mysql-server.cnf
[mysqld]
server-id=108
read-only=OFF
log-bin=/mysql/binlog/mysql-bin

#清除旧的master复制信息
mysql> set global read_only=off;
mysql> stop slave;
mysql> reset slave all;

#在新master(slave1)完全备份
[root@slave1 ~]#mysqldump -uroot --single-transaction --master-data=1 -F -A > /backup/all_`date +%F`.sql
[root@slave1 ~]#ll /backup/all_2021-03-04.sql 
-rw-r--r-- 1 root root 1010316 Mar  4 15:35 /backup/all_2021-03-04.sql

#主节点备份的内容复制到从节点
[root@slave1 ~]#scp /backup/all_2021-03-04.sql  10.0.0.118:/backup

#其它所有 slave 重新还原数据库,指向新的master
[root@slave2 ~]# vim /backup/all_2021-03-04.sql
#补全
CHANGE MASTER TO
  MASTER_HOST='10.0.0.108',
  MASTER_USER='user',
  MASTER_PASSWORD='123456',
  MASTER_PORT=3306,
 MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=155;

#将完全备份还原到新的从节点
mysql> set sql_log_bin=0;
mysql> source /backup/all_2021-03-04.sql;

#删除新主节点的从信息
[root@slave1 ~]#mysql
mysql> reset slave all;

#开启线程
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

mysql> set sql_log_bin=1;

范例:恢复未同步的数据

#故意造成数据差
master:
mysql> select count(*) from testlog;
+----------+
| count(*) |
+----------+
|   145501 |
+----------+
1 row in set (0.02 sec)

slave:
mysql> select count(*) from testlog;
+----------+
| count(*) |
+----------+
|   145500 |
+----------+
1 row in set (0.02 sec)

主节点13872872,从节点13872567
#分析旧的master 的二进制日志,将未同步到至新master的二进制日志导出来,恢复到新master,尽可能恢复数据

#导出二进制文件,复制从节点往后的数据
[root@master ~]#mysqlbinlog --start-position=13872567 /mysql/binlog/mysql-bin.000003 > /backup/inc.sql
[root@master ~]#mysqlbinlog  /mysql/binlog/mysql-bin.000004 >> /backup/inc.sql

#拷贝到从节点上
[root@slave1 ~]#scp /backup/inc.sql 10.0.0.108:/backup
[root@slave1 ~]#mysql
mysql> set sql_log_bin=0;
mysql> source /backup/inc.sql;
mysql> set sql_log_bin=1;

6.1.4 实现级联复制

需要在中间的从服务器启用以下配置 ,实现中间slave节点能将master的二进制日志在本机进行数据库更新,并且也同时更新本机的二进制,从而实现级联复制

[mysqld]
server-id=18
log_bin
log_slave_updates     #级联复制中间节点的必选项,MySQL8.0此为默认值,可以不要人为添加
read-only

案例:三台主机实现级联复制
18.MYSQL数据库(2)_第7张图片

#在10.0.0.8充当master
#在10.0.0.108充当级联slave
#在10.0.0.18充当slave

#在master实现
[root@master ~]#vim /etc/my.cnf.d/mysql-server.cnf 
[mysqld]
server-id=8
log-bin=/mysql/binlog/mysql-bin

[root@master ~]#systemctl restart mysqld.service
[root@master ~]#mysql
mysql>create user 'user'@'10.0.0.%';
mysql>grant replication slave on *.* to 'user'@'10.0.0.%';
[root@master ~]#mysqldump -A -F --single-transaction --master-data=1 > /backup/all_`date +%F`.sql
[root@master ~]#scp /backup/all_2021-03-04.sql 10.0.0.108:/backup 
[root@master ~]#scp /backup/all_2021-03-04.sql 10.0.0.118:/backup

#在中间级联slave实现
[mysqld]
server-id=108
log-bin=/mysql/binlog/mysql-bin		 #级联复制中间节点的必选项,MySQL8.0此为默认值,可以不要人为添加
read-only
log_slave-update

#还原数据库
[root@centos8 ~]# vim /backup/all_2021-03-04.sql 
CHANGE MASTER TO
  MASTER_HOST='10.0.0.8',
  MASTER_USER='user',
  MASTER_PASSWORD='123456',
  MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000012', MASTER_LOG_POS=155;

[root@centos8 ~]#mysql
mysql> set sql_log_bin=0;
mysql> source /backup/all.sql
mysql> show master logs;  #记录二进制位置,给第三个节点使用  
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 | 199718875 | No        |
+---------------+-----------+-----------+

mysql> set sql_log_bin=1;
mysql> start slave;


#在第三个节点slave上实现
[root@centos8 ~]# vim /etc/my.cnf.d/mysql-server.cnf 
[mysqld]
server-id=118
read_only
[root@centos8 ~]#systemctl restart mysqld.service
[root@centos8 ~]# mysql < /backup/all_2021-03-04.sql 

[root@centos8 ~]# vim /backup/all_2021-03-04.sql
CHANGE MASTER TO
MASTER_HOST='中间节点的IP',10.0.0.108
MASTER_USER='user',
MASTER_PASSWORD='123456',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS= 199718875; #中间节点
mysql> set sql_log_bin=0;
mysql> source /backup/all.sql
mysql> set sql_log_bin=1;

6.1.5 主主复制

主主复制:两个节点,都可以更新数据,并且互为主从

容易产生的问题:数据不一致;因此慎用

考虑要点:自动增长id

配置一个节点使用奇数id

auto_increment_offset=1   #开始点
auto_increment_increment=2 #增长幅度

另一个节点使用偶数id

auto_increment_offset=2
auto_increment_increment=2

主主复制的配置步骤:
(1) 各节点使用一个惟一server_id

(2) 都启动binary log和relay log

(3) 创建拥有复制权限的用户账号

(4) 定义自动增长id字段的数值范围各为奇偶

(5) 均把对方指定为主节点,并启动复制线程

范例:实现两个节点的主主复制模型

#在第一个master节点上实现
[root@master1 ~]#vim /etc/my.cnf.d/mysql-server.cnf
[mysqld]
server-id=8
log-bin
[root@mater1 ~]#systemctl restart mysqld

[root@mater1 ~]#mysql
mysql> create user repluser@'10.0.0.%' identified by '123456';
mysql> grant replication slave on *.* to repluser@'10.0.0.%';

[root@mater1 ~]#mysqldump -A --single-transaction --master-data=1 -F > all.sql
[root@mater1 ~]#scp all.sql 10.0.0.108:




#在第二个master节点上实现
[rootmaster2 ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]                                                                        
server-id=18


[root@master2 ~]# vim all.sql 
  CHANGE MASTER TO 
  MASTER_HOST='10.0.0.8',
  MASTER_USER='repluser',
  MASTER_PASSWORD='123456',
  MASTER_PORT=3306,
  MASTER_LOG_FILE='mater1-bin.000002', MASTER_LOG_POS=155;
[root@master2 ~]# systemctl  start mysqld     
[root@master2 ~]# mysql < all.sql
mysql> start slave;

mysql> show master logs;		#记录下来最新的一条
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |    979011 | No        |
| binlog.000002 |     12191 | No        |
+---------------+-----------+-----------+
2 rows in set (0.00 sec)

#在第一个master节点上实现
mysql> CHANGE MASTER TO 
    ->   MASTER_HOST='10.0.0.108',
    ->   MASTER_USER='repluser',
    ->   MASTER_PASSWORD='123456',
    ->   MASTER_PORT=3306,
    ->   MASTER_LOG_FILE='binlog.000002', MASTER_LOG_POS=12191;
Query OK, 0 rows affected, 2 warnings (0.13 sec)
mysql> start slave;
 
#两个节点同时插入数据
mysql> create table t2 (id int); 
容易造成数据冲突
Last_Errno: 1050
Last_SQL_Error: Error 'Table 't2' already exists' on query. Default database: 'hellodb'. Query: 'create table t2 (id int)'

#解决方法
1.mysql> stop slave;
mysql> SET GLOBAL sql_slave_skip_counter = 1;
mysql> start slave;

2.[root@master2 ~]# vim /etc/my.cnf.d/mysql-server.cnf 
[mysqld]
slave_skip_errors=1050|ALL
[root@master2 ~]# systemctl restart mysqld.service

6.1.6 半同步复制

默认情况下,MySQL的复制功能是异步的,异步复制可以提供最佳的性能,主库把binlog日志发送给从库即结束,并不验证从库是否接收完毕。这意味着当主服务器或从服务器端发生故障时,有可能从服务器没有接收到主服务器发送过来的binlog日志,这就会造成主服务器和从服务器的数据不一致,甚至在恢复时造成数据的丢失
18.MYSQL数据库(2)_第8张图片
半同步复制实现:
官方文档:

https://dev.mysql.com/doc/refman/8.0/en/replication-semisync.html
https://dev.mysql.com/doc/refman/5.7/en/replication-semisync.html
https://mariadb.com/kb/en/library/semisynchronous-replication/ 

范例: CentOS8 在MySQL8.0 实现半同步复制

#查看插件文件
[root@centos8 ~]#rpm -ql mysql-server |grep semisync
/usr/lib64/mysql/plugin/semisync_master.so
/usr/lib64/mysql/plugin/semisync_slave.so


#主服务器配置:
mysql> install plugin  rpl_semi_sync_master soname 'semisync_master.so';		 #永久安装插件
mysql> show plugins;		 #查看插件
mysql> set global rpl_semi_sync_master_enabled=1; #临时修改变量
mysql> set global rpl_semi_sync_master_timeout = 1000;  #超时长1s,默认值为10s
mysql> show global variables like '%semi%';
+-------------------------------------------+------------+
| 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)

#master服务器配置
[root@master ~]#vim /etc/my.cnf.d/mysql-server.cnf
[mysqld]
server-id=8
log-bin
rpl_semi_sync_master_enabled=ON 	#修改此行,需要先安装semisync_master.so插件后,再重启,否则无法启动
rpl_semi_sync_master_timeout=3000   #设置3s内无法同步,也将返回成功信息给客户端


#从服务器配置:
mysql> install plugin  rpl_semi_sync_slave soname 'semisync_slave.so';		#从节点安装插件
set global rpl_semi_sync_slave_enabled=1;	#临时修改变量
mysql> show global variables like '%semi%';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled     | ON    |
| rpl_semi_sync_slave_trace_level | 32    |
+---------------------------------+-------+
2 rows in set (0.02 sec)

#slave服务器配置
[root@slave1 ~]#vim /etc/my.cnf.d/mysql-server.cnf
[mysqld]
server-id=18
rpl_semi_sync_slave_enabled=ON #修改此行,需要先安装semisync_slave.so插件后,再重启,否则无法启动

#注意:如果已经实现主从复制,需要stop slave;start slave;

mysql> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 2     |	#半同步的信息
| 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)


#测试,master创建db3
开启slave,正常复制
mysql> create database db5;
Query OK, 1 row affected (0.00 sec)

关闭后
mysql> create database db4;
Query OK, 1 row affected (10.00 sec)

范例:CentOS 8 在Mariadb-10.3.11上实现 实现半同步复制

#在master实现,启用半同步功能
[root@master ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=8
log-bin
plugin-load-add = semisync_master
rpl_semi_sync_master_enabled=ON
rpl_semi_sync_master_timeout=3000   #设置3s内无法同步,也将返回成功信息给客户端

[root@centos8 ~]#systemctl restart mariadb
MariaDB [(none)]> SHOW GLOBAL VARIABLES LIKE '%semi%';
+---------------------------------------+--------------+
| Variable_name                         | Value       |
+---------------------------------------+--------------+
| rpl_semi_sync_master_enabled         | ON           |
| rpl_semi_sync_master_timeout         | 3000         |
| rpl_semi_sync_master_trace_level     | 32           |
| rpl_semi_sync_master_wait_no_slave   | ON           |
| rpl_semi_sync_master_wait_point       | AFTER_COMMIT |
| rpl_semi_sync_slave_delay_master     | OFF         |
| rpl_semi_sync_slave_enabled           | OFF         |
| rpl_semi_sync_slave_kill_conn_timeout | 5           |
| rpl_semi_sync_slave_trace_level       | 32           |
+---------------------------------------+--------------+
9 rows in set (0.002 sec)
MariaDB [(none)]> SHOW GLOBAL STATUS LIKE '%semi%';
+--------------------------------------------+-------+
| Variable_name                             | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 0     |
| Rpl_semi_sync_master_get_ack               | 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_request_ack           | 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     |
| Rpl_semi_sync_slave_send_ack               | 0     |
| Rpl_semi_sync_slave_status                 | OFF   |
+--------------------------------------------+-------+
18 rows in set (0.001 sec)

#在其它所有slave节点上都实现,启用半同步功能
[root@slave ~]#vim /etc/my.cnf.d/mariadb-server.cnf
[mysqld]
server-id=18
plugin_load_add = semisync_slave
rpl_semi_sync_slave_enabled=ON  
[root@slave ~]#systemctl restart mariadb
[root@slave ~]#mysql
MariaDB [(none)]>  SHOW GLOBAL VARIABLES  LIKE '%semi%';
+---------------------------------------+--------------+
| Variable_name                         | Value       |
+---------------------------------------+--------------+
| rpl_semi_sync_master_enabled         | OFF         |
| rpl_semi_sync_master_timeout         | 10000       |
| rpl_semi_sync_master_trace_level     | 32           |
| rpl_semi_sync_master_wait_no_slave   | ON           |
| rpl_semi_sync_master_wait_point       | AFTER_COMMIT |
| rpl_semi_sync_slave_delay_master     | OFF         |
| rpl_semi_sync_slave_enabled           | ON           |
| rpl_semi_sync_slave_kill_conn_timeout | 5           |
| rpl_semi_sync_slave_trace_level       | 32           |
+---------------------------------------+--------------+
9 rows in set (0.001 sec)
MariaDB [(none)]>  SHOW GLOBAL STATUS  LIKE '%semi%';
+--------------------------------------------+-------+
| Variable_name                             | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 0     |
| Rpl_semi_sync_master_get_ack               | 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_request_ack           | 0     |
| Rpl_semi_sync_master_status               | OFF   |
| 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     |
| Rpl_semi_sync_slave_send_ack               | 0     |
| Rpl_semi_sync_slave_status                 | ON   |
+--------------------------------------------+-------+
18 rows in set (0.001 sec)
MariaDB [(none)]> 

#在master上实现
MariaDB [db1]> SHOW GLOBAL STATUS LIKE '%semi%';
+--------------------------------------------+-------+
| Variable_name                             | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 2     |  #两个从节点
| Rpl_semi_sync_master_get_ack               | 4     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 4     |
| Rpl_semi_sync_master_no_times             | 1     |
| Rpl_semi_sync_master_no_tx                 | 1     |
| Rpl_semi_sync_master_request_ack           | 3     |
| Rpl_semi_sync_master_status               | ON   |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time     | 1177 |
| Rpl_semi_sync_master_tx_wait_time         | 2355 |
| Rpl_semi_sync_master_tx_waits             | 2     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx               | 2     |
| Rpl_semi_sync_slave_send_ack               | 0     |
| Rpl_semi_sync_slave_status                 | OFF   |
+--------------------------------------------+-------+
18 rows in set (0.001 sec)
#测试
#在master实现,创建数据库,立即成功
MariaDB [db1]> create database db2;
Query OK, 1 row affected (0.004 sec)
#在所有slave节点实现,停止复制线程
MariaDB [(none)]> stop slave;
Query OK, 0 rows affected (0.011 sec)
#在master实现,创建数据库,等待3s才能成功
MariaDB [db1]> create database db3;
Query OK, 1 row affected (3.003 sec)
#在任意一个slave节点实现,恢复复制线程
MariaDB [(none)]> start slave;
Query OK, 0 rows affected (0.006 sec)
#在master实现,创建数据库,立即成功
MariaDB [db1]> create database db4;
Query OK, 1 row affected (0.002 sec)
#在所有从节点停止同步线程,在主节点可以看到以下日志信息
MariaDB [db1]> stop slave;
[root@centos8 ~]#tail /var/log/mariadb/mariadb.log
2020-08-29 10:11:19 15 [Warning] IP address '10.0.0.28' could not be resolved:
Name or service not known
2020-08-29 10:11:19 15 [Note] Start binlog_dump to slave_server(28),
pos(mariadb-bin.000001, 330)
2020-08-29 10:11:19 15 [Note] Start semi-sync binlog_dump to slave (server_id:
28), pos(mariadb-bin.000001, 330)
2020-08-29 10:12:34 15 [Note] Stop semi-sync binlog_dump to slave (server_id:
28)
2020-08-29 10:16:05 17 [Note] Start binlog_dump to slave_server(28),
pos(mariadb-bin.000002, 27378670)
2020-08-29 10:16:05 17 [Note] Start semi-sync binlog_dump to slave (server_id:
28), pos(mariadb-bin.000002, 27378670)
2020-08-29 10:16:31 12 [Note] Stop semi-sync binlog_dump to slave (server_id:
18)
2020-08-29 10:16:37 17 [Note] Stop semi-sync binlog_dump to slave (server_id:
28)
2020-08-29 10:17:19 14 [Warning] Timeout waiting for reply of binlog (file:
mariadb-bin.000002, pos: 27378922), semi-sync up to file mariadb-bin.000002,
position 27378795.
2020-08-29 10:17:19 14 [Note] Semi-sync replication switched OFF.

范例:CentOS 7 实现Mariadb 5.5.65 的半同步复制

#主服务器配置:
MariaDB [(none)]>INSTALL PLUGIN rpl_semi_sync_master SONAME
'semisync_master.so';
MariaDB [(none)]>UNINSTALL PLUGIN rpl_semi_sync_master ;
MariaDB [(none)]>SHOW PLUGINS; #查看插件
MariaDB [(none)]>SET GLOBAL rpl_semi_sync_master_enabled=1;
MariaDB [(none)]>SET GLOBAL rpl_semi_sync_master_timeout = 1000;  #超时长1s,默认值
为10s
MariaDB [(none)]>SHOW GLOBAL VARIABLES LIKE '%semi%';
MariaDB [(none)]>SHOW GLOBAL STATUS LIKE '%semi%';
#从服务器配置:
MariaDB [(none)]>INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
MariaDB [(none)]>SET GLOBAL rpl_semi_sync_slave_enabled=1;

6.1.7 复制过滤器

让从节点仅复制指定的数据库,或指定数据库的指定表

复制过滤器两种实现方式:

(1) 服务器选项:主服务器仅向二进制日志中记录与特定数据库相关的事件

缺点:基于二进制还原将无法实现;不建议使用

优点: 只需要在主节点配置一次即可

注意:此项和 binlog_format相关

参看:https://mariadb.com/kb/en/library/mysqld-options/#-binlog-ignore-db

vim /etc/my.cnf
binlog-do-db=db1 #数据库白名单列表,不支持同时指定多个值,如果想实现多个数据库需多行实现
binlog-do-db=db2
binlog-ignore-db= #数据库黑名单列表

范例:设置白名单

[mysqld]
[root@mater1 ~]#vim /etc/my.cnf.d/mysql-server.cnf 
binlog-do-db=db1
binlog-do-db=db2
[root@mater1 ~]#systemctl restart mysqld.service 
mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| mater1-bin.000004 |      155 | db1,db2      |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+

(2) 从服务器SQL_THREAD在relay log中的事件时,仅读取与特定数据库(特定表)相关的事件并应用于本地

缺点:会造成网络及磁盘IO浪费,在所有从节点都要配置

优点: 不影响二进制备份还原

从服务器上的复制过滤器相关变量

replicate_do_db="db1,db2,db3" #指定复制库的白名单,变量可以指定逗号分隔的多个值,选项不支持多值,只能分别写多行实现
replicate_ignore_db= #指定复制库黑名单
replicate_do_table= #指定复制表的白名单
replicate_ignore_table= #指定复制表的黑名单
replicate_wild_do_table= foo%.bar%    #支持通配符
replicate_wild_ignore_table=

注意:跨库的更新将无法同步

6.1.8 主从复制加密

在默认的主从复制过程或远程连接到MySQL/MariaDB所有的链接通信中的数据都是明文的,外网里访问数据或则复制,存在安全隐患。

通过SSL/TLS加密的方式进行复制的方法,来进一步提高数据的安全性

官网文档:https://mariadb.com/kb/en/library/replication-with-secure-connections/

实现MySQL复制加密

  1. 生成 CA 及 master 和 slave 的证书
[root@centos8 ~]#mkdir /etc/my.cnf.d/ssl/
[root@centos8 ~]#cd /etc/my.cnf.d/ssl/
[root@centos8 ssl]#openssl genrsa 2048 > cakey.pem
Generating RSA private key, 2048 bit long modulus (2 primes)
.....................................................+++++
..............+++++
e is 65537 (0x010001)
[root@centos8 ssl]#openssl req -new -x509 -key cakey.pem --out cacert.pem -days 3650
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:beijing
Locality Name (eg, city) [Default City]:beijing
Organization Name (eg, company) [Default Company Ltd]:magedu
Organizational Unit Name (eg, section) []:opt
Common Name (eg, your name or your server's hostname) []:ca.magedu.org
Email Address []:
[root@centos8 ssl]#ls
cacert.pem  cakey.pem
[root@centos8 ssl]#openssl req -newkey rsa:2048 -nodes -keyout master.key > master.csrGenerating a RSA private key
.....................................+++++
..................................................+++++
writing new private key to 'master.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:beijing
Locality Name (eg, city) [Default City]:beijing
Organization Name (eg, company) [Default Company Ltd]:magedu
Organizational Unit Name (eg, section) []:opt
Common Name (eg, your name or your server's hostname) []:master.magedu.org
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
[root@centos8 ssl]#ls /data
[root@centos8 ssl]#ls
cacert.pem  cakey.pem  master.csr  master.key
[root@centos8 ssl]#openssl x509 -req -in master.csr  -CA cacert.pem -CAkey cakey.pem  --set_serial 01 > master.crt
Signature ok
subject=C = CN, ST = beijing, L = beijing, O = magedu, OU = opt, CN = master.magedu.org
Getting CA Private Key
[root@centos8 ssl]#ls
cacert.pem  cakey.pem  master.crt  master.csr  master.key
[root@centos8 ssl]#openssl req -newkey rsa:2048 -nodes -keyout slave.key  > slave.csr
Generating a RSA private key
..........................................................................+++++
.................+++++
writing new private key to 'slave.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:beijing
Locality Name (eg, city) [Default City]:beijing
Organization Name (eg, company) [Default Company Ltd]:magedu
Organizational Unit Name (eg, section) []:opt
Common Name (eg, your name or your server's hostname) []:slave.magedu.org
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
[root@centos8 ssl]#openssl x509 -req -in slave.csr -CA cacert.pem -CAkey cakey.pem --set_serial 02 > slave.crt
Signature ok
subject=C = CN, ST = beijing, L = beijing, O = magedu, OU = opt, CN = slave.magedu.org
Getting CA Private Key
[root@centos8 ssl]#ls
cacert.pem  master.crt  master.key  slave.csr
cakey.pem   master.csr  slave.crt   slave.key



#修改权限
[root@centos8 ~]#chown -R mysql.mysql /etc/my.cnf.d/ssl/

#如果上面没修改权限,会出现ssl无法启动和下面日志错误
mysql> show variables like '%ssl%';
+---------------------+----------------------------------+
| Variable_name       | Value                           |
+---------------------+----------------------------------+
| have_openssl       | YES                             |
| have_ssl           | DISABLED    #无法启用,正常启用为YES                     |
| ssl_ca              | /etc/my.cnf.d/ssl/cacert.pem     |
| ssl_capath          |                                 |
| ssl_cert            | /etc/my.cnf.d/ssl/master.crt     |
| ssl_cipher          |                                 |
| ssl_crl             |                                 |
| ssl_crlpath         |                                 |
| ssl_key             | /etc/my.cnf.d/ssl/master.key     |
| version_ssl_library | OpenSSL 1.1.1c FIPS  28 May 2019 |
+---------------------+----------------------------------+
10 rows in set (0.001 sec)





  1. 主服务器开启 SSL,配置证书和私钥路径(master)
[root@centos8 ~]#vim /etc/my.cnf.d/mysql-server.cnf 
[mysqld]
log-bin
server_id=8
ssl
ssl-ca=/etc/my.cnf.d/ssl/cacert.pem
ssl-cert=/etc/my.cnf.d/ssl/master.crt
ssl-key=/etc/my.cnf.d/ssl/master.key
[root@centos8 ~]#systemctl start mysqld.service 
[root@centos8 ~]#mysql
mysql> show variables like '%ssl%';
+--------------------+------------------------------+
| Variable_name      | Value                        |
+--------------------+------------------------------+
| have_openssl       | YES                          |
| have_ssl           | YES                          |
| mysqlx_ssl_ca      |                              |
| mysqlx_ssl_capath  |                              |
| mysqlx_ssl_cert    |                              |
| mysqlx_ssl_cipher  |                              |
| mysqlx_ssl_crl     |                              |
| mysqlx_ssl_crlpath |                              |
| mysqlx_ssl_key     |                              |
| ssl_ca             | /etc/my.cnf.d/ssl/cacert.pem |
| ssl_capath         |                              |
| ssl_cert           | /etc/my.cnf.d/ssl/master.crt |
| ssl_cipher         |                              |
| ssl_crl            |                              |
| ssl_crlpath        |                              |
| ssl_fips_mode      | OFF                          |
| ssl_key            | /etc/my.cnf.d/ssl/master.key |
+--------------------+------------------------------+
17 rows in set (0.00 sec)

  1. 创建一个要求必须使用 SSL 连接的复制账号(master)
GRANT REPLICATION SLAVE ON *.* TO  'repluser'@'10.0.0.%' IDENTIFIED BY 'magedu'
REQUIRE SSL;

#如果MySQL8.0,执行下面两条命令
create user 'repluser'@'10.0.0.%' identified by '123456' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO  'repluser'@'10.0.0.%'
  1. 从服务器slave上使用CHANGER MASTER TO 命令时指明ssl相关选项(slave)
#从主节点复制如下3个文件到从节点
[root@centos8 ~]# ls /etc/my.cnf.d/ssl/
cacert.pem  slave.crt  slave.key
[root@centos8 ~]# chown -R mysql.mysql /etc/my.cnf.d/ssl/
[mysqld]
server-id=108
ssl
ssl-ca=/etc/my.cnf.d/ssl/cacert.pem
ssl-cert=/etc/my.cnf.d/ssl/slave.crt
ssl-key=/etc/my.cnf.d/ssl/slave.key
[root@centos8 ~]# systemctl restart mysqld.service 
[root@centos8 ~]#mysql
mysql>CHANGE MASTER TO MASTER_HOST='10.0.0.8',
 MASTER_USER='repluser',
 MASTER_PASSWORD='123456',
 MASTER_PORT=3306,
 MASTER_AUTO_POSITION=1,
 MASTER_SSL=1;



mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: 
                  Master_Host: 10.0.0.8
                  Master_User: repluser2
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: 
          Read_Master_Log_Pos: 4
               Relay_Log_File: centos8-relay-bin.000001
                Relay_Log_Pos: 4
        Relay_Master_Log_File: 
             Slave_IO_Running: No
            Slave_SQL_Running: No
              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: 0
              Relay_Log_Space: 155
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: Yes    ###SSH加密
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: NULL
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: 0
                  Master_UUID: 
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: 
           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: 1e11cde4-7da4-11eb-8c41-000c29c066b6:1-5
                Auto_Position: 1
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
       Master_public_key_path: 
        Get_master_public_key: 0
            Network_Namespace: 
1 row in set (0.00 sec)

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

mysql>  show variables like '%ssl%';
+--------------------+------------------------------+
| Variable_name      | Value                        |
+--------------------+------------------------------+
| have_openssl       | YES                          |
| have_ssl           | YES                          |
| mysqlx_ssl_ca      |                              |
| mysqlx_ssl_capath  |                              |
| mysqlx_ssl_cert    |                              |
| mysqlx_ssl_cipher  |                              |
| mysqlx_ssl_crl     |                              |
| mysqlx_ssl_crlpath |                              |
| mysqlx_ssl_key     |                              |
| ssl_ca             | /etc/my.cnf.d/ssl/cacert.pem |
| ssl_capath         |                              |
| ssl_cert           | /etc/my.cnf.d/ssl/slave.crt  |
| ssl_cipher         |                              |
| ssl_crl            |                              |
| ssl_crlpath        |                              |
| ssl_fips_mode      | OFF                          |
| ssl_key            | /etc/my.cnf.d/ssl/slave.key  |
+--------------------+------------------------------+
17 rows in set (0.02 sec)

###连接
[root@centos8 ~]#mysql -urepluser -pmagedu -h10.0.0.8
ERROR 1045 (28000): Access denied for user 'repluser'@'10.0.0.18' (using
password: YES)
[root@centos8 ~]#mysql -urepluser -pmagedu -h10.0.0.8 --sslca=/etc/my.cnf.d/ssl/cacert.pem --ssl-cert=/etc/my.cnf.d/ssl/slave.crt --sslkey=/etc/my.cnf.d/ssl/slave.key

6.1.9 GTID复制

GTID复制:(Global Transaction ID 全局事务标识符) MySQL 5.6 版本开始支持,GTID复制不像传统的复制方式(异步复制、半同步复制)需要找到binlog文件名和POS点,只需知道master的IP、端口、账号、密码即可。开启GTID后,执行change master to master_auto_postion=1即可,它会自动寻找到相应的位置开始同步。

GTID 架构
18.MYSQL数据库(2)_第9张图片
GTID = server_uuid:transaction_id,在一组复制中,全局唯一

server_uuid 来源于 /var/lib/mysql/auto.cnf

GTID服务器相关选项

gtid_mode #gtid模式
enforce_gtid_consistency #保证GTID安全的参数

GTID配置范例

  1. 主服务器
[root@centos8 ~]#vim /etc/my.cnf.d/mysql-server.cnf 
server-id=8
log-bin=mysql-bin  #可选
gtid_mode=ON
enforce_gtid_consistency
[root@centos8 ~]#systemctl start mysqld.service

mysql> create user repluser@'10.0.0.%' identified by '123456';
mysql> grant replication slave on *.* to repluser@'10.0.0.%';


  1. 从服务器
[root@centos8 ~]#vim /etc/my.cnf.d/mysql-server.cnf
[mysqld]
server-id=108
gtid_mode=ON
enforce_gtid_consistency
[root@centos8 ~]#systemctl start mysqld.service 

mysql>CHANGE MASTER TO MASTER_HOST='10.0.0.8',
 MASTER_USER='repluser',
 MASTER_PASSWORD='123456',
 MASTER_PORT=3306,
 MASTER_AUTO_POSITION=1;
mysql>start slave;

6.1.10 复制的监控和维护

6.1.10.1 清理日志

PURGE { BINARY | MASTER } LOGS   { TO 'log_name' | BEFORE datetime_expr }
RESET MASTER TO # #mysql 不支持
RESET SLAVE [ALL]

6.1.10.2 复制监控

SHOW MASTER STATUS
SHOW BINARY LOGS
SHOW BINLOG EVENTS
SHOW SLAVE STATUS
SHOW PROCESSLIST

6.1.10.3 从服务器是否落后于主服务

Seconds_Behind_Master:0

6.1.10.4 如何确定主从节点数据是否一致

percona-toolkit

6.1.10.5 数据不一致如何修复

删除从数据库,重新复制

6.1.11 复制的问题和解决方案

6.1.11.1 数据损坏或丢失

  • Master:MHA + semisync replication
  • Slave: 重新复制

6.1.11.2 不惟一的 server id

重新复制

6.1.11.3 复制延迟

需要额外的监控工具的辅助
一从多主:mariadb10 版后支持
多线程复制:对多个数据库复制

6.1.11.4 MySQL 主从数据不一致

6.1.11.4.1 造成主从不一致的原因
  • 主库binlog格式为Statement,同步到从库执行后可能造成主从不一致。
  • 主库执行更改前有执行set sql_log_bin=0,会使主库不记录binlog,从库也无法变更这部分数据。
  • 从节点未设置只读,误操作写入数据
  • 主库或从库意外宕机,宕机可能会造成binlog或者relaylog文件出现损坏,导致主从不一致
  • 主从实例版本不一致,特别是高版本是主,低版本为从的情况下,主数据库上面支持的功能,从数据库上面可能不支持该功能
  • MySQL自身bug导致
6.1.11.4.2 主从不一致修复方法
  • 将从库重新实现
    虽然这也是一种解决方法,但是这个方案恢复时间比较慢,而且有时候从库也是承担一部分的查询
    操作的,不能贸然重建。

  • 使用percona-toolkit工具辅助
    PT工具包中包含pt-table-checksum和pt-table-sync两个工具,主要用于检测主从是否一致以及修复数据不一致情况。这种方案优点是修复速度快,不需要停止主从辅助,缺点是需要知识积累,需要时间去学习,去测试,特别是在生产环境,还是要小心使用
    关于使用方法,可以参考下面链接:https://www.cnblogs.com/feiren/p/7777218.html

  • 手动重建不一致的表
    在从库发现某几张表与主库数据不一致,而这几张表数据量也比较大,手工比对数据不现实,并且重做整个库也比较慢,这个时候可以只重做这几张表来修复主从不一致
    这种方案缺点是在执行导入期间需要暂时停止从库复制,不过也是可以接受的

范例:A,B,C这三张表主从数据不一致

1、从库停止Slave复制
mysql>stop slave;

2、在主库上dump这三张表,并记录下同步的binlog和POS点
mysqldump -uroot -pmagedu -q --single-transaction --master-data=2 testdb A B
C >/backup/A_B_C.sql

3、查看A_B_C.sql文件,找出记录的binlog和POS点
head A_B_C.sql
例如:MASTERLOGFILE='mysql-bin.888888', MASTERLOGPOS=666666;

#以下指令是为了保障其他表的数据不丢失,一直同步直到那个点结束,A,B,C表的数据在之前的备份已
经生成了一份快照,只需要导入进入,然后开启同步即可
4、把A_B_C.sql拷贝到Slave机器上,并做指向新位置
mysql>start slave until MASTERLOGFILE='mysql-bin.888888',
MASTERLOGPOS=666666;

5、在Slave机器上导入A_B_C.sql
mysql -uroot -pmagedu testdb
mysql>set sql_log_bin=0;
mysql>source /backup/A_B_C.sql
mysql>set sql_log_bin=1;

6、导入完毕后,从库开启同步即可。
mysql>start slave;
6.1.11.4.3 如何避免主从不一致
  • 主库binlog采用ROW格式
  • 主从实例数据库版本保持一致
  • 主库做好账号权限把控,不可以执行set sql_log_bin=0
  • 从库开启只读,不允许人为写入
  • 定期进行主从一致性检验

6.2 MySQL 中间件代理服务器

6.2.1 关系型数据库和 NoSQL 数据库

RDBMS和NOSQL的特点及优缺点:
18.MYSQL数据库(2)_第10张图片

6.2.2 数据切分

6.2.2.1 垂直切分

18.MYSQL数据库(2)_第11张图片
一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面,如下图:
18.MYSQL数据库(2)_第12张图片

6.2.2.2 水平切分

18.MYSQL数据库(2)_第13张图片
对应shard中查询相关数据
18.MYSQL数据库(2)_第14张图片
相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分 到一个数据库,而另外的某些行又切分到其他的数据库中,如图:
18.MYSQL数据库(2)_第15张图片

如图,切分原则都是根据业务找到适合的切分规则分散到不同的库,下面用用户 ID 求模举例:
18.MYSQL数据库(2)_第16张图片

6.2.3 MySQL 中间件各种应用

18.MYSQL数据库(2)_第17张图片

  • mysql-proxy:Oracle,https://downloads.mysql.com/archives/proxy/
  • Atlas:Qihoo,https://github.com/Qihoo360/Atlas/blob/master/README_ZH.md
  • dbproxy:美团,https://github.com/Meituan-Dianping/DBProxy
  • Cetus:网易乐得,https://github.com/Lede-Inc/cetus
  • Amoeba:https://sourceforge.net/projects/amoeba/
  • Cobar:阿里巴巴,Amoeba的升级版, https://github.com/alibaba/cobar
  • Mycat:基于Cobar http://www.mycat.io/ (原网站)
    http://www.mycat.org.cn/
    https://github.com/MyCATApache/Mycat-Server
  • ProxySQL:https://proxysql.com/

6.2.4 Mycat

6.2.4.1 Mycat 介绍

Mycat工作原理
18.MYSQL数据库(2)_第18张图片

6.2.4.2 Mycat 安装

下载安装JDK

yum -y install java
#确认安装成功
java -version
openjdk version "1.8.0_201"
OpenJDK Runtime Environment (build 1.8.0_201-b09)
OpenJDK 64-Bit Server VM (build 25.201-b09, mixed mode)

下载安装mycat

wget http://dl.mycat.org.cn/1.6.7.4/Mycat-server-1.6.7.4-release/Mycat-server1.6.7.4-release-20200105164103-linux.tar.gz
mkdir /apps
tar xvf Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz  -C /apps
ls /apps/mycat/
bin catlet conf lib logs version.txt

mycat安装目录结构:

  • bin mycat命令,启动、重启、停止等
  • catlet catlet为Mycat的一个扩展功能
  • conf Mycat 配置信息,重点关注
  • lib Mycat引用的jar包,Mycat是java开发的
  • logs 日志文件,包括Mycat启动的日志和运行的日志
  • version.txt mycat版本说明

logs目录:

  • wrapper.log mycat启动日志
  • mycat.log mycat详细工作日志

Mycat的配置文件都在conf目录里面,这里介绍几个常用的文件:

  • server.xml Mycat软件本身相关的配置文件,设置账号、参数等
  • schema.xml Mycat对应的物理数据库和数据库表的配置,读写分离、高可用、分布式策略定制、节点控制
  • rule.xml Mycat分片(分库分表)规则配置文件,记录分片规则列表、使用方法等

启动和连接

#配置环境变量
vim /etc/profile.d/mycat.sh
PATH=/apps/mycat/bin:$PATH
source /etc/profile.d/mycat.sh
#启动
mycat start
#查看日志,确定成功
cat /app/mycat/logs/wrapper.log
...省略...
INFO   | jvm 1   | 2019/11/01 21:41:02 | MyCAT Server startup successfully. see
logs in logs/mycat.log
#连接mycat:
mysql -uroot -p123456 -h 127.0.0.1 -P8066

6.2.4.3 Mycat 主要配置文件说明

6.2.4.4 实战案例:利用 Mycat 实现 MySQL 的读写分离

18.MYSQL数据库(2)_第19张图片
1、创建 MySQL 主从数据库

[root@centos8 ~]#yum -y install mysql-server
#或者
[root@centos8 ~]#yum -y install mariadb-server

1) 修改master和slave上的配置文件


#master上的my.cnf
[root@centos8 ~]# vim /etc/my.cnf.d/mysql-server.cnf
[mysqld]
server-id=101
log-bin

#slave上的my.cnf
[mysqld]
server-id=102
[root@centos8 ~]# systemctl restart mysqld.service 

2) Master上创建复制用户

[root@master ~]# mysql
mysql> create user repluser@'10.0.0.%' identified by '123456';
mysql> grant replication slave on *.* to repluser@'10.0.0.%';
mysql> show master logs;		#记录二进制位置
+-------------------+-----------+-----------+
| Log_name          | File_size | Encrypted |
+-------------------+-----------+-----------+
| master-bin.000001 |       178 | No        |
| master-bin.000002 |       680 | No        |
+-------------------+-----------+-----------+
2 rows in set (0.00 sec)

mysql> create user admin@'10.0.0.%' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
mysql> grant all on *.* to admin@'10.0.0.%';
Query OK, 0 rows affected (0.01 sec)

3) Slave上执行

[root@slave ~]# mysql
CHANGE MASTER TO
  MASTER_HOST='10.0.0.101',
  MASTER_USER='repluser',
  MASTER_PASSWORD='123456',
  MASTER_PORT=3306,
  MASTER_LOG_FILE='master-bin.000002',
  MASTER_LOG_POS=680;

2、在MySQL代理服务器10.0.0.8安装mycat并启动

[root@mycat ~]#yum -y install java
#确认安装成功
[root@mycat ~]#java -version
openjdk version "1.8.0_252"
OpenJDK Runtime Environment (build 1.8.0_252-b09)
OpenJDK 64-Bit Server VM (build 25.252-b09, mixed mode)

#下载并安装
[root@centos8 ~]#wget http://dl.mycat.org.cn/1.6.7.4/Mycat-server-1.6.7.4-release/Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz

[root@mycat ~]#mkdir /apps
[[root@mycat ~]#tar xvf Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz -C /apps

#配置环境变量
[root@mycat ~]#echo 'PATH=/apps/mycat/bin:$PATH' > /etc/profile.d/mycat.sh
[root@mycat ~]#source /etc/profile.d/mycat.sh

#查看端口
[root@mycat ~]#ss -ntl
State      Recv-Q     Send-Q         Local Address:Port         Peer Address:Port     
LISTEN     0          128                  0.0.0.0:22                0.0.0.0:*        
LISTEN     0          128                     [::]:22                   [::]:*  

4、在mycat 服务器上修改server.xml文件配置Mycat的连接信息

[root@centos8 ~]#vim /apps/mycat/conf/server.xml
...省略...
#此处将8066改为3306
 <property name="serverPort">3306</property> <property
name="managerPort">9066</property>
 .....
<user name="root">                                       #连接Mycat的用户名
   <property name="password">123456</property>          #连接Mycat的密码
   <property name="schemas">TESTDB</property>           #数据库名要和schema.xml相
对应
</user>
</mycat:server>

这里使用的是root,密码为magedu,逻辑数据库为TESTDB,这些信息都可以自己随意定义,读写权限都有,没有针对表做任何特殊的权限。重点关注上面这段配置,其他默认即可。

5、修改schema.xml实现读写分离策略

[root@centos8 ~]#vim /apps/mycat/conf/schema.xml
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="***false***" sqlMaxLimit="100"
dataNode="***dn1***"></schema>
<dataNode name="dn1" dataHost="localhost1" database="***mycat***" />  #其中mycat
表示后端服务器实际的数据库名称
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="***1***"
writeType="0" dbType="mysql" dbDriver="native" switchType="1"
slaveThreshold="100">
<heartbeat>select user()</heartbeat>
***<writeHost host="host1" url="10.0.0.101:3306" user="root"
password="123456">***
***<readHost host="host2" url="10.0.0.102:3306" user="root" password="123456"
/>***
</writeHost>
</dataHost>
</mycat:schema>


#以上***部分表示原配置文件中需要修改的内容
#注意大小写
#最终内容
[root@mycat ~]#cat /apps/mycat/conf/schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
 <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100"
dataNode="dn1">
 </schema>
 <dataNode name="dn1" dataHost="localhost1" database="hellodb" />
 <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
  writeType="0" dbType="mysql" dbDriver="native" switchType="1"
slaveThreshold="100">
 <heartbeat>select user()</heartbeat>
 <writeHost host="host1" url="10.0.0.101:3306" user="admin"
   password="123456">		#账户密码需要与所授权(6)一致
         <readHost host="host2" url="10.0.0.102:3306" user="admin"
password="123456" />
 </writeHost>
   </dataHost>
</mycat:schema>



范例::schema.xml
18.MYSQL数据库(2)_第20张图片

6、在后端主服务器创建用户并对mycat授权

[root@master ~]#mysql 
mysql> create user admin@'10.0.0.%' identified by '123456';
mysql> grant all on *.* to admin@'10.0.0.%';

7、在Mycat服务器上连接并测试

#启动mycat
[root@centos8 ~]#mycat restart
#查看日志,确定成功,可能需要等一会儿才能看到成功的提示
[root@centos8 ~]#cat /apps/mycat/logs/wrapper.log


#客户端连接代理服务器mycat
[root@centos7 ~]#mysql -uroot -p123456 -h10.0.0.100
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.6.29-mycat-1.6.7.4-release-20200105164103 MyCat Server (OpenCloudDB)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> status
--------------
mysql  Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (x86_64) using readline 5.1

Connection id:		4
Current database:	hellodb
Current user:		[email protected]
SSL:			Not in use
Current pager:		stdout
Using outfile:		''
Using delimiter:	;
Server:			MySQL
Server version:		5.6.29-mycat-1.6.7.4-release-20200105164103 MyCat Server (OpenCloudDB)
Protocol version:	10
Connection:		10.0.0.100 via TCP/IP
Server characterset:	utf8mb4
Db     characterset:	utf8
Client characterset:	utf8
Conn.  characterset:	utf8
TCP port:		3306
--------------

MySQL [(none)]> show databases;
+----------+
| DATABASE |
+----------+
| TESTDB   |
+----------+
1 row in set (0.00 sec)

MySQL [(none)]> use TESTDB
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

MySQL [TESTDB]> show tables;
+-------------------+
| Tables_in_hellodb |
+-------------------+
| classes           |
| coc               |
| courses           |
| scores            |
| students          |
| teachers          |
| toc               |
+-------------------+
7 rows in set (0.00 sec)


#查看连接位置
MySQL [TESTDB]>  show variables like 'hostname';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| hostname      | slave |
+---------------+-------+
1 row in set (0.01 sec)

#查看连接id
MySQL [TESTDB]>  show variables like 'server_id';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 102   |
+---------------+-------+
1 row in set (0.01 sec)

8、通过通用日志确认实现读写分离
在mysql中查看通用日志

show variables like 'general_log';  #查看日志是否开启
set global general_log=on;    #开启日志功能
show variables like 'general_log_file'; #查看日志文件保存位置
set global general_log_file='tmp/general.log'; #设置日志文件保存位置

9、停止从节点,MyCAT自动调度读请求至主节点

[root@slave ~]# systemctl stop mysqld.service 
[root@centos7 ~]#mysql -uroot -p123456 -h10.0.0.100
MySQL [TESTDB]>  show variables like 'server_id';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 101   |
+---------------+-------+
1 row in set (0.02 sec)

[root@slave ~]# systemctl start mysqld.service 
[root@centos7 ~]#mysql -uroot -p123456 -h10.0.0.100
MySQL [TESTDB]>  show variables like 'server_id';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 102   |
+---------------+-------+
1 row in set (0.00 sec)

#停止主节点,MyCAT不会自动调度写请求至从节点
[root@master ~]# systemctl stop mysqld.service 

[root@centos7 ~]#mysql -uroot -p123456 -h10.0.0.100 
MySQL [(none)]> insert teachers values(5,'wang',30,'M');
ERROR 1184 (HY000): java.net.ConnectException: Connection refused


10、MyCAT对后端服务器的健康性检查方法select user()

#开启通用日志
[root@slave ~]# mysql
mysql> set global general_log=1;
Query OK, 0 rows affected (0.00 sec)


#查看通用日志
[root@master ~]#tail -f /var/lib/mysql/centos.log
2021-03-08T03:19:52.739580Z	   13 Query	select user()
2021-03-08T03:19:52.744954Z	   15 Connect	[email protected] on hellodb using TCP/IP
2021-03-08T03:20:02.738451Z	   10 Query	select user()
2021-03-08T03:20:12.739170Z	   11 Query	select user()
2021-03-08T03:20:22.738396Z	    8 Query	select user()
2021-03-08T03:20:32.739272Z	    9 Query	select user()
2021-03-08T03:20:42.739463Z	   12 Query	select user()

[root@slave ~]#tail -f /var/lib/mysql/centos8.log  

6.2.5 ProxySQL(见文档)

6.3 MySQL 高可用

18.MYSQL数据库(2)_第21张图片

6.3.1 MySQL 高可用解决方案

MySQL官方和社区里推出了很多高可用的解决方案,大体如下,仅供参考(数据引用自Percona)
18.MYSQL数据库(2)_第22张图片

  • MMM: Multi-Master Replication Manager for MySQL,Mysql主主复制管理器是一套灵活的脚本程序,基于perl实现,用来对mysql replication进行监控和故障迁移,并能管理mysql MasterMaster复制的配置(同一时间只有一个节点是可写的)
    官网: http://www.mysql-mmm.org
    https://code.google.com/archive/p/mysql-master-master/downloads

  • MHA:Master High Availability,对主节点进行监控,可实现自动故障转移至其它从节点;通过提升某一从节点为新的主节点,基于主从复制实现,还需要客户端配合实现,目前MHA主要支持一主多从的架构,要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当master,一台充当备用master,另外一台充当从库,出于机器成本的考虑,淘宝进行了改造,目前淘宝TMHA已经支持一主一从。
    官方网站:https://code.google.com/archive/p/mysql-master-ha/
    https://github.com/yoshinorim/mha4mysql-manager/releases
    https://github.com/yoshinorim/mha4mysql-node/releases/tag/v0.58

  • Galera Cluster:wsrep(MySQL extended with the Write Set Replication)
    通过wsrep协议在全局实现复制;任何一节点都可读写,不需要主从复制,实现多主读写

  • GR(Group Replication):MySQL官方提供的组复制技术(MySQL 5.7.17引入的技术),基于原生复制技术Paxos算法,实现了多主更新,复制组由多个server成员构成,组中的每个server可独立地执行事务,但所有读写事务只在冲突检测成功后才会提交

6.3.2 MHA Master High Availability

6.3.2.1 MHA 工作原理和架构

MHA集群架构
18.MYSQL数据库(2)_第23张图片
MHA工作原理
18.MYSQL数据库(2)_第24张图片

  1. MHA利用 SELECT 1 As Value 指令判断master服务器的健康性,一旦master 宕机,MHA 从宕机崩溃的master保存二进制日志事件(binlog events)
  2. 识别含有最新更新的slave
  3. 应用差异的中继日志(relay log)到其他的slave
  4. 应用从master保存的二进制日志事件(binlog events)
  5. 提升一个slave为新的master
  6. 使其他的slave连接新的master进行复制

注意:
为了尽可能的减少主库硬件损坏宕机造成的数据丢失,因此在配置MHA的同时建议配置成MySQL的半同步复制。

MHA软件

MHA软件由两部分组成,Manager工具包和Node工具包

Manager工具包主要包括以下几个工具:

masterha_check_ssh       检查MHA的SSH配置状况
masterha_check_repl      检查MySQL复制状况
masterha_manger          启动MHA
masterha_check_status    检测当前MHA运行状态
masterha_master_monitor  检测master是否宕机
masterha_master_switch   故障转移(自动或手动)
masterha_conf_host       添加或删除配置的server信息
masterha_stop  --conf=app1.cnf 停止MHA
masterha_secondary_check 两个或多个网络线路检查MySQL主服务器的可用

Node工具包:这些工具通常由MHA Manager的脚本触发,无需人为操作)主要包括以下几个工具:

save_binary_logs     #保存和复制master的二进制日志
apply_diff_relay_logs   #识别差异的中继日志事件并将其差异的事件应用于其他的slave
filter_mysqlbinlog   #去除不必要的ROLLBACK事件(MHA已不再使用此工具)
purge_relay_logs #清除中继日志(不会阻塞SQL线程)

MHA自定义扩展:

secondary_check_script		 #通过多条网络路由检测master的可用性
master_ip_ailover_script 	#更新Application使用的masterip
shutdown_script 			#强制关闭master节点
report_script 				#发送报告
init_conf_load_script 		#加载初始配置参数
master_ip_online_change_script #更新master节点ip地址

MHA配置文件:

global配置,为各application提供默认配置,默认文件路径 /etc/masterha_default.cnf
application配置:为每个主从复制集群

6.3.2.2 实现 MHA 实战案例

18.MYSQL数据库(2)_第25张图片

环境:四台主机
10.0.0.7 CentOS7 MHA管理端
10.0.0.8 CentOS8 MySQL8.0 Master 
10.0.0.18 CentOS8 MySQL8.0 Slave1
10.0.0.28 CentOS8 MySQL8.0 Slave2

6.3.2.2.1 在管理节点上安装两个包mha4mysql-manager和mha4mysql-node

说明:

mha4mysql-manager-0.56-0.el6.noarch.rpm 不支持CentOS 8,只支持CentOS7 以下版本
mha4mysql-manager-0.58-0.el7.centos.noarch.rpm 支持MySQL5.7和MySQL8.0 ,但和CentOS8
版本上的Mariadb -10.3.17不兼容

两个安装包

mha4mysql-manager   
mha4mysql-node

范例:

[root@mha-manager ~]#yum -y install mha4mysql-manager-0.58-0.el7.centos.noarch.rpm 
[root@mha-manager ~]#yum -y install mha4mysql-node-0.58-0.el7.centos.noarch.rpm
6.3.2.2.2 在所有MySQL服务器上安装mha4mysql-node包

此包支持CentOS 8,7,6

mha4mysql-node

范例:

[root@master ~]#yum -y install mha4mysql-node-0.58-0.el7.centos.noarch.rpm 
6.3.2.2.3 在所有节点实现相互之间ssh key验证
[root@mha-manager ~]#ssh-keygen
[root@mha-manager ~]#ssh-copy-id 127.0.0.1
[root@mha-manager ~]#rsync -av .ssh 10.0.0.8:/root/
[root@mha-manager ~]#rsync -av .ssh 10.0.0.102:/root/
[root@mha-manager ~]#rsync -av .ssh 10.0.0.108:/root/


6.3.2.2.4 在管理节点建立配置文件

注意: 此文件的行尾不要加空格等符号

[root@mha-manager ~]#vim /etc/mastermha/app1.cnf 
[server default]
user=mhauser        					#用于远程连接MySQL所有节点的用户,需要有管理员的权限
password=magedu
manager_workdir=/data/mastermha/app1/   #目录会自动生成,无需手动创建
manager_log=/data/mastermha/app1/manager.log
remote_workdir=/data/mastermha/app1/
ssh_user=root       					#用于实现远程ssh基于KEY的连接,访问二进制日志
repl_user=repluser  					#主从复制的用户信息
repl_password=magedu
ping_interval=1     					#健康性检查的时间间隔
master_ip_failover_script=/usr/local/bin/master_ip_failover   #切换VIP的perl脚本
report_script=/usr/local/bin/sendmail.sh  #当执行报警脚本
check_repl_delay=0   					#默认如果slave中从库落后主库relaylog超过100M,主库不会选择这个从库为新的master,因为这个从库进行恢复需要很长的时间.通过这个参数,mha触发主从切换的时候会忽略复制的延时,通过check_repl_delay=0这个参数,mha触发主从切换时会忽略复制的延时,对于设置candidate_master=1的从库非常有用,这样确保这个从库一定能成为最新的master

master_binlog_dir=/data/mysql/  		#指定二进制日志存放的目录,mha4mysql-manager-0.58必须指定,之前版本不需要指定
[server1]
hostname=10.0.0.8

[server2]
hostname=10.0.0.102
   					
[server3]
hostname=10.0.0.108
candidate_master=1					#设置为优先候选master,即使不是集群中事件最新的slave,也会优先当master

说明: 主库宕机谁来接管新的master

1. 所有从节点日志都是一致的,默认会以配置文件的顺序去选择一个新主
2. 从节点日志不一致,自动选择最接近于主库的从库充当新主
3. 如果对于某节点设定了权重(candidate_master=1),权重节点会优先选择。但是此节点日志量落后主库超过100M日志的话,也不会被选择。可以配合check_repl_delay=0,关闭日志量的检查,强制选择候选节点
6.3.2.2.5 相关脚本
[root@mha-manager ~]#cat /usr/local/bin/sendmail.sh
echo "MySQL is down" | mail -s "MHA Warning" [email protected]
[root@mha-manager ~]#chmod +x /usr/local/bin/sendmail.sh
[root@mha-manager ~]#vim /usr/local/bin/master_ip_failover
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use Getopt::Long;
my (
$command, $ssh_user, $orig_master_host, $orig_master_ip,
$orig_master_port, $new_master_host, $new_master_ip, $new_master_port
);
my $vip = '10.0.0.100/24';#设置Virtual IP
my $gateway = '10.0.0.254';#网关Gateway IP
my $interface = 'eth0';    #指定VIP所在网卡
my $key = "1";
my $ssh_start_vip = "/sbin/ifconfig $interface:$key $vip;/sbin/arping -I 
$interface -c 3 -s $vip $gateway >/dev/null 2>&1";
my $ssh_stop_vip = "/sbin/ifconfig $interface:$key down";
GetOptions(
'command=s' => \$command,
'ssh_user=s' => \$ssh_user,
'orig_master_host=s' => \$orig_master_host,
'orig_master_ip=s' => \$orig_master_ip,
'orig_master_port=i' => \$orig_master_port,
'new_master_host=s' => \$new_master_host,
'new_master_ip=s' => \$new_master_ip,
'new_master_port=i' => \$new_master_port,
);
exit &main();
sub main {
print "\n\nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\n\n";
if ( $command eq "stop" || $command eq "stopssh" ) {
# $orig_master_host, $orig_master_ip, $orig_master_port are passed.
# If you manage master ip address at global catalog database,
# invalidate orig_master_ip here.
my $exit_code = 1;
eval {
print "Disabling the VIP on old master: $orig_master_host \n";
&stop_vip();
$exit_code = 0;
};
if ($@) {
warn "Got Error: $@\n";
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "start" ) {
# all arguments are passed.
# If you manage master ip address at global catalog database,
# activate new_master_ip here.
# You can also grant write access (create user, set read_only=0, etc) here.
my $exit_code = 10;
eval {
print "Enabling the VIP - $vip on the new master - $new_master_host \n";
&start_vip();
$exit_code = 0;
};
if ($@) {
warn $@;
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "status" ) {
print "Checking the Status of the script.. OK \n";
`ssh $ssh_user\@$orig_master_host \" $ssh_start_vip \"`;
exit 0;
}
else {
&usage();
exit 1;
}
}
# A simple system call that enable the VIP on the new master
sub start_vip() {
`ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
# A simple system call that disable the VIP on the old_master
sub stop_vip() {
`ssh $ssh_user\@$orig_master_host \" $ssh_stop_vip \"`;
}
sub usage {
print
"Usage: master_ip_failover --command=start|stop|stopssh|status --
orig_master_host=host --orig_master_ip=ip --orig_master_port=port --
new_master_host=host --new_master_ip=ip --new_master_port=port\n";
}

[root@mha-manager ~]#chmod +x /usr/local/bin/master_ip_failover
6.3.2.2.6 实现Master
[root@master ~]#mkdir /data/mysql/
[root@master ~]#yum -y install mysql-server
[root@master ~]#chown -R mysql:mysql /data/mysql

#配置二进制信息
[root@master ~]#vim /etc/my.cnf.d/mysql-server.cnf 
[mysqld]
server_id=1
log-bin=/data/mysql/mysql-bin
skip_name_resolve=1		#防止反向解析
general_log     #观察结果,非必须项,生产无需启用

[root@master ~]#systemctl enable --now mysqld
[root@master ~]#mysql
#记录日志位置
mysql> show master logs;
+------------------+-----------+-----------+
| Log_name         | File_size | Encrypted |
+------------------+-----------+-----------+
| mysql-bin.000001 |       178 | No        |
| mysql-bin.000002 |       155 | No        |
+------------------+-----------+-----------+
2 rows in set (0.00 sec)

#创建账号和授权
mysql> create user repluser@'10.0.0.%' identified by '123456';
mysql> grant replication slave on *.* to repluser@'10.0.0.%';
#该账号用来修改主从的决策,给与all权限
mysql> create user mhauser@'10.0.0.%' identified by '123456';
mysql> grant all on *.* to mhauser@'10.0.0.%' ;

#配置VIP
[root@master ~]#ifconfig eth0:1 10.0.0.100/24
6.3.2.2.7 实现slave
[root@slave1 ~]# mkdir /data/mysql
[root@slave1 ~]# yum -y install mysql-server
[root@slave1 ~]# chown -R mysql.mysql /data/mysql/

[root@slave1,2 ~]# vim /etc/my.cnf.d/mysql-server.cnf 
[mysqld]
server_id=2   #不同节点此值各不相同
log-bin=/data/mysql/mysql-bin
read_only
relay_log_purge=0
skip_name_resolve=1    #禁止反向解析
general_log  #方便观察的设置,生产无需启用

[root@slave1,2 ~]# systemctl enable --now mysqld
[root@slave1,2 ~]# mysql
mysql> CHANGE MASTER TO MASTER_HOST='10.0.0.8', MASTER_USER='repluser',  MASTER_PASSWORD='123456', MASTER_LOG_FILE='mysql-bin.000002',  MASTER_LOG_POS=155;
mysql> start slave;

6.3.2.2.8 检查Mha的环境
#检查环境
[root@mha-manager ~]#masterha_check_ssh --conf=/etc/mastermha/app1.cnf
[root@mha-manager ~]#masterha_check_repl --conf=/etc/mastermha/app1.cnf

#查看状态
[root@mha-manager ~]#masterha_check_status --conf=/etc/mastermha/app1.cnf
6.3.2.2.9 启动MHA
#开启MHA,默认是前台运行
nohup masterha_manager --conf=/etc/mastermha/app1.cnf &> /dev/null 
#查看状态
masterha_check_status --conf=/etc/mastermha/app1.cnf  
6.3.2.2.10 排错日志
tail /data/mastermha/app1/manager.log
6.3.2.2.11 模拟故障
#当 master down机后,mha管理程序自动退出
[root@master ~]#mysql hellodb
mysql> call sp_testlog;
 #master mysqld服务异常终止,mha管理程序自动退出
[root@master ~]#systemctl stop mysqld
[root@centos7 bin]#masterha_manager --conf=/etc/mastermha/app1.cnf
Mon Mar  8 19:29:13 2021 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Mon Mar  8 19:29:13 2021 - [info] Reading application default configuration from /etc/mastermha/app1.cnf..
Mon Mar  8 19:29:13 2021 - [info] Reading server configuration from /etc/mastermha/app1.cnf..
  Creating /data/mastermha/app1 if not exists..    ok.
  Checking output directory is accessible or not..
   ok.
  Binlog found at /data/mysql, up to mysql-bin.000002
Mon Mar  8 19:32:46 2021 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Mon Mar  8 19:32:46 2021 - [info] Reading application default configuration from /etc/mastermha/app1.cnf..
Mon Mar  8 19:32:46 2021 - [info] Reading server configuration from /etc/mastermha/app1.cnf..


#验证VIP漂移至新的Master上
[root@slave2 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:e6:de:1a brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.108/24 brd 10.0.0.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet 10.0.0.100/8 brd 10.255.255.255 scope global eth0:1
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fee6:de1a/64 scope link 
       valid_lft forever preferred_lft forever

注:手动删除新主节点read_only
[root@centos8 ~]# vim /etc/my.cnf.d/mysql-server.cnf 

MHA报警
18.MYSQL数据库(2)_第26张图片

6.3.2.2.12 如果再次运行MHA,需要先删除下面文件
[root@mha-manager ~]#ls /data/mastermha/app1/app1.failover.complete -l
-rw-r--r-- 1 root root 0 Aug 29 21:28 
/data/mastermha/app1//app1.failover.complete
[root@mha-manager ~]#rm -f /data/mastermha/app1/app1.failover.complete

6.3.3 Galera Cluster

6.3.3.1 Galera Cluster 介绍

6.3.3.3 实战案例:Percona XtraDB Cluster(PXC 5.7)

1 环境准备
四台主机:

pxc1:10.0.0.7
pxc2:10.0.0.17
pxc3:10.0.0.27
pxc4:10.0.0.37

OS 版本目前不支持CentOS 8

[root@pxc1 ~]#cat /etc/redhat-release 
CentOS Linux release 7.6.1810 (Core)

关闭防火墙和SELinux,保证时间同步
注意:如果已经安装MySQL,必须卸载

2 安装 Percona XtraDB Cluster 5.7


[root@pxc1 ~]#vim /etc/yum.repos.d/pxc.repo
[percona]
name=percona_repo
baseurl =
https://mirrors.tuna.tsinghua.edu.cn/percona/release/$releasever/RPMS/$basearch
enabled = 1
gpgcheck = 0

[root@pxc1 ~]#scp /etc/yum.repos.d/pxc.repo  10.0.0.105:/etc/yum.repos.d/
[root@pxc1 ~]#scp /etc/yum.repos.d/pxc.repo  10.0.0.120:/etc/yum.repos.d/

#在三个节点都安装好PXC 5.7 
[root@pxc1 ~]#yum install Percona-XtraDB-Cluster-57 -y
[root@pxc2 ~]#yum install Percona-XtraDB-Cluster-57 -y
[root@pxc3 ~]#yum install Percona-XtraDB-Cluster-57 -y


3 在各个节点上分别配置mysql及集群配置文件

/etc/my.cnf为主配置文件,当前版本中,其余的配置文件都放在/etc/percona-xtradb-cluster.conf.d目录里,包括mysqld.cnf,mysqld_safe.cnf,wsrep.cnf 三个文件

#主配置文件不需要修改
[root@pxc1 ~]#cat /etc/my.cnf
# The Percona XtraDB Cluster 5.7 configuration file.
...省略...
!includedir /etc/my.cnf.d/
!includedir /etc/percona-xtradb-cluster.conf.d/
[root@pxc1 ~]#ls /etc/my.cnf.d/
[root@pxc1 ~]#ls /etc/percona-xtradb-cluster.conf.d/
mysqld.cnf mysqld_safe.cnf wsrep.cnf


#下面配置文件不需要修改
[root@pxc1 ~]#cat /etc/percona-xtradb-cluster.conf.d/mysqld.cnf 
...省略...
[client]
socket=/var/lib/mysql/mysql.sock
[mysqld]
server-id=1     #建议各个节点不同
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
log-bin     #建议启用,非必须项
log_slave_updates
expire_logs_days=7
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0


#下面配置文件不需要修改
[root@pxc1 ~]#cat /etc/percona-xtradb-cluster.conf.d/mysqld_safe.cnf 
...省略...
[mysqld_safe]
pid-file = /var/run/mysqld/mysqld.pid
socket   = /var/lib/mysql/mysql.sock
nice     = 0


#PXC的配置文件必须修改
[root@pxc1 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf 
[root@pxc1 ~]#grep -Ev "^#|^$" /etc/percona-xtradb-cluster.conf.d/wsrep.cnf 
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27  #三个节点的IP
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2
wsrep_node_address=10.0.0.7             #各个节点,指定自已的IP
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-1       #各个节点,指定自已节点名称
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass"       #取消本行注释,同一集群内多个节点的验证用户和密码信息必须一致

[root@pxc2 ~]#grep -Ev "^#|^$" /etc/percona-xtradb-cluster.conf.d/wsrep.cnf 
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2          
wsrep_node_address=10.0.0.17     #各个节点,指定自已的IP
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-2    #各个节点,指定自已节点名称
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass"   #取消本行注释

[root@pxc3 ~]#grep -Ev "^#|^$" /etc/percona-xtradb-cluster.conf.d/wsrep.cnf 
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2   
wsrep_node_address=10.0.0.27 #各个节点,指定自已的IP
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-3    #各个节点,指定自已的IP
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass"   #取消本行注释

注意:尽管Galera Cluster不再需要通过binlog的形式进行同步,但还是建议在配置文件中开启二进制日志功能,原因是后期如果有新节点需要加入,老节点通过SST全量传输的方式向新节点传输数据,很可能会拖垮集群性能,所以让新节点先通过binlog方式完成同步后再加入集群会是一种更好的选择

配置文件各项配置意义
18.MYSQL数据库(2)_第27张图片

4 启动PXC集群中第一个节点

#启动第一个节点
[root@pxc1 ~]#systemctl start [email protected]

#查看root密码
[root@pxc1 ~]#grep "temporary password" /var/log/mysqld.log
2021-03-09T02:13:59.243101Z 1 [Note] A temporary password is generated for root@localhost: ta+6&47e8xsI

#修改root密码
[root@pxc1 ~]#mysql -uroot -p'ta+6&47e8xsI'
mysql> alter user 'root'@'localhost' identified by '123456';

#创建相关用户并授权
mysql> create user 'sstuser'@'localhost' identified by 's3cretPass';
mysql> grant reload,lock tables,process,replication client on *.* to 'sstuser'@'localhost';

#查看相关变量
mysql> SHOW VARIABLES LIKE 'wsrep%'\G

#查看相关状态变量
mysql> SHOW STATUS LIKE 'wsrep%'\G

#重点关注下面内容
mysql> show status like 'wsrep%';

5 启动PXC集群中其它所有节点

[root@pxc2 ~]#systemctl start mysql
[root@pxc3 ~]#systemctl start mysql

6 查看集群状态,验证集群是否成功

#在任意节点,查看集群状态
[root@pxc1 ~]#mysql -uroot -123456
mysql> SHOW VARIABLES LIKE 'wsrep_node_name';
+-----------------+--------------------+
| Variable_name   | Value             |
+-----------------+--------------------+
| wsrep_node_name | pxc-cluster-node-1 |
+-----------------+--------------------+
1 row in set (0.00 sec)
mysql> SHOW VARIABLES LIKE 'wsrep_node_address';
+--------------------+----------+
| Variable_name     | Value   |
+--------------------+----------+
| wsrep_node_address | 10.0.0.7 |
+--------------------+----------+
1 row in set (0.01 sec)
mysql> SHOW VARIABLES LIKE 'wsrep_on';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wsrep_on     | ON   |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name     | Value |
+--------------------+-------+
| wsrep_cluster_size | 3     |
+--------------------+-------+
1 row in set (0.01 sec)


#利用Xshell软件,同时在三个节点数据库,在其中一个节点成功
mysql> create database testdb2;
Query OK, 1 row affected (0.01 sec)

#在其它节点都提示失败
mysql> create database testdb2;
ERROR 1007 (HY000): Can't create database 'testdb2'; database exists

7 在PXC集群中加入节点

一个节点加入到Galera集群有两种情况:新节点加入集群、暂时离组的成员再次加入集群

1)新节点加入Galera集群

新节点加入集群时,需要从当前集群中选择一个Donor节点来同步数据,也就是所谓的state_snapshot_tranfer(SST)过程。SST同步数据的方式由选项wsrep_sst_method决定,一般选择的是xtrabackup。
必须注意,新节点加入Galera时,会删除新节点上所有已有数据,再通过xtrabackup(假设使用的是该
方式)从Donor处完整备份所有数据进行恢复。所以,如果数据量很大,新节点加入过程会很慢。而且,在一个新节点成为Synced状态之前,不要同时加入其它新节点,否则很容易将集群压垮。

如果是这种情况,可以考虑使用wsrep_sst_method=rsync来做增量同步,既然是增量同步,最好保证新节点上已经有一部分数据基础,否则和全量同步没什么区别,且这样会对Donor节点加上全局read only锁。

2)旧节点加入Galera集群
如果旧节点加入Galera集群,说明这个节点在之前已经在Galera集群中呆过,有一部分数据基础,缺少的只是它离开集群时的数据。这时加入集群时,会采用IST(incremental snapshot transfer)传输机制,即使用增量传输。

但注意,这部分增量传输的数据源是Donor上缓存在GCache文件中的,这个文件有大小限制,如果缺
失的数据范围超过已缓存的内容,则自动转为SST传输。如果旧节点上的数据和Donor上的数据不匹配
(例如这个节点离组后人为修改了一点数据),则自动转为SST传输。

#在PXC集群中再加一台新的主机PXC4:10.0.0.37
[root@pxc4 ~]#yum install Percona-XtraDB-Cluster-57 -y
[root@pxc4 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[root@pxc4 ~]#grep -Ev "^#|^$" /etc/percona-xtradb-cluster.conf.d/wsrep.cnf 
[mysqld]
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27,10.0.0.37
binlog_format=ROW
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
innodb_autoinc_lock_mode=2
wsrep_node_address=10.0.0.37
wsrep_cluster_name=pxc-cluster
wsrep_node_name=pxc-cluster-node-4
pxc_strict_mode=ENFORCING
wsrep_sst_method=xtrabackup-v2
wsrep_sst_auth="sstuser:s3cretPass"


[root@pxc4 ~]#systemctl start mysql
[root@pxc4 ~]#mysql -uroot -p123456
Server version: 5.7.27-30-57-log Percona XtraDB Cluster (GPL), Release rel30, 
Revision 
mysql> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name     | Value |
+--------------------+-------+
| wsrep_cluster_size | 4     |
+--------------------+-------+
1 row in set (0.00 sec)
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql             |
| performance_schema |
| sys               |
| testdb1           |
| testdb2           |
| testdb3           |
+--------------------+
8 rows in set (0.00 sec)

#将其它节点的配置文件加以修改
[root@pxc1 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
wsrep_cluster_address=gcomm://10.0.0.7,10.0.0.17,10.0.0.27,10.0.0.37
[root@pxc2 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[root@pxc3 ~]#vim /etc/percona-xtradb-cluster.conf.d/wsrep.cnf



8 在PXC集群中修复故障节点

[root@pxc4 ~]#systemctl stop mysql
#在其它任意节点查看wsrep_cluster_size变量少了一个节点
[root@pxc1 ~]#mysql -uroot -p123456
mysql>  SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 3     |
+--------------------+-------+
1 row in set (0.00 sec)


mysql> create database testdb4;
#在其它任意节点可看到数据已同步
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql             |
| performance_schema |
| sys               |
| testdb1           |
| testdb2           |
| testdb3           |
| testdb4           |
+--------------------+
10 rows in set (0.00 sec)

#恢复服务,数据同步
[root@pxc4 ~]#systemctl start mysql
[root@pxc4 ~]#mysql -uroot -p123456
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql             |
| performance_schema |
| sys               |
| testdb1           |
| testdb2           |
| testdb3           |
| testdb4           |
+--------------------+
10 rows in set (0.00 sec)
mysql> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name     | Value |
+--------------------+-------+
| wsrep_cluster_size | 4     |
+--------------------+-------+
1 row in set (0.01 sec)

6.3.3.4 实战案例:MariaDB Galera Cluster

#在三个节点上都实现
[root@centos8 ~]#dnf install mariadb-server-galera -y
[root@centos8 ~]#vim /etc/my.cnf.d/galera.cnf
#wsrep_cluster_address="dummy://" 在此行下面加一行
wsrep_cluster_address="gcomm://10.0.0.8,10.0.0.18,10.0.0.28"  
#启动第一节点
[root@centos8 ~]#galera_new_cluster
[root@centos8 ~]#systemctl enable mariadb
#再启动其它节点
[root@centos8 ~]#systemctl enable --now mariadb
[root@centos8 ~]#ss -ntul
Netid             State               Recv-Q             Send-Q                 
             Local Address:Port                             Peer Address:Port   
           
tcp               LISTEN              0                   128                   
                    0.0.0.0:22                                    0.0.0.0:*     
            
tcp               LISTEN              0                   128                   
                    0.0.0.0:4567                                  0.0.0.0:*     
            
tcp               LISTEN              0                   80                     
                   0.0.0.0:3306                                  0.0.0.0:*       
          
tcp               LISTEN              0                   128                   
                      [::]:22                                       [::]:*  

[root@centos8 ~]#mysql 
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 11
Server version: 10.3.11-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> 
MariaDB [(none)]>  show status like "wsrep_ready";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wsrep_ready   | ON   |
+---------------+-------+
1 row in set (0.001 sec)
MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_cluster_size'; 
+--------------------+-------+
| Variable_name     | Value |
+--------------------+-------+
| wsrep_cluster_size | 3     |
+--------------------+-------+
1 row in set (0.001 sec)
MariaDB [(none)]> SHOW VARIABLES LIKE 'wsrep_%'\G
MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_%';

范例:CentOS 7 实现 MariaDB Galera Cluster 5.5

#参考仓库:https://mirrors.tuna.tsinghua.edu.cn/mariadb/mariadb-5.5.X/yum/centos7-
amd64/
yum install MariaDB-Galera-server
vim /etc/my.cnf.d/server.cnf
[galera]
wsrep_provider = /usr/lib64/galera/libgalera_smm.so
wsrep_cluster_address="gcomm://10.0.0.7,10.0.0.17,10.0.0.27"
binlog_format=row
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0
#下面配置可选项
wsrep_cluster_name = 'mycluster' 默认my_wsrep_cluster
wsrep_node_name = 'node1'
wsrep_node_address = '10.0.0.7'

#首次启动时,需要初始化集群,在其中一个节点上执行命令
/etc/init.d/mysql start --wsrep-new-cluster
#而后正常启动其它节点
service mysql start
#查看集群中相关系统变量和状态变量
SHOW VARIABLES LIKE 'wsrep_%';
SHOW STATUS LIKE 'wsrep_%';
SHOW STATUS LIKE 'wsrep_cluster_size';

6.3.4 TiDB 概述

6.3.4.1 TiDB 核心特点

  1. 高度兼容 MySQL 大多数情况下,无需修改代码即可从 MySQL 轻松迁移至 TiDB,分库分表后的MySQL 集群亦可通过 TiDB 工具进行实时迁移
  2. 水平弹性扩展 通过简单地增加新节点即可实现 TiDB 的水平扩展,按需扩展吞吐或存储,轻松应对高并发、海量数据场景
  3. 分布式事务 TiDB 100% 支持标准的 ACID 事务
  4. 真正金融级高可用 相比于传统主从 (M-S) 复制方案,基于 Raft 的多数派选举协议可以提供金融级的 100% 数据强一致性保证,且在不丢失大多数副本的前提下,可实现故障的自动恢复 (autofailover),无需人工介入
  5. 一站式 HTAP 解决方案 TiDB 作为典型的 OLTP 行存数据库,同时兼具强大的 OLAP 性能,配合TiSpark,可提供一站式 HTAP解决方案,一份存储同时处理OLTP & OLAP(OLAP、OLTP的介绍和比较 )无需传统繁琐的 ETL 过程
  6. 云原生 SQL 数据库 TiDB 是为云而设计的数据库,同 Kubernetes 深度耦合,支持公有云、私有云和混合云,使部署、配置和维护变得十分简单。TiDB 的设计目标是 100% 的 OLTP 场景和 80% 的 OLAP 场景,更复杂的 OLAP 分析可以通过 TiSpark 项目来完成。 TiDB 对业务没有任何侵入性,能优雅的替换传统的数据库中间件、数据库分库分表等 Sharding 方案。同时它也让开发运维人员不用关注数据库 Scale 的细节问题,专注于业务开发,极大的提升研发的生产力

6.3.4.2 TiDB整体架构

18.MYSQL数据库(2)_第28张图片

TiDB Server

TiDB Server 负责接收SQL请求,处理SQL相关的逻辑,并通过PD找到存储计算所需数据的TiKV地址,与TiKV交互获取数据,最终返回结果。TiDB Server 是无状态的,其本身并不存储数据,只负责计算,可以无限水平扩展,可以通过负载均衡组件(LVS、HAProxy或F5)对外提供统一的接入地址。

PD Server

Placement Driver(简称PD)是整个集群的管理模块,其主要工作有三个:一是存储集群的元信息(某个Key存储在那个TiKV节点);二是对TiKV集群进行调度和负载均衡(如数据的迁移、Raft group leader的迁移等);三是分配全局唯一且递增的事务ID

PD 是一个集群,需要部署奇数个节点,一般线上推荐至少部署3个节点。PD在选举的过程中无法对外提供服务,这个时间大约是3秒

TiKV Server

TiKV Server 负责存储数据,从外部看TiKV是一个分布式的提供事务的Key-Value存储引擎。存储数据的基本单位是Region,每个Region负责存储一个Key Range(从StartKey到EndKey的左闭右开区间)的
数据,每个TiKV节点会负责多个Region。TiKV使用Raft协议做复制,保持数据的一致性和容灾。副本以Region为单位进行管理,不同节点上的多个Region构成一个Raft Group,互为副本。数据在多个TiKV之间的负载均衡由PD调度,这里也就是以Region为单位进行调度

7 性能优化

数据库服务衡量指标:

  • Qps:query per second
  • Tps:transaction per second

7.1 压力测试工具

7.1.1 常见 MySQL 压力测试工具

  • mysqlslap
  • Sysbench:功能强大,官网: https://github.com/akopytov/sysbench
  • tpcc-mysql
  • MySQL Benchmark Suite
  • MySQL super-smack
  • MyBench

7.1.2 mysqlslap

mysqlslap:来自于mariadb包,测试的过程默认生成一个mysqlslap的schema,生成测试表t1,查询和插入测试数据,mysqlslap库自动生成,如果已经存在则先删除。用–only-print来打印实际的测试过程,整个测试完成后不会在数据库中留下痕迹

使用格式:

mysqlslap [options]

常用参数 [options] 说明:

--auto-generate-sql, -a #自动生成测试表和数据,表示用mysqlslap工具自己生成的SQL脚本来测试
并发压力
--auto-generate-sql-load-type=type #测试语句的类型。代表要测试的环境是读操作还是写操作还
是两者混合的。取值包括:read,key,write,update和mixed(默认)
--auto-generate-sql-add-auto-increment #代表对生成的表自动添加auto_increment列,从
5.1.18版本开始支持
--number-char-cols=N, -x N #自动生成的测试表中包含多少个字符类型的列,默认1
--number-int-cols=N, -y N #自动生成的测试表中包含多少个数字类型的列,默认1
--number-of-queries=N #总的测试查询次数(并发客户数×每客户查询次数)
--query=name,-q #使用自定义脚本执行测试,例如可以调用自定义的存储过程或者sql语句来执行测试
--create-schema #代表自定义的测试库名称,测试的schema
--commint=N #多少条DML后提交一次
--compress, -C #如服务器和客户端都支持压缩,则压缩信息
--concurrency=N, -c N #表示并发量,即模拟多少个客户端同时执行select。可指定多个值,以逗号
或者--delimiter参数指定值做为分隔符,如:--concurrency=100,200,500
--engine=engine_name, -e engine_name #代表要测试的引擎,可以有多个,用分隔符隔开。例
如:--engines=myisam,innodb
--iterations=N, -i N #测试执行的迭代次数,代表要在不同并发环境下,各自运行测试多少次
--only-print #只打印测试语句而不实际执行。
--detach=N #执行N条语句后断开重连
--debug-info, -T #打印内存和CPU的相关信息

mysqlslap示例

#单线程测试
mysqlslap -a -uroot -pmagedu
#多线程测试。使用--concurrency来模拟并发连接
mysqlslap -a -c 100 -uroot -pmagedu
#迭代测试。用于需要多次执行测试得到平均值
mysqlslap -a -i 10 -uroot -pmagedu
mysqlslap ---auto-generate-sql-add-autoincrement -a
mysqlslap -a --auto-generate-sql-load-type=read
mysqlslap -a --auto-generate-secondary-indexes=3
mysqlslap -a --auto-generate-sql-write-number=1000
mysqlslap --create-schema world -q "select count(*) from City"
mysqlslap -a -e innodb -uroot -pmagedu
mysqlslap -a --number-of-queries=10 -uroot -pmagedu
#测试同时不同的存储引擎的性能进行对比
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --iterations=5 --
engine=myisam,innodb --debug-info -uroot -pmagedu
#执行一次测试,分别50和100个并发,执行1000次总查询
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --debug-info -uroot -
pmagedu
#50和100个并发分别得到一次测试结果(Benchmark),并发数越多,执行完所有查询的时间越长。为了准确
起见,可以多迭代测试几次
mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --iterations=5 --
debug-info -uroot -pmagedu

7.2 生产环境 my.cnf 配置案例

配置文件生成工具参考链接:https://imysql.com/my_cnf_generator
参考硬件:内存 32G

#打开独立表空间
innodb_file_per_table = 1
#MySQL 服务所允许的同时会话数的上限,经常出现Too Many Connections的错误提示,则需要增大此值
max_connections = 8000
#所有线程所打开表的数量
open_files_limit = 10240
#back_log 是操作系统在监听队列中所能保持的连接数
back_log = 300
#每个客户端连接最大的错误允许数量,当超过该次数,MYSQL服务器将禁止此主机的连接请求,直到MYSQL
服务器重启或通过flush hosts命令清空此主机的相关信息
max_connect_errors = 1000
#每个连接传输数据大小.最大1G,须是1024的倍数,一般设为最大的BLOB的值
max_allowed_packet = 32M
#指定一个请求的最大连接时间
wait_timeout = 10
# 排序缓冲被用来处理类似ORDER BY以及GROUP BY队列所引起的排序
sort_buffer_size = 16M 
#不带索引的全表扫描.使用的buffer的最小值
join_buffer_size = 16M 
#查询缓冲大小
query_cache_size = 128M
#指定单个查询能够使用的缓冲区大小,缺省为1M
query_cache_limit = 4M    
# 设定默认的事务隔离级别
transaction_isolation = REPEATABLE-READ
# 线程使用的堆大小. 此值限制内存中能处理的存储过程的递归深度和SQL语句复杂性,此容量的内存在每次
连接时被预留.
thread_stack = 512K 
# 二进制日志功能
log-bin=/data/mysqlbinlogs/
#二进制日志格式
binlog_format=row
#InnoDB使用一个缓冲池来保存索引和原始数据, 可设置这个变量到物理内存大小的80%
innodb_buffer_pool_size = 24G 
#用来同步IO操作的IO线程的数量
innodb_file_io_threads = 4
#在InnoDb核心内的允许线程数量,建议的设置是CPU数量加上磁盘数量的两倍
innodb_thread_concurrency = 16
# 用来缓冲日志数据的缓冲区的大小
innodb_log_buffer_size = 16M
在日志组中每个日志文件的大小
innodb_log_file_size = 512M 
# 在日志组中的文件总数
innodb_log_files_in_group = 3
# SQL语句在被回滚前,InnoDB事务等待InnoDB行锁的时间
innodb_lock_wait_timeout = 120
#慢查询时长
long_query_time = 2
#将没有使用索引的查询也记录下来
log-queries-not-using-indexes

7.3 MySQL配置最佳实践

高并发大数据的互联网业务,架构设计思路是"解放数据库CPU,将计算转移到服务层",并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现"增机器就加性能"

参考资料:

  • 阿里巴巴Java开发手册:https://developer.aliyun.com/topic/java2020
  • 58到家数据库30条军规解读:http://zhuanlan.51cto.com/art/201702/531364.htm

以下规范适用场景:并发量大、数据量大的互联网业务

7.3.1 基础规范

(1)必须使用InnoDB存储引擎
解读:支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高

(2)使用UTF8MB4字符集
解读:万国码,无需转码,无乱码风险,节省空间,支持表情包及生僻字

(3)数据表、数据字段必须加入中文注释
解读:N年后谁知道这个r1,r2,r3字段是干嘛的

(4)禁止使用存储过程、视图、触发器、Event
解读:高并发大数据的互联网业务,架构设计思路是"解放数据库CPU,将计算转移到服务层",并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现"增机器就加性能"。数据库擅长存储与索引,CPU计算还是上移吧!

(5)禁止存储大文件或者大照片
解读:为何要让数据库做它不擅长的事情?大文件和照片存储在文件系统,数据库里存URI多好。

7.3.2 命名规范

(6)只允许使用内网域名,而不是ip连接数据库

(7)线上环境、开发环境、测试环境数据库内网域名遵循命名规范
业务名称:xxx
线上环境:xxx.db
开发环境:xxx.rdb
测试环境:xxx.tdb
从库在名称后加-s标识,备库在名称后加-ss标识
线上从库:xxx-s.db
线上备库:xxx-sss.db

(8)库名、表名、字段名:小写,下划线风格,不超过32个字符,必须见名知意,禁止拼音英文混用

(9)库名与应用名称尽量一致,表名:t_业务名称_表的作用,主键名:pk_xxx,非唯一索引名:idx_xxx,唯
一键索引名:uk_xxx

7.3.3 表设计规范

(10)单实例表数目必须小于500
单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表

(11)单表列数目必须小于30

(12)表必须有主键,例如自增主键
解读:
a)主键递增,数据行写入可以提高插入性能,可以避免page分裂,减少表碎片提升空间和内存的使用
b)主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值,较短的数据类型可以有效的
减少索引的磁盘空间,提高索引的缓存效率
c) 无主键的表删除,在row模式的主从架构,会导致备库夯住

(13)禁止使用外键,如果有外键完整性约束,需要应用程序控制
解读:外键会导致表与表之间耦合,update与delete操作都会涉及相关联的表,十分影响sql 的性能,
甚至会造成死锁。高并发情况下容易造成数据库性能,大数据高并发业务场景数据库使用以性能优先

7.3.4 字段设计规范

(14)必须把字段定义为NOT NULL并且提供默认值
解读:
a)null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化
b)null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较
多空字段的时候,数据库的处理性能会降低很多
c)null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标识
d)对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符
号。如:where name!=‘shenjian’,如果存在name为null值的记录,查询结果就不会包含name为null
值的记录

(15)禁止使用TEXT、BLOB类型
解读:会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急
剧降低,影响数据库性能

(16)禁止使用小数存储货币
解读:使用整数吧,小数容易导致钱对不上

(17)必须使用varchar(20)存储手机号
解读:
a)涉及到区号或者国家代号,可能出现±()
b)手机号会去做数学运算么?
c)varchar可以支持模糊查询,例如:like"138%"

(18)禁止使用ENUM,可使用TINYINT代替
解读:
a)增加新的ENUM值要做DDL操作
b)ENUM的内部实际存储就是整数,你以为自己定义的是字符串?

7.3.5索引设计规范

(19)单表索引建议控制在5个以内

(20)单索引字段数不允许超过5个
解读:字段超过5个时,实际已经起不到有效过滤数据的作用了

(21)禁止在更新十分频繁、区分度不高的属性上建立索引
解读:
a)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能
b)"性别"这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类

(22)建立组合索引,必须把区分度高的字段放在前面
解读:能够更加有效的过滤数据

7.3.6 SQL使用规范

(23)禁止使用SELECT *,只获取必要的字段,需要显示说明列属性
解读:
a)读取不需要的列会增加CPU、IO、NET消耗
b)不能有效的利用覆盖索引
c)使用SELECT *容易在增加或者删除字段后出现程序BUG

(24)禁止使用INSERT INTO t_xxx VALUES(xxx),必须显示指定插入的列属性
解读:容易在增加或者删除字段后出现程序BUG

(25)禁止使用属性隐式转换
解读:SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,而不能命中phone
索引,猜猜为什么?(这个线上问题不止出现过一次)

(26)禁止在WHERE条件的属性上使用函数或者表达式
解读:SELECT uid FROM t_user WHERE from_unixtime(day)>=‘2017-02-15’ 会导致全表扫描
正确的写法是:SELECT uid FROM t_user WHERE day>= unix_timestamp(‘2017-02-15 00:00:00’)

(27)禁止负向查询,以及%开头的模糊查询
解读:
a)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描
b)%开头的模糊查询,会导致全表扫描

(28)禁止大表使用JOIN查询,禁止大表使用子查询
解读:会产生临时表,消耗较多内存与CPU,极大影响数据库性能

(29)禁止使用OR条件,必须改为IN查询
解读:旧版本Mysql的OR查询是不能命中索引的,即使能命中索引,为何要让数据库耗费更多的CPU帮
助实施查询优化呢?

(30)应用程序必须捕获SQL异常,并有相应处理

你可能感兴趣的:(数据库)