mysql

# mysql数据库

# 第一章 服务概述

## 1 数据库服务分类

### 1.1 关系型数据库

“`sh
把复杂的数据结构归结为简单的二元关系(RDBMS),即二维表格形式(二维表);会注重数据存储的
持久性,但存储效率低;
此类型数据库服务,类似于excel表格的存储数据方式,多采用SQL语言方式进行操作管理;
“`

关系型数据库四层结构信息

| 序号 | 数据库结构 | 描述说明 |
| —- | ———————- | ———————————— |
| 01 | 数据库管理系统(DBMS) | 进行数据存储应用管理的操作环境或命令 |
| 02 | 数据库(DB) | 数据存储的承载环境 |
| 03 | 数据表(Table) | 数据关系的构建环境 |
| 04 | 数据字段(Field) | 实际数据信息展现形式 |

常见的关系数据库应用程序:

| 序号 | 数据库程序 | 应用说明 |
| —- | ———- | ———————————————————— |
| 01 | MySQL | 互联网公司应用最广泛 |
| 02 | Mariadb | 企业场景应用较少(20%),主要用于教学环境较多 |
| 03 | Oracle | 传统企业和部分国企应用较多,但也逐步被国产数据库替代 |
| 04 | SQLserver | 适合windows server系统环境部署的数据库服务,属于微软公司发布的数据库服务 |
| 05 | PostgreSQL | 适合于海量数据信息存储,对于金融行业数据信息分析能力将强 |

说明:关系型数据库的极致追求:数据存储的安全性,但是在某种程度会损失数据存储和读取的性能。

#### 1.2 非关系型数据库(属于数据库拆分时代)

没有具体模型的数据结构,英文简称NoSQL(Not Only SQL),意为”不仅仅是SQL”,比较注重数据读取的效率;

利用NoSQL数据库主要处理高并发应用的场景,以及海量数据存储应用的场景

常见的非关系数据库应用程序:

| 序号 | 数据库程序 | 应用说明 |
| —- | ———- | ———————————————————— |
| 01 | Redis | 可以利用内存存储数据,也可以采用磁盘存储数据,数据常见展示形式为key-value形式 |
| 02 | Memcache | 可以利用内存存储数据,也可以采用磁盘存储数据,数据常见展示形式为key-value形式 |
| 03 | Mongodb | 属于面向文档数据存储的数据库 |
| 04 | ES | 主要用于做日志数据的收集与检索的数据库(ELK ELFK) |

> 说明:非关系型数据库的极致追求:数据存储的高效性,但是在某种程序会牺牲数据存储的安全性

#### 1.3 企业型数据库(属于数据整合时代)

属于近些年,由国人研发设计出的数据库服务,可以满足很多国内高并发量网站数据存储和读取业务的需求;

常见的新型数据库应用程序:

| 序号 | 数据库程序 | 应用说明 |
| —- | ———- | ———————————————————— |
| 01 | TiDB | 开源分布式关系型数据库,是一款同时支持在线事务处理与在线分析处理的融合型分布式数据库产品 |
| 02 | OceanBase | 是由蚂蚁集团完全自主研发的国产原生分布式数据库,兼顾分布式架构的扩展性与集中式架构的性能优势 |
| 03 | PolarXDB | 是由阿里巴巴自主研发的云原生分布式数据库,是一款基于云架构理念分布式数据库产品,专注海量数据处理 |
| 04 | RDS/TDSQL | 阿里云/腾讯云平台基于SaaS云计算服务环境构建的数据库产品(PolarXDB TDSQL TiDB) |

## 2 数据库服务优势特点

“`sh
– MySQL数据库服务性能卓越,服务稳定,很少出现异常宕机的情况;
– MySQL数据库服务是开放源代码且无版权制约,自主性强,且使用成本低;
– MySQL数据库服务使用历史悠久,社区及用户非常活跃,遇到问题可以获取大量帮助;
– MySQL数据库服务软件体积小,安装使用简单,并且易于维护管理,安装及维护成本低;
– MySQL数据库服务业界口碑好,使得企业无需考虑就能直接使用;
– MySQL数据库服务架构应用广泛,可以用于构建LAMP LNMP LNMT等流行web架构;
– MySQL数据库服务支持多种操作系统,提供多种API接口,支持多种开发语言利用驱动接口调用;
“`

# 第二章 安装部署

## 1 安装方式

| 序号 | 安装方式 | 特征说明 |
| —- | —————— | ———————————————————— |
| 01 | 采用二进制方式安装 | 直接解压缩二进制程序包,进行简单的配置和初始化操作即可 |
| 02 | 采用rpm包方式安装 | 需要从官方网站下载rpm程序包,并且需要考虑系统环境的兼容性,解决软件程序包依赖 |
| 03 | 采用yum源方式安装 | 需要根据官方网站说明,配置yum下载的仓库源信息,在联网情况下进行安装部署 |
| 04 | 采用源码包方式安装 | 需要从官方网站下载源码程序包,并且需要解决程序包依赖问题,以及需要采用编译安装 |

“`sh
下载地址:https://downloads.mysql.com/archives/community/
“`

## 2 安装过程

### 1 二进制安装

“`sh
一 安装前准备
1 关闭防火墙
systemctl disabled –now firewalld

2 添加hosts解析
echo ‘192.168.137.51 db01’ >> /etc/hosts

3 关闭selinux
sed -i ‘s/SELINUX=enforcing/SELINUX=disabled/g’ /etc/sysconfig/selinux

4 删除系统自带的mysql或mariadb
rpm -e `rpm -qa | egrep ‘mysql|mariadb’`

5 安装依赖
yum install -y libaio-devel

二 安装过程
1 下载上传安装包

2 解压安装包
cd /usr/local/
tar xf mysql-8.0.26-linux-glibc2.12-x86_64.tar.xz

3 创建软连接(目的:以后方便升级更换)
ln -s mysql-8.0.32-linux-glibc2.12-x86_64 mysql

4 添加环境变量
echo ‘export PATH=/usr/local/mysql/bin:$PATH’ >> /etc/profile
source /etc/profile

5 添加用户组
gropadd -g 3306 mysql
useradd -g mysql -u 3306 -M -s /sbin/nologin mysql

6 创建mysql目录
mkdir -p /data/3306/{data,logs}

7 初始化
7.1 不安全初始化
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data

7.2 安全初始化
mysqld –initialize –user=mysql –basedir=/usr/local –datadir=/data/3306/data

8 创建修改/etc/my.cnf
cat > /etc/my.cnf < shutdown |
| 07 | | mysqladmin -uroot -poldboy123 shutdown |

“`sh
1 mysqld &
[root@db01 ~]# ps -ef | grep mysql
mysql 19042 19023 15 10:17 pts/0 00:00:00 mysqld

2 mysqld_safe &
root@db01 ~]# ps -ef | grep mysql
root 19094 19023 0 10:19 pts/0 00:00:00 /bin/sh /software/mysql/bin/mysqld_safe
mysql 19208 19094 15 10:19 pts/0 00:00:00 /software/mysql/bin/mysqld –basedir=/software/mysql –datadir=/data/3306/data –plugin-dir=/software/mysql/lib/plugin –user=mysql –log-error=db01.err –pid-file=db01.pid –socket=/tmp/mysql.sock

mysqld_safe 比mysqld多了一个mysqld_safe进程
数据库服务运行启动默认包含两个进程:

– 数据库服务主进程(管理进程):mysqld_safe

利用mysqld_safe脚本信息,可以满足定制修改的需求,设置一些灵活的变量信息,支持一些高级的启动功能;

利用mysqld_safe命令启动数据库服务,可以进行特殊功能信息配置,并且可以实现数据库维护性操作,还可以设置启停重启;

– 数据库服务子进程(工作进程):mysqld

利用mysqld命令可以启动数据库服务,并显示启动的命令进程信息,但不能负责完成数据库服务的停止和重启操作;

利用mysqld命令可以启动数据库服务,有些服务功能是锁定死的,不能编辑改动(比如日志功能);

> 说明:通过以上进程信息,可以看出数据库服务可以更灵活的启动,使用mysqld和mysqld_safe命令来完成,主要用于维护操作。

“`

![image-20250117120019502](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20250117120019502.png)

## 4 密码管理

### 1 密码设置

“`sh
给mysql配置登陆密码,并使用新密码进行登录数据库
[root@oldboy ~]# mysqladmin password ‘oldboy123’
或者
[root@oldboy ~]# mysqladmin -uroot -p password ‘oldboy123’
— 表示第一次登录设置密码信息

# 进行登录测试:
[root@oldboy ~]# mysql -uroot -p
Enter password:
quit
“`

### 2 修改密码

“`sh
# 利用数据库服务命令修改密码信息
[root@oldboy ~]# mysqladmin -uroot -poldboy123 password ‘oldboy’
— 将原有oldboy123密码 改为oldboy

# 进行测试登录
[root@oldboy ~]# mysql -uroot -poldboy
Welcome to the MySQL monitor. Commands end with ; or \g.

# 利用数据库服务SQL语句修改密码信息
alter user root@’localhost’ identified by ‘oldboy123’;
flush privileges;
— 适合于mysql 8.0

update mysql.user set authentication_string=PASSWORD(‘oldboy123′) where user=’root’ and host=’localhost’;
flush privileges;
— 适合于mysql 5.7

set password for ‘oldboy’@’localhost’=PASSWORD(‘oldboy123′);
flush privileges;
— 适合于mysql 5.6
“`

### 3 密码重置

“`sh
1 关闭数据库
shutdown或停止服务

2 重启数据库
mysqld_safe –skip-grant-tables –skip-networking &
— skip-grant-tables 表示忽略授权表启动
— skip-networking 忽略网络通讯方式启动

3 采用免密登录,重置密码
mysql -uroot
flush privileges;
–表示先将数据库服务授权表加载到内存中,也可以将内存中修改的授权信息存入到磁盘中
alter user root@’localhost’ identified by ‘oldboy123′;
–修改数据库服务用户密码信息

4 关闭数据库,重启数据库,使用新的密码登录

# 杀死已经运行的数据库服务进程信息
[root@oldboy ~]# pkill mysqld
[root@oldboy ~]# ps -ef|grep mysql

# 重新启动数据库服务
[root@oldboy ~]# systemctl start mysqld
[root@oldboy ~]# netstat -lntup|grep 3306

# 重新登录进行测试
[root@oldboy ~]# mysql -uroot -poldboy123
“`

## 5 用户管理

### 1 创建用户

“`sh
数据库用户创建:’用户名称信息’@’白名单信息’
— 用户名称信息:创建的用户字符串信息
— 白名单信息:网段IP地址信息或者名称信息
create user blog@’192.168.56.%’ identified by ‘oldboy123′;
# 方式一:书写方法(企业常用)
root@’192.168.30.0/24′
root@’192.168.30.0/255.255.255.0′
— 授权一个网段主机均可远程登录数据库服务,表示这个网段中192.168.30.1~192.168.30.254均可访问
— 在数据库中识别默认掩码就是/24,其他掩码信息需要具体写明,需要注意特殊VLSM子网掩码需要长格式指明,不能简写***

# 方式二:书写方法(企业常用)
root@’192.168.30.%’
— 授权一个网段主机均可远程登录数据库服务,表示这个网段中192.168.30.1~192.168.30.254均可访问

# 方式三:书写方法
root@’172.16.%’
— 授权一个更大网段主机均可远程登录数据库服务,表示这个网段中192.168.0.1~192.168.255.254均可访问

# 方式四:书写方法
root@’%’
— 授权任意主机都可以远程登录数据库服务

# 方式五:书写方法
root@’192.168.30.51′
— 授权一个具体的主机可以远程登录数据库服务,表示这个主机192.168.30.51可以访问
root@’192.168.30.5%’
— 授权多个具体的主机可以远程登录数据库服务,表示多个主机192.168.30.50~192.168.30.59均可以访问
“`

### 2 查看用户

“`sh
select user,host,authentication_string from mysql.user;
查看当前用户信息
select user();
“`

### 3 删除用户

“`sh
drop user test@’localhost’;
“`

### 4 锁定用户

“`sh
alter user test@’localhost’ account lock;
Query OK, 0 rows affected (0.00 sec)

select user,account_locked from mysql.user;
+——————+—————-+
| user | account_locked |
+——————+—————-+
| mysql.infoschema | Y |
| mysql.session | Y |
| mysql.sys | Y |
| root | N |
| test | Y |
+——————+—————-+
“`

### 5 解锁用户

“`sh
alter user test@’localhost’ account unlock;
Query OK, 0 rows affected (0.04 sec)

select user,account_locked from mysql.user;
+——————+—————-+
| user | account_locked |
+——————+—————-+
| mysql.infoschema | Y |
| mysql.session | Y |
| mysql.sys | Y |
| root | N |
| test | N |
+——————+—————-+
“`

### 6 不同版本用户管理

#### 1 创建用户并授权区别

##### 1 5.7版本

“`sh
只需要grant命令即可完成用户创建操作+用户密码设置+用户授权操作
grant all on *.* to test@’localhost’ identified by ‘123’;
“`

##### 2 8.0版本

“`sh
所有系统表都是InnoDB管理了,需要先建立用户再进行授权,并且grant命令不再支持indentified by语句
create user test1@’localhost’ identified by ‘123’;
Query OK, 0 rows affected (0.01 sec)

grant all on *.* to test1@’localhost’ ;
Query OK, 0 rows affected (0.00 sec)
“`

#### 2 用户密码插件区别

##### 1 5.7版本

“`sh
用户密码加密插件使用的早期版本时,可以保证客户端程序正常登陆,保证主从架构数据正常,保证集群通讯不受到影响;
show variables like ‘%auth%’;
+——————————-+———————–+
| Variable_name | Value |
+——————————-+———————–+
| authentication_policy | *,, |
| default_authentication_plugin | caching_sha2_password |
+——————————-+———————–+
“`

##### 2 8.0版本

“`sh
show variables like ‘%auth%’;
+——————————-+———————–+
| Variable_name | Value |
+——————————-+———————–+
| authentication_policy | *,, |
| default_authentication_plugin | caching_sha2_password |
+——————————-+———————–+
“`

##### 3 用户插件说明

“`sh
解决数据库服务升级后,用户密码加密插件影响连接建立问题,可以采取以下两种方案

– 方案一:替换原有默认密码加密插件,更换为历史版本使用的加密插件(mysql_native_passwordl) `运维职责`
– 方案二:替换客户端连接数据库服务端的驱动程序软件,使之兼容新版本加密插件功能
`开发职责’
“`

##### 4 修改用户插件

“`sh
1 创建用户过程中修改插件信息
create user test2@’localhost’ identified with mysql_native_password by ‘123’ ;
select user,plugin from mysql.user where user=’test2′;
+——-+———————–+
| user | plugin |
+——-+———————–+
| test2 | mysql_native_password |
+——-+———————–+

2 修改已创建的用户密码插件
alter user test1@’localhost’ identified with mysql_native_password by ‘123’;
select user,plugin from mysql.user where user=’test1′;
+——-+———————–+
| user | plugin |
+——-+———————–+
| test1 | mysql_native_password |
+——-+———————–+

3 修改数据库配置文件信息
cat >>/etc/my.cnf< 说明:all privileges(all)包含查看的所有权限信息,但是唯独缺了Grant option,不能授权用户,此权限只能给root@local用户

### 2 权限设置

“`sh
1 语法格式
grant all on 授权对象范围 to 用户信息

2 举例
grant all on *.* –对所有数据库的所有表进行授权
grant all on test.* –对test库中的所有表进行授权
grant all on test.test –对指定库的指定表进行授权
grant insert,select,update,delete on test.* to test@’localhost’;

3 查看某个用户权限
show grants for test@’localhost’;

4 挥手权限
revoke delete on test.* from test@’localhost’;
“`

### 3 授权思路

“`sh
对生产表授权一定要小,越小越安全,给业务用户常规的授权,包含增删改查
“`

| 序号 | 权限 | 互联网应用 |
| —- | —————— | ————————————– |
| 01 | SELECT 查询数据 | 访问网站页面时,表示浏览查看数据信息; |
| 02 | INSERT 插入数据 | 访问网站页面时,表示发表文章数据信息; |
| 03 | UPDATE 修改数据 | 访问网站页面时。表示修改文章数据信息; |
| 04 | DELETE 删除数据 | 访问网站页面时,表示删除文章数据信息; |

“`sh
生产常规授权方案
create database bbs;
create user bbs@’localhost’ identified by ‘XZnh@95599′;
grant all on bbs.* to bbs@’localhost’ with grant option;
with grant option;这个的意思是可以授予其他用户权限
特殊数据库权限授权方法:
# 授权命令语法格式
create user ‘user’@’ip’ identified by ‘password’;
grant SELECT on op_db.* to ‘user’@’ip’ with grant option;
# 授权命令实际应用
create user ‘root’@’127.0.0.1’ identified by ‘****’;
GRANT ALL PRIVILEGES ON *.* TO ‘root’@’127.0.0.1’ WITH GRANT OPTION;
flush privileges;
此句中all privileges跟 all等价,但是加了privileges更清晰,适用的版本更多

grant SELECT on op_db.* to ‘user’@’ip’ identified by ‘password’ with grant
option;
flush privileges;

说明:WITH GRANT OPTION 这个选项表示该管理用户可以将自己拥有的授权权限授权给别人。
经常有人在创建操作用户的时候不指定WITH GRANT OPTION选项,导致后来该用户不能使用GRANT命令创建用户;
或者给其它用户授权。如果不想这个用户有这个grant的权限,可以不加这句;
“`

## 7 连接管理

### 1 利用socket建立远程连接(unix 套接字文件连接)

“`sh
[root@db01 bin]# cat /etc/my.cnf
[mysql]
socket=/tmp/mysql.sock
[mysqld]
user=mysql
basedir=/software/mysql
datadir=/data/3306/data
socket=/tmp/mysql.sock

mysql -uroot -poldboy123 -S /tmp/mysql.sock
— -S指定socket的信息可以省略,因为在数据库服务配置文件的客户端已经配置过了
“`

### 2 利用TCP/IP建立远程连接(网络地址与端口)

“`sh
# 利用TCP/IP建立数据库服务连接
[root@oldboy ~]# mysql -uroot -poldboy123 -h 192.168.30.101 -P3306 -S
/var/lib/mysql/mysql.sock
说明:需要在数据库服务中授权网络白名单用户,才能实现采用TCP/IP方式登录操控数据库服务
“`

## 8 配置文件管理

### 1 配置文件书写格式规范说明

“`sh
# 通过数据库服务启动命令获取加载文件顺序
mysqld –help –verbose|grep my.cnf
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
— 数据库服务加载配置文件顺序:
— /etc/my.cnf –> /etc/mysql/my.cnf –> /usr/local/mysql/etc/my.cnf –>
~/.my.cnf
# 数据库服务加载特定路径中的配置文件信息
配置文件特定路径举例:/opt/my.cnf, /data/3306/my.cnf, /data/3307/my.cnf,
/data/3308/my.cnf
mysqld –defaults-file=/opt/my.cnf &
mysqld_safe –defaults-file=/opt/my.cnf &
— 利用启动脚本加载参数信息,手工指定加载配置文件的路径位置信息
“`

### 2 配置文件信息结构说明

| | 配置信息 | 解释说明 |
| —- | ————————————————— | ———————————————— |
| | [mysqld] | 服务端配置标签 |
| 01 | user=mysql | 表示数据库服务管理用户信息 |
| 02 | basedir=/usr/local/mysql | 表示数据库服务程序安装路径 |
| 03 | datadir=/data/3306/data | 表示数据库服务数据存储路径 |
| 04 | server_id=6 | 表述数据库服务标识节点编号(主从复制的唯一编号) |
| 05 | port=3306 | 表示数据库服务启动端口设定 |
| 06 | socket=/tmp/mysql.sock | 表示数据库服务本地连接文件(套接字文件) |
| 07 | default_authentication_plugin=mysql_native_password | 表示数据库服务加密插件设定,用于向下兼容 |
| | [mysql] | 客户端配置标签(本地连接数据库生效) |
| 01 | socket=/tmp/mysql.sock | 表示数据库服务本地连接文件(套接字文件) |

“`sh
利用数据库服务命令启动服务加参数信息,完成初始化定制操作
mysqld_safe –skip-grant-tables –skip-networking &
“`

## 9 实例管理

### 1 概念

“`sh
一般在一个系统环境中,可以运行多个相同的服务程序信息,并且产生不同的进程和网络端口信息,就可以称为多实例概念;
“`

### 2 多实例目的

“`sh
数据库多实例是指在同一台服务器上运行多个独立的数据库实例。
每个实例都有自己的系统进程、内存结构、数据文件和日志文件,它们之间相互独立,互不干扰。
提高系统的可用性
提高系统整体性能
简化系统日常管理
可以有效节约成本
“`

### 3 多实例部署

| | 实例服务端口 | 实例存储路径 | 实例配置文件 | 套接字文件 |
| ——– | ————– | ————— | ———————- | ——————- |
| mysql-01 | 端口信息:3307 | /data/3307/data | /data/3307/data/my.cnf | /tmp/mysql3307.sock |
| mysql-02 | 端口信息:3308 | /data/3308/data | /data/3308/data/my.cnf | /tmp/mysql3308.sock |
| mysql-03 | 端口信息:3309 | /data/3309/data | /data/3309/data/my.cnf | /tmp/mysql3309.sock |

“`sh
mkdir -p /data/330{7..9}/data
chown -R mysql. /data/*
mv /etc/my.cnf /tmp
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql —
datadir=/data/3307/data
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql —
datadir=/data/3308/data
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql —
datadir=/data/3309/data
— 初始化完毕后可以检查数据库目录中,是否已经存在了数据库初始化产生的数据文件信息;
# vim /data/3307/data/my.cnf
[mysql]
socket=/tmp/mysql3307.sock
[mysqld]
user=mysql
port=3307
basedir=/usr/local/mysql
datadir=/data/3307/data
socket=/tmp/mysql3307.sock
vim /data/3308/data/my.cnf
[mysqld]
user=mysql
port=3308
basedir=/usr/local/mysql
datadir=/data/3308/data
socket=/tmp/mysql3308.sock
vim /data/3309/data/my.cnf
[mysqld]
user=mysql
port=3309
basedir=/usr/local/mysql
datadir=/data/3309/data
socket=/tmp/mysql3309.sock
mysqld_safe –defaults-file=/data/3307/data/my.cnf &
mysqld_safe –defaults-file=/data/3308/data/my.cnf &
mysqld_safe –defaults-file=/data/3309/data/my.cnf &
“`

# 第三章 数据库语句应用

## 1 概述

“`sh
关系型数据库要进行交互,就需要用到sql
有sql92 和sql99
“`

## 2 sql分类

根据sql的功能作用,划分为4个语句类型

### 1 数据定义语言(DDL)

Data Definition Language

“`sh
主要用于定义数据库、表等数据对象,其中包括create、alter、drop
? data definition;
“`

| 序号 | 语句命令 | 功能作用 |
| —- | ——– | ——————– |
| 01 | create | 用于创建数据库、表 |
| 02 | alter | 用于修改表的结构信息 |
| 03 | drop | 用于删除数据库、表 |

### 2 数据控制语言(DCL)

Data Control Language

“`sh
主要用于控制用户的访问权限,其中包括grant语句、revoke语句、commit语句和rollback语句;
? account management;
“`

| 序号 | 语句命令 | 功能作用 |
| —- | ——– | ——————– |
| 01 | grant | 用于给用户增加权限 |
| 02 | revoke | 用于收回用户的权限 |
| 03 | commit | 用于提交事务操作信息 |
| 04 | rollback | 用于撤销事务操作信息 |

### 3 数据操纵语言(DML)

Data Manipulation Language

“`sh
用于多数据库的数据进行添加、修改和删除操作,其中包含insert语句,update语句和delete语句
“`

| 序号 | 语句命令 | 功能作用 |
| —- | ——– | —————- |
| 01 | insert | 用于插入数据信息 |
| 02 | update | 用于修改数据信息 |
| 03 | delete | 用于删除数据信息 |

### 4 数据查询语言(DQL)

Data Query Language

“`sh
主要用于查询数据,也就是指select语句;通过select语句可以查询数据表中的一条或多条数据
“`

## 3 字符集

### 1 查看修改字符集

“`sh
查询数据库服务编码信息
show charset;
查看获取数据库服务默认字符编码
mysql > show variables like “%character%”;
show variables like ‘%character%’;
+————————–+—————————————————————-+
| Variable_name | Value |
+————————–+—————————————————————-+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
| character_sets_dir | /usr/local/mysql-8.0.32-linux-glibc2.12-x86_64/share/charsets/ |
+————————–+—————————————————————-+
“`

| 序号 | 参数信息 | 解释说明 |
| —- | ————————– | ———————————————————— |
| 01 | `character_set_client` | 用来设置客户端使用的字符集 |
| 02 | `character_set_connection` | 用来设置连接数据库时的字符集 如果程序中没有指明连接数据库使用的字符集类型则按照这个字符集设置。 |
| 03 | `character_set_database` | 用来设置默认创建数据库的编码格式 如果在创建数据库时没有设置编码格式,就按照这个格式设置。 |
| 04 | character_set_filesystem | 文件系统的编码格式,把操作系统上的文件名转化成此字符集 即把 character_set_client转换character_set_filesystem, 默认binary是不做任何转换 |
| 05 | `character_set_results` | 数据库给客户端返回时使用的编码格式,如果没有指明,使用服务器默认的编码格式。 |
| 06 | `character_set_server` | 服务器安装时指定的默认编码格式,这个变量建议由系统自己管理,不要人为定义。 |
| 07 | character_set_system | 数据库系统使用的编码格式,这个值一直是utf8 不需要设置,它是为存储系统元数据的编码格式。 |
| 08 | character_sets_dir | 这个变量是字符集安装的目录。 |

字符编码中utf8编码和utf8mb4编码之间差异

| 序号 | 区别说明 | 解释说明 |
| —- | ——– | ——————————————————– |
| 01 | utf8 | 最多存储3字节长度字符,不能支持特殊emoji表情符号信息存储 |
| 02 | utf8mb4 | 最多存储4字节长度字符,可以支持特殊emoji表情符号信息存储 |

“`sh
修改数据库服务编码信息(配置文件)
[mysqld]
character-set-server=utf8mb4
— 设置服务端字符集编码为utf8mb4
[mysql]
socket=/tmp/mysql.sock
default-character-set=utf8mb4
— 设置客户端字符集编码为utf8mb4

修改数据库服务编码信息:(命令操作修改)
假设数据库表原有字符集为gbk,并且已经存储数据了,需要将表和数据字符集进行调整转换utf8mb4
# 方法一:
mysql > alter table t1 charset utf8mb4;
— 不严谨的方法,只会影响之后存储的数据,不会修改之前存储的数据
# 方法二:
·锁表逻辑导出数据(例如:mysqldump)
·重新创建数据空表(设置目标字符集)
·导入备份数据信息
— 严谨的方法,可以影响之后存储的数据,也会修改之前存储的数据
— 字符集转换是可以的,但是必须保证修改后的字符集是修改前的严格超集(包含)
“`

### 2 字符集校对规则

“`sh
查询数据库服务校对规则:
show collation;

一般数据库校对规则设置,与对应字符编码设置是有关联得;
其中utf8mb4字符集中,常用的排序规则有utf8mb4_unicode_ci、utf8mb4_general_ci、
utf8mb4_bin:
排序规则,就是指字符比较时按照字符编码还是直接用二进制数据比较,以及是否区分大小写。
主要可以根据校对规则定义或设置的不同:在查询数据信息时,影响数据信息的查询输出和排序效果;

“`

| 排序规则后缀 | 解释说明 |
| ———— | ———————————— |
| _ci | 不区分大小写,Case-insensitive的缩写 |
| _cs | 区分大小写,Case-sensitive的缩写 |
| _ai | 不区分重音,Accent-insensitive的缩写 |
| _as | 区分重音,Accent-sensitive的缩写 |
| _bin | 采用二进制方式存储数据信息 |

**utf8mb4_unicode_ci**

是基于标准Unicode来排序和比较,能够在各种语言之间精确排序。

且在特殊情况下,Unicode排序规则为了能够处理特殊字符的情况,实现了略微复杂的排序算法。

**utf8mb4_general_ci**

没有实现Unicode排序规则,在遇到某些特殊字符情况下,排序结果可能不一致。

但是,在绝大多数情况下,这些特殊字符的顺序并不需要那么精确。

**utf8mb4_bin**

将字符串的每个字符用二进制数据编译存储,区分大小写,而且可以存二进制的内容。

> 综合来说:
>
> utf8mb4_unicode_ci比较准确,utf8mb4_general_ci速度较快。
>
> utf8mb4_unicode_ci对于特殊字符的处理,在中文、英文应用中不会使用到;
>
> 除非你的应用有德语、法语、俄语等,则需要使用utf8mb4_unicode_ci,否则一般选用utf8mb4_general_ci就可以了。

## 4 数据类型

“`sh
使用MySQL数据库存储数据时,不同类型数据的存储格式各不相同。MySQL数据库提供了多种数据类型;

主要包括数值类型、日期和时间类型、字符串类型。
“`

### 1 数值类型

“`sh
主要包含整数类型,浮点类型
“`

#### 1 整数类型

| 数据类型 | 字节数 | 无负号取值 | 有符号取值 |
| ——— | —— | ————- | ———————– |
| tinyint | 1 | 0~255 | -128~127 |
| smallint | 2 | 0~65535 | -32768~32767 |
| mediumint | 3 | 0~16777215 | -8388608~8388607 |
| int | 4 | 0~4294967295 | -2147483648~2147483647 |
| bigint | 8 | 0~2的64次方-1 | -2的64次方~-2的63次方-1 |

#### 2 浮点数类型

“`sh
在MySQL数据库中,小数的表示分为浮点和定点数两种类型。

其中浮点数类型分为两种:单精度浮点数类型和双精度浮点数类型;

“`

| 数据类型 | 字节数 | 数据类型说明 |
| ———– | —— | ———————————————— |
| float(m,d) | 4字节 | 单精度浮点型 8位精度(4字节) m总个数,d小数位 |
| double(m,d) | 8字节 | 双精度浮点型 16位精度(8字节) m总个数,d小数位 |

### 2 时间类型

| 数据类型 | 字节数 | 取值范围 | 日期格式 |
| ——— | —— | ————————————— | ——————- |
| year | 1 | 1901~2155 | YYYY |
| date | 3 | 1000-01-01~9999-12-31 | YYYY-MM-DD |
| time | 3 | -838:59:59~838:59:59 | hh:mm:ss |
| datatime | 8 | 1000-01-01 00:00:00~9999-12-31 23:59:59 | YYYY-MM-DD hh:mm:ss |
| timestamp | 4 | 1970-01-01 00:00:01~2038-01-19 03:14:07 | YYYY-MM-DD hh:mm:ss |

### 3 字符串类型

| 序号 | 数据类型 | 类型说明 |
| —- | ——– | ———————————————————— |
| 01 | char | 表示定长的字符串类型,n表示可以存储字符的字节上限(n取值 0~255) |
| 02 | varchar | 表示变长的字符串类型,n表示可以存储字符的字节上限(n取值 0~65535) |
| 03 | enum | 枚举类型值 |

定义char和varchar的类型方式:`char(M) varchar(M)`;

定义类型中M指的是字符串的最大长度,为了更好理解char和varchar之间区别,以char(4)和varchar(4)做对比进行说明

| 插入数值 | char(4)存储情况 | varchar(4)存储情况 |
| ——– | ————— | —————— |
| ” | 4字节 | 1字节 |
| ‘ab’ | 4字节 | 3字节 |
| ‘abc’ | 4字节 | 4字节 |
| ‘abcd’ | 4字节 | 5字节 |

当数据为char(4),不管插入值的长度是多少,所占用的存储空间都是4字节;

而数据为varchar(4),对应的数据所占用的字节数为实际长度加1;

> 说明:如果插入的字符串尾部存在空格,char类型会去除空格后进行存储,而varchar类型会保留空格完整地存储字符串

## 5 约束

“`sh
通过数据类型设置的约束与属性,可以让数据库服务限制人类录入的数据信息,从而避免录入数据信息混乱的局面;
并且,通过数据类型的约束与属性设置,还可以避免数据信息输入的重复与输入数据信息不能为空;
“`

常见的约束定义:

| 序号 | 约束方法 | 解释说明 |
| —- | —————– | ———————————————- |
| 01 | PK(primary key) | 表示主键约束,非空且唯一(表中只能有一个主键) |
| 02 | UK(unique key) | 表示唯一约束 |
| 03 | NN(not null) | 表示非空约束 |
| 04 | FK(foreign key) | 表示外键约束,多表之间关联使用(了解) ??? |

常见的属性定义:

| 序号 | 属性信息 | 解释说明 |
| —- | ————– | ———————————————————— |
| 01 | default | 设定默认数据信息,可以实现自动填充 |
| 02 | auto_increment | 设定数值信息自增,可以实现数值编号自增填充(一般配合主键使用) |
| 02 | comment | 设定数据注释信息 |
| 03 | unsigned | 设定数值信息非负,可以实现数值信息列不能出现负数信息 |

## 6 外键

建库

![image-20250206174126765](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20250206174126765.png)

![image-20250206174144707](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20250206174144707.png)

### 1 在建表的时候增加外键

“`sh
# 创建外键语法格式
foreign key(外键字段) references 主表(主键);

# 创建外键关联的父表
create table class(
id int primary key auto_increment,
name varchar(10) not null comment “班级名字,不能为空”,
room varchar(10) comment ‘教室:允许为空’
) charset utf8;

# 创建子表使用外键
create table student(
id int primary key auto_increment,
number char(10) not null unique comment “学号:不能重复”,
name varchar(10) not null comment “姓名”,
c_id int,
foreign key(c_id) references class(id)
) charset utf8;
— 增加外键:c_id是外键字段,class是引用表(父表),id是引用字段(主键)
“`

### 2 在创建表之后增加外键

“`sh
# 创建外键语法格式
alter table 表名 add constraint 外键名 foreign key(外键字段) references 父表(主键字段)

# 创建没有外键信息的表
create table t_foreign(
id int primary key auto_increment,
c_id int
)charset utf8;

# 在没有外键的表中添加外键
alter table t_foreign add constraint class_foreign foreign key(c_id) references class(id);
外键增加条件: 外键字段必须与引用表(父表主键)的数据类型严格保持一致
“`

### 3 删除外键

“`sh
外键不能被修改,只能先删除后再新增;
# 删除外键语法格式
alter table 表名 drop foreign key 外键名;

# 删除表(t_foreign)中外键信息
alter table t_foreign drop foreign key class_foreign;
“`

### 4 外键说明

“`sh
外键也称之为外键约束,主要作用在于对数据进行约束:

约束01:外键对子表的数据写操作约束(增加和更新)

如果子表中插入的数据所对应的外键在父表不存在,创建不能成功.

select * from class;
Empty set (0.00 sec)

select * from student;
Empty set (0.00 sec)

insert into student values(null,’2023110001′,’xiaoQ’,1);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`oldboy`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`c_id`) REFERENCES `class` (`id`))

约束02:外键对父表也有数据约束

当父表操作一个记录,但是该记录被子表所引用的时候,那么父表的操作将会被限制(更新: 主键和删除)

insert into class values (1,’Linux80′,’001′),(2,’Linux81′,’002′);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0

insert into student values(null,’2023110001′,’xiaoQ’,1);
Query OK, 1 row affected (0.00 sec)

select * from student;
+—-+————+——-+——+
| id | number | name | c_id |
+—-+————+——-+——+
| 2 | 2023110001 | xiaoQ | 1 |
+—-+————+——-+——+
1 row in set (0.00 sec)

select * from class;
+—-+———+——+
| id | name | room |
+—-+———+——+
| 1 | Linux80 | 001 |
| 2 | Linux81 | 002 |
+—-+———+——+
2 rows in set (0.00 sec)

delete from class where id=1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`oldboy`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`c_id`) REFERENCES `class` (`id`))
— 删除父表数据,数据被字表所引用,所以不能更新或者删除父行记录

“`

## 7 数据库模式概念

在数据库服务应用过程中存在SQL_mode概念(SQL模式),规范SQL执行行为和数据的准确性,能够符合数据录入常识和执行结果意义

例如:日期信息不能出现 0000-00-00 信息,月份只能是1-12,日期只能是1-31,一旦违反常识便会报错;

例如:在进行数据运算时,除法运算时,除数不能为0;

例如:当定义数据类型为char(10),不能超过字符长度,超过长度就报错;

例如:设置only_full_group_by(5.7以后的特性),禁止进行分组查询时,出现聚合信息1对多的显示输出;

获取SQLmode设置的默认信息:

“`tiki wiki
select @@sql_mode;
+———————————————————————————————————————–+
| @@sql_mode |
+———————————————————————————————————————–+
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+———————————————————————————————————————–+
1 row in set (0.00 sec)
“`

SQLmode配置参数信息解释说明:

| 序号 | 模式参数配置 | 解释说明 |
| —- | —————————- | ———————————————————— |
| 01 | `ONLY_FULL_GROUP_BY` | 对于GROUP BY聚合操作,如果在SELECT中的列,没有在GROUP BY中出现,那么这个SQL是不合法的,因为列不在GROUP BY从句中。 |
| 02 | `STRICT_TRANS_TABLES` | 在该模式下,如果一个值不能插入到一个事物表中,则中断当前的操作,对非事物表不做限制 |
| 03 | `NO_ZERO_IN_DATE` | 在严格模式下,不允许日期和月份为零 |
| 04 | `NO_ZERO_DATE` | 设置该值,mysql数据库不允许插入零日期,插入零日期会抛出错误而不是警告 |
| 05 | `ERROR_FOR_DIVISION_BY_ZERO` | 在INSERT或UPDATE过程中,如果数据被零除,则产生错误而非警告。如 果未给出该模式,那么数据被零除时MySQL返回NULL |
| 06 | NO_ENGINE_SUBSTITUTION | 如果需要的存储引擎被禁用或未编译,那么抛出错误。不设置此值时,用默认的存储引擎替代,并抛出一个异常 |
| 07 | NO_AUTO_VALUE_ON_ZERO | 该值影响自增长列的插入。默认设置下,插入0或NULL代表生成下一个自增长值。如果用户希望插入的值为0,该列又是自增长的,那么这个选项就有用了。 |
| 08 | NO_AUTO_CREATE_USER | 禁止GRANT创建密码为空的用户 |
| 09 | PIPES_AS_CONCAT | 将”\|\|”视为字符串的连接操作符而非或运算符,这和Oracle数据库是一样的,也和字符串的拼接函数Concat相类似 |
| 10 | ANSI_QUOTES | 启用ANSI_QUOTES后,不能用双引号来引用字符串,因为它被解释为识别符 |

当进行数据库服务版本升级时,可能之前版本数据信息不能满足新版本数据库服务的SQL_mode信息设定,需要暂时设置SQLmode为空

“`tiki wiki
set global sql_mode=”;
— 配置完毕后,可以重新登录数据库服务进行检查确认
“`

###

# 第四章 语句应用实践

“`sh
帮助信息
\h
help contents
? contents

help contents Data Definition;
? create
? create database
“`

## 1 数据定义语言实践(DDL)

“`sh
创建数据库
mysql > create database oldboy;
mysql > create schema oldboy;
— 创建新的数据库
mysql > create database oldboy character set utf8mb4;
mysql > create database oldboy charset utf8 collate utf8_general_mysql500_ci;
— 创建新的数据库,并修改调整默认的字符编码
mysql > show databases;
— 查看是否已经创建好
mysql > show create database oldboy;
— 查看创建库的语句信息
mysql > show databases;
— 查看所有数据库信息
mysql > show databases like ‘%xiao%’;
— 检索查看指定的数据库信息
mysql > show create database oldboy;
— 查看创建库的语句信息
“`

数据库安装完毕后,默认的数据库说明:

| 序号 | 数据库名称 | 作用说明 |
| —- | —————— | —————————————————- |
| 01 | information_schema | 数据库系统运行状态,性能配置等数据信息存储的数据库 |
| 02 | mysql | 数据库用户授权权限、用户管理相关数据信息存储的数据库 |
| 03 | performance_schema | 数据库系统运行状态,性能配置等数据信息存储的数据库 |

> 说明:以上三个是数据库系统中默认的数据库,可以用于管理应用。

“`sh
mysql > use xiaoq
Database changed
— 在已有数据库之间进行切换
mysql > select database();
+————–+
| database() |
+————–+
| xiaoq |
+————–+
1 row in set (0.00 sec)
— 查看当前所在数据库信息

数据定义语句定义数据库规范说明:**

– 创建数据库名称规范:要和业务有关,不要有大写字母(为了多平台兼容),不要数字开头,不要含有系统关键字信息;
– 创建数据库明确字符:创建数据库时明确(显示)的设置字符集信息,为了避免跨平台兼容性与不同版本兼容性问题;
– 删除数据库操作慎用:在对数据库进行删除操作时,一定要经过严格审计后再进行操作,并且数据库普通用户不能有drop权限;

mysql > use mysql;
— 切换到指定数据库中
mysql > show tables;
— 查看数据库中所有表信息
mysql > desc stu1;
— 查看数据库中指定表数据结构信息
mysql > show create table stu1;
— 查看数据库中指定表创建语句信息

# 修改数据表名称信息
mysql > rename table stu1 to stu2;
或者
mysql > alter table stu2 rename stu3;
— 利用上面种方式均可修改表名称信息
mysql > show tables;
— 查看表名称信息是否修改

# 修改数据表编码信息
mysql > alter table stu1 charset utf8mb4;
— 修改表结构中字符集编码信息
mysql > show create table stu1;
— 查看表字符编码信息情况

# 数据表结构调整命令语法
mysql > alter table <表名> add column <字段名称> <数据类型> <约束与属性> [comment ‘注释’] [选项参数];
— 利用alter在数据表中添加新的表结构字段
mysql > alter table <表名> drop column <字段名称>;
— 利用alter在数据表中删除已有表结构字段
mysql > alter table <表名> modify column <字段名称> <数据类型> <约束与属性> [comment ‘注释’] [选项参数];
— 利用alter在数据表中修改已有表结构字段(数据类型 约束与属性)
mysql > alter table <表名> change column <旧字段名称> <新字段名称> <数据类型> <约束与属性> [comment ‘注释’] [选项参数];
— 利用alter在数据表中修改已有表结构字段(字段名称 数据类型 约束与属性)
mysql > alter table <表名> drop index <字段名称> ;
— 利用alter在数据表中删除已有表结构字段(约束与属性)

# 具体实际操作过程(添加新的表结构字段)
mysql > alter table stu add column telno char(11) not null unique key comment ‘手机号’;
— 在学生表中,添加新的表结构字段列(追加字段列-单列操作)
mysql > alter table stu add column wechat varchar(64) not null unique key comment ‘微信号’ after age;
— 在学生表中,添加新的表结构字段列(插入字段列-单列操作)
mysql > alter table stu add column sid int not null unique key comment ‘微信号’ first;
— 在学生表中,添加新的表结构字段列(插入首行列-单列操作)
mysql > desc stu;
— 查看表结构字段信息变化

# 具体实际操作过程(删除已有表结构字段)
mysql > alter table stu drop column sid;
— 在学生表中,删除已有表结构字段列(删除指定字段列-单列操作)

# 具体实际操作过程(修改已有表结构字段)
mysql > alter table stu modify name varchar(64);
— 在学生表中,修改已有表结构字段列(修改表结构数据类型)
mysql > alter table stu modify name varchar(64) not null comment ‘学生名’;
— 在学生表中,修改已有表结构字段列,最后带有保持原有配置的属性信息,否则其他属性信息会被还原为默认
mysql > alter table stu change name stuname varchar(64) not null comment ‘学生名’;
或者
mysql > alter table stu change column name stuname varchar(64) not null comment ‘学生名’;
— 在学生表中,修改已有表结构字段列(修改表结构字段名称)
mysql > alter table stu modify name varchar(64) not null unique comment ‘学生名称’;
— 在学生表中,修改已有表结构字段列(修改表结构属性信息)了解即可
mysql > alter table stu drop index `name`;
— 在学生表中,修改已有表结构字段列(删除表结构属性信息)了解即可
mysql > desc stu;
— 查看表结构字段信息变化

# 数据表删除命令语法
mysql > drop table <表名>;
# 具体实际操作过程
mysql > drop table stu1;
— 删除操作过程,会将定义的表结构和表中数据内容一并删除
mysql > truncate table stu1;
— 删除操作过程,只是清空表中数据内容,但保留定义的表结构信息

**数据定义语句定义数据表规范说明:**

– 创建数据表名称规范:要和业务有关(含库前缀),不要有大写字母,不要数字开头,不要含有系统关键字信息,名称不要太长;

– 创建数据表属性规范:属性信息显示设置,引擎选择InnoDB,字符集选择utf8/utf8mb4,表信息添加注释;

– 创建数据列属性规范:名称要有意义,不要含有系统关键字信息,名称不要太长;

– 创建数据类型的规范:数据类型选择合适的、足够的、简短的;

– 创建数据约束的规范:每个表中必须都要有主键,最好是和业务无关列,实现自增功能,建议每个列都非空(避免索引失效)/加注释

– 删除数据表操作规范:对于普通用户不能具有删表操作,需要操作删表时需要严格审核

– 修改数据表结构规范:在数据库8.0之前,修改数据表结构需要在业务不繁忙时进行,否则会产生严重的锁 19:00

如果出现紧急修改表结构信息需求时,可以使用工具进行调整,比如使用:pt-osc、gh-ost,从而降低对业务影响
“`

## 2 数据操作语言实践(DML)

### 1 增加数据内容信息(insert)

“`sh
insert into stu1 (字段1.。。) values (值1.。。);
# 具体实际操作过程
desc stu1;
insert into stu1(id,name,age,dept) values(1,’oldboy’,35,’net sec’);
— 插入单行信息标准方法(信息输入不要重复,且特定信息不要为空)
insert into stu1(id,name,age,dept) values(0,’oldboy’,35,’net sec’);
insert into stu1(id,name,age,dept) values(null,’oldboy’,35,’net sec’);
— 插入单行信息标准方法(自增列信息可以填入0或null,表示默认实现自增效果)

insert into stu1 values(2,’oldgirl’,25,’linux’);
— 插入单行信息可以不含有表字段信息
insert into stu1 values(03,’littlegirl’,2,’net sec’),(04,’littleboy’,1,’Linux’);
— 插入多行信息可以不含有表字段信息
insert into stu1(name,age) values(‘oldboy’,35);
— 插入当行信息可以只含部分字段信息,但是省略字段信息必须具有自增特性 或 可以为空 或有默认值输入
insert into stu1 values(6,”,32,’python,linux,net sec’);
— 插入中文信息

# 检查信息是否插入成功
select * from stu1;
“`

### 2 修改数据内容(update)

“`sh
# 数据表数据修改命令语法
update 表名 set 字段=新值,… where 条件;
— 属于表内容信息变更操作,需要按照表结构预先定义好的字段信息修改,并且按照条件修改,默认全表修改
# 具体实际操作过程
update stu1 set name=”zhangsan” where id=6;
— 修改表数据内容标准方式,修改时一定要加条件信息(条件信息建议为主键或具有唯一性信息)

# 检查信息是否修改成功
select * from stu1;
“`

### 3 删除表数据(delete)

“`sh
# 数据表数据删除命令语法
delete from 表名 where 表达式;
— 属于表内容信息变更操作,需要按照表结构预先定义好的字段信息删除,并且按照条件删除,默认全表删除
# 具体实际操作过程
delete from stu1 where id=6;
delete from stu1 where id<3; delete from stu1 where age=2 or age=1; delete from stu1; -- 删除表信息时,如果不加条件会进行逐行删除全表信息(效率比较慢) # 检查信息是否删除成功 select * from stu1; **删除数据库信息扩展:伪删除操作** 由于执行删除语句信息时,有可能会对一些业务数据造成影响,甚至可能会将表中所有数据清空,虽然可以通过日志信息恢复(闪回) 但是整体操作过程还是比较危险的,因此在进行数据信息删除操作时,可以利用伪删除操作代替真实删除操作; 一般在数据库中删除数据信息,是因为从业务层面有些数据不想被查询获取到,伪删除就是不让查询时可以获取想要删除的数据; `伪删除的本质:利用update替代delete` 可以在相应表中添加状态列信息,可以将状态列设置为:1表示存在 0表示不存在 在进行伪删除操作时,只是将状态列信息改为0,但是并没有把相应行的数据信息删除,但是在查询时可以忽略状态列为0的信息; 这样可以有效规避误删除操作对业务数据的影响,万一伪删除操作有问题,可以再将状态列信息0改为1即可 # 真实删除数据信息操作举例 delete from stu1 where id=6; # 伪删除数据信息操作举例 alter table stu1 add state tinyint not null default 1; -- 在原有表中添加新的状态列 update stu1 set state=0 where id=6; -- 将原本删除列信息的状态改为0,实现伪删除效果 select * from stu1 where state=1; -- 实现查询时不要获取状态为0的信息,即不查看获取伪删除数据信息 ``` ## 3 数据管理语言实践(DQL) ### 1 查询获取服务配置信息 ```sh select @@配置参数信息 show variables like '信息' select @@port; select database(); select @@socket; select @@innodb_flush_log_at_trx_commit; show variables like 'po%'; 数据库服务在线调整配置参数方法 set session innodb_flush_log_at_trx_commit=1 set sql_log_bin=0; -- 表示在线临时调整配置参数,并且只是当前会话生效(session是默认方式,不是所有配置都可以调整) set global innodb_flush_log_at_trx_commit=1 -- 表示在线临时调整配置参数,并且将会影响所有连接(global是全局方式,可以进行所有配置调整) 说明:数据库服务配置参数在线调整参数,只是临时生效,数据库服务重启后配置会失效,想要永 久生效需要修改配置文件信息 函数 select version(); select now(); select concat(user,"@","'",host,"'") from mysql.user; +-------------------------------+ | concat(user,"@","'",host,"'") | +-------------------------------+ | test@'%' | | testroot@'%' | | root@'192.168.137.%' | | mysql.infoschema@'localhost' | | mysql.session@'localhost' | | mysql.sys@'localhost' | | testroot@'localhost' | +-------------------------------+ 函数拼接 ``` ### 2 单表查询 ```sh 语法 select 字段1.。。 from 表名 where group by 字段1.。。 having 条件 order by 字段 limit 限制信息 ``` #### 1 select + from ```sh use world; select * from city; -- > 等价于 cat a.txt
select id,name,countrycode,district,population from city;
— 进入world数据库中,查询数据库中city表所有内容信息(企业应用不要对大表查看所有数据)
select name,population from city; — > 等价于 awk $1 $2
— 查询city表中的部分字段信息,显示查看城市名称和人口数量
“`

#### 2 select+from+where

“`sh
1 定义等值条件
#查询中国的所有城市信息,中国代码信息 ”CHN”
select * from city where countrycode=’CHN’;
# 查询中国的所有城市信息,只关注城市名称和人口数量列信息
select `Name`,Population from city where countrycode=’CHN’;

2 定义区间条件
# > < >= <= !=/<> 大于 小于 大于等于 小于等于 不等于
# 查询大于700万人的所有城市信息
select * from city where Population>=7000000;
# 查询小于等于1000人的所有城市信息
select * from city where Population<=1000; 3 定义逻辑条件 # and或&&两个条件均满足才能被查出来 # or 或|| 多个条件有一个满足就能被查出来 # not或! 取反 # 查询中国境内,大于520万人口的城市信息 select * from city where city.CountryCode='CHN' and city.Population>5200000;
# 查询中国和美国的所有城市
select * from city where city.CountryCode=’CHN’ or
city.CountryCode=’USA’;
# 查询人口数在100w到200w之间的城市信息
select * from city where city.Population>=1000000 and city.Population<=2000000; 4 定义模糊条件 #like # 查询国家代号是CH开头的城市信息 select * from city where city.CountryCode like 'CH%'; # 查询国家代号含US内容的城市信息 select * from city where city.CountryCode like '%US%'; -- 在模糊查询时,%符号在前面进行检索数据时,是不会走索引信息进行检索的,查询性能较慢 5 特殊查询条件 #配合in, not in, between and # 查询中国和美国的所有城市 select * from city where city.CountryCode in ('CHN','USA'); #查询世界上的所有城市信息,但排除中国和美国的城市不查询 SELECT * FROM city WHERE city.CountryCode NOT IN ('CHN','USA'); # 查询人口数量在50w-100w之间的城市信息 SELECT * FROM city WHERE city.Population BETWEEN 500000 AND 1000000; -- in的查询条件方式表示包含意思,实际应用更广泛 -- not in的查询条件方式表示排除意思,实际应用比较少见,因为not in不能走索引扫描,查询检索性能较慢 -- between and的查询条件方式是包含边界取值信息的,即包含50w人口的城市,也包含100w人口的城市 6 查询重复消息 #distinct SELECT DISTINCT city.CountryCode FROM city WHERE city.CountryCode='USA'; -- 列字段信息必须完全相同内容,才可以实现去重; 7 查询数据为空的内容 #is null -- 查询国家编码字段为空的信息 SELECT * FROM city WHERE city.CountryCode IS NULL; -- 查询国家编码字段为非空的信息 SELECT * FROM city WHERE city.CountryCode IS NOT NULL; #实际操作命令演示:select+from+where+group by+聚合函数(统计函数) 结合使用情况** #在利用select语句查询数据信息,结合group by子句可以实现分组查询,并且还必须配合聚合函数对分组查询的数据做相应处理; #数据库服务中常用的聚合函数(统计函数): #sum() avg() min() max() group_concat() distict #统计每个国家人口总数 select CountryCode,sum(Population) from city group by city.CountryCode; #查询统计每个省份的城市个数 select District,count(name) from city where city.CountryCode='CHN' group by district; #查询统计每个省份的城市数,以及城市名称信息(经常面试题考到) select District,count(name),name from city where CountryCode='CHN' group by District; 此sql报错 -- 由于数据库sql_mode配置了only_full_group_by,由于输出的name信息不能和district信息实现1对1关系,因此报错 select district ,count(name),group_concat(name) from city where city.CountryCode='CHN' group by district; -- 利用group_concat()就可以实现没有出现在分组和聚合函数中的字段,采取拼接整合方式显示,满足分组1对1关系 #select+from+where+group by+聚合函数+having+order by # 查询统计每个国家的人口总数,只显示人口数量超过5千万的信息,并且按照国家人口总数排序显示 select city.CountryCode,sum(city.Population) from city group by city.CountryCode having sum(city.Population)>50000000 order by sum(city.Population) desc limit 3;

# 查询统计每个国家的人口总数,只显示人口数量超过5千万的信息,并且按照国家人口总数从大到小排序,只显示三~五名
select city.CountryCode,sum(city.Population) from city group by city.CountryCode having sum(city.Population) >50000000 order by sum(city.Population) desc limit 2,3;

select city.CountryCode,sum(city.Population) from city group by city.CountryCode having sum(city.Population) >50000000 order by sum(city.Population) desc limit 3 offset 2;– 跳过前2名,显示后面的三名数据信息

SELECT:指定要查询的列或表达式。
FROM:指定查询的表。
WHERE:用于筛选满足特定条件的行。
GROUP BY:对数据进行分组。
聚合函数:对分组后的数据进行聚合计算(如 SUM()、COUNT()、AVG() 等)。
HAVING:对分组后的结果进行筛选。
ORDER BY:对结果进行排序。
LIMIT:限制返回的记录数量。

虽然查询语句的书写顺序是 SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT,但实际执行顺序是:
FROM:确定查询的表。
WHERE:筛选符合条件的行。
GROUP BY:对筛选后的数据进行分组。
聚合函数:对分组后的数据进行聚合计算。
HAVING:对聚合后的结果进行筛选。
SELECT:选择需要的列。
ORDER BY:对最终结果进行排序。
LIMIT:限制返回的记录数量
“`

### 3 多表查询

“`sh
1 多表联合查询数据-横向拼接

在对数据库中数据信息查询时,有些需求情况要获取的数据信息,是通过多个表的数据信息整合获取到的,就称为多表查询;

查询命令语法格式:

笛卡尔乘积连接多表
select * from t1,t2;

内连接查询多表
select * from t1,t2 where t1.字段= t2.字段;
select * from t1 inner jion t2 on t1.字段=t2.字段;

左外连接
select * from t1 left join t2 on t1.字段=t2.字段
右外连接
select * from t1 right join t2 on t1.字段=t2.字段

说明:多表查询的最终目的是将多张表的信息整合为一张大表显示,并将显示的结果信息可以做相应单表的操作处理;
多表查询方式类型:笛卡尔乘积
— 默认方式多表查询时,会出现组合乘积效果(4*4=16)

**多表查询方式类型:内连接(取交集)**

join,其实就是“inner join”,为了简写才写成join;内连接表示以两个表的交集为主,查出来是两个表有交集的部分,

其余没有关联就不额外显示出来,这个用的情况也是挺多的;

可以基于笛卡尔乘积方式的结果集,将有意义的信息进行展示,并且是基于两张表里的相同含义字段,进行比较后输出相等的结果信息;

`内连接查询的简单描述:两个表中有关联条件的行显示出来;`

**多表查询方式类型:外连接(应用更广泛)**

利用外连接查询时,是可以进行性能优化处理的,因为内连接在底层查询时,是逐行进行比较后输出,整体数据查询检索的效率较低;

– **外连接可以细分为:左外连接-left join on**

左连接:表1左连接表2,以左为主,表示以表1为主,关联上表2的数据,查出来的结果显示左边的所有数据,

然后右边显示的是和左边有交集部分的数据。

左外连接表示查询数据结构包含:左表所有数据行+右表满足关联条件的行;
# 左连接查询语法
a left join b on a.x = b.x
— a表示左表,b表示右表,基于左表a建立关联

# 实际操作演示过程
select * from teacher left join course on teacher.tno=course.tno;
+—–+——–+——+——–+——+
| tno | tname | cno | cname | tno |
+—–+——–+——+——–+——+
| 101 | oldboy | 1001 | linux | 101 |
| 102 | xiaoQ | 1002 | python | 102 |
| 103 | xiaoA | 1003 | mysql | 103 |
| 104 | xiaoB | NULL | NULL | NULL |
+—–+——–+——+——–+——+
4 rows in set (0.00 sec)
— 包含了左表的所有数据行信息(teacher),包含了右表的关联数据行信息(course)

– **外连接可以细分为:右外连接-right join on**

右连接:表1右连接表2,以右为主,表示以表2为主,关联查询表1的数据,查出表2所有数据以及表1和表2有交集的数据,

右外连接表示查询数据结构包含:右表所有数据行+左表满足关联条件的行;


# 右连接查询语法
a right join b on a.x = b.x
— a表示左表,b表示右表,基于右表b建立关联

# 实际操作演示过程
select * from teacher right join course on teacher.tno=course.tno;
+——+——–+——+——–+—–+
| tno | tname | cno | cname | tno |
+——+——–+——+——–+—–+
| 101 | oldboy | 1001 | linux | 101 |
| 102 | xiaoQ | 1002 | python | 102 |
| 103 | xiaoA | 1003 | mysql | 103 |
| NULL | NULL | 1004 | go | 105 |
+——+——–+——+——–+—–+
4 rows in set (0.00 sec)
— 包含了右表的所有数据行信息(course),包含了左表的关联数据行信息(teacher)

# 显示差集信息:
select * from teacher right join course on teacher.tno=course.tno where teacher.tname is null;
+——+——-+——+——-+—–+
| tno | tname | cno | cname | tno |
+——+——-+——+——-+—–+
| NULL | NULL | 1004 | go | 105 |
+——+——-+——+——-+—–+
1 row in set (0.01 sec)

外连接方式左连接与右连接区别举例:


# 会将右表作为驱动表,进行外层循环
for each row in b
for each row in a
if b.y=a.x print row
else print b.y a.null

**多表连接查询的步骤思路:**

– 进行需求分析,根据查询需求找寻所有需要的表信息;
– 找寻表的关联,根据多张表字段信息获取关联的字段;(也可以查询间接关系)
– 组合后的需求,根据多张表组合后定义查询条件信息;
“`

## 4 操作管理应用实践(获取元数据)

### 1 概念

“`sh
元(meta)一般会被翻译成中文是”关于…的…”,元数据(meta data)等价于data about data,表示关于数据的数据;

一般是元数据就是结构化数据,例如存储在数据库里的数据,规定了字段的长度。类型等;

元数据就是描述数据的数据,在MySQL中就是描述database的数据,属性,状态等相关信息;

`表示在数据库服务中有哪些数据库,库中有哪些表,表中有多少字段,字段是什么类型等等,这样的数据就是数据库的元数据;`
“`

### 2 元数据获取方法

“`sh
方法一:利用各种shwo命令获取元数据信息

方法二:利用各种select命令查看视图信息获取元数据信息

方法三:利用各种mysql数据库中的各种函数获取元数据信息

# 常用SQL语句的show命令查看元数据信息
show databases;
— 查询数据库服务中的所有数据库信息(数据库名称-元数据)

show tables;
show tables from mysql;
— 查询数据库服务中的相应数据表信息(数据表名称-元数据)

show create database <库名>;
— 查询数据库服务中的建库语句信息 (建库语句参数-元数据 建库语句就是DDL语句,定义建立数据库的属性信息)

show create table <表名>;
— 查询数据库服务中的建表语句信息 (建表语句参数-元数据 建表语句就是DDL语句,定义建立数据表的属性信息)

desc <表名>;
show columns from <表名>;
— 查询数据库服务中的数据表的结构(数据表的列定义信息-元数据)

show table status from <库名>;
— 查询数据库服务中的相应数据表状态 (数据表的状态信息/统计信息-元数据)
show table status from world like ‘city’ \G
*************************** 1. row ***************************
Name: city — 数据表名称信息
Engine: InnoDB — 使用的数据库引擎信息
Version: 10
Row_format: Dynamic
Rows: 4046 — 数据表的行数信息
Avg_row_length: 101 — 平均行长度
Data_length: 409600
Max_data_length: 0
Index_length: 114688 — 索引长度信息
Data_free: 0
Auto_increment: 4080 — 自增列的值计数
Create_time: 2022-11-04 09:13:27 — 数据表创建时间
Update_time: NULL
Check_time: NULL
Collation: utf8mb4_0900_ai_ci — 校对规则信息
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)
— 查看数据库服务中的具体数据库表的状态信息(属于单库或单表查询)

show index from world.city;
— 查询数据库服务中的相应数据表的索引情况(了解即可)

show grants for root@’localhost’;
— 查询数据库服务中的用户权限属性配置信息

show [full] processlist;
— 查询数据库服务的系统状态信息,表示当前数据库的所有连接情况

show variables;
show variables like ‘%xx%’;
— 查询数据库服务的所有配置信息

show status;
show status like ‘%lock%’;
— 查询数据库服务的系统整体状态,表示当前数据库服务运行的即时状态情况

show binary logs;
— 查询数据库服务的所有二进制日志信息(binlog日志)
show master status;
— 查询数据库服务正在使用的二进制日志
show binlog events in ‘binlog.000009’;
— 查询数据库服务具体二进制日志内容事件信息

show engine innodb status \G
— 查询数据库服务存储引擎相关信息

show slave hosts;
— 在数据库服务主库查看从库信息
show slave status;
— 查询数据库服务主从状态信息

元数据获取方式二:利用库中视图获取元数据

视图概念:

`将查询基表元数据语句信息方法封装在一个变量或别名中,这个封装好的变量或别名就成为视图,视图信息都是存储在内存中的表`

元数据信息存储在系统基表中,通过一般的select命令只能查看数据信息,不能查看到系统基表,以免被随意调整篡改;

而查询基表的语句过于复杂,可以将整个查询基表语句定义为一个视图信息(等价于别名/变量),调取视图等价于调取查询基表语句;

information_schema库中的内存表都是每次数据库服务启动时生成的,里面存储了查询元数据基表的视图信息;

视图定义:

# 假设查询基表语句信息如下
select a.tname as ‘老师名’,group_concat(d.sname) as ‘不及格学生名’
from teacher as a
join course as b
on a.tno=b.tno
join sc as c
on b.cno=c.cno
join student as d
on c.sno=d.sno
where c.score<60 group by a.tno; -- 会获取如下查询后的信息 +-----------+--------------------+ | 老师名 | 不及格学生名 | +-----------+--------------------+ | xiaoQ | zhang3 | | xiaoA | li4,zh4 | +-----------+--------------------+ # 可以将复杂的查询语句定义为视图 create view tv as select a.tname as '老师名',group_concat(d.sname) as '不及格学生名' from teacher as a join course as b on a.tno=b.tno join sc as c on b.cno=c.cno join student as d on c.sno=d.sno where c.score<60 group by a.tno; # 调取视图信息等价于调取复杂的查询语句 select * from tv; +-----------+--------------------+ | 老师名 | 不及格学生名 | +-----------+--------------------+ | xiaoQ | zhang3 | | xiaoA | li4,zh4 | +-----------+--------------------+ 2 rows in set (0.00 sec) # 切换进入information_schema数据库中查看表信息 use information_schema; show tables; -- 此时看到的所有表信息,其实都是视图信息 # 查看获取视图信息创建语句 show create view tables; -- 查看tables这个视图表的创建过程 # 查看视图表信息应用 # 统计数据库资产信息(数据资产),获取每个库中表的个数和名称信息(业务相关) desc information_schema.tables; -- 查看information_scheam中的tables表的结构信息; select table_schema,count(*),group_concat(table_name) from information_schema.tables group by table_schema; -- 获取相应数据库中表的个数,与数据库中拥有的表信息 select table_schema,count(*),group_concat(table_name) from information_schema.tables where table_schema not in ('mysql','sys','performance_schema','information_') group by table_schema; # 统计数据库资产信息(数据资产),获取每个数据库数据占用磁盘空间 select table_schema,sum(table_rows*avg_row_length+index_length)/1024/1024 from information_schema.tables where table_schema not in ('mysql','sys','performance_schema','information_') group by table_schema; # 统计数据库资产信息(数据资产),获取具有碎片信息的表 select table_schema,table_name,data_free from information_schema.tables where table_schema not in ('mysql','sys','performance_schema','information_') and data_free >0 ;
— 碎片信息过多会导致索引信息失效,以及统计信息不真实的情况

# 统计数据库资产信息(数据资产),处理具有碎片信息的表
alter table t1 engine=innodb;
— 可以对已经是innodb存储引擎的表做操作,实现整理碎片功能
select concat(“alter table “,table_schema,”.”,table_name,” engine=innodb”) from information_schema.tables where table_schema not in (‘mysql’,’sys’,’performance_schema’,’information_’) and data_free >0 ;
— 可以对已经是innodb存储引擎的表做操作,实现批量整理碎片功能

# 统计数据库资产信息(数据资产),获取数据库中非innodb表信息
mysql>select table_schema,table_name,engine from information_schema.tables where table_schema not in (‘mysql’,’sys’,’performance_schema’,’information_’) and engine!=’innodb’;
— 获取非innodb数据库引擎表
use school;
create table t1 (id int) engine=myisam;
create table t2 (id int) engine=myisam;
create table t3 (id int) engine=myisam;
— 模拟创建一些myisam引擎数据表

# 统计数据库资产信息(数据资产),修改数据库中非innodb表信息替换成innodb
alter table world.t1 engine=innodb;
— 可以对不是innodb存储引擎的表做操作,实现数据表引擎修改
select concat(“alter table “,table_schema,”.”,table_name,” engine=innodb”) from information_schema.tables where table_schema not in (‘mysql’,’sys’,’performance_schema’,’information_’) and engine !=’innodb’;
select concat(“alter table “,table_schema,”.”,table_name,” engine=innodb;”) from information_schema.tables where table_schema not in (‘mysql’,’sys’,’performance_schema’,’information_’) and engine!=’innodb’ into outfile ‘/tmp/alter.sql’;
ERROR 1290 (HY000): The MySQL server is running with the –secure-file-priv option so it cannot execute this statement
vim /etc/my.cnf
[mysqld]
secure-file-priv=/tmp
— 修改配置文件参数信息,实现将数据库操作的数据信息导入到系统文件中,配置完毕重启数据库服务
source /tmp/alter.sql
— 可以对不是innodb存储引擎的表做操作,实现数据表批量化引擎修改,调用数据库脚本信息

“`

关注的视图表字段说明:

| 序号 | 字段信息 | 解释说明 |
| —- | ————– | ————————– |
| 01 | TABLE_SCHEMA | 表示数据表所属库的名称信息 |
| 02 | TABLE_NAME | 表示数据库中所有数据表名称 |
| 03 | ENGINE | 表示数据库服务中的引擎信息 |
| 04 | TABLE_ROWS | 表示数据库相应数据表的行数 |
| 05 | AVG_ROW_LENGTH | 表示数据表中每行的平均长度 |
| 06 | INDEX_LENGTH | 表示数据表中索引信息的长度 |
| 07 | DATA_FREE | 表示数据库服务碎片数量信息 |
| 08 | CREATE_TIME | 表示数据表创建的时间戳信息 |
| 09 | UPDATE_TIME | 表示数据表修改的时间戳信息 |
| 10 | TABLE_COMMENT | 表示数据表对应所有注释信息 |

说明:使用information_schema的视图查看功能,可以看到全局数据库或数据表的元数据信息,探究全局层面的元数据

### 3 元数据调取函数

利用程序代码在语句数据库交互的过程,也可以调取数据库中的相关元数据信息:

“`tiki wiki
getDatabaseProductName
— 获取数据库的产品名称
getDatabaseProductName
— 获取数据库的版本号
getUserName
— 获取数据库的用户名
getURL
— 获取数据库连接的URL
getDriverName
— 获取数据库的驱动名称
driverVersion
— 获取数据库的驱动版本号
isReadOnly
— 查看数据库是否只允许读操作
supportsTransactions
— 查看数据库是否支持事务
“`

数据库元数据信息参考资料:https://www.yisu.com/zixun/579710.html

# 第五章 日志管理

## 1 日志分类

常用日志信息介绍:

| 序号 | 日志名称 | 解释说明 |
| —- | ————– | ———————————————————— |
| 01 | general_log | 表示查询日志(通用日志),默认日志状态处于关闭,可以进行在线调整配置
作用:记录了客户端从会话连接开始,执行过的所有SQL语句信息; |
| 02 | log_error | 表示错误日志(运行日志),默认日志状态处于激活
作用:记录了数据库服务启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息; |
| 03 | log_bin | 表示二进制日志(binlog日志),默认日志状态处于激活(8.0之后)
作用:记录了所有的DDL语句和DML语句,但是不包括数据库查询语句;语句以事件的形式保存,描述了数据的更改过程,此日志对于灾难时的数据恢复起着极其重要的作用。 |
| 04 | slow_query_log | 表示慢查询日志,记录了所有执行时间超过参数long_query_time设置值并且扫描记录数小于min_examined_row_limit的所有SQL语句的日志。 |

“`sh
set global general_log=1
# 修改日志存储路径(永久配置):
[root@xiaoq ~]# vim /etc/my.cnf
log_error=/tmp/mysql3306.err
— 配置文件编写完毕后,需要重启数据库服务生效
— 根据错误日志的错误提示信息,进行错误信息进行分析,从而排查故障可能出现的原因;
##### **分类日志信息配置:二进制日志(log_bin)**

在进行增量恢复数据时,需要先了解什么是binlog日志,此日志文件其实就是用于记录对数据库进行操作更改的语句信息的;

并且记录更改的语句信息以事件形式进行记录,但是需要注意的是查询相关的语句是不会被记录的,比如:select、show;

然而作为所有对数据库的改操作事件信息都会被记录,比如:insert、update、create、drop。。。

server_id=6
— 进行主从操作时,需要进行此信息配置;
log_bin=ON
— 默认日志功能处于关闭状态
log_bin_basename=/data/3306/data/binlog
— 定义日志文件存储的路径信息,建议日志文件路径与数据存放路径进行分离;

# 配置信息简写方式:开启数据库binlog日志记录功能
[root@xiaoq ~]# vim /etc/my.cnf
— 激活binlog日志记录功能,需要对数据库服务配置文件进行编辑修改
[mysqld]
server_id=6
log_bin=/data/3306/binlog/mysql-bin
— 进行binlog日志目录路径信息修改时,需要创建指定的目录并设置权限信息,最后需要重新启动数据库服务生效;
或者
log_bin=binlog
— 只是设置日志名称信息,日志会自动保存到数据库服务指定的数据目录中;
# 未开启binlog日志功能时,查看系统binlog功能配置参数状态
show variables like ‘%log_bin%’;
— 数据库服务重启后,已经可以在数据库的数据存储目录中,看到binlog日志文件的踪影

# 参数一:sync_binlog 表示刷新日志到磁盘策略
select @@sync_binlog;
+———————+
| @@sync_binlog |
+———————+
| 1 |
+———————+
1 row in set (0.00 sec)
— 在进行主从同步过程的双一标准的其中一个1的信息配置,主要是控制缓冲区里的binlog日志信息如何刷写到磁盘中;
— 此参数信息是有三种方式进行配置的:
— 参数信息配置0:表示由操作系统缓存自己决定,什么时候刷新日志到磁盘中;
— 参数信息配置1:表示每次事务提交,立即刷新日志到磁盘中;(此方式配置更安全)
— 参数信息配置N:表示每组事务提交,按照组的事务次数定义,确定刷新日志到磁盘中的频次;(可以有效减少IO性能损耗)

# 参数二:binlog_format 定义binlog日志的格式信息
select @@binlog_format;
+————————+
| @@binlog_format |
+————————+
| ROW |
+————————+
1 row in set (0.00 sec)
— 在进行主从同步数据恢复时,此参数配置可能会影响数据恢复的一致性问题;
— 此参数信息是有三种方式进行配置的,确定了主从复制的级别,只针对DML语句的日志才有效;
— 参数信息配置 statement(SBR):语句格式记录binlog;
create database xiaoQ; — DDL DCL语句只能使用statement 表示的就是原原本本的语句信息,即做什么就记录什么;
— 参数信息配置 row(RBR):行格式记录binlog(默认模式)
update t1 set a=10 where id<10; -- 会记录行的变化信息,属于底层的记录信息,可能会有多个变化日志信息记录 -- 参数信息配置 mixed(MBR):混合格式记录binlog -- 由数据库服务自行决定,是记录语句信息,还是记录行的变化信息; ``` ## 2 sbr和rbr记录的优缺点(面试) SBR(statement-based replication)与RBR(Row-Based Replication)记录的优缺点分析:(面试常见问题) | 记录方式 | 优点说明 | 缺点说明 | | -------- | ------------------------------------------------------------ | -------------------------------------- | | SBR | 可读性强,日志量相对较少; | 数据信息可能不准确,数据一致性不足 | | RBR | 数据信息记录更准确,数据一致性更强 | 可读性弱,日志量相对较多,数据记录准确 | | 举例说明 | update t1 set a=10 where id<10000; 记录一条语句即可 | insert into 随机数函数; | | 举例说明 | update t1 set a=10 where id<10000; 记录多条语句修改信息,生成日志 | insert into 随机数函数; | ```sh bin_log 可以通过查看方式,获取binlog日志里的数据信息,一般在数据库启动时,日志记录功能就开启了; 可以利用日志中记录信息,将数据库服务的数据信息恢复到指定的时间点,同时也可以支持主从数据复制(在其它机器上回放日志); `对于binlog日志信息的查看,主要目的是为了日后日志事件信息的截取操作;` 查看方式一:确认数据库binlog日志数量 show binary logs; flush logs; Query OK, 0 rows affected (0.12 sec) -- 可以执行flush刷新命令,从而生成新的binlog日志文件,类似于实现了日志切割功能; show binlog events in 'binlog.000002'; -- binlog日志信息是以事件方式进行记录的,所以日志查看过程是查看事件信息 -- 一般binlog日志的前两行,表示日志格式头信息(日志简单的描述信息) -- 一般binlog日志中的query信息,就是对数据库的操作语句,其中包含了创建数据库的语句; ``` 具体binlog事件记录信息分析: | 列号 | 列信息 | 解释说明 | | ---- | ----------- | ---------------------------------------------------------- | | 01 | Log_name | 表示指定查看的binlog日志文件名称信息 | | 02 | Pos | 表示binlog日志事件开始的位置点,用于截取二进制日志信息标识 | | 05 | End_log_pos | 表示binlog日志事件结束的位置点,用于截取二进制日志信息标识 | | 06 | Info | 表示binlog中具体的事件内容信息 | ```sh # 筛选数据库日志方式一: [root@xiaoq data]# mysql -e "show binlog events in 'binlog.000002'"|grep "drop database" binlog.000002 722789 Query 1 722896 drop database world /* xid=5363 */ -- 获取指定事件信息产生的起点位置和终点位置信息; # 筛选数据库日志方式二: pager less -- 在数据库中定义pager功能,数据库连接会话退出即失效; show binlog events in 'binlog.000002'; -- 此时查看日志事件信息具有了翻页功能 /drop database | binlog.000002 | 722789 | Query | 1 | 722896 | drop database world /* xid=5363 */ pager grep "drop database" PAGER set to 'grep "drop database"' -- 表示开启数据库pager的过滤功能 show binlog events in 'binlog.000002'; | binlog.000002 | 722789 | Query | 1 | 722896 | drop database world /* xid=5363 */ -- 再次查看binlog事件信息时,只过滤显示删除数据库的操作事件日志 mysqlbinlog /var/lib/mysql/binlog.000001 ``` ## 3 数据库异常恢复(简单情况) ```sh 切割日志 flush logs show master status; -- 在没有进行事务提交前,操作的事务事件信息,是不会出现在binlog事件日志中的 commit; -- 对于数据库的binlog日志,只会记录事务已经提交的DML语句信息,没有提交的DML语句是不会进行记录的; -- 在日志中变化的DML语句信息是无法识别的,因为记录DML操作的语句默认是以ROW模式记录的; -- 对于数据库binlog日志信息,是无法直接查看内容信息,需要利用相关命令工具进行查看 [root@db01 data]# mysqlbinlog binlog.000017 # The proper term is pseudo_replica_mode, but we use this compatibility alias # to make the statement usable on server versions 8.0.24 and older. /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 4 #250208 10:45:03 server id 1 end_log_pos 126 CRC32 0xcf7bbba5 Start: binlog v 4, server v 8.0.32 created 250208 10:45:03 # Warning: this binlog is either in use or was not closed properly. BINLOG ' L8WmZw8BAAAAegAAAH4AAAABAAQAOC4wLjMyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAEwANAAgAAAAABAAEAAAAYgAEGggAAAAICAgCAAAACgoKKioAEjQA CigAAaW7e88= '/*!*/; # at 126 #250208 10:45:03 server id 1 end_log_pos 157 CRC32 0x7d5528c8 Previous-GTIDs # [empty] # at 157 #250208 10:45:36 server id 1 end_log_pos 234 CRC32 0x3e25b0c0 Anonymous_GTID last_committed=0 sequence_number=1 rbr_only=no original_committed_timestamp=173898273618702immediate_commit_timestamp=1738982736187022 transaction_length=187 # original_commit_timestamp=1738982736187022 (2025-02-08 10:45:36.187022 CST) # immediate_commit_timestamp=1738982736187022 (2025-02-08 10:45:36.187022 CST) /*!80001 SET @@session.original_commit_timestamp=1738982736187022*//*!*/; /*!80014 SET @@session.original_server_version=80032*//*!*/; /*!80014 SET @@session.immediate_server_version=80032*//*!*/; SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 234 #250208 10:45:36 server id 1 end_log_pos 344 CRC32 0xe50698f5 Query thread_id=8 exec_time=0 error_code=0 Xid = 16 SET TIMESTAMP=1738982736/*!*/; SET @@session.pseudo_thread_id=8/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=1168113696/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C utf8mb4 *//*!*/; SET @@session.character_set_client=255,@@session.collation_connection=255,@@session.collation_server=255/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; /*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/; drop database oldboy /*!*/; # at 344 #250208 10:45:43 server id 1 end_log_pos 421 CRC32 0xb77d507e Anonymous_GTID last_committed=1 sequence_number=2 rbr_only=no original_committed_timestamp=173898274306998immediate_commit_timestamp=1738982743069988 transaction_length=167 # original_commit_timestamp=1738982743069988 (2025-02-08 10:45:43.069988 CST) # immediate_commit_timestamp=1738982743069988 (2025-02-08 10:45:43.069988 CST) /*!80001 SET @@session.original_commit_timestamp=1738982743069988*//*!*/; /*!80014 SET @@session.original_server_version=80032*//*!*/; /*!80014 SET @@session.immediate_server_version=80032*//*!*/; SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 421 #250208 10:45:43 server id 1 end_log_pos 511 CRC32 0x918fb551 Query thread_id=8 exec_time=0 error_code=0 SET TIMESTAMP=1738982743/*!*/; SET @@session.time_zone='SYSTEM'/*!*/; flush privileges /*!*/; SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; [root@db01 data]# mysqlbinlog /data/3306/data/binlog.000003 # The proper term is pseudo_replica_mode, but we use this compatibility alias # to make the statement usable on server versions 8.0.24 and older. /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 4 #250206 15:52:37 server id 1 end_log_pos 126 CRC32 0xd24df123 Start: binlog v 4, server v 8.0.32 created 250206 15:52:37 at startup ROLLBACK/*!*/; BINLOG ' RWqkZw8BAAAAegAAAH4AAAAAAAQAOC4wLjMyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAABFaqRnEwANAAgAAAAABAAEAAAAYgAEGggAAAAICAgCAAAACgoKKioAEjQA CigAASPxTdI= '/*!*/; # at 126 #250206 15:52:37 server id 1 end_log_pos 157 CRC32 0xbc193d0e Previous-GTIDs # [empty] # at 157 #250206 15:55:10 server id 1 end_log_pos 180 CRC32 0x94a43686 Stop SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; -- binlog日志文件156之前的内容是可以忽略的,表示是日志文件的头格式内容信息 -- binlog日志文件以事件形式进行记录,主要关注两个at内容之间的信息,即表示的是一个事件信息; # at 233 -- binlog日志中一个事件的开始,就表示上一个事件的结束,在binlog中记录的事件日志信息是连续的; binlog日志内容中主要关注的信息: - **通过日志信息查看DDL操作语句信息(记录方式 SBR)** **通过日志信息查看DML操作语句信息(记录方式 RBR)** mysqlbinlog --base64-output=decode-rows -vvv /data/3306/data/binlog.000003 -- 以上添加的参数信息,表示将DML的ROW格式语句信息,进行格式化处理输出; -- 利用DML语句做的插入语句信息就显示出来了 -- 以上日志记录的信息,可以用命令实现,如下: 数据库异常情况数据恢复操作: mysqlbinlog --start-position=233 --stop-position=1162 /data/3306/data/binlog.000003 >/tmp/bin.sql
— 依据binlog日志的position号码,即可获取到想要恢复数据信息;

# 根据截取的日志信息,进行数据库服务数据恢复
set sql_log_bin=0;
— 建议在进行数据日志恢复数据时,将数据恢复时执行的SQL语句信息,不做binlog日志记录;
source /tmp/bin.sql

# 查看确认数据信息是否恢复
# 方法一:
flush logs;
[root@xiaoq ~ ]# mysql -uroot -p123456 -e “flush logs”
— 滚动更新前的日志文件就会处于静止状态,不会在进行数据信息的更新

# 方式二:
[root@xiaoq ~ ]# mysqladmin -uroot -p123456 flush-logs

# 方式三:
restart;
— mysql 8.0之后支持的数据库中重启服务;之前的版本只支持shutdown关闭数据库;
[root@xiaoq ~ ]# /etc/init.d/mysqld restart

# 方式四:
select @@max_binlog_size;
+————————–+
| @@max_binlog_size |
+————————–+
| 1073741824 |
+————————–+
— 配置binlog日志最大数据存储量,默认大小为1G,到达最大日志存储量也会进行自动切割;

— 在最新数据库8.0中,可以以秒为单位进行日志信息清理,默认是30天进行日志清理,或者也可以以天为单位进行清理;
— 在最先数据库8.0前,主要是以天为单位进行清理,但默认清理功能并未激活;
— 在企业实战环境中,建议过期时间最少保留一轮全备周期以上,有条件最好是保留两轮+1;
help purge binary logs;
— 获取清理日志命令帮助信息

purge binary logs to ‘mysql-bin.010’
— 删除到指定日志文件前结束
PURGE BINARY LOGS BEFORE ‘2019-04-02 22:46:26’;
— 可以基于日志时间点信息进行日志清理

日志信息远程备份:

可以实现将数据库中(特别是主库)生成的binlog日志文件,及时备份保存到专门的日志备份服务器中,并且整个备份操作都是在线的;

mkdir -p /binlog_backup
cd /binlog_backup/
[root@xiaoQ-01 binlog_backup]# mysqlbinlog -R –host=192.168.30.101 –user=root –password=123456 –raw –stop-never binlog.000008 &
— 备份过程可以放后台一直运行,但是需要注意当连接的数据库服务器停止或重启了,也会导致备份中断;

# 数据库服务多实例情况binlog日志备份
mysqlbinlog -R –host=10.0.0.51 -P 3306 –user=root –password=123456 –raw –stop-never binlog.000002 &
mysqlbinlog -R –host=10.0.0.51 -P 3307 –user=root –password=123456 –raw –stop-never binlog.000002 &
— 需要考虑备份后日志文件名称一样的覆盖问题
“`

远程备份命令参数说明

| 参数信息 | 解释说明 |
| ——————————- | ———————————- |
| -R
–read-from-remote-server | 读取binlog日志文件从数据库服务端 |
| -h
–host | 指定binlog日志文件存储服务器地址 |
| -u
–user=name | 指定binlog日志服务器连接用户信息 |
| -p
–password[=name] | 指定binlog日志服务器连接密码信息 |
| –raw | 指定binlog日志信息记录二进制信息 |
| –stop-never | 指定binlog日志信息将会一直备份记录 |
| binlog.000008 | 代表从哪个binlog日志开始进行备份 |

## 4 慢查询

“`sh
select @@slow_query_log;
此参数配置信息,表示是否激活启动慢日志记录功能,默认处于关闭状态
select @@slow_query_log_file;
— 此参数配置信息,表示慢日志文件保存的路径信息;建议日志文件路径与数据存放路径进行分离;
select @@long_query_time;
— 此参数信息配置,表示记录慢日志的条件,默认是大于10s执行的语句,就会记录为慢查询语句;(测试时建议时间为0.01~0.1)
select @@log_queries_not_using_indexes;
— 此参数信息配置,表示慢日志中会记录没有使用索引的语句信息;

设置
set global slow_query_log=1;
set global long_query_time=0.01;
set global log_queries_not_using_indexes=1;
set global slow_query_log_file=/data/3306/data/mysql-slow.log;

也可以放入my.cnf
slow_query_log_file=/data/3306/logs/mysqld.slow.log
slow_query_log = 1
log_queries_not_using_indexes = 1
log_slow_admin_statements = 1
log_throttle_queries_not_using_indexes = 10

慢查询日志分析方法
mysqldumpslow -s c -t 3 /data/3306/data/xiaoQ-01-slow.log
— 按照慢查询语句的重复执行次数(c)进行排序(-s),取出其中靠前(t)的前三名慢查询语句
— 还可以扩展使用pt-query-digest更好的分析慢查询日志,支持图形化展示
— what to sort by (al, at, ar, c, l, r, t), ‘at’ is default
al: average lock time
ar: average rows sent
at: average query time
c: count
l: lock time
r: rows sent
t: query time
“`

## 5 数据库数据异常恢复数据

### 1 数据库数据异常恢复(痛点情况)

“`sh
**情况一:日志文件被清理过,可能建库语句所在日志已经丢失;**(在后面课程章节处理)

项目背景:一个数据库三年前就创建了,但是日志信息只记录一个月,这个库被误删除了;

解决方案:

A计划:最近一次全备+全备之后,误删除之前所有binlog,进行一同恢复;(`全备数据+增量数据`)

B计划:利用延时从库,进行数据恢复;

**情况二:所需日志跨越多个文件,如何进行日志信息的截取;**

解决方案:

A计划:只有position号的方式,可以进行分段截取,进行分段恢复数据;

B计划:根据Datatime时间信息方式,可能会出现准确性不高的情况(因为每一秒可能有多个事件产生);

C计划:启用GTID(全局事务ID)方式,无论跨越多少个日志文件,每个事务操作的事件ID信息都是唯一且递增的(5.6+引入);

实践操作:

**C计划:基于GTID方式对binlog进行管理(利用GTID实现日志截取)**

数据库异常恢复情况环境准备:

# 刷新新的binlog日志进行操作
flush logs;
— 生成新的binlog日志信息

# 确认新的日志编号是否是连续的
create database test5;
show binlog events in “binlog.000004”
— 可以看出新的binlog日志文件中,记录的gtid编号信息是延续了上一个binlog日志gtid集合信息,继续连续进行记录;

# 进行基本的数据库SQL语句操作:
create database gtdb;
use gtdb;
create table t1(id int);
insert into t1 values(1);
commit;
insert into t1 values(2);
commit;
insert into t1 values(3);
commit;

# 进行binlog事件信息查看
show binlog events in ‘binlog.000004’;
— 可以获取以上的数据操作事件信息,
drop database gtdb;
— 模拟破坏性操作,删除数据库

# 根据日志信息查看相关的事件情况(获取GTID编号范围)
show binlog events in ‘binlog.000004′;

# 需要恢复建库开始,删除之前的所有操作(即所有binlog日志信息),实现日志信息的截取
[root@oldboyxiaoq ~]# mysqlbinlog –include-gtids=’7afe4f8c-5e36-11ed-b083-000c29d44f34:3-7′ /data/3306/data/binlog.000004 >/tmp/gtid.sql
— 依据binlog日志的GTID信息,即可获取到想要恢复数据信息;

# 根据截取的日志信息,进行数据库服务数据恢复
set sql_log_bin=0;
— 建议在进行数据日志恢复数据时,将数据恢复时执行的SQL语句信息,不做binlog日志记录;恢复后别忘在改为1;
source /tmp/gtid.sql
— 默认此时报错恢复失败,因为GTID截取的日志恢复数据时,具有幂等性,由于binlog中已经记录了3-7的GTID事件信息
show master status;
+—————+———-+————–+——————+——————————————+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+—————+———-+————–+——————+——————————————+
| binlog.000004 | 1905 | | | 7afe4f8c-5e36-11ed-b083-000c29d44f34:1-8 |
+—————+———-+————–+——————+——————————————+
1 row in set (0.00 sec)
— 通过查看确认,核实清楚binlog中已经记录了3-7的GTID事件信息

# 利用GTID日志信息恢复报错处理方式一:将系统中日志中的GTID信息清除掉(不建议)
# 利用GTID日志信息恢复报错处理方式二:删除与幂等性冲突的记录信息
[root@oldboyxiaoq ~]# mysqlbinlog –skip-gtids –include-gtids=’7afe4f8c-5e36-11ed-b083-000c29d44f34:3-7′ /data/3306/data/binlog.000004 binlog.000005 binlog.000006>/tmp/gtid.sql
— 表示跳过gtid的检查过程,即截取的日志中不再含有GTID的配置语句信息,自然解决了幂等性冲突问题;
— 开启了GTID之后,依然可以使用pos方式进行日志信息截取与恢复;

# 查看确认数据信息是否恢复
use gtdb;
show tables;
select * from t1;
— 查看test1数据库中的t1表的数据信息是否恢复

# 操作扩展:可以实现排除指定gtid信息不做日志记录截取
[root@oldboyxiaoq ~]# mysqlbinlog –exclude-gtids=’7afe4f8c-5e36-11ed-b083-000c29d44f34:4′ –include-gtids=’7afe4f8c-5e36-11ed-b083-000c29d44f34:3-7′ /data/3306/data/binlog.000004

# 操作扩展:跨多日志文件信息截取
[root@oldboyxiaoq ~]# mysqlbinlog –skip-gtids –include-gtids=’7afe4f8c-5e36-11ed-b083-000c29d44f34:1-10’ /data/3306/data/binlog.000001 /data/3306/data/binlog.000002 /data/3306/data/binlog.000003 >/tmp/gtid.sql
“`

### 2 GTID(全局事务ID)

#### 1 概念介绍

“`sh
GTID(global transation id)称为全局事务(事件)ID,标识binlog日志记录的唯一性;

GTID信息的表示方式:
“`

| 表现形式 | 关键列 | 解释说明 |
| ————- | ———– | ——————————————————– |
| server_uuid:N | server_uuid | 表示数据库初始化启动之后,自动生成的随机数信息(唯一的) |
| | N | 表示第几个相关的事务或事件信息,会不断进行自增 |

“`sh
select @@server_uuid;– 表示数据库每次初始化之后自动生成,不建议手工进行修改;
cat /data/3306/data/auto.cnf– 在数据库的数据目录文件中也可以查询到
“`

#### 2 功能作用

“`sh
利用GTID方式管理binlog,实质上就是对于数据库的每个事务产生事件信息打上唯一标识信息(id号);

利用GTID方式管理binlog,主要目的是处理数据库主从问题,解决主从数据库的数据一致性问题;

简单描述:标识事务的唯一性,保证日志恢复时的一致性,并且具备”幂等性”;
“`

#### 3 功能配置

“`sh
select @@gtid_mode;
— 设置是否开启显示gtid信息功能(在5.7之后是有个匿名的gtid,是数据库系统自己维护的)
select @@enforce_gtid_consistency;
— 设置是否开启GTID强制一致性功能
— 对某些 SQL 会有限制,例如 CREATE TABLE … SELECT 必须得分成两条语句执行。
— OFF: 表示事务允许违反 GTID 一致性。
— ON: 表示事务不允许违反 GTID 一致性,有相关 SQL 会直接返回异常。
— WARN:表示事务允许违反 GTID 一致性,但会将警告信息记录到 ERROR LOG。
select @@log_slave_updates;
— 和配置主从有关(在8.0.26开始 推荐配置log_replica_updates替代log_slave_updates参数)
— 此参数表示从服务器从主服务器接收的更新信息,是否也会记录在从服务器本地的二进制文件中
[root@xiaoq ~]# vim /etc/my.cnf
[mysqld]
gtid_mode=on
enforce_gtid_consistency=1
log_slave_updates=on
— 配置文件信息修改完毕后,重启数据库服务使配置生效

gtid信息查看
show master status
— 在GTID功能被激活后,就会在Executed_Gtid_Set列中显示GTID集合信息;
create database test3;
— 模拟创建数据库,产生新的事件信息
show master status;
— GTID信息随着新的事件产生,随之发生变化
create database test4;
— 模拟创建数据库,产生新的事件信息
show master status;
— GTID信息随着新的事件产生,随之发生变化
show binary logs;
–查看所有二进制日志
show binlog events in ‘binlog.000004’;
— 在每个数据库操作事件之前,会显示GTID的唯一标识信息
“`

### 3 从日志文件中恢复单库、单表、或部分行数据

“`sh
A计划:可以利用命令单独截取某个数据库的日志信息;`mysqlbinlog -d world xxx > xxxx`

B计划:可以借助第三方工具实现单表或部分数据恢复;`binlog2sql(python)` 过滤指定表数据或过滤指定表的部分数据;

实战操作:

**A计划:单库日志信息截取,企业实战过程**:

数据库异常恢复情况环境准备:

# 查看获取当前binlog日志状态信息
show master status;
# 进行基本的数据库SQL语句操作:
create database test1;
use test1
create table t1 (id int);
insert into t1 values(1);
insert into t1 values(2);
commit;
select * from t1;

— 创建了一个test1数据库,并在数据库中创建了一个表,在表中插入了一些数据信息
create database test2;
use test2;
create table t2 (id int);
insert into t2 values(1);
insert into t2 values(2);
commit;
— 创建了一个test2数据库,并在数据库中创建了一个表,在表中插入了一些数据信息
use test1;
insert into t1 values(3);
insert into t1 values(4);
use test2;
insert into t2 values(3);
insert into t2 values(4);
commit;
select * from test1.t1;
select * from test2.t2;
— 通过操作不同的数据库,以及不同的数据表,实现binlog日志事件信息的交叉

数据库模拟异常情况破坏操作:
drop database test1;
— 模拟破坏性操作,删除数据库

数据库异常情况数据恢复操作:
# 根据日志信息查看相关的事件情况
show binlog events in ‘binlog.000003′;

# 需要恢复建库开始,删除之前的所有操作(即所有binlog日志信息),实现日志信息的截取
mysqlbinlog –start-position=1346 –stop-position=4116 -d test1 /data/3306/data/binlog.000003 >/tmp/bin.sql
— 依据binlog日志的position号码,即可获取到想要恢复数据信息,并利用-d参数导出指定数据库相关数据;

# 根据截取的日志信息,进行数据库服务数据恢复
set sql_log_bin=0;
— 建议在进行数据日志恢复数据时,将数据恢复时执行的SQL语句信息,不做binlog日志记录;恢source /tmp/bin.sql

# 查看确认数据信息是否恢复
use test1;
show tables;
select * from t1;
— 查看test1数据库中的t1表的数据信息是否恢复
use test2;
show tables;
select * from t2;
— 查看test2数据库中的t2表的数据信息是否破坏

**B计划:可以借助第三方工具实现单表或部分数据恢复;**

利用binlog2sql工具可以处理上面的企业需求,此软件是利用python语言开发的,主要用来处理binlog日志信息;

从软件应用方面来说主要包含两个核心功能:

– 可以友好的展示或者管理二进制日志信息(binlog),进而可以过滤出单独表的信息,甚至表中指定行的信息;
– 可以快速的实现DML操作语句的闪回功能,即实现通过日志信息翻转方式,进行数据信息的恢复;

> 说明:binlog2sql工具是模拟了一个从库,进行日志信息分析,需要保证数据库服务启动状态,且不支持离线方式分析日志内容;
数据库异常恢复情况环境准备:

# 下载第三方日志分析工具
cd /opt/
git clone https://github.com/danfengcao/binlog2sql.git
cd /opt/binlog2sql
— 此工具在mariadb中可以通过打补丁方式,进行部署安装;但是在mysql 8.0中暂时还没有集成,需要单独安装

# 部署第三方工具运行环境
yum install -y python3
pip3 install -r requirements.txt
pip3 show pymysql
pip3 install –upgrade pymysql (此步骤可以忽略)
— 以上pip3下载软件缓慢,可以优化pip3下载源
— 下载源优化方法:https://developer.aliyun.com/mirror/pypi?spm=a2c6h.13651102.0.0.3e221b11H9Q7La

# 在指定数据库中创建多个数据表
use test1;
create table t11 (id int);
insert into t11 values (1),(2);
commit;
数据库日志信息工具分析查看:(解析日志事件SQL)

python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –start-file=’binlog.000003′
INSERT INTO `test1`.`t1`(`id`) VALUES (1); #start 1460 end 1704 time 2022-11-21 22:16:32 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (2); #start 1735 end 1979 time 2022-11-21 22:16:35 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (3); #start 2939 end 3183 time 2022-11-21 22:20:53 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (4); #start 3214 end 3458 time 2022-11-21 22:22:19 gtid
python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t11 –start-file=’binlog.000003′
INSERT INTO `test1`.`t11`(`id`) VALUES (1); #start 4704 end 4954 time 2022-11-21 23:47:51 gtid
INSERT INTO `test1`.`t11`(`id`) VALUES (2); #start 4704 end 4954 time 2022-11-21 23:47:51 gtid
— 表的数据信息导出后,可以直接复制命令信息恢复,或者导出sql文件进行导入恢复;

数据库模拟异常情况破坏操作:
# 在指定数据库的相应数据表中做修改操作
use test1;
update t1 set id=10 where id=1;
commit;

# 在指定数据库的相应数据表中做删除操作
use test1;
delete from t1 where id=3;
commit;
数据库日志信息工具分析查看:(解析日志事件SQL)

python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –start-file=’binlog.000003′
INSERT INTO `test1`.`t1`(`id`) VALUES (1); #start 1460 end 1704 time 2022-11-21 22:16:32 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (2); #start 1735 end 1979 time 2022-11-21 22:16:35 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (3); #start 2939 end 3183 time 2022-11-21 22:20:53 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (4); #start 3214 end 3458 time 2022-11-21 22:22:19 gtid
UPDATE `test1`.`t1` SET `id`=10 WHERE `id`=1 LIMIT 1; #start 4985 end 5244 time 2022-11-21 23:52:33 gtid
DELETE FROM `test1`.`t1` WHERE `id`=3 LIMIT 1; #start 5275 end 5519 time 2022-11-21 23:54:17 gtid

# 只想查看删除操作信息
python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –sql-type=delete –start-file=’binlog.000003′
DELETE FROM `test1`.`t1` WHERE `id`=3 LIMIT 1; #start 5275 end 5519 time 2022-11-21 23:54:17 gtid
— sql-type参数只能过滤DML类型语句信息,一般常见过滤的是 insert update delete

# 只想查看修改操作信息
python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –sql-type=update –start-file=’binlog.000003′
UPDATE `test1`.`t1` SET `id`=10 WHERE `id`=1 LIMIT 1; #start 4985 end 5244 time 2022-11-21 23:52:33 gtid

# 只想查看插入操作信息
python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –sql-type=insert –start-file=’binlog.000003′
INSERT INTO `test1`.`t1`(`id`) VALUES (1); #start 1460 end 1704 time 2022-11-21 22:16:32 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (2); #start 1735 end 1979 time 2022-11-21 22:16:35 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (3); #start 2939 end 3183 time 2022-11-21 22:20:53 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (4); #start 3214 end 3458 time 2022-11-21 22:22:19 gtid

数据库日志信息工具回滚操作:(生成指定事件回滚语句-闪回操作)

假设在某个企业的应用场景中,有3000万行数据,占用200G的存储空间,其中误删除了10行数据信息,请问如何进行恢复数据?

# 误删除操作语句反转操作
python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –sql-type=delete –start-file=’binlog.000003′
DELETE FROM `test1`.`t1` WHERE `id`=3 LIMIT 1; #start 5275 end 5519 time 2022-11-21 23:54:17 gtid
— 获取删除操作语句信息

python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –sql-type=delete –start-file=’binlog.000003′ -B
INSERT INTO `test1`.`t1`(`id`) VALUES (3); #start 5275 end 5519 time 2022-11-21 23:54:17 gtid
— 在获取删除操作语句命令后加 -B 参数,正好获得了反转语句的操作信息

# 误修改操作语句反转操作
python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –sql-type=update –start-file=’binlog.000003′
UPDATE `test1`.`t1` SET `id`=10 WHERE `id`=1 LIMIT 1; #start 4985 end 5244 time 2022-11-21 23:52:33 gtid
— 获取修改操作语句信息

python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –sql-type=update –start-file=’binlog.000003′ -B
UPDATE `test1`.`t1` SET `id`=1 WHERE `id`=10 LIMIT 1; #start 4985 end 5244 time 2022-11-21 23:52:33 gtid
— 在获取修改操作语句命令后加 -B 参数,正好获得了反转语句的操作信息

# 误插入操作语句反转操作
python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –sql-type=insert –start-file=’binlog.000003′
INSERT INTO `test1`.`t1`(`id`) VALUES (1); #start 1460 end 1704 time 2022-11-21 22:16:32 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (2); #start 1735 end 1979 time 2022-11-21 22:16:35 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (3); #start 2939 end 3183 time 2022-11-21 22:20:53 gtid
INSERT INTO `test1`.`t1`(`id`) VALUES (4); #start 3214 end 3458 time 2022-11-21 22:22:19 gtid
— 获取插入操作语句信息

python3 binlog2sql.py -h 10.0.0.101 -P3306 -uroot -p123456 -d test1 -t t1 –sql-type=insert –start-file=’binlog.000003’ -B
DELETE FROM `test1`.`t1` WHERE `id`=4 LIMIT 1; #start 3214 end 3458 time 2022-11-21 22:22:19 gtid
DELETE FROM `test1`.`t1` WHERE `id`=3 LIMIT 1; #start 2939 end 3183 time 2022-11-21 22:20:53 gtid
DELETE FROM `test1`.`t1` WHERE `id`=2 LIMIT 1; #start 1735 end 1979 time 2022-11-21 22:16:35 gtid
DELETE FROM `test1`.`t1` WHERE `id`=1 LIMIT 1; #start 1460 end 1704 time 2022-11-21 22:16:32 gtid
— 在获取插入操作语句命令后加 -B 参数,正好获得了反转语句的操作信息
“`

# 第六章 备份恢复

## 1 物理备份

“`sh
找个业务不忙的时间段停机停业务
cp或者tar命令备份或打包,或者nbu备份
当企业数据库服务产生的需要备份的数据量在50G以上,可以选择物理备份(xtrabackup);
“`

## 2 逻辑备份

可以采用以SQL语句形式把数据库的数据导出保存备份为数据库文件(xxx.sql),文件中会含有大量SQL语句信息;

可以在任意时间节点在不停机不停止业务时

### 1 mysqldump

“`sh
主要的备份逻辑是将建库、建表、数据插入语句信息导出,实现数据的备份操作;

基于mysqldump备份数据的逻辑原理,对于数据量比较小的场景(单表数据行百万以内),mysqldump备份工具做备份会更适合些;

在跨平台或跨版本进行数据库数据信息迁移时,mysqldump备份工具做备份也会比较适合,可以避免物理备份的兼容性问题;

> 说明:在一般情况下,对数据库进行数据恢复的时间耗费,大约是数据库进行数据备份的时间耗费的3~5倍。
mysqldump -uroot -p [参数] >备份文件.sql
— 在执行mysqldump命令时,也会用到数据库连接登录的基础参数:-u -p -S -h -P
“`

工具命令常用参数:

| 序号 | 参数信息 | 官方说明 | 解释说明 |
| —- | ——– | ———————————————- | ——————————– |
| 01 | -A | Dump all the databases | 表示备份所有库中数据信息 |
| 02 | -B | Dump several databases. | 表示备份指定库中数据信息 |
| 03 | -F | Flush logs file in server before starting dump | 表示在备份启动前自动刷新日志文件 |

“`sh
1 全部备份(-A)
mkdir -p /database_backup
mysqldump -uroot -p123456 -A >/database_backup/all_database.sql
ll -h /database_backup/all_database.sql
说明:利用-A创建数据库备份数据时,在备份数据中会含有 create建库语句和use切换库语句,可以直接进行恢复操作即可;
单个数据库进行备份(-B)
mysqldump -uroot -p123456 -B oldboy >/database_backup/oldboy.sql
egrep -vi ‘^-|^/\*|^$|lock’ /databases_backup/oldboy.sql
恢复
source /database_backup/oldboy.sql;
mysql -uroot -p123456 oldboy /database_backup/oldboy_world.sql

# 过滤部分内容后查看备份数据库文件信息:
[root@xiaoQ ~]# egrep -vi ‘^-|^/\*|^$|lock’ /database_backup/oldboy_world.sql

> 说明:利用-B创建数据库备份数据时,在备份数据中会含有 create建库语句和use切换库语句,可以直接进行恢复操作即可;

– 单个数据表进行备份**

# 备份指定数据库中的单个数据表:
[root@xiaoQ ~]# mysqldump -uroot -poldboy123 oldboy stu1 >/databases_backup/oldboy_tables_stu1.sql

# 恢复指定数据库中的单个数据表:
[root@xiaoQ ~]# mysql -uroot -poldboy123 oldboy /database_backup/world_tables_city_country.sql

# 恢复指定数据库中的多个数据表:
[root@xiaoQ ~]# mysql -uroot -poldboy123 world 说明:数据库单表或多表进行数据备份时,在备份数据中不含有create建库语句和use切换库语句,需要建库并指定库再恢复数据;

现数据库信息备份备份:
# 进行数据库数据复原恢复操作:
# 方式一:在数据库系统中加载数据库备份文件
source /database_backup/oldboy.sql;

# 方式二:在操作系统命令行执行数据恢复命令
[root@xiaoQ ~]# mysql -uroot -p123456 oldboy /backup/backup.sql

实现数据库信息远程备份:

mysqldump -uroot -p123456 -h数据库服务IP地址 -P数据库服务端口 [备份参数] >/backup/backup.sql

> 说明:可以利用第三方远程连接数据库工具,也可以实现数据备份

数据库数据备份进阶方式一:利用命令参数 –single-transaction
利用–single-transaction参数进行数据备份,就等价于在备份的时候给数据库的数据拍了照,备份时候数据库可以继续更新;

–single-transaction
通过在单个事务中备份所有表时,会创建一致性快照
仅适用于存储在支持多版本控制的存储引擎中的表(目前只有InnoDB)
对于InnoDB,会利用MVCC中一致性快照进行备份;
当–single-transaction参数应用在备份进程中时,确保备份文件的有效性
在进行备份数据期间,不要出现相关DDL的操作信息,导致备份数据不一致;
Option automatically turns off –lock-tables.

在 MySQL 8.0.26 及更高版本中,–master-data 参数已被重命名为 –source-data。这两个参数的作用相同,都是用于在导出的 SQL 文件中包含主服务器(或源服务器)的二进制日志(binlog)信息,以便在主从复制环境中使用。
参数作用
–source-data 或 –master-data:
在导出的 SQL 文件中包含当前服务器的二进制日志文件名和位置信息。
这些信息用于主从复制的搭建,帮助从服务器(slave)从指定的二进制日志位置开始复制。
参数值
–source-data=1 或 –master-data=1:
在导出的文件中包含未注释的 CHANGE MASTER TO 语句。在从服务器导入时,该语句会直接生效。
–source-data=2 或 –master-data=2:
在导出的文件中包含注释的 CHANGE MASTER TO 语句。在从服务器导入时,需要手动取消注释后才能生效。
使用场景
主从复制:
在主服务器上使用 –source-data 或 –master-data 参数导出数据,然后将导出的文件导入到从服务器。从服务器会根据导出文件中的二进制日志信息开始复制。
增量恢复:
在全量备份后,可以使用二进制日志进行增量恢复。
示例命令
MySQL 8.0.26 及更高版本:
bash复制
mysqldump -uroot -p –all-databases –source-data=2 > backup.sql
MySQL 8.0.26 之前的版本:
bash复制
mysqldump -uroot -p –all-databases –master-data=2 > backup.sql
注意事项
确保二进制日志已启用,否则该参数无效。
使用该参数时,可能会自动启用 –lock-all-tables,除非同时使用了 –single-transaction。
如果你正在使用 MySQL 8.0.26 或更高版本,建议使用 –source-data 参数。如果你使用的是更早的版本,则需要使用 –master-data。
在这种情况下,全局读锁只在备份开始时占用很短的时间
mysqldump -u -p -h -P -S -A/-B –source-data=1 –single-transaction
mysqldump -uroot -poldboy123 –master-data=2 –single-transaction -A -B|gzip >/tmp/bak.sql.gz
gzip -d /tmp/bak.sql.gz

库数据备份进阶方式三:利用命令参数 -R -E –triggers
mysqldump -uroot -poldboy123 -B mdb –master-data=2 –single-transaction -R -E –triggers >/databases_backup/oldboy_`date +%F`.sql
“`

以上mysqldump备份中的特殊参数说明:

| 序号 | 参数信息 | 官方说明 | 解释说明 |
| —- | ———- | ———————————————– | ————————– |
| 01 | -R | Dump stored routines (functions and procedures) | 表示进行数据库存储过程备份 |
| 02 | -E | Dump events | 表示进行数据库事件信息备份 |
| 03 | –triggers | Dump triggers for each dumped table. | 表示进行触发器信息备份 |

“`sh
数据备份进阶方式四:利用命令参数 –max_allowed_packet=64M**

此参数表示数据库服务端与客户端在一次传送数据包的过程中,最大允许进行传输的数据包大小;

在某些时候如果备份的数据为大表数据,需要调整此参数信息;

如果没有正确的设置此参数信息,可能会导致备份大表数据时,会出现数据备份失败的情况;

应用场景:

– 有时候业务的需要,可能会存在某些字段数据长度非常大,造成插入和更新数据库会被max_allowed_packet 参数限制掉;

导致数据库操作失败。

– 将本地数据库迁移到远程数据库时运行sql错误。错误信息是max_allowed_packet

以上问题出现,经常出现的错误提示如下:

Packet for query is too large (20682943>1048576).
You can change this value on the server by setting the max_allowed_packet’ variable.

结合以上参数信息,进行标准化数据备份操作:

mysqldump -uroot -p123456 -A –master-data=2 –single-transaction -R -E –triggers –max_allowed_packet=64M >/database_backup/full_`date +%F`
vim /database_backup/full_2022-11-26.sql
SET @@GLOBAL.GTID_PURGED=/*!80000 ‘+’*/ ‘9d14be39-6423-11ed-bb21-000c2996c4f5:1-6′;
— 表示在进行数据恢复操作时,会将gtid1-6的事件信息删除掉,因为在之前备份数据中已经有了1-6的事件数据信息;
— 因此,从GTID的编号来看,可以从编号7事件开始进行数据增量恢复;
CHANGE MASTER TO MASTER_LOG_FILE=’binlog.000013’, MASTER_LOG_POS=1312;
— 输出信息表示增量数据的临界点在binlog.000013日志文件的1312位置,同时是备份结束时的位置点;

参数说明
-uroot
指定数据库用户为 root。
-p123456
指定数据库用户的密码为 123456。注意,直接在命令中明文写入密码可能存在安全风险。建议在生产环境中使用更安全的方式,如配置 .my.cnf 文件或使用 –prompt 参数。
-A
等同于 –all-databases,表示导出所有数据库。
–master-data=2
将当前的二进制日志文件名和位置写入导出文件中。这对于主从复制环境非常有用,可以确保备份文件中包含足够的信息用于恢复。
–single-transaction
启动一个事务,确保在导出过程中数据库的状态保持一致。适用于使用事务存储引擎(如 InnoDB)的数据库,不会锁定表。
-R
等同于 –routines,表示导出存储过程和函数。
-E
等同于 –events,表示导出事件调度器中的事件。
–triggers
表示导出表的触发器。
–max_allowed_packet=64M
设置最大允许的数据包大小为 64MB。这可以防止在导出大表时出现 max_allowed_packet 错误。
>/database_backup/full_date +%F“
将备份文件输出到 /database_backup 目录,并以当前日期(格式为 YYYY-MM-DD)命名备份文件。

使用mysqldump备份test1库中的t2表和test2库中的t1表
mysqldump -u root -p –single-transaction test1 t2 test2 t1 > backup.sql

mysqldump备份多个库所有表
mysqldump -u root -p –single-transaction test1 test2 > backup.sql
“`

### 2 mysqldump总结

“`sh
备份全库
mysqldump -uroot -p123456 -A –master-data=2 –single-transaction -R -E –triggers –max_allowed_packet=64M >/database_backup/full_`date +%F`
备份不同库中的一些表
mysqldump -u root -p –single-transaction test1 t2 test2 t1 > backup.sql
mysqldump备份多个库所有表
mysqldump -u root -p –single-transaction test1 test2 > backup.sql

“`

## 3 逻辑备份应用案例

“`sh
模拟企业生产场景,数据库管理人员误删除了数据库数据信息,通过mysqldump全备的部分数据信息,进行部分数据信息恢复;

再结合binlog日志文件增量数据信息,实现数据库增量数据恢复,最终实现数据库全部数据的完整复原。

– **项目故障说明:**

在某周周三下午14点左右,由于开发人员连接数据库实例错误,导致企业数据库服务生产数据被误删除了,亟待相关人员解决;

**01 模拟时间-某周周一~周二,正常网站用户访问网站进行数据库信息录入**

flush logs;
— 将binlog日志文件进行刷新,创建一个新的日志文件
create database mdb;
use mdb;
— 模拟创建用户存储数据的数据库信息
create table t1 (id int);
create table t2 (id int);
— 模拟创建用户存储数据的数据表信息
insert into t1 values(1),(2),(3);
insert into t2 values(1),(2),(3);
commit;
— 模拟用户向数据表中添加新的数据
select * from t1;
select * from t2;
— 检查用户创建的数据信息是否生成

**02 模拟时间-某周周二晚零点,企业数据库管理员进行一次数据库服务数据全备操作**

[root@xiaoQ ~]# mysqldump -uroot -p -A –master-data=2 –single-transaction -R -E –triggers –max_allowed_packet=64M >/database_backup/full_`date +%F`.sql

以上mysqldump备份中的特殊参数说明:

| 序号 | 参数信息 | 官方说明 | 解释说明 |
| —- | ———- | ———————————————– | ————————– |
| 01 | -R | Dump stored routines (functions and procedures) | 表示进行数据库存储过程备份 |
| 02 | -E | Dump events | 表示进行数据库事件信息备份 |
| 03 | –triggers | Dump triggers for each dumped table. | 表示进行触发器信息备份 |

**03 模拟时间-某周周二晚零点之后,模拟用户继续访问网站业务产生了增量的数据信息**

use mdb;
create table t3 (id int);
insert into t3 values(1),(2),(3);
insert into t2 values(4),(5),(6);
commit;

**04 模拟时间-某周周三下午14点,模拟系统相关技术人员误删除了数据库,并且已紧急跑路**

drop database mdb;

– **项目实战复原:**

**01 修复操作-查看找寻数据库服务全备数据,并进行全备数据恢复**

[root@xiaoQ-01 database_backup]# ll
-rw-r–r– 1 root root 51256606 11月 26 01:53 full_2022-11-26.sql
[root@xiaoQ ~]# mysql -uroot -p123456
source /database_backup/full_2022-11-26.sql
— 强调说明 强调说明 强调说明,此步骤操作了解作用后,请在后面进行操作,不要在此步骤就进行数据恢复

use mdb;
show tables;
+——————–+
| Tables_in_mdb |
+——————–+
| t1 |
| t2 |
+——————–+
2 rows in set (0.00 sec)
— 查看全备的数据是否恢复成功

**02 修复操作-查看找寻数据库服务增量备份,并进行增量数据恢复**

# 检索恢复binlog临界位置
[root@xiaoQ ~]# vim /database_backup/full_2022-11-26.sql
SET @@GLOBAL.GTID_PURGED=/*!80000 ‘+’*/ ‘9d14be39-6423-11ed-bb21-000c2996c4f5:1-6′;
— 表示在进行数据恢复操作时,会将gtid1-6的事件信息删除掉,因为在之前备份数据中已经有了1-6的事件数据信息;
— 因此,从GTID的编号来看,可以从编号7事件开始进行数据增量恢复;
CHANGE MASTER TO MASTER_LOG_FILE=’binlog.000013’, MASTER_LOG_POS=1312;
— 输出信息表示增量数据的临界点在binlog.000013日志文件的1312位置,同时是备份结束时的位置点;

# 检索查看binlog日志文件获取误删除操作前的GTID
ll /data/3306/data/binlog.*
-rw-r—– 1 mysql mysql 1833 11月 26 01:55 /data/3306/data/binlog.000013
-rw-r—– 1 mysql mysql 64 11月 26 01:50 /data/3306/data/binlog.index
— 具体binlog日志是哪个,以企业具体情况而定,不一定是binlog.000013
show binlog events in ‘binlog.000013’;
+——————+——-+——————+————-+—————–+————————————————–+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+——————+——-+——————+————-+—————–+————————————————–+
| binlog.000013 | 2060 | Gtid | 1 | 2137 | SET @@SESSION.GTID_NEXT= ‘9d14be39-6423-11ed-bb21-000c2996c4f5:10′ |
| binlog.000013 | 2137 | Query | 1 | 2238 | drop database mdb /* xid=840 */ |
+——————+——-+——————+————-+—————–+————————————————–+
— 需要将GTID编号10的误删除数据库事件信息忽略,然后再进行数据信息的恢复

# 移动迁移binlog文件位置
[root@xiaoQ ~]# cp /var/lib/mysql/binlog.* /databases_backup/

# 操作截取binlog文件信息
[root@xiaoQ ~]# mysqlbinlog –skip-gtids –include-gtids=’9d14be39-6423-11ed-bb21-000c2996c4f5:7-9’ /data/3306/data/binlog.000013 >/database_backup/add_bin.sql
— include-gtids是指定前面临界位置点,截取之后的日志文件信息

# 增量恢复binlog数据信息
set sql_log_bin=0;
— 建议在进行数据日志恢复数据时,将数据恢复时执行的SQL语句信息,不做binlog日志记录;恢复后别忘在改为1;
source /database_backup/add_bin.sql
— 完成数据信息的增量恢复

**03 修复操作-进行测试核验数据信息是否完全恢复,并进行最终全量备份**

# 核验检查恢复后的数据信息
use mdb;
show tables;
select * from t1;
select * from t2;
select * from t3;

# 完成核验之后数据完整备份
[root@xiaoQ ~]# mysqldump -uroot -p -A –master-data=2 –single-transaction -R -E –triggers –max_allowed_packet=64M >/database_backup/full_`date +%F`.sql

> 说明:数据库数据修复复原完毕后,别忘让开发人员或测试人员进行业务功能测试,最终让运维人员恢复业务上线。
“`

## 4 物理备份实践

“`sh
在数据库服务运行使用过程中,除了上面介绍的逻辑备份数据方法,还可以采用物理方式备份数据信息;

物理备份数据方式又可以细分为`冷备份和热备份`两种,和逻辑备份相比,它的最大优点是备份和恢复的速度更快;

因为物理备份的原理都是基于文件的cp。
“`

### 1 物理冷备份

“`sh
冷备份其实就是停掉数据库服务,cp数据文件的方法;这种方法对MyISAM和InnoDB存储引擎都合适,但是一般很少使用,

因为很多应用是不允许长时间停机的。

– 进行备份的操作过程:

停掉MySQL服务,在操作系统级别备份MySQL的数据文件和日志文件到备份目录;

– 进行恢复的操作过程:

停掉MySQL服务,在操作系统级别恢复MySQL的数据文件,然后重启MySQL服务,使用mysqlbinlog工具恢复增量数据日志;
“`

### 2 物理热备份

“`sh
– 数据库存储引擎应用:MyISAM

MyISAM存储引擎的热备份有很多方法,本质其实就是将要备份的表加读锁,然后再cp数据文件到备份目录:
**方法一:使用mysqlhotcopy工具**

mysqlhotcopy是MySQL自带的一个热备份工具,使用方法很简单:

​“`tiki wiki
[root@xiaoQ ~]# mysqlhotcopy db_name [/path/to/new_directory]
— mysqlhotcopy有很多选项,具体可以使用–help查看帮助信息;
​“`

**方法二:手工锁表copy**

在mysqlhotcopy使用不熟悉的情况下,可以手工来做热备份,操作步骤如下:

​“`tiki wiki
# 对数据库中所有表加读锁:
mysql> flush tables for read;
— 然后cp数据文件到备份目录即可;
​“`

– 数据库存储引擎应用:InnoDB

Xtrabackup(PXB)是Percona公司CTO Vadim参与开发的一款基于InnoDB的在线热备工具,属于物理备份数据工具;

具有开源、免费、支持在线热备、备份恢复速度快、占用磁盘空间小等特点,并且支持不同情况下的多种备份形式。

官方软件下载链接:https://www.percona.com/downloads/

对于数据库8.0.20版本,需要使用PXB 8.0.12+以上版本,对于数据库8.0.11~8.0.19,可以使用PXB 8.0正式版本;

PXB 8.0只能备份MySQL 8.0版本数据,不能备份低版本数据信息;如果想备份数据库服务低版本程序数据,需要下载使用PXB 2.4版本;

xtrabackup包含两个主要的工具:xtrabackup和innobackupex

– xtrabackup 只能备份InnoDB和XtraDB两种类型的数据表,而不能备份MyISAM类型数据表;
– innobackupex 是一个封装了xtrabackup的perl脚本,支持同时备份InnoDB和MyISAM,但对MyISAM备份时需要加全局读锁;

由于PXB属于第三方软件工具程序,因此需要进行单独下载安装:
# 进行软件程序上传
wget https://downloads.percona.com/downloads/Percona-XtraBackup-8.0/Percona-XtraBackup-8.0.35-31/binary/redhat/7/x86_64/percona-xtrabackup-80-debuginfo-8.0.35-31.1.el7.x86_64.rpm?_gl=1*5s32hw*_gcl_au*MTM1NzMzMDIzMi4xNzM5MTU5ODYz

# 进行软件程序安装
[root@xiaoQ-01 local]# yum install -y percona-xtrabackup-80-8.0.13-1.el7.x86_64.rpm
— 利用yum方式安装本地的rpm包程序,可以有效解决软件依赖的问题;
**Xtrabackup数据备份方式01:实现全量备份**

​“`tiki wiki
# 全量备份操作:
mkdir /data/backup/full -p
— 进行物理备份的目标目录不能存在数据信息,需要指定一个空目录进行备份
xtrabackup –defaults-file=/etc/my.cnf –host=192.168.30.101 –user=root –password=123456 –port=3306 –backup –target-dir=/data/backup/full
或者使用参数–datadir替换掉参数–defaults-file
xtrabackup –datadir=/data/3306/data –user=root –password=123456 –port=3306 –backup –target-dir=/data/backup/full
— backup参数信息表示进行全备操作
# 物理备份命令执行输出信息说明:
221127 02:46:11 >> log scanned up to (277574297)
— 记录日志位置点信息,表示进行拷贝数据的checkpoint的SN号码,相当于磁盘当前数据页的SN号码;
Using undo tablespace ‘./undo_001’.
Using undo tablespace ‘./undo_002’.
Opened 2 existing undo tablespaces.
221127 02:46:11 [01] Copying ./ibdata1 to /data/backup/full/ibdata1
221127 02:46:11 [01] …done
221127 02:46:11 [01] Copying ./sys/sys_config.ibd to /data/backup/full/sys/sys_config.ibd
221127 02:46:11 [01] …done
221127 02:46:11 [01] Copying ./mysql.ibd to /data/backup/full/mysql.ibd
… 省略部分信息…
— 进行相关数据文件、日志文件、共享表空间文件、数据表空间文件等文件的拷贝,并且拷贝过程是不会进行锁表操作的;
— 数据表空间信息备份完毕后,会有提示字段信息,并且此时会锁定binlog日志文件,并将binlog日志文件复制到备份目录;
— 在进行binlog日志文件备份时,会生成xtrabackup_binlog_info文件信息,用于记录物理备份后的二进制日志位置点;
— 二进制日志文件备份完毕后,会释放binlog日志文件锁定状态,并再次检查checkpoint的SN号码,确认redo日志是否变化;

[root@xiaoQ-01 backup]# ll /data/backup/full/
— 可以看到将原有数据库的数据目录信息,已经基本迁移到指定的物理备份目录中;
​“`

Xtrabackup数据备份工具在热备操作后产生的特殊数据文件说明:

| 序号 | 文件名称 | 解释说明 |
| —- | ———————- | ———————————————————— |
| 01 | xtrabackup_binlog_info | 表示用于存储备份时的binlog位置点信息 |
| 02 | xtrabackup_checkpoints | 表示用于记录备份时的数据页LSN信息,主要用于接下一次备份,需要保证连续性; |
| 03 | xtrabackup_info | 表示整体物理备份信息的总览 |
| 04 | xtrabackup_logfile | 表示存储在备份数据期间产生的新的的redo日志的信息; |
| 05 | xtrabackup_tablespaces | 表示用于存储表空间的其余信息 |

**Xtrabackup数据恢复方式01:全量备份恢复**

模拟进行数据库数据破坏性操作:

​“`tiki wiki
[root@xiaoQ ~]# pkill mysqld
[root@xiaoQ ~]# rm -rf /data/3306/data/*
[root@xiaoQ ~]# rm -rf /data/3306/logs/*
[root@xiaoQ ~]# rm -rf /data/3306/binlog/*
​“`

进行数据库数据恢复的操作过程:

​“`tiki wiki
[root@xiaoQ ~]# xtrabackup –prepare –target-dir=/data/backup/full
…忽略部分信息…
Shutdown completed; log sequence number 19214860
221127 16:31:58 completed OK!
— 表示模拟CR过程,将redo日志进行前滚,undo日志进行回滚,让恢复数据信息保持一致性状态

[root@xiaoQ ~]# xtrabackup –copy-back –target-dir=/data/backup/full
— 将进行物理备份后的数据,再次进行还原恢复到备份前的目录中(拷贝回数据)
xtrabackup –copy-back –target-dir=/data/backup/full –datadir=/data/3306/data/
[root@xiaoQ ~]# chown -R mysql.mysql /data/*
[root@xiaoQ ~]# /etc/init.d/mysqld start
— 重新设置数据目录权限,并重新启动恢复数据库业务
​“`

“`

xtrabackup下载流程

https://www.percona.com/

![image-20250211105006345](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20250211105006345.png)

点击mysql–software

![Image 1](C:\Users\Administrator\Desktop\Image 1.png)

![2025-02-11 10 54 07](C:\Users\Administrator\Desktop\2025-02-11 10 54 07.png)

![image-20250211105614431](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20250211105614431.png)

根据版本选择下载

## 5 克隆备份

“`sh
在数据库MySQL 8.0(8.0.17+)版本中,引入了数据库的克隆功能,主要是借助clone-plugin实现的,是对数据页底层克隆;

克隆的数据是InnoDB存储引擎中的物理快照信息,包括schemas, tables, tablespaces, and data dictionary metadata;

在数据库中出现克隆功能,主要是为了满足目前云原生的技术应用场景,同时也是为了海量数据备份而诞生的;

在数据库中实现克隆功能应用有两种方式:
**本地克隆(Local Cloning):**

启动克隆操作的MySQL数据库服务器实例中的数据,将会克隆到同服务器或同节点上的一个目录里;
**远程克隆(Remote Cloning)**:

默认情况下,远程克隆操作会删除接受者(recipient)数据目录中的数据,并将其替换为捐赠者(donor)的克隆数据;

也可以将数据克隆到接受者的其他目录中,以避免删除现有数据;(属于可选操作);

主要用于实现数据远程的快速热迁移操作,在迁移过程中,除了DDL操作情况,其他操作都不会出现阻塞情况;

还可以利用远程克隆技术,实现快速构建数据库的主从架构环境,实现主从数据信息快速复制同步;

数据库克隆原理说明**

在进行数据库克隆操作时,会经历几个重要的过程或步骤:

**01 Page copy:**

在进行数据页复制操作时,会涉及到两个操作动作:

开启redo archiving功能,从当前点开始存储新增的redo_log,这样从当前位置点开始所有的增量修改都不会丢失;

同时上一步在page track的page被发送到目标端,确保当前位置点之前所有做的变更一定发送到目标端;

关于redo archiving实际上这是官方早就存在的功能,主要用于官方的企业级备份工具,clone利用了该特性来维持记录增量产生的redo

在开始克隆前会做一次checkpoint;

对于redo archiving功能应用,会开启一个后台线程log_archiver_thread()来做日志归档;

当有新的写入时(notify_about_advanced_write_lsn),也会通知线程去进行归档,当arch_log_sys处于活跃状态时,

线程会控制日志写入以避免未归档的日志被覆盖(log_write_wait_on_archiver),注意如果log_write等待时间过长的话,

archive任务会被中断掉;

**02 Redo copy:**

停止redo archiving功能,所有归档的日志被发送到目标端,这些日志包含了从page copy阶段开始到现在的所有日志;

另外可能还需要记下当前的复制点,例如:最后一个事务提交时的binlog位置点或者gtid信息,在系统页中可以找到;

**03 Done:**

目标端重启实例,通过crash recovery将redo log应用上去;

克隆原理过程分析参考链接:https://zhuanlan.zhihu.com/p/437760913

> 说明:整个克隆过程都会以事件信息记录,可以很清晰的看到克隆的流程,如果克隆过程中断,也会以追加方式进行继续克隆;

在进行克隆功能应用时,也是存在一些限制性操作的:(结合官方列出的限制)

– 对于MySQL 8.0.27之前版本,在进行克隆操作期间,是不允许在捐赠者和接受者上进行DDL操作,包括:truncate table操作;

对于MySQL 8.0.27之后版本,在捐赠者上默认允许并发DDL操作,对于捐赠者上并发DDL的支持由clone_block_DDL变量控制;

– 对于不同版本的MySQL数据库实例之间,是不能进行克隆操作的。对于捐赠者和接受者必须是确切相同数据库服务版本;

例如:你不能克隆数据在between MySQL 5.7 and MySQL 8.0, or between MySQL 8.0.19 and MySQL 8.0.20;

这个克隆功能只支持在数据库8.0.17版本或之后的版本

参考官方链接说明:https://dev.mysql.com/doc/refman/8.0/en/clone-plugin-limitations.html

**实现本地克隆操作过程:**

克隆需求:实现快速创建和源数据库服务一模一样的多实例服务程序;

克隆操作步骤01:加载克隆插件信息

​“`tiki wiki
# 进行克隆插件加载配置
mysql> INSTALL PLUGIN clone SONAME ‘mysql_clone.so’;
或者
[mysqld]
plugin-load-add=mysql_clone.so
clone=FORCE_PLUS_PERMANENT

# 查看克隆插件加载情况
mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = ‘clone’;
+——————+———————-+
| PLUGIN_NAME | PLUGIN_STATUS |
+——————+———————-+
| clone | ACTIVE |
+——————+———————-+
1 row in set (0.00 sec)
​“`

克隆操作步骤02:创建克隆专用用户

​“`tiki wiki
mysql> create user clone_user@’%’ identified by ‘password’;
mysql> grant backup_admin on *.* to ‘clone_user’;
— backup_admin权限是mysql 8.0才有的备份导出的权限;
​“`

克隆操作步骤03:进行本地克隆操作

​“`tiki wiki
mkdir -p /data/test
chown -R mysql.mysql /data/
mysql -uclone_user -ppassword
mysql> clone local data directory = ‘/data/test/clonedir’;
— 完成本地数据库目录的克隆操作,如果出现异常需要删除克隆目录,在进行重新克隆操作
​“`

克隆操作步骤04:观测本地克隆状态(另开窗口使用管理员用户查看)

​“`tiki wiki
mysql> select stage,state,end_time from performance_schema.clone_progress;
+—————+—————-+———————————–+
| stage | state | end_time |
+—————+—————-+———————————–+
| DROP DATA | Completed | 2022-11-28 01:11:17.715901 |
| FILE COPY | Completed | 2022-11-28 01:11:17.752819 |
| PAGE COPY | Completed | 2022-11-28 01:11:17.756830 |
| REDO COPY | Completed | 2022-11-28 01:11:17.757802 |
| FILE SYNC | Completed | 2022-11-28 01:11:17.912679 |
| RESTART | Not Started | NULL |
| RECOVERY | Not Started | NULL |
+—————+—————-+———————————–+
7 rows in set (0.00 sec)
— 当克隆数据量比较大的时候,可以使用此SQL语句进行克隆状态查看
​“`

克隆操作步骤05:实现克隆日志观测

​“`tiki wiki
mysql> set global log_error_verbosity=3;
tail -f db01.err
clone local data directory = ‘/data/test/clonedir’
​“`

克隆操作步骤06:启动运行克隆实例

​“`tiki wiki
mysqld_safe –datadir=/data/test/clonedir –port=3333 –socket=/tmp/mysql3333.sock –user=mysql –mysqlx=off &
netstat -lntup|grep 3333
tcp6 0 0 :::3333 :::* LISTEN 52674/mysqld

# 核实查看克隆后数据库信息
mysql -uroot -p123456 -S /tmp/mysql3333.sock
mysql> show databases;
​“`

**实现远程克隆操作过程:**

在进行实现远程克隆操作步骤之前,可以利用虚拟软件再次克隆出一台新的数据库8.0版本的服务器主机;

克隆操作步骤01:克隆操作环境准备

​“`tiki wiki
# 在克隆接收者主机上清理数据库服务环境:
pkill mysqld
rm -rf /data/3306/data/*
rm -rf /data/3306/binlog/*
rm -rf /data/3306/logs/*

# 在克隆接收者主机上进行实例初始化操作:
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data
vim /etc/my.cnf
[mysqld]
server_id=16
— 修改克隆接收者主机上的server_id的配置信息

# 在克隆接收者主机上进行实例的运行操作:
/etc/init.d/mysqld start
​“`

克隆操作步骤02:加载克隆插件信息

​“`tiki wiki
# 进行克隆插件加载配置
mysql> INSTALL PLUGIN clone SONAME ‘mysql_clone.so’;
或者
[mysqld]
plugin-load-add=mysql_clone.so
clone=FORCE_PLUS_PERMANENT
— 克隆插件信息需要在克隆主机的捐赠者和接受者上都进行安装

# 查看克隆插件加载情况
mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = ‘clone’;
+——————+———————-+
| PLUGIN_NAME | PLUGIN_STATUS |
+——————+———————-+
| clone | ACTIVE |
+——————+———————-+
1 row in set (0.00 sec)
— 克隆插件信息需要在克隆主机的捐赠者和接受者上都进行安装后确认
​“`

克隆操作步骤03:创建克隆专用用户

​“`tiki wiki
# 在克隆捐赠者主机上进行授权(数据库01主机上设置)
mysql> create user test_jz@’%’ identified by ‘password’;
mysql> grant backup_admin on *.* to test_jz@’%’ ;
— backup_admin权限是mysql 8.0才有的备份导出的权限;

# 在克隆接收者主机上进行授权(数据库02主机上设置)
mysql> create user test_js@’%’ identified by ‘password’;
mysql> grant clone_admin on *.* to test_js@’%’ ;
— clone_admin权限是mysql 8.0才有的克隆同步数据的权限;
​“`

> 说明:可以在克隆捐赠者主机上和接收者主机上均创建两个用户信息,防止克隆同步数据后,接收者主机上不再含有接收用户信息;

克隆操作步骤04:进行远程克隆操作

​“`tiki wiki
# 在克隆接收者主机上进行设置
mysql> set global clone_valid_donor_list=’10.0.0.51:3306′;
— 设置克隆同步数据的信任列表

# 在克隆接收者主机上进行克隆
mysql -utest_js -ppassword -h192.168.30.102 -P3306
mysql> clone instance from test_jz@’192.168.30.101′:3306 identified by ‘password’;
— 在接收者主机上实现远程克隆操作
​“`

克隆操作步骤05:观测本地克隆状态

​“`tiki wiki
mysql> select stage,state,end_time from performance_schema.clone_progress;
+—————+—————-+———————————–+
| stage | state | end_time |
+—————+—————-+———————————–+
| DROP DATA | Completed | 2022-11-29 00:15:34.002378 |
| FILE COPY | Completed | 2022-11-29 00:15:35.218397 |
| PAGE COPY | Completed | 2022-11-29 00:15:35.225659 |
| REDO COPY | Completed | 2022-11-29 00:15:35.229777 |
| FILE SYNC | Completed | 2022-11-29 00:15:35.773431 |
| RESTART | Completed | 2022-11-29 00:15:39.189607 |
| RECOVERY | Completed | 2022-11-29 00:15:39.978301 |
+—————+—————-+———————————–+
7 rows in set (0.00 sec)
— 当克隆数据量比较大的时候,可以使用此SQL语句进行克隆状态查看,在克隆接收者主机上进行查看
mysql> show databases;
— 此时克隆接收者主机上查看到的数据信息,与克隆捐赠者主机上查看到的数据信息一致,即远程克隆操作完成
​“`

###
“`

## 6 数据库备份总结

“`sh
数据库备份恢复管理 (进阶)
逻辑方式备份数据信息 mysqldump 参数应用
扩展应用参数 –master-data(source-data)
功能作用:
1)可以实现对数据表信息进行锁定
2)会在备份文件中生成备份数据结束的位置点信息 –master-data=1(默认–没有注释显示change master命令信息) –master-data=2(含有注释显示change master命令信息 ???)
CHANGE MASTER TO MASTER_LOG_FILE=’bin-log.000025′, MASTER_LOG_POS=137469;
— 可以区分全量备份数据和增量数据 可以用于从库进行数据同步 ???

扩展应用参数 –single-transaction
功能作用:
1)在进行备份时,会完成一次语句信息提交操作
2)可以实现生成数据快照信息,从而避免利用锁机制保证数据一致性,影响业务

扩展应用参数 –max-allowed-packet=128M
功能作用:
1)影响客户端的DML语句的执行操作
2)影响数据库服务备份恢复操作
PS:在备份时应用此参数信息,可以在恢复数据信息,恢复大表的大量数据信息
若不配置此参数,在早期5.6 5.5 数据库中,恢复大表数据时,会出现以下报错信息
ERROR 1301 (HY000): Result of repeat() was larger than max_allowed_packet (1024) – truncated

Packet for query is too large (20682943>1048576).
You can change this value on the server by setting the max_allowed_packet’ variable.

扩展应用参数 -R -E –triggers
-R 备份数据库存储过程信息 (开发相关) 开发代码 SQL语句 C客户端 – 传参信息 网络(带宽固定)-》 mysql server (脚本信息-注册脚本 登录脚本 — 存储过程)
-E 备份数据库事件信息 (开发相关) 数据库中可以实现自动执行语句的定时任务信息(银行卡利息)
–triggers 备份数据库触发器信息 (开发相关) 当执行增 删 改时,会自动完成一些列操作

mysqldump -uroot -p123456 -A –source-data –single-transaction –max-allowed-packet=64M -R -E –triggers >/backup/all.sql
— 以上mysqldump操作命令备份数据库所有数据,是最全最合理备份方式

物理方式备份数据信息:(进阶-热备)
应用第三方软件:xtrabackup

部署安装/应用 xbk工具:
步骤一:下载xbk软件程序包
MySQL 高于8.0.19版本 应用xbk软件版本 对应数据库版本 (MySQL 8.0.32 )
MySQL 8.0.11~8.0.19 应用xbk软件版本 PXB 8.0
MySQL 低于8.0.11版本 应用xbk软件版本 PXB 2.4

步骤二:安装xbk软件程序包
yum localinstall -y percona-xtrabackup-80-8.0.32-26.1.el7.x86_64.rpm

步骤三:应用xbk软件进行备份恢复(物理备份/恢复)
备份过程–全量备份
mkdir -p /backup/full — 空目录
xtrabackup –defaults-file=/etc/my.cnf –host=10.0.0.51 –port=3306 –user=root –password=123 –backup –target-dir=/backup/full
–defaults-file — 加载需要备份数据的实例信息(datadir 加载数据目录信息)

2025-01-20T06:12:42.807871+08:00 0 [Note] [MY-011825] [Xtrabackup] completed OK!
— 以上备份操作成功,会看到命令执行后最后的输出信息为 OK

或者使用参数–datadir替换掉参数–defaults-file
xtrabackup –datadir=/data/3306/data –host=10.0.0.51 –port=3306 –user=root –password=123 –backup –target-dir=/backup/full
PS:利用xbk工具只能实现本地数据库数据信息备份
备份数据特点:xbk可以将备份开始前的所有磁盘文件数据保存备份,也可以将备份期间增量数据做备份

恢复过程–全量恢复
步骤一:需要停止数据库服务
/etc/init.d/mysql.server stop

步骤二:清理原有数据目录信息
cp -a /data/3306/data/ /backup/data-02
rm -rf /data/3306/data/*
rm -rf /data/3306/log/*

步骤三:恢复数据信息
xtrabackup –prepare –target-dir=/backup/full — 将内存部分数据恢复
[Note] [MY-011825] [Xtrabackup] completed OK!
xtrabackup –copy-back –target-dir=/backup/full — 将磁盘文件部分恢复
[Note] [MY-011825] [Xtrabackup] completed OK!

chown -R mysql.mysql /data/3306/data/
chown -R mysql.mysql /data/3306/log/

步骤四:启动恢复数据库服务/测试数据信息
/etc/init.d/mysql.server start

备份过程–增量备份
步骤一:创建备份数据的目录
mkdir -p /backup/full — 周日做的全量备份数据保存目录
mkdir -p /backup/week-01 — 周一做的增量备份数据保存目录
mkdir -p /backup/week-02 — 周二做的增量备份数据保存目录

步骤二:实现数据备份操作
xtrabackup –defaults-file=/etc/my.cnf –host=10.0.0.51 –user=root –password=123 –port=3306 –backup –parallel=4 –target-dir=/backup/full
— 以上备份操作,表示第一次的全量备份 (参数–parallel=4 在进行备份数据时开启的线程数量 可以提高备份数据效率)

创建周一的增量数据
mysql> create database test10;
Query OK, 1 row affected (0.03 sec)

mysql> use test10;
Database changed
mysql> create table t1 (id int);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t1 values (1),(2),(3);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0

xtrabackup –defaults-file=/etc/my.cnf –host=10.0.0.51 –user=root –password=123 –port=3306 –backup –parallel=4 –target-dir=/backup/week-01 –incremental-basedir=/backup/full
— 以上备份操作,表示第一次的增量备份 (–incremental-basedir 加载识别上一次备份数据目录信息)

创建周二的增量数据
mysql> create database test11;
Query OK, 1 row affected (0.03 sec)

mysql> use test11;
Database changed
mysql> create table t1 (id int);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t1 values (1),(2),(3);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0

xtrabackup –defaults-file=/etc/my.cnf –host=10.0.0.51 –user=root –password=123 –port=3306 –backup –parallel=4 –target-dir=/backup/week-02 –incremental-basedir=/backup/week-01
— 以上备份操作,表示第二次的增量备份 (–incremental-basedir 加载识别上一次备份数据目录信息)

恢复过程–增量恢复
步骤一:需要停止数据库服务
/etc/init.d/mysql.server stop

步骤二:清理原有数据目录信息
cp -a /data/3306/data/ /backup/data-02
rm -rf /data/3306/data/*
rm -rf /data/3306/log/*

步骤三:恢复数据信息
xtrabackup –prepare –apply-log-only –target-dir=/backup/full
— 以上操作,表示加载全量数据目录中信息 (加载全量数据目录中内存数据信息 加载磁盘数据信息)
xtrabackup –prepare –apply-log-only –target-dir=/backup/full –incremental-dir=/backup/week-01
xtrabackup –prepare –apply-log-only –target-dir=/backup/full –incremental-dir=/backup/week-02
— 以上操作,表示加载全量数据目录中信息 (加载增量数据目录中内存数据信息 加载磁盘数据信息)
xtrabackup –prepare –target-dir=/backup/full
— 将全量和增量整合后的内存信息进行恢复

xtrabackup –datadir=/data/3306/data –copy-back –target-dir=/data/backup/full
或者
xtrabackup –copy-back –target-dir=/backup/full
— 将全量和增量整合后的磁盘信息进行恢复

–apply-log-only
— 表示不会在恢复数据过程中,触发回滚操作

chown -R mysql.mysql /data/3306/data/
chown -R mysql.mysql /data/3306/log/

步骤四:启动恢复数据库服务/测试数据信息
/etc/init.d/mysql.server start
“`

# 第七章 主从

## 1 主从复制扩展应用:延时从库

“`sh
#### 主从复制扩展应用:延时从库

**概念介绍说明:**

表示人为主动方式将一个从库进行配置,使从库可以按照指定的时间延时后,再进行和主库完成相应数据信息同步;

**功能作用说明:**

通常对于数据库服务中的数据信息产生损坏,可能有两方面因素造成:

物理损坏:主机故障、磁盘异常、数据文件损坏…,可以利用传统主从复制方式,规避此类问题,利用从库替代主库工作任务;

逻辑损坏:误删除操作(drop truncate delete),可以利用备份数据+binlog日志方式,可以实现数据信息的修复,但是代价比较高;

利用延时从库同步功能,主要是对逻辑原因造成的数据损坏进行弥补修复,从而避免全备数据恢复业务产生的代价较高问题;

当出现逻辑损坏操作时,可以利用延时从库的延时同步特性,将异常操作不做同步,将从库未做破坏的数据信息恢复到主库中;
“`

“`sh
**功能应用实践:**

① 创建新的从库环境

​“`tiki wiki
mysql -S /tmp/mysql3309.sock
mysql> set global server_id=9;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@server_id;
+————-+
| @@server_id |
+————-+
| 9 |
+————-+
1 row in set (0.00 sec)
— 调整从库server_id信息,避免和主库产生冲突

# 可以将主库上的部分数据在从库上先进行同步
mysqldump -uroot -A -S /tmp/mysql3307.sock –master-data=2 –single-transaction >/tmp/full.sql
— 在3307主库上进行数据的全备(模拟企业环境的历史数据全备)
mysql -S /tmp/mysql3309.sock
mysql> source /tmp/full.sql;
— 在3309从库上进行数据的恢复(模拟企业环境的历史数据恢复)
— 将原有主机的数据先备份,然后从库中进行恢复一部分数据,随后再进行数据信息同步追加
— 可以利用同步方式有很多:mysqldump xtrabackup clone_plugin

# 设置从库连接主库信息,定义从库连接主库同步位置点自动复制
mysql> create user repl@’%’ identified by ‘XZnh@95599′;
Query OK, 0 rows affected (0.00 sec)

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

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

mysql> help change master to
— 获取连接主库,以及定义同步位置点的数据库配置模板信息
vim /tmp/full.sql
— CHANGE MASTER TO MASTER_LOG_FILE=’binlog.000004′, MASTER_LOG_POS=156;
— 通过备份文件获取同步位置点信息
mysql> CHANGE MASTER TO
MASTER_HOST=’192.168.137.51′,
MASTER_USER=’repl’,
MASTER_PASSWORD=’XZnh@95599′,
MASTER_PORT=3307,
MASTER_LOG_FILE=’binlog.000004′,
MASTER_LOG_POS=156,
MASTER_CONNECT_RETRY=10;
— 以上配置主从同步信息在从库进行执行;

# 利用相应线程实现主从数据库的数据同步复制
mysql> start slave;
— 在从库上激活数据复制同步功能

# 进行核实主从同步功能是否实现
mysql -S /tmp/mysql3307.sock
mysql> create database xiaoh;
— 在主库模拟创建数据信息
mysql -S /tmp/mysql3307.sock
mysql> show databases;
— 在从库模拟查看数据信息(确认是否同步数据)
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for source to send event
Master_Host: 192.168.30.101
Master_User: repl
Master_Port: 3307
Connect_Retry: 10
Master_Log_File: binlog.000004
Read_Master_Log_Pos: 347
Relay_Log_File: xiaoQ-01-relay-bin.000002
Relay_Log_Pos: 512
Relay_Master_Log_File: binlog.000004
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
— 从库上查看数据同步状态情况,看到上面的两个Yes信息,就表示主从数据同步功能设置成功了
​“`

② 配置延时从库功能

​“`tiki wiki
# 在从库上配置应用延时同步功能
mysql> stop slave;
mysql> change master to master_delay=300;
mysql> start slave;
mysql> show slave status\G
*************************** 1. row ***************************
SQL_Delay: 300
SQL_Remaining_Delay: NULL
— 设置延时时间为300s后同步数据(生产建议延时3~6小时),以及最近事件要做同步的延时剩余时间;
​“`

③ 延时从库应用过程

延时从库的应用思路分析:

延时的根本效果是主库执行操作完成后,会经过指定的时间后,从库在执行主库曾经执行的操作;

基于主从同步原理分析,延时同步效果是在SQL线程上进行控制实现的,并非在IO线程上进行控制实现的;

SQL线程的延时控制机制,主要是需要识别同步操作任务的时间戳信息,根据时间戳和延时时间信息结合,判断相关任务是否同步执行;

> 简述:基于主从同步原理,IO线程同步主库操作事件是持续同步的,只是SQL线程在进行事件信息回放时,进行了延时控制;

企业应用延时从库事件模拟:

| 事件序号 | 操作语句 | 解释说明 |
| ——– | ————— | ———————————————– |
| 01 | 插入语句 insert | 假设在09:59时,持续有插入操作行为,需要进行同步 |
| 02 | 删除语句 drop | 假设在10:00时,产生了删除操作行为,需要避免同步 |

企业异常情况处理过程说明:

1)网站页面需要挂维护页面进行说明;

2)从库服务关闭SQL线程,停止事件任务回放;

3)将从库出现故障前的数据信息,即由于延时配置没有执行的操作回放,到出现故障点的时刻停止回放;

​“`tiki wiki
# 核实当前主从同步是完整状态
mysql> show master status;
+——————+———–+——————-+———————–+————————+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+——————+———–+——————-+———————–+————————+
| binlog.000004 | 347 | | | |
+——————+———–+——————-+———————–+————————+
1 row in set (0.00 sec)
— 核实主库应用日志文件和事件位置点情况;
mysql> show slave status\G
*************************** 1. row ***************************
Master_Log_File: binlog.000004
Read_Master_Log_Pos: 347
Relay_Log_File: xiaoQ-01-relay-bin.000003
Relay_Log_Pos: 277
— 核实从库应用日志文件和事件位置点情况,确认和主库应用日志信息和事件位置点情况一致;

# 延时从库应用效果环境模拟
mysql > create database relaydb;
mysql > use relaydb;
mysql > create table t1 (id int);
mysql > insert into t1 values(1),(2),(3);
mysql > commit;
mysql > insert into t1 values(11),(12),(13);
mysql > commit;
mysql > insert into t1 values(111),(112),(113);
mysql > commit;
mysql > drop database relaydb;
— 以上操作语句在主库上进行执行;
mysql> show master status;
+——————+———–+——————-+———————–+————————+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+——————+———–+——————-+———————–+————————+
| binlog.000004 | 1793 | | | |
+——————+———–+——————-+———————–+————————+
1 row in set (0.00 sec)
— 核实主库应用日志文件和事件位置点情况;
mysql> show slave status\G
*************************** 1. row ***************************
Master_Log_File: binlog.000004
Read_Master_Log_Pos: 1793
Relay_Log_File: xiaoQ-01-relay-bin.000003
Relay_Log_Pos: 277
SQL_Delay: 300
SQL_Remaining_Delay: 91
— 核实从库应用日志文件和事件位置点情况,

# 数据信息修复方式一:手工截取日志信息进行回放数据,恢复业务;
# 操作过程01:停止从库SQL线程回放日志事件
mysql > stop slave sql_thread;
— 停止从库SQL线程,终止持续同步操作,使从库不再回放同步数据;
mysql > show slave status\G
*************************** 1. row ***************************
Master_Log_File: binlog.000004
Read_Master_Log_Pos: 3239
Relay_Log_File: xiaoQ-01-relay-bin.000003
Relay_Log_Pos: 1723
Slave_IO_Running: Yes
Slave_SQL_Running: No
SQL_Delay: 300
SQL_Remaining_Delay: N
— 核实从库SQL线程状态是否为NO,以及获取读取的relay_log日志文件信息

# 操作过程02:根据relaylog起点信息以及异常操作位置点信息,截取日志内容信息
起点信息:
Relay_Log_File: xiaoQ-01-relay-bin.000003
Relay_Log_Pos: 1723

mysql> show relaylog events in ‘xiaoQ-01-relay-bin.000003’;
.. 省略部分…
| xiaoQ-01-relay-bin.000003 | 3056 | Query | 1 | 3239 | drop database relaydb /* xid=745 */
— 获取终点信息 3056
cd /data/3309/data/
[root@xiaoQ-01 data]# mysqlbinlog –start-position=1723 –stop-position=3056 xiaoQ-01-relay-bin.000003 >/tmp/relay.sql
— 在从库服务器上完成日志信息的截取操作

# 操作过程03:从库中恢复截取日志数据
mysql> set sql_log_bin=0;
mysql> source /tmp/relay.sql;
mysql> show databases;
+——————–+
| Database |
+——————–+
| relaydb |
+——————–+
— 核实数据库以及数据表信息已恢复,并且原有主从关系已经彻底奔溃,需要进行主从关系重构
mysql> stop slave;
mysql> reset slave all;
— 从库身份解除

# 数据信息修复方式二:持续延时从库数据回放同步过程,但同步过程停止在异常操作前;
# 操作过程01:停止从库SQL线程回放日志事件
mysql > stop slave sql_thread;
— 停止从库SQL线程,终止持续同步操作,使从库不再回放同步数据;
mysql > show slave status\G
*************************** 1. row ***************************
Master_Log_File: binlog.000004
Read_Master_Log_Pos: 4685
Relay_Log_File: xiaoQ-01-relay-bin.000003
Relay_Log_Pos: 3169
Slave_IO_Running: Yes
Slave_SQL_Running: No
SQL_Delay: 300
SQL_Remaining_Delay: NULL
— 核实从库SQL线程状态是否为NO,以及获取读取的relay_log日志文件信息

# 操作过程02:回放日志事件在异常操作位置点前
mysql> show relaylog events in ‘xiaoQ-01-relay-bin.000003’;
+———————————-+——+——————-+———–+—————–+——————————————+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+———————————-+——+——————-+———–+—————–+——————————————+
…忽略部分..
| xiaoQ-01-relay-bin.000003 | 4394 | Xid | 1 | 4495 | COMMIT /* xid=757 */ |
| xiaoQ-01-relay-bin.000003 | 4425 | Anonymous_Gtid | 1 | 4572 | SET @@SESSION.GTID_NEXT= ‘ANONYMOUS’ |
| xiaoQ-01-relay-bin.000003 | 4502 | Query | 1 | 4685 | drop database relaydb /* xid=759 */ |
+———————————-+——+——————-+———–+—————–+——————————————+
— 获取异常操作日志文件信息和事件位置点信息,其中位置点信息以Pos列显示的为准,并且是提前一个事务位置点;
mysql > change master to master_delay=0;
— 在从库重启进行日志回放操作前,关闭从库延迟回放的功能
mysql > start slave until relay_log_file=”log_name”, relay_log_pos=log_pos;
mysql > start slave until relay_log_file=’xiaoQ-01-relay-bin.000003′, relay_log_pos=4425;
— 启动日志信息回放功能,直到指定位置点结束日志信息回放
mysql > start slave until sql_before_gtids=”xxxx3:4″;
— 如果开启了GTID功能,也可以按照GTID位置点进行数据信息回放(参考)
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_Running: Yes
Slave_SQL_Running: No
Until_Log_File: xiaoQ-01-relay-bin.000003
Until_Log_Pos: 4425
— 从库重新回放操作恢复数据后,从库状态信息中SQL还是为NO,是正常的,因为直到指定位置点就终止回放;

# 操作过程03:核实异常数据信息是否恢复
mysql > show databases;
+——————–+
| Database |
+——————–+
| relaydb |
+——————–+
— 核实数据库以及数据表信息已恢复,并且原有主从关系已经彻底奔溃,需要进行主从关系重构
mysql> stop slave;
mysql> reset slave all;
— 从库身份解除
— 参考官方资料:https://dev.mysql.com/doc/refman/8.0/en/start-replica.html
​“`

####
“`

## 2 主从复制扩展应用:过滤复制

“`sh
主库上有多个数据库业务,希望将不同的数据库业务同步到不同的从库上,实现数据库业务分离;

为了满足以上需求,就可以利用过滤复制功能,将指定的数据信息复制到指定从库上,而不是全备方式同步数据;
“`

![image-20250212170016745](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20250212170016745.png)

“`sh
**实现工作机制:**

– 解决方案一:在主库上进行限制

在主库上进行复制同步数据时,主库上存在A、B、C三个数据库信息,若只想复制其中A数据库的数据信息;

可以让数据库服务只记录A数据库的事件日志信息,对于B和C数据库信息进行不写入日志操作;

但是利用这种方法实现主从信息同步的过滤,可能会导致B和C库数据一旦损坏,由于没有记录日志,无法进行恢复的情况;

– 解决方案二:在从库上进行限制

在从库上进行复制同步数据时,利用从库上的SQL线程进行控制,只回放同步过来的A库数据信息,屏蔽其他数据库的信息不做回放;

利用从库进行同步数据过滤,不能减轻主库同步数据的压力,但可以减轻从库进行数据回放的压力;

**功能应用实践:**

① 查看主从限制过滤参数信息

​“`tiki wiki
# 查看主库复制过滤限制参数信息(了解即可)
mysql> show master status;
+——————+———–+——————-+———————–+————————-+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+——————+———–+——————-+———————–+————————-+
| binlog.000004 | 4685 | | | |
+——————+———–+——————-+———————–+————————-+
1 row in set (0.00 sec)
— 主库状态信息中,Binlog_Do_DB表示同步复制白名单过滤设置,Binlog_Ignore_DB表示同步复制黑名单过滤设置
— 过滤白名单表示会记录事件日志信息,过滤黑明白表示不会记录事件日志信息,一般选择其一进行应用即可
— 可以应用主库的过滤同步功能,实现数据库中默认库的同步复制限制;

# 查看从库复制过滤限制参数信息
mysql> show slave status\G
*************************** 1. row ***************************
Replicate_Do_DB: xiaoQ
Replicate_Ignore_DB: xiaoA
— 表示库级别的过滤操作,白名单设置表示回放库级别操作,黑名单设置表示忽略库级别操作

Replicate_Do_Table: xiaoQ.t1
Replicate_Ignore_Table: xiaoA.t1
— 表示表级别的过滤操作,白名单设置表示回放表级别操作,黑名单设置表示忽略表级别操作

Replicate_Wild_Do_Table: xiaoQ.t%
Replicate_Wild_Ignore_Table: xiaoA.t%
— 表示模糊级别的过滤操作,主要是可以针对多表信息,配置白名单或黑名单;
— 以上在从库上线实现数据同步过滤机制的参数信息有6个,主要可以分为3组,一般应用使用一个参数即可;
​“`

② 数据同步复制过滤效果配置

​“`tiki wiki
# 编写配置文件实现过滤
vim /data/3309/my.cnf
replicate_do_db=ppt
replicate_do_db=word

# 在线调整参数实现过滤
mysql> help change replication filter
mysql> stop slave sql_thread;
mysql> CHANGE REPLICATION FILTER REPLICATE_DO_DB = (word, ppt);
mysql> start slave sql_thread;
— 一般编写配置文件和在线配置都会进行,可以不重启数据库服务生效过滤机制,日后重启数据库后过滤机制依然生效;

# 查看获取从库过滤配置
mysql> show slave status\G
Replicate_Do_DB: word,ppt
Replicate_Ignore_DB:
​“`

③ 进行同步复制过滤效果测试

​“`tiki wiki
# 在主库上进行数据库创建模拟
mysql> create database word;
mysql> show slave status\G
*************************** 1. row ***************************
Master_Log_File: binlog.000004
Read_Master_Log_Pos: 4870
— 从库日志信息同步查看
mysql> show databases;
+——————–+
| Database |
+——————–+
| word |
+——————–+
— 查看从库数据同步情况

mysql> create database ppt;
mysql> show slave status\G
*************************** 1. row ***************************
Master_Log_File: binlog.000004
Read_Master_Log_Pos: 5052
— 从库日志信息同步查看
mysql> show databases;
+——————–+
| Database |
+——————–+
| ppt |
+——————–+
— 查看从库数据同步情况

mysql> create database xiaoA;
mysql> show slave status\G
*************************** 1. row ***************************
Master_Log_File: binlog.000004
Read_Master_Log_Pos: 5240
— 从库日志信息同步查看
mysql> show databases;
+——————–+
| Database |
+——————–+
| |
+——————–+
— 查看从库数据同步情况,并未实现xiaoA数据库的复制,即实现了数据同步过滤效果;
​“`

####
“`

## 3 半同步复制

“`sh
在MySQL5.5版本之前,数据库的复制是异步操作,主库和从库的数据之间存在一定的延迟,这样就存在数据存储不一致的隐患;

假设当主库上写入一个事务并提交成功, 而从库尚未得到主库推送的binlog日志时,主库宕机了;

例如主库可能因磁盘损坏、内存故障等造成主库上该事务binlog丢失,此时从库就可能损失这个事务,从而造成主从不一致;

为了解决这个问题,数据库服务引入了半同步复制机制。

当采用异步方式同步数据,由于从库异常宕机情况出现,造成主从数据不一致情况出现,还会有以下影响情况:

– 会造成从库可能创建语句没有执行,后续的插入语句也必然失败,形成SQL线程运行故障;

– 由于主从数据信息不一致,在架构设计上在读取从库数据信息时,就会读取数据信息异常;

> 说明:利用半同步复制机制,主要是用于解决主从数据复制不一致的问题,即解决主从数据一致性问题,也可以避免SQL线程故障;
半同步复制技术与传统主从复制技术不同之处:

– 在主库提交操作时候会受到阻塞,等待从库IO线程返回ack确认信号后,才能使主库提交操作成功;

– 从库IO线程接收到binlog日志信息,当日志信息写入到磁盘上的relaylog文件时,会给主库返回ack信号;

在主库上会利用ack_receiver线程接收返回的ack信号;

– 当主库上的ack_receiver线程接收到ack信号信息时,会产生事件触发机制,告诉主库事务提交操作成功了;

– 如果在接收ack信号时,等待信号时间超过了预设值的超时时间,半同步复制会切换为原始的异步复制方式;

预设的等待超时时间的数值,由参数rpl_semi_sync_master_timeout设置的毫秒数决定
“`

![1689180659727](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/1689180659727.png)

“`sh
**功能应用实践:**

① 主从数据库安装半同步功能插件:

​“`tiki wiki
# 进行主从同步重构
mysqldump -uroot -A -S /tmp/mysql3307.sock –master-data=2 –single-transaction >/tmp/full.sql
grep “\– CHANGE” /tmp/full.sql
— CHANGE MASTER TO MASTER_LOG_FILE=’binlog.000004′, MASTER_LOG_POS=5240;
— 在主库进行数据备份,并获取备份位置点信息
mysql> stop slave;
mysql> reset slave all;
mysql> CHANGE MASTER TO
MASTER_HOST=’192.168.30.101′,
MASTER_USER=’repl’,
MASTER_PASSWORD=’123456′,
MASTER_PORT=3307,
MASTER_LOG_FILE=’binlog.000004′,
MASTER_LOG_POS=5240,
MASTER_CONNECT_RETRY=10;
mysql> start slave;
— 实现从库数据库同步功能重构

# 主库安装半同步插件(3307)
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME ‘semisync_master.so’;
或者
[mysqld]
plugin_dir=/usr/local/mysql/lib/plugin
plugin-load=rpl_semi_sync_master=semisync_master.so
— 主库利用插件控制ack_receiver线程接收ack确认信息,并且会控制commit阻塞,实现半同步复制功能
mysql> show plugins;
+———————————+———-+——————–+————————–+———-+
| Name | Status | Type | Library | License |
+———————————+———-+——————–+————————–+———-+
| rpl_semi_sync_master | ACTIVE | REPLICATION | semisync_master.so | GPL |
+———————————+———-+——————–+————————–+———-+
— 查看插件是否进行加载

# 从库安装半同步插件(3309)
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME ‘semisync_slave.so’;
或者
[mysqld]
plugin_dir=/usr/local/mysql/lib/plugin
plugin-load=rpl_semi_sync_slave=semisync_slave.so
— 从库利用插件控制IO线程发送ack确认信息;
mysql> show plugins;
+———————————+———-+——————–+————————–+———-+
| Name | Status | Type | Library | License |
+———————————+———-+——————–+————————–+———-+
| rpl_semi_sync_slave | ACTIVE | REPLICATION | semisync_slave.so | GPL |
+———————————+———-+——————–+————————–+———-+
— 查看插件是否进行加载
​“`

> 说明:一般在高可用数据库架构环境中,可以在高可用的两台主机上均安装好主库插件和从库插件;

② 主从数据库启动半同步插件功能:

​“`tiki wiki
# 主库启动半同步功能
mysql> set global rpl_semi_sync_master_enabled =1;

# 从库启动半同步功能
mysql> set global rpl_semi_sync_slave_enabled =1;

# 重启从库上的IO线程
mysql> stop slave IO_THREAD;
mysql> start slave IO_THREAD;

# 核实确认半同步功能状态:
mysql> show status like ‘rpl_semi_sync_master_status’;
+————————————–+——-+
| Variable_name | Value |
+————————————–+——-+
| Rpl_semi_sync_master_status | ON |
+————————————–+——-+
1 row in set (0.01 sec)
— 核实主库半同步功能是否激活

mysql> show status like ‘rpl_semi_sync_slave_status’;
+————————————–+——-+
| Variable_name | Value |
+————————————–+——-+
| Rpl_semi_sync_slave_status | ON |
+————————————–+——-+
1 row in set (0.00 sec)
— 核实从库半同步功能是否激活
​“`

③ 主从数据库半同步功能永久配置:

​“`tiki wiki
# 在数据库配置文件中编写以下参数
rpl_semi_sync_master_enabled=on
— 主库半同步功能启停设置,on为激活设置
rpl_semi_sync_master_timeout=1000
— 主库接收从库确认信息的超时时间设置(单位毫秒)
rpl_semi_sync_master_trace_level=32
— 用于开启半同步复制模式时的调试级别,默认是32
rpl_semi_sync_master_wait_for_slave_count=1
— 用于控制在主库需要从库确认的数量,默认为1
rpl_semi_sync_master_wait_no_slave=on
— 是否允许每个事物的提交都要等待slave的信号
— on为每一个事物都等待,除非超过等待时间
— off表示master发现Rpl_semi_sync_master_clients小于rpl_semi_sync_master_wait_for_slave_count,则master立即转为异步模式
rpl_semi_sync_master_wait_point=after_sync
— 用来控制半同步模式下主库在返回给session事物成功之前的事务提交方式
— after_sync (新版半同步复制同步机制)
— 主服务器将每个事务写入其二进制日志和从服务器,并将二进制日志同步到磁盘。同步后,主设备等待从设备确认事务接收。在收到确认后,主服务器将事务提交给存储引擎,并将结果返回给客户端,然后客户端可以继续。
— after_commit (旧版半同步复制同步机制)
— 主服务器将每个事务写入其二进制日志和从服务器,同步二进制日志,并将事务提交给存储引擎。主提交后等待从服务器确认事务接收。在收到确认后,主将结果返回给客户端,然后客户端可以继续。
binlog_group_commit_sync_delay=1
— 等待多少微秒后才进行fsync
binlog_group_commit_sync_no_delay_count=1000
— 达到等待的事务数量后调用fsync操作
— 实现事务组提交方式,将多个事务合并成组推送到从库上,避免dump线程采用串型方式提交事务,造成主从同步延时;
https://blog.csdn.net/yaoxie1534/article/details/126259320

rpl_semi_sync_slave_enabled=on
— 从库半同步功能启停设置,on为激活设置
rpl_semi_sync_slave_trace_level=32
​“`

当半同步功能配置完毕后,需要重启主从数据库服务,重启后slave会在master上注册为半同步复制的slave角色;

这时候在master数据库的错误日志中会看到以下日志信息:

​“`
2017-04-19 11:09:26 28300 [Note] Semi-sync replication switched ON with slave (server_id: 28703307) at (mysql-bin.000002, 510)
2017-04-19 11:09:26 28300 [Note] Start semi-sync binlog_dump to slave (server_id: 28703307), pos(mysql-bin.000002, 510)
2017-04-19 11:09:26 28300 [Note] Stop asynchronous binlog_dump to slave (server_id: 28703307)
​“`

半同步复制配置参数参考链接:

https://www.cnblogs.com/konggg/p/12205505.html

半同步复制和增强半同步复制配置区别:

半同步复制主从配置:

​“`tiki wiki
# 数据库配置信息:
plugin_load = “rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so”
rpl_semi_sync_master_enabled=1
rpl_semi_sync_slave_enabled=1
rpl_semi_sync_master_timeout=1000
rpl_semi_sync_master_wait_for_slave_count=1

STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;

# 查看半同步是否在运行:
# 主库执行:
mysql> show status like ‘Rpl_semi_sync_master_status’;
# 从库执行:
mysql> show status like ‘Rpl_semi_sync_slave_status’;
— 这两个变量常用来监控主从是否运行在半同步复制模式下。

# 主库执行
mysql> show status like ‘%semi%’;
Rpl_semi_sync_master_status
— 指示主服务器使用的是异步复制模式,还是半同步复制模式。
Rpl_semi_sync_master_clients:
— 显示有多少个从服务器配置成了半同步复制模式。
Rpl_semi_sync_master_yes_tx:
— 显示从服务器确认的成功提交数量。
Rpl_semi_sync_master_no_tx:
— 显示从服务器确认的不成功提交数量。
​“`

增强半同步复制主从配置:(在MySQL5.7.2版本之后,官方推出了增强型半同步复制技术;)

​“`tiki wiki
# 数据库配置信息:
plugin_load = “rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so”
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_slave_enabled = 1
rpl_semi_sync_master_timeout = 5000
–超过5s不返回信息就降为半同步复制
rpl_semi_sync_master_wait_point = AFTER_SYNC
rpl_semi_sync_master_wait_for_slave_count = 1
​“`

参考链接资料:https://blog.csdn.net/HD243608836/article/details/118220422

配置参数信息补充说明:rpl_semi_sync_master_wait_point

当参数信息配置为:AFTER_COMMIT(5.6默认值)

master将每个事务写入binlog ,传递到slave 刷新到磁盘(relay log),同时主库提交事务;

master等待slave 反馈收到relay log,只有收到ACK后master才将commit OK结果反馈给客户端。

即主库commit时,先保证了binlog日志写入了从库中继日志后主库才提交binlog落盘OK给客户端;

当参数信息配置为:AFTER_SYNC(5.6默认值)

master 将每个事务写入binlog , 传递到slave 刷新到磁盘(relay log)。

master等待slave 反馈接收到relay log的ack之后,再提交事务并且返回commit OK结果给客户端。

即使主库crash,所有在主库上已经提交的事务都能保证已经同步到slave的relay log中。

此方法中存储引擎可以批量提交,降低了对主库性能的影响.
“`

## 4 主从同步的三种机制

“`sh
异步复制:(Asynchronous replication)

MySQL主从同步数据时,默认使用的复制数据机制就是异步的;

简单理解:主库在执行完客户端提交的事务后会立即将结果返给客户端,并不关心从库是否已经接收并处理;

同步痛点:若主库宕机了,此时主上已经提交的事务可能并没有传到从上,此时若从被提升为主,可能导致新主上的数据不完整;

全同步复制(Fully synchronous replication)

指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端;

写主库的时候,同步写从库,如果有多个从库,那么需要多个从库都成功,主库才能写成功;

因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会受到严重的影响。

一看就知道慢,但能达到实时同步,强一致性

面试问题:异步复制 半同步复制 增强型半同步复制区别;

异步复制:

客户端发起数据更新操作请求,主库执行更新操作完成后立即向客户端发起响应,然后再向从库发起数据同步;

主库执行更新操作不需要等待从库的响应,因此主库对于客户端的响应较快,但是数据同步到从库并不是实时同步的;

所以主从延迟情况下,主库发生故障可能会导致主从数据不一致;

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/数据库同步方式图示-异步方式.png)

半同步复制:(Semisynchronous replication)

客户端发起数据更新操作请求,主库执行更新操作完成后立即向从库复制数据,从库接受到数据写入relay log后;

从库会向主库返回ack确认,然后主库收到从库的ack确认后向客户端发起响应;

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/数据库同步方式图示-半同步复制方式.png)

半同步复制关键配置项:

​“`mysql
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_wait_for_slave_count=1
rpl_semi_sync_master_wait_point=after_commit
​“`

半同步复制缺陷:

– 出现幻读

当用户发起一个事务,该事务已经写入redo日志和binlog日志,但该事务还没写入从库,此时处在waiting slave dump处;

此时另一个用户可以读取到这条数据,而当前事务会话本身却不能;

– 数据丢失

一个事务在waiting slave dump处崩溃后或者超过等待时间后,主库将比从库多一条数据,造成主从数据不一致;

> 说明:复制发生异常时,复制方式会降为异步复制,复制恢复时会自动切换为半同步;

增强型半同步:(Enhanced Semisynchronous replication)

客户端发起数据更新操作请求,主库执行更新操作完成后,立即向从库复制数据,从库接收到数据并执行完成后;

会向主库返回ack确认,主库必须接收到从库的ack确认后,才向客户端进行响应;

从而实现了主从复制的实时性,所以在很大程度上保证了主从数据的一致性,但主库的每次操作依赖于从库的操作成功写入;

可能会影响主库的正常操作,所以一般是一主两从,避免一台故障影响主库的正常操作;

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/数据库同步方式图示-增强型半同步复制方式.png)

半同步复制关键配置项:

​“`mysql
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_wait_no_slave=1
rpl_semi_sync_master_wait_point=after_sync
​“`

增强半同步复制优势:

– 避免幻读问题

当用户发起一个事务,该事务写入二进制后,便向从库进行同步,此时其他并行会话用户无法读取到该数据,解决了幻读;

– 避免数据丢失

一个事务在waiting slave dump处崩溃掉后,可以通过观察从库上是否存在主库的last gtid值,

如果存在,这条数据正常恢复,如果不存在则删除主库的那条多余的GTID值,然后恢复,保证了数据的完整性;

增强半同步应用痛点:

当启用增强型半同步复制时,主库会等待至少一个备库确认已经接收到事务的复制数据后,才认为事务提交成功;

如果启用了增强半同步且延迟过大,可能会导致以下问题:

– **写入延迟增加:**

由于主数据库必须等待备库的确认,如果延迟过大,主数据库的写入操作可能会受到阻塞,从而增加写入延迟;

– **主库性能下降:**

如果增强半同步延迟过大,主库在等待备库确认时,可能无法及时释放资源,导致主库的性能下降;

– **可用性降低:**

如果主库发生故障或不可用,而延迟过大的备库无法及时成为新的主库,可能导致整个系统的可用性下降;

– **数据不一致:**

增强半同步的目的是提供更高的数据一致性,但是如果延迟过大,可能导致备库无法及时复制主库的所有事务;

从而导致主从之间数据不一致的情况;

因此,延迟过大可能会影响到系统的性能、可用性和数据一致性。为了避免延迟过大的问题,建议进行以下操作:

– [ ] 优化网络连接带宽:确保主库与备库之间的网络连接畅通,并具备足够的带宽来传输复制数据。
– [ ] 优化备份库的性能:确保备库具备足够的处理能力和资源,以便及时接收和处理主库发送的复制数据。
– [ ] 监控优化调整配置:定期监控复制延迟,并根据情况调整配置,如调整主库和备库的硬件配置、调整复制线程数等。

通过合理的优化和配置,可以减小增强半同步的延迟,并提高系统的性能和可靠性。

> 说明:
>
> 如果增强半同步的延迟变得非常大,超过了设定的阈值,MySQL 的增强半同步机制将会切换为异步模式。
>
> 在异步模式下,主数据库不再等待备库的确认,而是立即提交事务,从而降低了延迟,但也带来了一定的数据风险。
“`

## 5 GTID复制

“`sh
**概念介绍说明:**

GTID(global transaction id)是对于一个已提交事务的唯一编号,并且是一个全局唯一编号(主从复制过程);

是数据库5.6版本开始的一个功能新特性,主要是用于解决主从复制的一致性问题;

**复制原理机制:**

– master节点在更新数据的时候,会在事务前产生GTID信息,一同记录到binlog日志中;
– slave节点的io线程将主库推送的binlog写入到本地relay log中;
– 然后SQL线程从relay log中读取GTID,设置gtid_next的值为该gtid,然后对比slave端的binlog是否有记录;
– 如果有记录的话,说明该GTID的事务已经运行,slave会忽略;
– 如果没有记录的话,slave就会执行该GTID对应的事务,并记录到binlog中。
“`

![1670602175403](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/1670602175403.png)

**功能应用实践:**

① 主从复制GTID功能实现环境:

为了实现GTID机制的主从复制,需要准备好主从架构环境:

| 主机角色 | 主机名称 | 地址信息 |
| ———- | ——– | ————– |
| 主库服务器 | db-01 | 192.168.10.101 |
| 从库服务器 | db-02 | 192.168.10.102 |
| 从库服务器 | db-03 | 192.168.10.103 |

对原有数据库服务环境清理:

“`tiki wiki
# 在所有主从节点均进行清理操作:
pkill mysqld
rm -rf /data/3306/*
rm -rf /data/binlog/*
mv /etc/my.cnf /tmp
mkdir -p /data/3306/data /data/binlog
chown -R mysql.mysql /data/*
— 所有数据库主从节点均进行以上清理操作;
“`

② 主从复制GTID功能配置编写

“`tiki wiki
# 配置参数信息
gtid-mode=on
— 启用gtid复制方式,默认采用传统的复制方式
enforce-gtid-consistency=true
— 开启gtid所有节点的强制一致性,主要会限制 CREATE TABLE … SELECT 类似语句的执行;
log-slave-updates=1
— 定义slave更新是否记入二进制日志,从而增强数据一致性,是在高可用架构中重要配置环节

# 主库db01配置文件编写
cat >/etc/my.cnf <
EOF

# 主库db02配置文件编写
cat >/etc/my.cnf <
EOF

# 主库db03配置文件编写
cat >/etc/my.cnf <
EOF

# 进行数据库所有节点初始化操作
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data

# 启动数据库所有节点服务
/etc/init.d/mysqld start
/etc/init.d/mysqld start
/etc/init.d/mysqld start
“`

③ 主从复制GTID配置重构主从

“`tiki wiki
# 重构主从关系-主库操作
create user repl@’192.168.30.%’ identified with mysql_native_password by ‘123456’;
Query OK, 0 rows affected (0.01 sec)
grant replication slave on *.* to repl@’192.168.30.%’;
Query OK, 0 rows affected (0.00 sec)
— 主库上创建主从复制用户信息

# 重构主从关系-从库操作
change master to
master_host=’192.168.30.101′,
master_user=’repl’,
master_password=’123456′,
master_auto_position=1;
— 表示让从库自己找寻复制同步数据的起点;
— 在第一次启动gtid功能时,会读取从库中的binlog日志信息,根据主库uuid信息,获取从库中执行过的主库gtid信息
— 从从库中没有执行过的主库gtid信息之后进行进行数据同步操作
start slave;
— 其他从库一并操作
“`

**知识扩展:实现自动获取同步位置点**

主从同步获取主库的gtid信息,获取同步位置点,并且不断更新位置点:

“`tiki wiki
show master status;
+———————–+———–+——————-+———————–+——————————————————-+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+———————–+———–+——————-+———————–+——————————————————-+
| mysql-bin.000002 | 681 | | | 3cfa5898-771a-11ed-b8d7-000c2996c4f5:1-2 |
+———————–+———–+——————-+———————–+——————————————————-+
1 row in set (0.00 sec)
— 主库查看状态信息,获取gtid同步信息,gtid信息将会存储的位置:binlog、relaylog、master-info(uuid)

show master status;
+———————–+———–+——————-+———————–+——————————————————-+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+———————–+———–+——————-+———————–+——————————————————-+
| mysql-bin.000002 | 695 | | | 3cfa5898-771a-11ed-b8d7-000c2996c4f5:1-2 |
+———————–+———–+——————-+———————–+——————————————————-+
1 row in set (0.00 sec)
show master status;
+———————–+———–+——————-+———————–+——————————————————-+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+———————–+———–+——————-+———————–+——————————————————-+
| mysql-bin.000002 | 695 | | | 3cfa5898-771a-11ed-b8d7-000c2996c4f5:1-2 |
+———————–+———–+——————-+———————–+——————————————————-+
1 row in set (0.00 sec)
— 从库查看状态信息,获取gtid同步信息;gtid信息将会存储的位置:binlog、relaylog、master-info(uuid)
— show binlog events in ‘mysql-bin.000002’ 获取从库自己的binlog信息,得到gitd同步的位置点;
“`

**知识扩展:进行全备恢复数据时不要加 set-gtid-purged参数**

如果是已经运行很久的数据库,需要构建主从,都是需要备份恢复主库数据后,再开启实现主从功能的;

在mysqldump进行备份数据时,不要加set-gtid-purged参数,否则会造成从库依旧从第一个gtid信息开始同步数据;

造成主从同步数据信息冲突,影响主从构建过程,导致主从同步过程失败;

“`tiki wiki
# 未加set-gtid-purged参数实现的数据备份效果
mysqldump -A –master-data=2 –single-transaction >/tmp/full.sql
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database. If you don’t want to restore GTIDs, pass –set-gtid-purged=OFF. To make a complete dump, pass –all-databases –triggers –routines –events.

# 备份警告信息解释说明
A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions,
— 默认情况下,来自具有GTID的服务器的部分备份数据信息将包括所有事务的GTID,
even those that changed suppressed parts of the database.
— 即使是那些更改了的部分也会抑制数据库。
If you don’t want to restore GTIDs, pass –set-gtid-purged=OFF.
— 如果您不想还原GTID,请传递–set-gtid-purged=OFF。
To make a complete dump, pass –all-databases –triggers –routines –events.
— 要进行完整的转储,请传递–all-database–trigger–例程–events。

vim /tmp/full.sql
SET @@GLOBAL.GTID_PURGED=/*!80000 ‘+’*/ ‘3cfa5898-771a-11ed-b8d7-000c2996c4f5:1-2’;
— 表示让从库删除1-2的集合信息,即通过备份文件已经恢复了1-2的数据,可以从1-2之后进行数据信息同步;

# 已加set-gtid-purged参数实现的数据备份效果
mysqldump -A –master-data=2 –single-transaction –set-gtid-purged=OFF >/tmp/full02.sql
vim /tmp/full.sql
SET @@GLOBAL.GTID_PURGED=/*!80000 ‘+’*/ ‘3cfa5898-771a-11ed-b8d7-000c2996c4f5:1-2’;
SET SQL_LOG_BIN=0;
— 以上信息不会出现在备份文件中
— 表示会让从库把备份文件中的操作语句,再次根据gtid请求执行一遍,容易产生异常冲突问题;
“`

`–set-gtid-purged=on/auto` 此为默认参数,即使在备份命令中不写,依旧生效;

它的效果是在mysqldump输出的备份文件中生成 `SET@@GLOBAL.GTID_PURGED`语句;备份文件中的这条语句记录了GTID号。

– **参数应用场景-主从复制:**

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/3f3d36bb5e1a45bba03d2ff6fcd6abcf.png)

当需要构建主从的时候,主库上有许多数据需要先备份出来并恢复到从库上,以此来保持两个库没有差异,然后再去配置主从。

这种场景下一定要用on;想要基于GTID实现主从复制,从库是基于MASTER_AUTO_POSITION=1自动获取并应用GTID的。

因此如果再主库导出的备份文件中没有GTID,那么从库无法自动获取并应用GTID。

设为`–set-gtid-purged=OFF`时,在`mysqldump`输出中不包含`SET@@GLOBAL.GTID_PURGED`语句;

**参数应用场景-普通备份(避免幂等性):**

在GTID开启的情况下,MySQL通过binlog恢复时,若有匹配到重复,相同的GTID日志记录,则会直接跳过;

所以,在截取binlog时,需要忽略原日志中的GTID,如果不忽略,则在恢复时就会因为GTID相同而跳过,导致恢复失败;

在执行恢复前,同样需要执行sql_log_bin=0,因为原binlog文件中已有记录,无需再次记录。

## 6 克隆复制

“`sh
**概念介绍说明:**

利用clone plugin方式可以实现数据迁移备份恢复操作,同样也可以利用克隆技术实现主从数据同步操作,即完成快速构建从库;

主要应用于运行一段时间的数据库,需要进行主从架构环境的构建时,可以实现主库数据信息的快速迁移;

利用克隆复制备份恢复迁移数据信息,可以使备份恢复数据的效率提升;

**功能应用实践:**

① 主从复制克隆功能实现环境:

为了实现克隆机制的主从复制,需要准备好主从架构环境:

| 主机角色 | 主机名称 | 地址信息 |
| ———- | ——– | ————– |
| 主库服务器 | db-01 | 192.168.10.101 |
| 从库服务器 | db-03 | 192.168.10.103 |

对原有数据库服务环境清理:

​“`tiki wiki
# 清理原有从库配置应用(db03)
stop slave;
reset slave all;

# 准备数据库空白的节点
pkill mysqld
rm -rf /data/3306/*
rm -rf /data/binlog/*
mv /etc/my.cnf /tmp
mkdir -p /data/3306/data /data/binlog
chown -R mysql.mysql /data/*
— 在新的数据库节点进行以上清理操作;

cat >/etc/my.cnf <
EOF
— 从库db03配置文件编写

mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data
— 进行数据库所有节点初始化操作

/etc/init.d/mysqld start
— 启动数据库相应节点服务
​“`

② 主从复制克隆环境功能配置:

​“`tiki wiki
# 实现免交互方式安装插件和创建用户(主库操作)
mysql -e “INSTALL PLUGIN clone SONAME ‘mysql_clone.so’;create user test@’%’ identified by ‘123456’;grant backup_admin on *.* to ‘test’@’%’;”

# 实现免交互方式安装插件和创建用户(从库操作)
mysql -e “INSTALL PLUGIN clone SONAME ‘mysql_clone.so’;create user test@’%’ identified by ‘123456’;grant clone_admin on *.* to ‘test’@’%’;set global clone_valid_donor_list=’192.168.30.101:3306′;”
​“`

③ 主从复制克隆功能应用启动:

​“`tiki wiki
# 从库上启用克隆功能
mysql -utest -p123456 -h192.168.30.103 -P3306 -e “clone instance from test@’192.168.30.101′:3306 identified by ‘123456’;”

# 实现克隆状态情况监控(可以实现每秒监控)
mysql -e “select stage,state,end_time from performance_schema.clone_progress;”
+———–+———–+—————————-+
| stage | state | end_time |
+———–+———–+—————————-+
| DROP DATA | Completed | 2022-12-11 23:58:04.373236 |
| FILE COPY | Completed | 2022-12-11 23:58:05.772489 |
| PAGE COPY | Completed | 2022-12-11 23:58:05.781343 |
| REDO COPY | Completed | 2022-12-11 23:58:05.784746 |
| FILE SYNC | Completed | 2022-12-11 23:58:06.123775 |
| RESTART | Completed | 2022-12-11 23:58:07.796073 |
| RECOVERY | Completed | 2022-12-11 23:58:08.819861 |
+———–+———–+—————————-+
— 可以实现每秒关注监控输出的结果信息,最后看到RECOVERY,并且状态为Completed,表示克隆完毕
​“`

④ 主从复制克隆完毕实现主从:

​“`tiki wiki
# 主从方式构建一:利用position
mysql -e “select binlog_file,binlog_position from performance_schema.clone_status;”
+——————+—————–+
| binlog_file | binlog_position |
+——————+—————–+
| mysql-bin.000002 | 1210 |
+——————+—————–+

# 主从方式构建二:利用gtid
mysql -e “change master to master_host=’192.168.30.101′,master_user=’repl’,master_password=’123456′,master_auto_position=1;start slave”

# 核实展示最后主从状态结果
mysql -e “show slave status\G”|grep “Running:”
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
​“`

> 说明:利用clone功能实现主从,可以利用脚本自动化完成,并且可以实现主从的自愈能力,为实现主从功能上云提供方便;
“`

## 7 数据组复制(MGR)

“`sh
在MGR出现之前,用户常见的MySQL高可用方式,无论怎么变化架构,本质就是Master-Slave架构。

MySQL 5.7.17版本开始支持无损半同步复制(lossless semi-syncreplication),从而进一步提升数据复制的强一致性。

MySQL Group Replication(MGR)是MySQL官方在5.7.17版本引进的一个数据库高可用与高扩展的解决方案,以插件形式提供。

MGR基于分布式paxos协议,实现组复制,保证数据一致性。

MGR采用多副本,在2N+1个节点集群中,集群只要N+1个节点还存活着,数据库就能稳定的对外提供服务;

`数据库组复制功能,可以理解为是数据库主从关系的高可用环境,一般需要三个数据库实例,构成一个具有高可用、高一致性的复制环境`

主要涉及的功能应用包含:

– 具有多节点之间互相通过投票的方式进行监控功能;(基于paxos协议)

– 具有内置故障检测和自动选主功能,只要不是集群中的大多数节点都宕机,就可以继续正常工作;

\- 如果主节点异常,会自动选举新节点实现故障转移

\- 如果从节点异常,会自动将从节点从复制节点踢除

– 提供单主模式与多主模式,多主模式支持多点写入;

“`

**应用模式说明:**

– **MGR单主模式(single-primary mode)**

在这种模式下,组具有设置为读写模式的单主服务器,该组中的所有其他成员都设置为只读模式(这会自动发生);

主服务器通常是引导该组的第一台服务器,所有其它加入的服务器会自动了解主服务器,并设置为只读;

MGR单主模式选举原理

单主模式下,如果主节点挂了,那么其他的成员会自动选举出新的主成员,成员之间可以通过配置权重来确定下一个主成员是谁,

如果没有配置权重,则会对所有在线成员的UUID进行排序,然后选取UUID最小的成员作为主成员。

![1670949237999](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/1670949237999.png)

– **MGR多主模式(multi-primary mode)**

在多主的模式下,没有单个主概念。无需进行节点选举,因为没有服务器扮演任何特殊角色,所有服务器均设置为读写模式。

MGR多主模式选举原理

多主模式,所有的组内成员对外提供读写服务,是真正意义上的并发,MGR对于高并发有很好的的处理能力。

多主模式下,组内所有成员没有主从之分,对用户来说,就像在操作一个MySQL一样。

所以在多主模式下,不存在选举主节点,因为所有节点都是主节点。

![1670949335470](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/1670949335470.png)

利用MGR工作模式可以实现业务架构的读写分离需求,应用MySQL原生态的router功能即可实现,并且原生态router技术更兼容MGR;

因为,当MGR中主节点出现异常下线后,会选举出现的主节点,原生态router技术可以自动识别新的主节点,做读写分离的写库;

将MySQL MGR + MySQL Router + MySQL Shell = InnoDB Cluster

![1670925420469](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/1670925420469.png)

官方扩展学习资料链接:https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-innodb-cluster.html

“`sh
**工作机制原理:**

组复制是一种可用于实现容错系统的技术,复制组是一个通过消息传递实现相互交互的server集群;

复制组由多个server成员组成,如下图master01、master02、master03,所有成员独立完成各自的事务;

1. 当客户端发起一个更新事务时,该事务先在本地执行,执行完成之后就要发起对事务的提交操作;

2. 在还没有真正提交之前,需要将产生的复制写集广播出去,复制到其它所有成员节点;

主库事务提交时,会将事务修改记录相关的信息和事务产生的binlog事件打包生成一个写集,将写集发送给所有节点;

3. 如果冲突检测成功,组内决定该事务可以提交,其它成员可以应用,否则就回滚;

冲突检测成功的标准是:至少半数以上个节点投票通过才能事务提交成功;

4. 最终,所有组内成员以相同的顺序接收同一组事务;

因此,组内成员以相同的顺序应用相同的修改,保证组内数据强一致性(采用了分布式事务特性)

![1670867803361](D:/学习//4_数据库/视频和笔记/系统运维95期-Day08-数据库服务主从同步/课程笔记目录/MySQL中级数据库课程-14章-数据库主从同步应用/课件配图资料/1670867803361.png)

**功能应用实践:**

① MGR复制同步功能实现环境:

为了实现MGR复制的主从同步,需要准备好主从架构环境:

| 主机角色 | 主机名称 | 地址信息 |
| ———- | ————– | ——– |
| 主库服务器 | 192.168.30.101 | 3306 |
| 从库服务器 | 192.168.30.102 | 3306 |
| 从库服务器 | 192.168.30.103 | 3306 |

对原有数据库服务环境清理:(基于GTID环境构建)

​“`tiki wiki
# 在所有主从节点均进行清理操作:
pkill mysqld
rm -rf /data/13306/*
rm -rf /data/binlog/*
\mv /etc/my.cnf /tmp
mkdir -p /data/13306/data /data/13306/binlog
chown -R mysql.mysql /data/*
— 所有数据库主从节点均进行以上清理操作;

# 获取随机数信息充当uuid信息
cat /proc/sys/kernel/random/uuid
eb8441e9-8aef-4a86-a4bc-5beea315f04f
— 借助随机数文件生成uuid信息,因为组复制过程也是通过GTID的uuid号码,达到复制环境中的事务一致性
— 这里采用内部GTID功能,也就是组复制的各个节点通过同一个GTID的标识,进行事务管理,所以需要给组复制设置唯一号码

# 主库db01配置文件编写
cat >/etc/my.cnf <
EOF

# 从库db02配置文件编写
cat >/etc/my.cnf <
EOF

# 从库db03配置文件编写
cat >/etc/my.cnf <
EOF

# 进行数据库所有节点初始化操作
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/13306/data
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/13306/data
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/13306/data

# 启动数据库所有节点服务
/etc/init.d/mysqld start
/etc/init.d/mysqld start
/etc/init.d/mysqld start
​“`

异常配置信息参数说明:

group_replication变量使用的loose-前缀是指server启用时尚未加载复制插件也将继续启动

| num | conf_info |
| —- | ———————————————————— |
| 01 | transaction_write_set_extraction=XXHASH64
指示server为每个事务收集写集合,并使用XXHASH64哈希算法将其编码为散列 |
| 02 | loose-group_replication_group_name=”eb8441e9-8aef-4a86-a4bc-5beea315f04f”
表示将加入或创建的复制组命名为eb8441e9-8aef-4a86-a4bc-5beea315f04f
可以自定义或者通过cat /proc/sys/kernel/random/uuid获取 |
| 03 | loose-group_replication_start_on_boot=OFF
表示设置server启动时不自动启动组复制 |
| 04 | loose-group_replication_local_address=”192.168.30.101:33061″
表示绑定本地的192.168.30.101:33061端口接受其他组成员的连接,IP地址必须为其他组成员可正常访问 |
| 05 | loose-group_replication_group_seeds=”192.168.30.101:33061,192.168.30.102:33062,192.168.30.103:33063″
表示告诉服务器在加入组时,应当连接到这些种子服务器进行配置。本设置可以不是全部的组成员服务地址 |
| 06 | loose-group_replication_bootstrap_group=OFF
表示配置是否自动引导组 |
| 07 | loose-group_replication_ip_whitelist=”10.30.0.0/16,10.31.0.0/16,10.27.0.0/16″
表示配置白名单,默认情况下只允许192.168.30.101/102/103连接到复制组,如果是其他IP则需要配置 |

② MGR复制同步功能配置过程:

MGR单主模式配置过程:

​“`tiki wiki
# 设置本地root用户密码和密码插件(所有节点)
mysql -S /tmp/mysql13306.sock -e “alter user ‘root’@’localhost’ identified with mysql_native_password by ‘XZnh@95599’;”

# 安装部署MGR组复制功能插件(所有节点)
mysql -uroot -p123 -S /tmp/mysql13306.sock -e “install plugin group_replication SONAME ‘group_replication.so’;”

# 设置创建MGR组复制功能账号(所有节点)
mysql -uroot -p123 -S /tmp/mysql13306.sock
set sql_log_bin=0;
create user repl@’%’ identified by ‘123’;
create user repl@’localhost’ identified by ‘123’;
create user repl@’127.0.0.1′ identified by ‘123’;
grant replication slave,replication client on *.* to repl@’%’;
grant replication slave,replication client on *.* to repl@’localhost’;
grant replication slave,replication client on *.* to repl@’127.0.0.1′;
flush privileges;
set sql_log_bin=1;

# 启动MGR单主模式:启动MGR引导节点(在主库上执行)
change master to master_user=’repl’,master_password=’XZnh@95599′ for channel ‘group_replication_recovery’;
set global group_replication_bootstrap_group=ON;
start group_replication;
set global group_replication_bootstrap_group=OFF;
— 相当于创建一个组复制集群,并指定集群中的引导节点
select * from performance_schema.replication_group_members;
— 查看集群节点状态信息,以及集群成员信息
select * from performance_schema.replication_group_members;
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| group_replication_applier | f90d44f9-7b94-11ed-ab2d-000c2996c4f5 | 192.168.30.101 | 13306 | ONLINE | PRIMARY | 8.0.26 |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
1 row in set (0.00 sec)

# 其他几点加入MGR(在所有从库上执行)
reset master;
— 表示清除从库上所有日志信息,重新做日志信息的复制或生成;
change master to master_user=’repl’,master_password=’123′ for channel ‘group_replication_recovery’;
start group_replication;
— 将指定从库节点加入到组复制集群中(企业中最好先备份恢复一定的数据,在进行组复制应用)
select * from performance_schema.replication_group_members;
— 查看集群节点状态信息,以及集群成员信息
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| group_replication_applier | f90d44f9-7b94-11ed-ab2d-000c2996c4f5 | 192.168.30.101 | 13306 | ONLINE | PRIMARY | 8.0.26 |
| group_replication_applier | fe73f0b4-7b94-11ed-96ea-000c2961cd06 | 192.168.30.102 | 13306 | ONLINE | SECONDARY | 8.0.26 |
| group_replication_applier | 0a09b03e-7b95-11ed-9af8-000c29f5669f | 192.168.30.103 | 13306 | ONLINE | SECONDARY | 8.0.26 |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
3 rows in set (0.00 sec)
— 此时可以看到3个节点状态为online,并且主节点为192.168.30.101,只有主节点可以写入,其他节点只读,MGR单主模式搭建成功
show variables like ‘%only%’;
+—————————————-+——-+
| Variable_name | Value |
+—————————————-+——-+
| read_only | ON |
| super_read_only | ON |
+—————————————-+——-+
— 此时所有从库节点只能实现只读操作,只有主库可以进行写操作

# 遇到集群构建异常,可以进行重置操作
stop group_replication;
reset master;
set sql_log_bin=0;
change master to master_user=’repl’,master_password=’123′ for channel ‘group_replication_recovery’;
start group_replication;
​“`

MGR多主模式配置过程:从单主模式切换到多主模式

MGR切换模式需要重新启动组复制,因此需要在所有节点上先关闭组复制,设置group_replication_single_primary_mode=OFF参数

再重新启动组复制功能

​“`tiki wiki
# 多主模式需要的参数信息
group_replication_single_primary_mode=0
— 设置参数表示关闭掉单master模式
group_replication_enforce_update_everywhere_checks=1
— 这个参数设置表示多主模式下,各个节点进行严格一致性检查

# 多主模式功能配置(在所有节点上执行)
stop group_replication;
set global group_replication_single_primary_mode=OFF;
set global group_replication_enforce_update_everywhere_checks=1;
select @@group_replication_single_primary_mode,@@group_replication_enforce_update_everywhere_checks;
— 检查参数配置信息是否生效
set global group_replication_bootstrap_group=ON;
start group_replication;
set global group_replication_bootstrap_group=OFF;
— 重新启动MGR组复制功能,是多主模式生效(主节点操作)
start group_replication;
— 重新启动MGR组复制功能,是多主模式生效(从节点操作)
select * from performance_schema.replication_group_members;
— 查看集群节点状态信息,以及集群成员信息
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| group_replication_applier | 0a09b03e-7b95-11ed-9af8-000c29f5669f | 192.168.30.103 | 13306 | ONLINE | PRIMARY | 8.0.26 |
| group_replication_applier | f90d44f9-7b94-11ed-ab2d-000c2996c4f5 | 192.168.30.101 | 13306 | ONLINE | PRIMARY | 8.0.26 |
| group_replication_applier | fe73f0b4-7b94-11ed-96ea-000c2961cd06 | 192.168.30.102 | 13306 | ONLINE | PRIMARY | 8.0.26 |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
3 rows in set (0.00 sec)

# 修改从库只读功能配置(在所有从库上执行)
set global read_only=0;
set global super_read_only=0;
— 默认启动组复制功能都是单master模式,从库节点都是自动设置read_only super_read_only这两个参数,需要手工修改
​“`

完成上面的配置后就可以执行多点写入了,多点写入会存在冲突检查,这对数据库性能耗损是挺大的,官方建议采用网络区分功能,

在程序端把相同的业务定位到同一节点,尽量减少冲突发生的几率;

​“`tiki wiki
# 停止组复制功能(在所有节点执行)
stop group_replication;
set global group_replication_single_primary_mode=OFF;
set global group_replication_enforce_update_everywhere_checks=ON;

# 随便选择某个节点执行操作
set global group_replication_bootstrap_group=ON;
start group_replication;
set global group_replication_bootstrap_group=OFF;

# 其他节点执行
start group_replication;

# 查看组信息,所有节点的member_role 都为primary;
select * from performance_schema.replication_group_members;
​“`

MGR多主模式配置过程:从多主模式切换到单主模式

​“`tiki wiki
# 所有节点执行以下操作
stop group_replication;
set global group_replication_enforce_update_everywhere_checks=OFF;
set global group_replication_single_primary_mode=ON;

# 在主节点执行以下操作
set global group_replication_bootstrap_group=ON;
start group_replication;
set global group_replication_bootstrap_group=OFF;

# 在从节点执行以下操作
start group_replication;

# 查看MGR组信息:
select * from performance_schema.replication_group_members;
​“`

③ MGR复制同步功能运维管理:

​“`tiki wiki
# MGR日常管理监控操作:
select * from performance_schema.replication_group_members;
— 根据命令信息输出,获取各个节点主机的状态情况;

# MGR故障模拟操作过程:
/etc/init.d/mysqld stop
select * from performance_schema.replication_group_members;
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| group_replication_applier | 0a09b03e-7b95-11ed-9af8-000c29f5669f | 192.168.30.103 | 13306 | ONLINE | PRIMARY | 8.0.26 |
| group_replication_applier | fe73f0b4-7b94-11ed-96ea-000c2961cd06 | 192.168.30.102 | 13306 | ONLINE | SECONDARY | 8.0.26 |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
2 rows in set (0.00 sec)
— 模拟主节点宕掉,会自动选举新的主节点

/etc/init.d/mysqld start
start group_replication;
select * from performance_schema.replication_group_members;
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
| group_replication_applier | 0a09b03e-7b95-11ed-9af8-000c29f5669f | 192.168.30.103 | 13306 | ONLINE | PRIMARY | 8.0.26 |
| group_replication_applier | f90d44f9-7b94-11ed-ab2d-000c2996c4f5 | 192.168.30.101 | 13306 | ONLINE | SECONDARY | 8.0.26 |
| group_replication_applier | fe73f0b4-7b94-11ed-96ea-000c2961cd06 | 192.168.30.102 | 13306 | ONLINE | SECONDARY | 8.0.26 |
+—————————+————————————–+—————-+————-+————–+————-+—————-+
3 rows in set (0.00 sec)
— 立刻恢复宕机节点,恢复节点自动成为从节点

# 通过克隆功能添加新的节点
pkill mysqld
rm -rf /data/13306/*
rm -rf /data/binlog/*
\mv /etc/my.cnf /tmp
mkdir -p /data/13306/data /data/13306/binlog
chown -R mysql.mysql /data/*
— 初始化新的节点

cat >/etc/my.cnf <
EOF
— 编写新的节点配置文件

mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/13306/data
— 进行数据库所有节点初始化操作

/etc/init.d/mysqld start
— 启动数据库所有节点服务

mysql -S /tmp/mysql13306.sock -e “alter user ‘root’@’localhost’ identified with mysql_native_password by ‘123’;”
— 设置本地root用户密码和密码插件(所有节点)

mysql -uroot -p123 -S /tmp/mysql13306.sock -e “install plugin group_replication SONAME ‘group_replication.so’;”
— 安装部署MGR组复制功能插件(所有节点)

mysql -uroot -p123 -S /tmp/mysql13306.sock -e “INSTALL PLUGIN clone SONAME ‘mysql_clone.so’;create user test@’%’ identified by ‘123’;grant backup_admin on *.* to ‘test’@’%’;”
— 在数据库服务正常节点上,创建克隆捐赠者用户信息

mysql -uroot -p123 -S /tmp/mysql13306.sock -e “INSTALL PLUGIN clone SONAME ‘mysql_clone.so’;create user test1@’%’ identified by ‘123’;grant clone_admin on *.* to ‘test1’@’%’;set global clone_valid_donor_list=’192.168.30.103:13306′;”
mysql -utest1 -p123 -h192.168.30.101 -P13306 -e “clone instance from test@’192.168.30.103′:13306 identified by ‘123’;”
— 在新添加节点上,创建克隆接收者用户信息
mysql -uroot -p123 -S /tmp/mysql13306.sock -e “select stage,state,end_time from performance_schema.clone_progress;”
— 检查克隆是否完毕

change master to master_user=’repl’,master_password=’123′ for channel ‘group_replication_recovery’;
start group_replication;
— 将新节点加入到组复制集群中

select * from performance_schema.replication_group_members;
— 查看组复制成员状态信息
​“`

**应用限制说明:**

在应用MGR组复制功能时,也存在一些应用的限制条件:

– 仅支持innodb存储引擎应用组复制功能;

MGR集群中只支持innodb存储引擎,能够创建非innodb引擎的表,但是无法写入数据,向非innodb表写入数据直接报错;

– 数据表中必须有主键,或者非null的唯一键;

MGR集群中只支持innodb存储引擎,并且该表必须有显示的主键,或者非null的唯一键,否则即使能够创建表,也无法向表中写数据

– 组复制存在网络限制,MGR组通信引擎目前仅支持IPv4网络,并且对节点间的网络性能要求较高;

对于低延迟、高带宽的网络是部署MGR集群的基础;

– 组复制功能会自动忽略表锁和命名锁,在MGR中lock tables、unlock tables、get_lock、release_lock等这些表锁和命名锁将忽略

– MGR多主模式中,默认不支持 SERIALIZABLE 隔离级别,建议使用RC隔离级别;

– 组复制多主模式中,对同一个对象进行并发是有冲突的,ddl和dml操作导致这种冲突在部分成员节点中无法检测到;

最终可能导致数据不一致

– 组复制多主模式中,不支持级联约束的外键,可能造成有冲突的操作无法检查;

– 组复制功能不支持超大事务同步;

– 组复制多主模式下可能导致死锁,比如select … for update在不同节点执行,由于多节点锁无法共享,很容易导致死锁;

– 组复制是不支持复制过滤的,如果有节点设置了复制过滤功能,将影响节点间决议的达成;

– 组复制功能最多支持9个节点,当大于9个节点,将拒绝新节点的加入;

“`

## 8 主从复制总结

“`sh
02 数据库服务同步扩展
数据库服务克隆同步
数据库服务事务同步(GTID 事件唯一编号)
数据库服务半同步 (异步方式 半同步(普通半同步 增强半同步) 全同步)
数据库服务过滤同步

课程任务安排:
企业恢复数据情况01:有些需要恢复数据的binlog文件被清理了
企业恢复数据情况02:需要恢复的数据信息分不在不同binlog文件中 备份数据恢复
企业恢复数据情况03:只是误操作影响了部分行的数据 1000w行 误操作影响4行数据,如何将误操作的数据修改
delete from t1 where name in (‘李四’,’张三’,’王五’)

02 数据库服务同步扩展

– 数据库服务延时同步
应用作用:可以利用延时从库,恢复主库误操作数据
构建过程:
步骤一:创建两个数据库实例
10.0.0.51 3306 主库
10.0.0.52 3306 从库(延时)

步骤二:实现主从同步配置
主库进行数据备份 并 创建同步用户
从库恢复数据信息
从库做主从配置 change master to
启动主从同步关系 start slave

步骤三:将从库设置为延时同步
stop slave sql_thread;
change master to master_delay=300; — 设置延时300s
start slave sql_thread;

show slave status\G
SQL_Delay: 300 — 开启了延时功能
SQL_Remaining_Delay: 258 — 显示延时同步倒计时信息

应用延时从库恢复数据:
步骤一:模拟企业操作管理主库信息
主库写操作
— 正常的写操作
insert into test01.t1 values (4),(5),(6);
— 错误的写操作(误操作)
drop database world;

步骤二:利用从库恢复数据信息

– 只是不会回放错误数据,正确回放合理的SQL语句
– 将从库上正确数据保存备份,导入到主库中恢复数据

stop slave sql_thread;
定位错误信息的位置点 db02-relay-bin.000002 993

1)在从库上回放正确数据信息
change master to master_delay=0;
start slave until relay_log_file=”db02-relay-bin.000002″, relay_log_pos=993;

数据库服务克隆同步 (主库-物理主机 从库–云主机)
克隆数据应用方式:
方式一:本地克隆备份数据
方式二:远程克隆迁移数据

方式一:本地克隆备份数据
步骤一:创建一个数据库实例
10.0.0.51 3306

步骤二:在数据库中安装克隆插件
INSTALL PLUGIN clone SONAME ‘mysql_clone.so’;
或者
[mysqld]
plugin-load-add=mysql_clone.so
clone=FORCE_PLUS_PERMANENT
PS:以上安装克隆插件方式,都是表示永久配置

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = ‘clone’;
+————-+—————+
| PLUGIN_NAME | PLUGIN_STATUS |
+————-+—————+
| clone | ACTIVE |
+————-+—————+

步骤三:创建克隆数据使用的用户信息
create user clone_user@’%’ identified by ‘password’;
grant backup_admin on *.* to ‘clone_user’;

步骤四:实现数据本地克隆备份
mkdir -p /data/test
chown -R mysql.mysql /data/
mysql -uclone_user -ppassword
clone local data directory = ‘/data/test/clonedir’;

mysql> select stage,state,end_time from performance_schema.clone_progress; — 可以查看本地克隆进度情况

方式二:远程克隆迁移数据 (ECS RDS)
步骤一:安装数据库实例环境
10.0.0.51 主库
10.0.0.52 从库/备份 清理数据

步骤二:在数据库服务中安装克隆插件
INSTALL PLUGIN clone SONAME ‘mysql_clone.so’;
— 在数据捐赠主机和接收主机都安装

SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = ‘clone’;

步骤三:创建克隆所需的用户信息

# 在克隆捐赠者主机上进行授权(数据库01主机上设置)

mysql> create user test_jz@’%’ identified by ‘password’;
mysql> grant backup_admin on *.* to test_jz@’%’ ;
— backup_admin权限是mysql 8.0才有的备份导出的权限;

# 在克隆接收者主机上进行授权(数据库02主机上设置)

mysql> create user test_js@’%’ identified by ‘password’;
mysql> grant clone_admin on *.* to test_js@’%’ ;
— clone_admin权限是mysql 8.0才有的克隆同步数据的权限;

步骤四:实现数据远程克隆操作
set global clone_valid_donor_list=’10.0.0.51:3307′;
— 在接收者数据库主机上配置克隆白名单

mysql -utest_js -ppassword -h10.0.0.52 -P3306
clone instance from test_jz@’10.0.0.51′:3307 identified by ‘password’;

select stage,state,end_time from performance_schema.clone_progress;
— 在数据克隆过程中,只能在捐赠者数据库中进行查看克隆进度

远程克隆完毕,如何建立主从关系
方式一:利用pos位置点信息,进行主从建立
需要在主库binlog文件中,查看相关事件信息,通过事件信息和克隆后从库数据最对比,找到位置点
CHANGE MASTER TO
MASTER_HOST=’10.0.0.51′,
MASTER_USER=’repl’,
MASTER_PASSWORD=’123456′,
MASTER_PORT=3307,
MASTER_LOG_FILE=’binlog.000002′,
MASTER_LOG_POS=157,
MASTER_CONNECT_RETRY=10;

方式二:利用GTID方式实现主从同步(可以自动识别位置点)
步骤一:需要克隆前配置gtid功能
vim /etc/my.cnf
— 以下配置信息,需要在主库和从库配置文件都编写
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1

步骤二:完成数据目录克隆
省略

步骤三:建立主从同步(云数据库 -从 — 物理数据库 — 主)
CHANGE MASTER TO
MASTER_HOST=’10.0.0.51′,
MASTER_USER=’repl’,
MASTER_PASSWORD=’123456′,
MASTER_PORT=3307,
master_auto_position=1,
MASTER_CONNECT_RETRY=10;

数据库服务事务同步 (基于GTID方式建立主从)
GTID功能概念
GTID(global transaction id)是对于一个已提交事务的唯一编号,并且是一个全局唯一编号(主从复制过程);
是数据库5.6版本开始的一个功能新特性,主要是用于解决主从复制的一致性问题;

GTID同步原理

– master节点在更新数据的时候,会在事务前产生GTID信息,一同记录到binlog日志中;
– slave节点的io线程将主库推送的binlog写入到本地relay log中;
– 然后SQL线程从relay log中读取GTID,设置gtid_next的值为该gtid,然后对比slave端的binlog是否有记录;
– 如果有记录的话,说明该GTID的事务已经运行,slave会忽略;
– 如果没有记录的话,slave就会执行该GTID对应的事务,并记录到binlog中

GTID主从建立
步骤一:创建两个数据库实例
10.0.0.51 3307 主节点 有数据
10.0.0.52 3306 从节点

步骤二:配置开启GTID功能
gtid-mode=on
— 启用gtid复制方式,默认采用传统的复制方式
enforce-gtid-consistency=true
— 开启gtid所有节点的强制一致性,主要会限制 CREATE TABLE … SELECT 类似语句的执行;

log-slave-updates=1
— 定义slave更新是否记入二进制日志,从而增强数据一致性,是在高可用架构中重要配置环节

vim /etc/my.cnf
— 以下配置信息,需要在主库和从库配置文件都编写

gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1

mysql> show master status;
+—————-+———-+————–+——————+——————————————+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+—————-+———-+————–+——————+——————————————+
| bin-log.000035 | 388 | | | cd3cc675-bd25-11ef-bd84-000c29141679:1-3 |
+—————-+———-+————–+——————+——————————————+

mysql> show binlog events in ‘bin-log.000034’;
+—————-+—–+—————-+———–+————-+——————————————————————-+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+—————-+—–+—————-+———–+————-+——————————————————————-+
| bin-log.000034 | 4 | Format_desc | 1 | 126 | Server ver: 8.0.32, Binlog ver: 4 |
| bin-log.000034 | 126 | Previous_gtids | 1 | 157 | |
| bin-log.000034 | 157 | Gtid | 1 | 234 | SET @@SESSION.GTID_NEXT= ‘cd3cc675-bd25-11ef-bd84-000c29141679:1’ |
| bin-log.000034 | 234 | Query | 1 | 348 | create database test31 /* xid=4 */ |
| bin-log.000034 | 348 | Gtid | 1 | 425 | SET @@SESSION.GTID_NEXT= ‘cd3cc675-bd25-11ef-bd84-000c29141679:2’ |
| bin-log.000034 | 425 | Query | 1 | 539 | create database test32 /* xid=6 */ |
| bin-log.000034 | 539 | Rotate | 1 | 584 | bin-log.000035;pos=4 |
+—————-+—–+—————-+———–+————-+——————————————————————-+
7 rows in set (0.02 sec)

步骤三:将主库数据进行备份,并将数据导入到从库
mysqldump -uroot -p123456 -h10.0.0.51 -P3307 -A –source-data >/backup/all.sql
mysql start slave;

步骤五:检查主从同步状态/检查同步数据

# mysqldump -uroot -p123456 -h10.0.0.51 -P3307 -A –source-data >/backup/all.sql

mysqldump: [Warning] Using a password on the command line interface can be insecure.
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions,
even those that changed suppressed parts of the database.
If you don’t want to restore GTIDs, pass –set-gtid-purged=OFF. To make a complete dump, pass –all-databases –triggers –routines –events.

–set-gtid-purged=ON (默认) — 为了实现主从同步
表示会记录gtid事务编号信息(在备份文件中)
主要利用备份文件可以实现主从建立,从库加载恢复备份文件,会将事务编号记录到从库中

–set-gtid-purged=OFF — 可以应用修复数据
表示不会记录gtid事务编号信息(在备份文件中)
gtid备份的数据信息,当备份文件中具有gtid编号信息时,再恢复数据时,会具有幂等性问题(程序代码可以多次执行)

数据库服务半同步

– 异步方式: 不会确认dump线程发送binlog信息,是否在从库接收到
实现异步方式:利用pos位置点实现同步 利用gtid事务编号实现同步

– 半同步方式
· 普通半同步方式 会确认dump线程发送binlog信息,在IO线程存储relay log后, 会返回确认消息给dump线程
· 增强半同步方式 会确认dump线程发送binlog信息,在SQL线程回放relay log后,会返回确认消息给dump线程

实现方法:
步骤一:安装半同复制插件
主库安装的半同步插件:master
INSTALL PLUGIN rpl_semi_sync_master SONAME ‘semisync_master.so’;
SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = ‘rpl_semi_sync_master’;

从库安装的半同步插件:slave
INSTALL PLUGIN rpl_semi_sync_slave SONAME ‘semisync_slave.so’;
SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = ‘rpl_semi_sync_slave’;

步骤二:启动半同步复制功能
主库需要启动插件功能:
— 临时激活插件功能:
set global rpl_semi_sync_master_enabled=1;
— 永久激活插件功能:
[mysqld]
rpl_semi_sync_master_enabled=ON

从库需要启动插件功能:
— 临时激活插件功能:
set global rpl_semi_sync_slave_enabled=1;
— 永久激活插件功能:
[mysqld]
rpl_semi_sync_slave_enabled=ON

set global rpl_semi_sync_master_timeout=10000; (1000毫秒=1s)
— 主节点进行半同步超时时间设置

半同步复制有关扩展知识:
1)配置参数信息
rpl_semi_sync_master_trace_level=32
— 以上设置项表示,是否将半同步复制过程产生信息记录到错误日志中;
rpl_semi_sync_master_wait_for_slave_count=1
— 用于控制在主库需要从库确认的数量,默认为1(多个从库同步数据)
rpl_semi_sync_master_wait_no_slave=on
— 是否允许每个事物的提交都要等待slave的信号
— on为每一个事物都等待,除非超过等待时间
— off表示master发现Rpl_semi_sync_master_clients小于rpl_semi_sync_master_wait_for_slave_count,则master立即转为异步模式
Rpl_semi_sync_master_clients — 状态变量 显示从库数量信息
rpl_semi_sync_master_wait_point=after_sync *****
after_commit :此时半同步复制为普通半同步 5.6版本 默认是普通半同步
AFTER_SYNC :此时半同步复制为增强半同步 5.7版本 默认是增强半同步

– 全同步方式 主从数据库会对执行的操作进行投票机制(投票允许过半,就完成事件操作,投票失败过半,就自动进行回滚操作)

数据库服务过滤同步:
功能应用:可以灵活控制主库同步到从库的数据信息

方式一:在主库上,利用binlog文件实现同步过滤
控制binlog文件记录的内容

– 白名单设置:binlog_do_db
[mysqld]
binlog_do_db=xiaoJ

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

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

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

mysql> show master status;
+—————-+———-+————–+——————+——————————————–+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+—————-+———-+————–+——————+——————————————–+
| bin-log.000036 | 385 | xiaoJ | | cd3cc675-bd25-11ef-bd84-000c29141679:1-352 |
+—————-+———-+————–+——————+——————————————–+
1 row in set (0.01 sec)

mysql> show binlog events in ‘bin-log.000036’;
+—————-+—–+—————-+———–+————-+———————————————————————+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+—————-+—–+—————-+———–+————-+———————————————————————+
| bin-log.000036 | 4 | Format_desc | 1 | 126 | Server ver: 8.0.32, Binlog ver: 4 |
| bin-log.000036 | 126 | Previous_gtids | 1 | 197 | cd3cc675-bd25-11ef-bd84-000c29141679:1-351 |
| bin-log.000036 | 197 | Gtid | 1 | 274 | SET @@SESSION.GTID_NEXT= ‘cd3cc675-bd25-11ef-bd84-000c29141679:352′ |
| bin-log.000036 | 274 | Query | 1 | 385 | create database xiaoJ /* xid=18 */ |
+—————-+—–+—————-+———–+————-+———————————————————————+
4 rows in set (0.00 sec)

– 黑名单设置:binlog_ignore_db
[mysqld]
binlog_ignore_db=xiaoQ
binlog_ignore_db=xiaoK

mysql> show master status;
+—————-+———-+————–+——————+——————————————–+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+—————-+———-+————–+——————+——————————————–+
| bin-log.000038 | 197 | | xiaoQ,xiaoK | cd3cc675-bd25-11ef-bd84-000c29141679:1-356 |
+—————-+———-+————–+——————+——————————————–+
1 row in set (0.00 sec)

思考问题:
1)在白名单和黑名单中都配置了xiaoQ数据库信息,数据库信息会不会记录
2)在白名单和黑名单中都没配置了xiaoQ数据库信息,数据库信息会不会记录

方式二:在从库上,利用SQL线程实现同步过滤
过滤数据库
— 白名单 :replicate_do_db:
[mysqld]
replicate_do_db=xiaoQ

Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB: xiaoQ

— 黑名单 :replicate_ignore_db:
[mysqld]
replicate_ignore_db=xiaoQQ;

过滤数据表
— 白名单 :replicate_do_table:
[mysqld]
replicate_do_table=xiaoQ.t1

— 黑名单 :replicate_ignore_table:
[mysqld]
replicate_ignore_table=xiaoQ.t2;

进行模糊匹配过滤
— 白名单 :replicate_wild_do_table:
[mysqld]
replicate_wild_do_table=xiaoQ.t%

— 黑名单 :replicate_wild_ignore_table:
[mysqld]
replicate_wild_ignore_table=xiaoQ.t%

课程任务安排:
企业恢复数据情况01:有些需要恢复数据的binlog文件被清理了
方式一:利用延时从库修复数据
方式二:利用全备数据+binlog增量数据
步骤一:默认日常存储数据
create database oldboy01; — bin-log.000039
create table student (id int,name char(5),age tinyint); — bin-log.000039

mysql> insert into student values (1,’xiaoA’,18); — bin-log.000040
Query OK, 1 row affected (0.01 sec)
mysql> insert into student values (1,’xiaoB’,20);
Query OK, 1 row affected (0.01 sec)
mysql> insert into student values (1,’xiaoC’,20);
Query OK, 1 row affected (0.01 sec)

mysqldump -uroot -p123456 -B oldboy01 –set-gtid-purged=OFF >/backup/oldboy01.sql

mysql> flush logs;
mysql> insert into student values (1,’xiaoD’,20); — bin-log.000041
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values (1,’xiaoE’,20);
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values (1,’xiaoF’,20);

步骤二:模拟数据损坏操作
drop database oldboy01;

步骤三:恢复数据信息
mysql -uroot -p123456 /backup/add.sql
mysqlbinlog –skip-gtids –include-gtids=cd3cc675-bd25-11ef-bd84-000c29141679:372-374 bin-log.000041 >/backup/add.sql

mysql -uroot -p123456 /backup/oldboy03.sql
mysql -uroot -p123456 xxxx`
方式二:利用第三方工具读取binlog文件,实现数据闪回功能

步骤一:安装数据闪回工具
unzip binlog2sql-master.zip

yum install -y python3
pip3 install -r requirements.txt
pip3 show pymysql
pip3 install –upgrade pymysql

步骤二:利用软件工具恢复数据
mysql> delete from city where id=1;
Query OK, 1 row affected (0.03 sec)

mysql> delete from city where id=2;
Query OK, 1 row affected (0.00 sec)

mysql> delete from city where id=3;
Query OK, 1 row affected (0.00 sec)

python3 binlog2sql.py -h 10.0.0.51 -P3307 -uroot -p123456 -d world -t city –start-file=’bin-log.000044′ –sql-type=delete -B
INSERT INTO `world`.`city`(`CountryCode`, `District`, `ID`, `Name`, `Population`) VALUES (3, ‘Herat’, ‘AFG’, ‘Herat’, 186800); #start 2108 end 2310 time 2024-12-19 18:14:00 gtid
INSERT INTO `world`.`city`(`CountryCode`, `District`, `ID`, `Name`, `Population`) VALUES (2, ‘Qandahar’, ‘AFG’, ‘Qandahar’, 237500); #start 1790 end 1998 time 2024-12-19 18:13:59 gtid
INSERT INTO `world`.`city`(`CountryCode`, `District`, `ID`, `Name`, `Population`) VALUES (1, ‘Kabul’, ‘AFG’, ‘Kabol’, 1780000); #start 1478 end 1680 time 2024-12-19 18:13:55 gtid

INSERT INTO `world`.`city` VALUES (3, ‘Herat’, ‘AFG’, ‘Herat’, 186800); #start 2108 end 2310 time 2024-12-19 18:14:00 gtid
INSERT INTO `world`.`city` VALUES (2, ‘Qandahar’, ‘AFG’, ‘Qandahar’, 237500); #start 1790 end 1998 time 2024-12-19 18:13:59 gtid
INSERT INTO `world`.`city` VALUES (1, ‘Kabul’, ‘AFG’, ‘Kabol’, 1780000); #start 1478 end 1680 time 2024-12-19 18:13:55 gtid

01:数据库服务高可用MHA
“`

# 第八章 数据库冗余架构

“`sh
数据库中的高可用功能,主要是用于避免数据库服务或数据信息的损坏问题,其中数据损坏的类型有:

– 数据物理损坏:磁盘、主机、程序实例、数据文件误删除
– 数据逻辑损坏:drop update …

其中,数据库高可用技术的出现主要解决的是物理损坏的业务中断问题,而主从架构技术主要解决的是数据物理损坏问题;

数据库高可用解决方案选型依据:(全年无故障率)
“`

| 无故障率 | 故障时间 | 解决方案 |
| ——– | ——————– | ———————————————————— |
| 99.9% | 0.1%(525.6min) | keepalived+双主架构,但需要人为干预 |
| 99.99% | 0.01%(52.56min) | MHA ORCH TMHA,具有自动监控,自动切换,自动数据补偿,但还是属于半自动化
比较适合非金融类互联网公司 eg: facebook taobao前端-TMHA–>polaradb |
| 99.999% | 0.001%(5.256min) | PXC MIC MGC,数据是高一致性
比较适合金融类互联网公司 |
| 99.9999% | 0.0001%(0.5256min) | 自动化、云计算化、平台化,仍然属于概念阶段 |

## 1 高可用环境构建

“`sh
#### 1.15.2 数据库服务高可用软件介绍

MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton研发,

此人目前就职于Facebook公司,MHA是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。

MySQL进行故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换过程中;

MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。

MHA主要有两部分组成:

**MHA Manager(管理节点)**

可以单独部署在一台独立的机器上管理多个master-slave集群,也可以部署在一台slave上。

**MHA Node(数据节点)**

运行在每台MySQL服务器上

MHA Manager 会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master;

然后将所有其他的slave重新指向新的master,整个故障转移过程对应用程序是完全透明的;

MHA软件结构介绍:(MHA中的所有组件就是perl语言编写的功能脚本)

| 节点信息 | 软件组件 | 作用介绍 |
| ———————– | ———————– | —————————————————– |
| MHA Manager(管理节点) | masterha_manger | 用于启动MHA |
| | masterha_check_ssh | 用于检查MHA的SSH配置互信状况 |
| | masterha_check_repl | 用于检查MySQL复制状态,以及配置信息 |
| | masterha_master_monitor | 用于检测master是否宕机 |
| | masterha_check_status | 用于检测当前MHA运行状态 |
| | masterha_master_switch | 用于控制故障转移(自动或者手动) |
| | masterha_conf_host | 添加或删除配置的server信息 |
| MHA Node(数据节点) | save_binary_logs | 保存和复制master的二进制日志 |
| | apply_diff_relay_logs | 识别差异的中继日志事件并将其差异的事件应用于其他slave |
| | purge_relay_logs | 清除中继日志(不会阻塞SQL线程) |

#### 1.15.3 数据库服务高可用环境构建

① MHA高可用架构基础环境:

为了实现MHA高可用架构构建,需要准备好三节点数据库+GTID复制环境:

| 主机角色 | 主机名称 | 地址信息 |
| ———- | —————————- | ——– |
| 主库服务器 | 192.168.30.101 | 3306 |
| 从库服务器 | 192.168.30.102 | 3306 |
| 从库服务器 | 192.168.30.103(兼做管理节点) | 3306 |

对原有数据库服务环境清理:(基于GTID环境构建)

​“`tiki wiki
# 在所有主从节点均进行清理操作:
pkill mysqld
rm -rf /data/3306/*
rm -rf /data/binlog/*
mkdir -p /data/3306/data /data/binlog
chown -R mysql.mysql /data/*
— 所有数据库主从节点均进行以上清理操作;

# 主库db01配置文件编写
cat >/etc/my.cnf <
EOF

# 从库db02配置文件编写
cat >/etc/my.cnf <
EOF

# 从库db03配置文件编写
cat >/etc/my.cnf <
EOF

# 进行数据库所有节点初始化操作
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data
mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data

# 启动数据库所有节点服务
/etc/init.d/mysqld start
/etc/init.d/mysqld start
/etc/init.d/mysqld start

# 重构主从关系-主库操作
create user repl@’192.168.30.%’ identified with mysql_native_password by ‘123456’;
Query OK, 0 rows affected (0.01 sec)
grant replication slave on *.* to repl@’192.168.30.%’;
Query OK, 0 rows affected (0.00 sec)
— 主库上创建主从复制用户信息

# 重构主从关系-从库操作
change master to
master_host=’10.0.0.51′,
master_user=’repl’,
master_password=’123456′,
master_auto_position=1;
— 表示让从库自己找寻复制同步数据的起点;
— 在第一次启动gtid功能时,会读取从库中的binlog日志信息,根据主库uuid信息,获取从库中执行过的主库gtid信息
— 从从库中没有执行过的主库gtid信息之后进行进行数据同步操作
start slave;
— 其他从库一并操作
​“`

② MHA高可用软件安装部署:

​“`tiki wiki
# 创建程序命令软链接
ln -s /usr/local/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog
ln -s /usr/local/mysql/bin/mysql /usr/bin/mysql
— 所有节点均执行以上操作,因为MHA程序加载数据库命令,会默认在/usr/bin下面进行加载(会影响数据补偿和监控功能)

# 配置各节点互信
rm -rf /root/.ssh
ssh-keygen
cd /root/.ssh
mv id_rsa.pub authorized_keys
scp -r /root/.ssh 192.168.30.102:/root
scp -r /root/.ssh 192.168.30.103:/root
ssh 192.168.30.101 date
ssh 192.168.30.102 date
ssh 192.168.30.103 date
— 各节点验证

# 安装软件程序
yum install perl-DBD-MySQL -y
rpm -ivh mha4mysql-node-0.58-0.el7.centos.noarch.rpm
— 所有节点安装Node软件依赖包
yum install -y perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes
yum install -y mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
— Manager软件安装(db03)

# 在db01主库中创建mha需要的用户
create user mha@’192.168.30.%’ identified with mysql_native_password by ‘mha’;
grant all privileges on *.* to mha@’192.168.30.%’;
— 在主库创建完毕后,主从复制功能,核实所有从库也都有mha用户信息

# Manager配置文件准备(db03)
mkdir -p /etc/mha
— 创建配置文件目录
mkdir -p /var/log/mha/app1
— 创建日志目录

cat > /etc/mha/app1.cnf < /var/log/mha/app1/manager.log 2>&1 &

# 查看MHA状态
[root@db03 ~]# masterha_check_status –conf=/etc/mha/app1.cnf
app1 (pid:30770) is running(0:PING_OK), master:192.168.30.101
— 显示以上提示信息,表示MHA基础环境搭建成功了,但还不能在生产环境使用,还需要有后续的操作配置
​“`

####
“`

## 2 工作原理

![image-20250217114058534](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20250217114058534.png)

| 功能 | 实现 |
| ——– | —————————————- |
| 监控 | 发现主库服务程序产生了运行异常 |
| 选主 | 找到可以替代主库的服务器主机进行切换 |
| 数据补偿 | 新的主库接管后可以保证与原有主库数据一致 |
| 应用透明 | 将应用程序的读写请求对接切换到新的主库上 |
| 报警 | 及时向管理员发起告知提醒使之进行修复 |
| 额外补偿 | 当整体主库系统环境都异常时实现数据的补偿 |
| 异常自愈 | 主库服务器的异常情况进行原有主库修复 |

### 1 mha的设计原理分析(failover过程)

“`sh
01 MHA软件启动

根据启动命令,分析MHA软件启动原理:

​“`tiki wiki
nohup masterha_manager –conf=/etc/mha/app1.cnf –remove_dead_master_conf –ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1 &
​“`

根据以上启动命令,需要先调取MHA启动脚本文件`masterha_manager` ,然后在调取加载MHA软件的配置文件`–conf=…/app1.cnf`

会根据加载的MHA的配置文件不同,实现管理多个高可用架构环境,进行高可用业务的架构环境的区分;

`–remove_dead_master_conf`参数表示在主节点出现宕机情况时,将会从集群中被踢出,即从配置文件中删除掉故障节点;

`–ignore_last_failover` 默认MHA服务是不能频繁进行故障切换的,需要有一定的间隔时间,加此参数表示忽略切换的间隔时间;

最后将MHA启动运行的信息放入到日志文件中即可 `/var/log/mha/app1/manager.log 2>&1`

> 补充说明:–ignore_last_failover
>
> 【官方解释】
>
> If the previous failover failed, MHA does not start failover because the problem might happen again.
>
> 如果上一次高可用功能失败,MHA不会再启动高可用功能,因为这个问题可能会再次出现
>
> The normal step to start failover is manually remove failover error file
>
> 后续再想启动故障转移的正常步骤是手动删除故障转移错误文件
>
> which is created under (manager_workdir)/(app_name).failover.error .
>
> 错误文件会在manager_workdir目录下,创建一个名为app_name.failover.error的文件
>
> By setting –ignore_last_failover, MHA continues failover regardless of the last failover status.
>
> 对于设置–ignore_last_failover,表示无论上次故障切换状态如何,MHA都会继续进行启动故障切换功能
>
> 【博文参考】
>
> 在缺省情况下,如果 MHA 检测到连续发生宕机,且两次宕机间隔不足 8 小时的话,则不会进行 Failover;
>
> 之所以这样限制是为了避免 ping-pong 效应。该参数代表忽略上次 MHA 触发切换产生的文件;
>
> 默认情况下,MHA 发生切换后会在日志记录,在日志目录中会生成 app1.failover.complete 文件;
>
> 下次再次切换的时候如果发现该目录下存在该文件将不允许触发切换,除非在第一次切换后删除该文件;

02 MHA实现监控

利用MHA启动脚本文件`masterha_manager`会自动调用监控脚本文件`masterha_master_monitor`,并且每隔配置文件指定时间;

`ping_interval=2` 进行脚本监控一次,从而判断主节点是否处于存活状态,连续4次还没有主库心跳,即说明主库宕机;

​“`tiki wiki
# 监控脚本验证主节点存活方法
[root@xiaoQ-03 ~]# mysql -umha -pmha -h192.168.30.101 -e “select user();”
mysql: [Warning] Using a password on the command line interface can be insecure.
+——————-+
| user() |
+——————-+
| mha@xiaoQ-03 |
+——————-+
​“`

03 MHA选主过程

在MHA中进行选主时,根据选主源码文件信息分析,主要会利用到四个数组:alive latest pref bad,并且会识别节点编号信息;

在进行选主时,主要会关注竞选新主节点的日志量、以及是否设置candidate_master参数配置信息;

| 数组信息 | 简述 | 作用说明 |
| ——– | ——– | ———————————————————— |
| alive | 存活数组 | 主要用于探测存活的节点状态;当主库宕机后,探测的就是两个从库节点 |
| latest | 最新数组 | 表示获取日志最新的从库信息,即数据量最接近主库的从库(根据GTID信息 或 position信息) |
| pref | 备选数组 | 在数组中具有candidate_master参数判断条件,此参数可以放入配置文件节点中,便于节点优先选择为新主 |
| bad | 不选数组 | 如果设定了参数:no_master=1,表示相应节点不参与竞选主;
如果设定了参数:log_bin=0(二进制日志没开),表示相应节点不参与竞选主;
如何设定了参数:check_slave_delay,检查从库延迟主库100M数据信息日志量,表示节点不参与竞选主 |

MHA选主判断总结(利用if判断选主的情况)

– 循环对比latest数组和pref数组的slave,如果存在相同的slave,并且这个slave不在bad数组当中,该slave会被推选为新的master

DB02节点即满足latest数组信息,又满足perf数组信息,但不满足bad数据信息,即会被选为新主,有多个按照号码顺序选举;

– 如果pref和bad数组当中的个数为0,则选择latest数组当中的第一个slave为master;

DB02节点没有candidate_master参数配置,又没有不选数组里的三种情况配置,即db02恰好是latest,为新主;

– 循环对比alive数组和pref数组当中的slaves,如果有一个slave相同,并且不在bad数组当中,该节点就会成为新的master;

DB02节点即不满足latest,也不满足bad,但是满足pref,也会被选择作为新主;

– 循环latest数组,如果又循环到slave不在bad数组当中,这个slave就会成为master,就算添加了candidate_master=1参数;

该slave也不一定会成为主库;

DB02节点即满足latest数组,不是bad数组,也会成为新的主;

– 从活着的slave当中进行循环,如果循环到slave不在bad数组当中,那么这个slave就会成为主库;

DB02节点是活着的,不满足bad,也可以成为新的主;

– 如果进行了多次选择都找不到主库,那么主库选择失败,failover失败;

选主策略简述表:

| 优先级 | alive数组 | latest数组 | pref数组 | bad数组 | 选主策略 | 多个选择情况 |
| —— | ——— | ———- | ——– | ——- | ———— | ——————– |
| 01 | `满足` | `满足` | `满足` | 不满足 | 优选选择 | 按照节点号码顺序选择 |
| 02 | `满足` | `满足` | 不满足 | 不满足 | 优选选择 | 按照节点号码顺序选择 |
| 03 | `满足` | 不满足 | `满足` | 不满足 | 优选选择 | 按照节点号码顺序选择 |
| 04 | `满足` | 不满足 | 不满足 | 不满足 | 优选活着节点 | 按照节点号码顺序选择 |

> 说明:在进行手工指定切换新主时,即应用了prio_new_master_host参数信息时,会最优先选择相应节点为新主;

04 MHA数据补偿

在进行数据补偿之前,需要让新主库与原有宕机主库进行对比,获悉需要补偿的数据量情况,即对比差的数据日志量信息;

然后可以从binlog日志中,进行补充数据信息的截取,随之进行数据信息补偿,但是有种特殊情况,若原有主库无法访问了;

所以进行数据补偿操作,也需要分各种情景进行处理:

– 原主库SSH连接正常:

各个从节点自动调用:`save_binary_logs`脚本文件,立即保存缺失部分的bin_log,到各节点/var/tmp/目录;

– 原主库SSH连接异常:

各个从节点自动调用:`apply_diff_relay_logs`脚本文件,进行relay_log日志差异信息补偿;

– 额外特殊数据补充:(利用主库日志冗余机制)

MHA提供了binlog_server功能,可以实时拉取主库的binlog日志到备份节点,从而进行数据额外补偿;

05 MHA业务切换

自动解除原有的主从关系,实现新的主从关系的建立;

​“`tiki wiki
# 所有从库解除主从关系操作
stop slave;
reset slave;

# 所有从库重构主从关系操作
change master to …
​“`

06 MHA应用透明

实现MHA的VIP功能,利用脚本实现,上传mha_script.tar文件到/usr/local/bin目录中,然后进行解压处理;

​“`tiki wiki
# 上传MHA所需的脚本文件
[root@xiaoQ-03 ~]# cd /usr/local/bin/
[root@xiaoQ-03 bin]# chmod +x /usr/local/bin/*

# 修改MHA脚本文件的信息
[root@xiaoQ-03 bin]# cp master_ip_failover master_ip_failover.bak
[root@xiaoQ-03 bin]# dos2unix /usr/local/bin/*
[root@xiaoQ-03 bin]# vim master_ip_failover
13 my $vip = ‘192.168.30.110/24’;
14 my $key = ‘1’;
15 my $if = ‘eth0’;
16 my $ssh_start_vip = “/sbin/ifconfig $if:$key $vip”;
17 my $ssh_stop_vip = “/sbin/ifconfig $if:$key down”;
18 my $ssh_Bcast_arp= “/sbin/arping -I $if -c 3 -A 192.168.30.110”;

# 修改配置文件
[root@xiaoQ-03 ~]# vim /etc/mha/app1.cnf
master_ip_failover_script=/usr/local/bin/master_ip_failover

# 重启MHA服务
[root@xiaoQ-03 bin]# masterha_stop –conf=/etc/mha/app1.cnf
[root@xiaoQ-03 bin]# nohup masterha_manager –conf=/etc/mha/app1.cnf –remove_dead_master_conf –ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &

# 手工在主库上添加VIP
[root@xiaoQ-03 bin]# masterha_check_status –conf=/etc/mha/app1.cnf
app1 (pid:103046) is running(0:PING_OK), master:192.168.30.101
— 核实此时的MHA的主库节点
ifconfig eth0:1 192.168.30.110/24
— 在主库节点手工添加vip地址信息

# 进行VIP地址连接测试
— 可以使用navcat软件,连接MHA的vip地址,查看所连主机信息是否为主节点,当故障转移后可以核实VIP地址是否持续连接;
​“`

> 说明:进行MHA的VIP地址漂移时,只能在局域网环境进行漂移,不能实现跨网段的VIP地址漂移;

07 MHA故障报警

实现MHA的报警功能,利用脚本实现,上传mha_script.tar文件到/usr/local/bin目录中,然后进行解压处理;

​“`tiki wiki
# 准备脚本文件
[root@xiaoQ-03 bin]# cp send_report send_report.bak
28 my $smtp=’smtp.qq.com’;
— smtp服务器地址域名
29 my $mail_from=’330882721@qq.com’;
— 发件箱信息配置
30 my $mail_user=’330882721′;
— 用户名 QQ号
31 my $mail_pass=’ypokkranqlgkcbba’;
— 邮箱授权码
32 my $mail_to=’330882721@qq.com’;
or
my $mail_to=[‘to1@qq.com’,’to2@qq.com’];
— 收件箱信息配置

# 修改配置文件
[root@xiaoQ-03 ~]# vim /etc/mha/app1.cnf
report_script=/usr/local/bin/send_report

# 重启MHA服务
[root@xiaoQ-03 bin]# masterha_stop –conf=/etc/mha/app1.cnf
[root@xiaoQ-03 bin]# nohup masterha_manager –conf=/etc/mha/app1.cnf –remove_dead_master_conf –ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
​“`

08 MHA额外补偿

利用binlog_server作为额外的日志补偿的冗余方案,即实时保存主库的bin_log日志文件到特定节点目录中;

​“`tiki wiki
# 创建日志存放目录
[root@xiaoQ-03 ~]# mkdir -p /data/binlog_server/
[root@xiaoQ-03 ~]# chown -R mysql.mysql /data/*
[root@xiaoQ-03 ~]# cd /data/binlog_server
[root@xiaoQ-03 binlog_server]# mysql -e “show slave status\G”|grep “Master_Log”
Master_Log_File: mysql-bin.000002
Read_Master_Log_Pos: 1201
Relay_Master_Log_File: mysql-bin.000002
Exec_Master_Log_Pos: 1201
— 拉取日志的起点,需要按照目前从库的已经获取到的二进制日志点为起点
[root@xiaoQ-03 binlog_server]# mysqlbinlog -R –host=192.168.30.101 –user=mha –password=mha –raw –stop-never mysql-bin.000002 &

# 编写配置文件信息
[root@xiaoQ-03 ~]# vim /etc/mha/app1.cnf
[binlog1]
no_master=1
— 不存于竞选
hostname=192.168.30.103
— 将日志额外补偿到哪个主机上
master_binlog_dir=/data/binlog_server/
— 日志额外补偿的存储目录

# 重启MHA服务
[root@xiaoQ-03 bin]# masterha_stop –conf=/etc/mha/app1.cnf
[root@xiaoQ-03 bin]# nohup masterha_manager –conf=/etc/mha/app1.cnf –remove_dead_master_conf –ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
​“`

####
“`

## 3 高可用切换

“`sh
模拟进行指定主库节点故障情况,检查核实MHA相应功能脚本是否能够正确运行:

​“`tiki wiki
# 确认目前的MHA的状态是良好的
[root@db03 ~]# masterha_check_status –conf=/etc/mha/app1.cnf
app1 (pid:30770) is running(0:PING_OK), master:192.168.30.101

# 模拟DB01数据库节点宕机
[root@xiaoQ-01 ~]# /etc/init.d/mysqld stop
Shutting down MySQL……….. SUCCESS!

# 监控DB03日志信息的变化
[root@xiaoQ-03 ~]# tail -f /var/log/mha/app1/manager
Mon Jan 2 15:32:26 2023 – [warning] Got error on MySQL select ping: 1053 (Server shutdown in progress)
Mon Jan 2 15:32:26 2023 – [info] Executing SSH check script: exit 0
Mon Jan 2 15:32:26 2023 – [info] HealthCheck: SSH to 192.168.30.101 is reachable.
Mon Jan 2 15:32:28 2023 – [warning] Got error on MySQL connect: 2003 (Can’t connect to MySQL server on ‘192.168.30.101’ (111))
… 省略部分信息…
​“`

**实现MHA高可用切换的六个步骤:**

01 MHA健康检查报错,显示主数据库节点无法正常连接

​“`tiki wiki
# 日志信息分析
[waring] Got error on MySQL connect: 2003 (Can’t connect to MySQL server on ‘192.168.30.101’ (111))
[waring] Connection failed 2 time(s)..
[waring] Got error on MySQL connect: 2003 (Can’t connect to MySQL server on ‘192.168.30.101’ (111))
[waring] Connection failed 3 time(s)..
[waring] Got error on MySQL connect: 2003 (Can’t connect to MySQL server on ‘192.168.30.101’ (111))
[waring] Connection failed 4 time(s)..
[waring] Master is not reachable from health checker!
[waring] Master 192.168.30.101(192.168.30.101:3306) is not reachable!
— 对故障主节点进行4次健康检查,主节点数据库服务仍旧无法连接,即判定主节点故障
​“`

02 MHA进行重新选主,根据数组信息选择合适的备用新主节点

​“`tiki wiki
[info] Connecting to a master server failed. Reading configuration file /etc/masterha_default.cnf and /etc/mha/app1.cnf again, and trying to connect to all servers to check server status..
[info] Reading application default configuration from /etc/mha/app1.cnf
….
[info] Starting master failover
….
[info] ** Phase 1: Configuration Check Phase completed.
​“`

03 MHA进行节点关闭,选择完新的主节点后会将原有主节点的VIP地址消除

​“`tiki wiki
[info] * Phase 2: Dead Master Shutdown Phase..
[info] Forcing shutdown so that applications never connect to the current master..
Disabling the VIP on old master: 192.168.30.101
​“`

04 MHA进行节点切换,在新的主节点上进行非同步数据信息的补偿,

​“`tiki wiki
[info] * Phase 3: Master Recovery Phase..
[info] * Phase 3.1: Getting Latest Slaves Phase..
….
[info] * Phase 3.3: Determining New Master Phase..
[info] New master is 192.168.30.102(192.168.30.102:3306)
[info] Starting master failover..
[info] * Phase 3.3: New Master Recovery Phase..
[info] Executing binlog save command: save_binary_logs –command=save –start_file=mysql-bin.000002 –start_pos=1201 –output_file=/var/tmp/saved_binlog_binlog1_20230102153233.binlog –handle_raw_binlog=0 –skip_filter=1 –disable_log_bin=0 –manager_version=0.58 –oldest_version=8.0.26 –binlog_dir=/data/binlog_server/
[info] Additional events were not found from the binlog server. No need to save.
Enabling the VIP – 192.168.30.110/24 on the new master – 192.168.30.102
Mon Jan 2 15:32:36 2023 – [info] OK.
Mon Jan 2 15:32:36 2023 – [info] ** Finished master recovery successfully.
[info] * Phase 3: Master Recovery Phase completed.
​“`

05 MHA进行主从重构,将从库连接到新的主库上

​“`tiki wiki
[info] * Phase 4: Slaves Recovery Phase..
[info] * Phase 4.1: Starting Slaves in parallel..
[info] Resetting slave 192.168.30.103(192.168.30.103:3306) and starting replication from the new master 192.168.30.102(192.168.30.102:3306)..
[info] Executed CHANGE MASTER.
[info] Slave started.
[info] All new slave servers recovered successfully.
​“`

06 MHA切换完毕过程,架构故障转移切换完毕后做清理阶段,并进行最终汇报

​“`tiki wiki
[info] * Phase 5: New master cleanup phase..
—– Failover Report —–
Master failover to 192.168.30.102(192.168.30.102:3306) completed successfully.
Mon Jan 2 15:32:37 2023 – [info] Sending mail..
​“`

**实现MHA高可用切换的最终结果:**

01 MHA故障节点转移后,邮件信息提示:

02 核实MHA的VIP地址已经发生漂移:

03 核实MHA进行切换的新主从关系,以及配置文件中的故障节点信息已经踢除:

​“`tiki wiki
# 获取新主从关系
[root@xiaoQ-03 ~]# mysql -e “show slave status\G”
*************************** 1. row ***************************
Slave_IO_State: Waiting for source to send event
Master_Host: 192.168.30.102
Master_User: repl
Master_Port: 3306

# 查看MHA配置文件信息
[root@xiaoQ-03 ~]# cat /etc/mha/app1.cnf
[binlog1]
hostname=192.168.30.103
master_binlog_dir=/data/binlog_server/
no_master=1

[server default]
manager_log=/var/log/mha/app1/manager
manager_workdir=/var/log/mha/app1
master_binlog_dir=/data/binlog
master_ip_failover_script=/usr/local/bin/master_ip_failover
password=mha
ping_interval=2
repl_password=123456
repl_user=repl
report_script=/usr/local/bin/send_report
ssh_user=root
user=mha

[server2]
hostname=192.168.30.102
port=3306

[server3]
hostname=192.168.30.103
port=3306
— 故障节点信息已从配置文件中清理
​“`

04 MHA程序终止(管理程序manager终止)

以上故障模拟操作完成后,查看MHA程序进程已经终止:

​“`tiki wiki
[root@xiaoQ-03 binlog_server]# ps -ef|grep mha
root 109328 1798 0 15:48 pts/0 00:00:00 grep –color=auto mha
[root@xiaoQ-03 ~]# masterha_check_status –conf=/etc/mha/app1.cnf
app1 is stopped(2:NOT_RUNNING).
​“`

####
“`

## 4 高可用修复

“`sh
MHA故障通用修复方法步骤:`(前提是MHA进行正常故障切换,通过日志可以检查是否进行正常的故障转移)`

01 检查节点状态:

​“`tiki wiki
# 检查节点数据库运行状态
[root@xiaoQ-01 ~]# /etc/init.d/mysqld status
ERROR! MySQL is not running
— 发现数据库服务有异常节点进行恢复
[root@xiaoQ-01 ~]# /etc/init.d/mysqld start
Starting MySQL. SUCCESS!
— 恢复DB01异常数据库服务节点
​“`

在实际生产环境中,如果主库异常无法实现重新启动修复,可能就需要准备一台新的节点主机,重新构建1主2从的架构;

但是如果使用新的节点主机,进行主从架构重构,修复高可用环境,就需要考虑新主机的在恢复数据的时间损耗;

总之,需要将新节点的数据信息进行同步后,再将新节点变为新的从库,从而修复高可用主从关系,具体如何修复数据需要考虑实际情况

02 检查主从关系:

​“`tiki wiki
[root@xiaoQ-03 ~]# mysql -e “show slave status\G”|grep “Master_Host”
Master_Host: 192.168.30.102
— 确认切换后的新主库信息
[root@xiaoQ-02 ~]# /etc/init.d/mysqld status
SUCCESS! MySQL running (27332)
— 在DB02上核实节点运行的状态
[root@xiaoQ-01 ~]# mysql -e “show slave status\G”|grep “Master_Host”
— 在DB01上无法获取主从关系信息,需要修复DB01主从,最终实现1主2从效果
​“`

03 修复主从关系:

​“`tiki wiki
# 在DB01上修复主从:
[root@xiaoQ-01 ~]# mysql
db02 [(none)]>change master to
master_host=’192.168.30.102′,
master_user=’repl’,
master_password=’123456′,
master_auto_position=1;
db02 [(none)]> start slave;

# 在DB01上核实主从关系
[root@xiaoQ-01 ~]# mysql -e “show slave status\G”|grep “Master_Host”
Master_Host: 192.168.30.102
​“`

04 检查虚拟地址:

​“`tiki wiki
[root@xiaoQ-02 ~]# ip a s eth0
2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:61:cd:06 brd ff:ff:ff:ff:ff:ff
inet 192.168.30.102/24 brd 192.168.30.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet 192.168.30.110/24 brd 192.168.30.255 scope global secondary eth0:1
valid_lft forever preferred_lft forever
inet6 fe80::560c:13cd:6107:b0de/64 scope link tentative noprefixroute dadfailed
valid_lft forever preferred_lft forever
inet6 fe80::727b:5d03:94c0:9382/64 scope link noprefixroute
valid_lft forever preferred_lft forever

ifconfig eth0:1 192.168.30.110/24
— 如果上面vip漂移失败,出现问题可以手工添加VIP
​“`

05 恢复日志同步:

​“`tiki wiki
# 检查binlog_server状态
[root@xiaoQ-03 ~]# ps -ef|grep mysqlbinlog
— binlog_server日志同步进程消失

# 修复binlog_server状态
[root@xiaoQ-03 ~]# rm -rf /data/binlog_server/*
[root@xiaoQ-03 ~]# cd /data/binlog_server/
[root@xiaoQ-03 binlog_server]# mysql -e “show slave status\G”|grep “Master_Log”
Master_Log_File: mysql-bin.000002
Read_Master_Log_Pos: 1229
Relay_Master_Log_File: mysql-bin.000002
Exec_Master_Log_Pos: 1229
[root@xiaoQ-03 binlog_server]# mysqlbinlog -R –host=192.168.30.102 –user=mha –password=mha –raw –stop-never mysql-bin.000002 &
[root@xiaoQ-03 binlog_server]# ll
总用量 4
-rw-r—– 1 root root 1229 1月 2 16:28 mysql-bin.000002
​“`

06 调整配置文件:

​“`tiki wiki
# 确认核实配置文件中三个节点信息
[root@xiaoQ-03 binlog_server]# cat /etc/mha/app1.cnf
[binlog1]
hostname=192.168.30.103
master_binlog_dir=/data/binlog_server/
no_master=1

[server default]
manager_log=/var/log/mha/app1/manager
manager_workdir=/var/log/mha/app1
master_binlog_dir=/data/binlog
master_ip_failover_script=/usr/local/bin/master_ip_failover
password=mha
ping_interval=2
repl_password=123456
repl_user=repl
report_script=/usr/local/bin/send_report
ssh_user=root
user=mha

[server2]
hostname=192.168.30.102
port=3306

[server3]
hostname=192.168.30.103
port=3306

[server1]
hostname=192.168.30.101
port=3306
— 添加DB01故障节点到配置文件中

[root@xiaoQ-03 ~]# masterha_conf_host –command=add –conf=/etc/mha/app1.cnf –hostname=192.168.30.101 –block=server1 –params=”port=3306″
— 利用命令脚本添加新的节点信息
[root@xiaoQ-03 ~]# masterha_conf_host –command=delete –conf=/etc/mha/app1.cnf –bl =server1
— 利用命令脚本删除指定的节点信息
​“`

07 核实互信情况:

​“`tiki wiki
[root@xiaoQ-03 ~]# masterha_check_ssh –conf=/etc/mha/app1.cnf
Mon Jan 2 16:42:35 2023 – [info] All SSH connection tests passed successfully.
[root@xiaoQ-03 binlog_server]# masterha_check_repl –conf=/etc/mha/app1.cnf
MySQL Replication Health is OK.
​“`

08 恢复启动MHA:

​“`tiki wiki
[root@xiaoQ-03 ~]# nohup masterha_manager –conf=/etc/mha/app1.cnf –remove_dead_master_conf –ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
[root@xiaoQ-03 ~]# masterha_check_status –conf=/etc/mha/app1.cnf
app1 (pid:109463) is running(0:PING_OK), master:192.168.30.102
​“`

####
“`

## 5 高可用维护

“`sh
**实现MHA高可用主节点在线切换(手工操作)**

可以在主库没有故障的情况下,利用手工方式将主库业务切换到其它的从库节点上,从而解放原有主库节点(维护性操作时应用);

​“`tiki wiki
# 关闭MHA服务程序
masterha_stop –conf=/etc/mha/app1.cnf
— 关闭mha程序是保证手工切换时,不会受到mha自动切换的影响

# 执行MHA手工切换
masterha_master_switch –conf=/etc/mha/app1.cnf –master_state=alive –new_master_host=192.168.30.101 –orig_master_is_new_slave –running_updates_limit=10000
…省略部分信息…
It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 192.168.30.102(192.168.30.102:3306)? (YES/no):
— 以上说明信息,表示在进行切换之前,在原有主库节点执行FLUSH NO_WRITE_TO_BINLOG TABLES这个命令
— 此命令表示,关闭所有打开的表,强制关闭所有正在使用的表,刷新binlog日志信息,确保完整日志信息推送到从库;

# 关闭原主库的写入功能(暂时不让主库进行binlog日志记录)
db02 [(none)]>FLUSH NO_WRITE_TO_BINLOG TABLES;
Query OK, 0 rows affected (0.01 sec)

It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 192.168.30.102(192.168.30.102:3306)? (YES/no): yes

Starting master switch from 192.168.30.102(192.168.30.102:3306) to 192.168.30.101(192.168.30.101:3306)? (yes/NO): yes
— 进行再一次核实确认,是否进行手工切换

master_ip_online_change_script is not defined. If you do not disable writes on the current master manually, applications keep writing on the current master. Is it ok to proceed? (yes/NO): yes
— 表示master_ip_online_change_script此脚本没有定义,如果没有禁止当前主库写入的禁止,业务应用仍旧访问当前主库
— 因为此时VIP还没有进行转移;
[info] Switching master to 192.168.30.101(192.168.30.101:3306) completed successfully.
— 提示切换完成

# 进行MHA切换核验
[root@xiaoQ-03 ~]# nohup masterha_manager –conf=/etc/mha/app1.cnf –remove_dead_master_conf –ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
— 重新启动MHA程序

[root@xiaoQ-03 ~]# masterha_check_status –conf=/etc/mha/app1.cnf
app1 is stopped(2:NOT_RUNNING).
[1]+ 退出 1 nohup masterha_manager –conf=/etc/mha/app1.cnf –remove_dead_master_conf –ignore_last_failover < /dev/null > /var/log/mha/app1/manager.log 2>&1
— 检查状态失败,mha没有启动成功,因为vip信息并没有进行有效漂移

masterha_master_switch –conf=/etc/mha/app1.cnf –master_state=alive –new_master_host=192.168.30.102 –orig_master_is_new_slave –running_updates_limit=10000
— 临时先切换回原有主节点,恢复MHA服务状态

[root@xiaoQ-03 ~]# nohup masterha_manager –conf=/etc/mha/app1.cnf –remove_dead_master_conf –ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
[1] 65799
[root@xiaoQ-03 ~]# masterha_check_status –conf=/etc/mha/app1.cnf
app1 (pid:65799) is running(0:PING_OK), master:192.168.30.102
— mha服务状态恢复
​“`

执行切换命令参数信息说明:

| 序号 | 参数信息 | 解释说明 |
| —- | ————————– | ———————————————————— |
| 01 | –master_state | 执行手工切换的状态,alive表示主节点存活状态进行切换 |
| 02 | –new_master_host | 指定切换后的新主库节点的地址信息 |
| 03 | –orig_master_is_new_slave | 将原有主库指定为新的从库角色 |
| 04 | –running_updates_limit | 指定切换过程的时间限制,超过指定时间未完成切换,即切换失败,单位毫秒 |

在进行MHA高可用节点在线手工切换时,有以下信息需要注意:

– 在进行MHA高可用手工切换时,无法自动调整原有主库的binlog_server,需要手工重新拉取新主库的binlog;
– 在进行MHA高可用手工切换时,无法进行触发邮件脚本功能,邮件发送功能只能在MHA产生故障转移时触发;
– 在进行MHA高可用手工切换时,需要进行架构主从关系的切换,以及可以调整转移VIP地址信息;
– 在进行MHA高可用手工切换时,需要对切换前的主库进行锁定(FTWRL flush tables with read lock),避免数据不一致

**进行MHA手工在线切换的合理操作:**

**01 应用master_ip_online_change_script功能脚本**

功能描述:此脚本可以在线进行切换时,自动锁定原主库,以及将原主库VIP地址进行自动飘移;

编写应用切换脚本文件信息:

​“`tiki wiki
# 编写MHA手工切换脚本文件:
[root@xiaoQ-03 ~]# cd /usr/local/bin/
[root@xiaoQ-03 bin]# cp master_ip_online_change master_ip_online_change.bak
[root@xiaoQ-03 bin]# vim master_ip_online_change
21 my $vip = “192.168.30.110”;
22 my $key = “1”;
23 my $ssh_start_vip = “/sbin/ifconfig eth0:$key $vip”;
24 my $ssh_stop_vip = “/sbin/ifconfig eth0:$key $vip down”;
25 my $ssh_Bcast_arp= “/sbin/arping -I eth0 -c 3 -A 192.168.30.110”;

# 修改MHA服务程序配置文件:
[root@xiaoQ-03 ~]# vim /etc/mha/app1.cnf
master_ip_online_change_script=/usr/local/bin/master_ip_online_change

# 关闭MHA服务程序进行核查:
[root@xiaoQ-03 ~]# masterha_stop –conf=/etc/mha/app1.cnf
[root@xiaoQ-03 ~]# masterha_check_status –conf=/etc/mha/app1.cnf
app1 is stopped(2:NOT_RUNNING).
[root@xiaoQ-03 ~]# masterha_check_repl –conf=/etc/mha/app1.cnf
MySQL Replication Health is OK.

# 进行MHA服务手工在线切换:
[root@xiaoQ-03 ~]# masterha_master_switch –conf=/etc/mha/app1.cnf –master_state=alive –new_master_host=192.168.30.101 –orig_master_is_new_slave –running_updates_limit=10000
…省略部分信息…
It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 192.168.30.102(192.168.30.102:3306)? (YES/no): yes
— FLUSH NO_WRITE_TO_BINLOG TABLES 命令在原有主库节点需要再执行一次;
Starting master switch from 192.168.30.102(192.168.30.102:3306) to 192.168.30.101(192.168.30.101:3306)? (yes/NO): yes
Sat Jan 7 13:48:50 2023 – [info] Switching master to 192.168.30.101(192.168.30.101:3306) completed successfully.

# 重构binlogserver功能
[root@xiaoQ-03 ~]# cd /data/binlog_server/
[root@xiaoQ-03 binlog_server]# rm -rf ./*
[root@xiaoQ-03 binlog_server]# mysql -e “show slave status\G”|grep “Master_Log”
Master_Log_File: mysql-bin.000005
Read_Master_Log_Pos: 236
Relay_Master_Log_File: mysql-bin.000005
Exec_Master_Log_Pos: 236
[root@xiaoQ-03 binlog_server]# mysqlbinlog -R –host=192.168.30.101 –user=mha –password=mha –raw –stop-never mysql-bin.000005 &
— 此功能不进行重新配置,会导致MHA服务无法正常启动(若已经启动过,可以将进程杀死,重新启动)

# 进行MHA服务手工切换核验:
[root@xiaoQ-03 ~]# nohup masterha_manager –conf=/etc/mha/app1.cnf –remove_dead_master_conf –ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
— 重新启动MHA程序
[root@xiaoQ-03 binlog_server]# masterha_check_status –conf=/etc/mha/app1.cnf
app1 (pid:17462) is running(0:PING_OK), master:192.168.30.101
​“`

##
“`

# 第九章 读写分离

“`sh
读写分离架构最终目的:实现业务写的请求到达主库,实现业务读的请求到达从库,从而减少主库的压力,实现不同请求的压力分担;

可以利用读写分离中间件实现以上的功能需求:atlas(360公司出品) `proxySQL`

利用读写分离中间件的设置,当业务请求有select查询时,将请求发送给从库,当业务请求有update insert等修改时,将请求发送给主库
“`

![1673922233191](D:/学习//4_数据库/视频和笔记/系统运维95期-Day11-数据库服务读写分离架构-PrxoySQL/课程笔记目录/MySQL中级数据库课程-16章-数据库读写分离架构/课件配图资料/1673922233191.png)

“`sh
proxySQL是基于MySQL的一款开源的中间件的产品,是一个灵活的MySQL代理层,可以实现读写分离:

– proxySQL数据库中间件支持Query路由功能;
– pxoxySQL数据库中间件支持动态指定某个SQL进行缓存;
– proxySQL数据库中间件支持动态加载配置信息(无需重启ProxySQL服务)
– proxySQL数据库中间件支持故障切换和SQL的过滤功能(安全机制)

ProxySQL的参考网站连接:

ProxySQL

https://github.com/sysown/proxysql/releases

步骤一:读写分离架构部署环境规划

为了实现读写分离架构构建,需要准备好三节点数据库+GTID复制环境+MHA环境(`普通主从环境也可以构建`);

| 主机角色 | 主机名称 | 地址信息 |
| ———- | —————————- | ——– |
| 主库服务器 | 192.168.30.101 | 3306 |
| 从库服务器 | 192.168.30.102 | 3306 |
| 从库服务器 | 192.168.30.103(兼做管理节点) | 3306 |

步骤二:读写分离架构软件下载安装

通过官方网站或者github可以下载proxySQL软件程序,并上传到数据库服务器中进行安装;

​“`tiki wiki
# 上传安装软件程序
[root@xiaoQ-03 ~]# rpm -ivh proxysql-2.4.6-1-centos7.x86_64.rpm
警告:proxysql-2.4.6-1-centos7.x86_64.rpm: V4 RSA/SHA512 Signature, 密钥 ID 8217c97e: NOKEY
准备中… ################################# [100%]

# 启动运行软件程序
[root@xiaoQ-03 ~]# systemctl start proxysql
[root@xiaoQ-03 ~]# netstat -lntup
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:6032 0.0.0.0:* LISTEN 83020/proxysql
tcp 0 0 0.0.0.0:6033 0.0.0.0:* LISTEN 83020/proxysql
— 启动生成的6032端口为管理端口,用于配置数据库中间件的功能信息连接此端口
— 启动生成的6033端口为访问端口,用于提供对外的业务访问此端口
​“`

步骤三:读写分离架构软件管理配置

在连接进入6032端口之后,表示进行proxysql的管理终端环境,终端环境中会加载五个重要的功能库:

| 序号 | 库信息 | 配置信息 | 解释说明 |
| —- | ————- | —————————- | —————————————————— |
| 01 | `main` | mysql_servers | 表示后端可以连接mysql服务器的列表 |
| | | mysql_users | 表示配置后端数据库的连接账号和监控账号 |
| | | mysql_query_rules | 表示指定query路由到后端不同服务器的规则列表 |
| | | mysql_replication_hostgroups | 表示节点分组配置信息,可以配置多个写或读节点到一个组中 |
| 02 | disk | | 表示持久化的磁盘配置信息 |
| 03 | `stats` | | 表示统计信息的汇总 |
| 04 | `monitor` | | 表示监控收集的信息,比如数据库的监控状态等 |
| 05 | stats_history | | 表示收集的有关软件内部功能的历史指标 |

> 说明:一般服务是通过配置文件保存功能配置信息,proxySQL是通过数据库中的表进行配置信息的存储设置;

​“`tiki wiki
# 连接进入到proxySQL管理终端
[root@xiaoQ-03 ~]# mysql -uadmin -padmin -h127.0.0.1 -P6032
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.5.30 (ProxySQL Admin Module)

# 查看proxySQL终端数据库信息
>show databases;
+—–+—————–+———————————————-+
| seq | name | file |
+—–+—————–+———————————————-+
| 0 | main | |
| 2 | disk | /var/lib/proxysql/proxysql.db |
| 3 | stats | |
| 4 | monitor | |
| 5 | stats_history | /var/lib/proxysql/proxysql_stats.db |
+—–+—————–+———————————————-+
5 rows in set (0.00 sec)
— 不需要use到相应数据库中进行操作,可以操作的数据表信息如下

# 查看proxySQL终端数据表信息
>show tables ;
+—————————————————————–+
| tables
+—————————————————————–+
| global_variables
| mysql_aws_aurora_hostgroups
| mysql_collations
| mysql_firewall_whitelist_rules
| mysql_firewall_whitelist_sqli_fingerprints
| mysql_firewall_whitelist_users
| mysql_galera_hostgroups
| mysql_group_replication_hostgroups
| mysql_query_rules
| mysql_query_rules_fast_routing
| mysql_replication_hostgroups
| mysql_servers
| mysql_users
| proxysql_servers
| restapi_routes
| runtime_checksums_values
| runtime_global_variables
| runtime_mysql_aws_aurora_hostgroups
| runtime_mysql_firewall_whitelist_rules
| runtime_mysql_firewall_whitelist_sqli_fingerprints
| runtime_mysql_firewall_whitelist_users
| runtime_mysql_galera_hostgroups
| runtime_mysql_group_replication_hostgroups
| runtime_mysql_query_rules
| runtime_mysql_query_rules_fast_routing
| runtime_mysql_replication_hostgroups
| runtime_mysql_servers
| runtime_mysql_users
| runtime_proxysql_servers
| runtime_restapi_routes
| runtime_scheduler
| scheduler
+—————————————————————–+
32 rows in set (0.00 sec)
— 表名以runtiem_开头的表示proxySQL服务中当前运行的配置内容,不能直接修改,不带runtime是下文图中mem相关的配置
​“`

**ProxySQL管理接口的多层配置关系:**

ProxySQL整套配置系统分为三层:

**第一层:RUNTIME:**

代表proxySQL当前正在使用的配置,无法直接修改此配置,必须要从下一层(MEM层)load加载进来;

**第二层:MEMORY(主要修改的配置表)**

memory层上面连接runtime层,下面连接disk持久化存储层;

在这层可以在线操作ProxySQL配置,随意进行修改,不会影响生产环境,确认正常之后再加载到runtime和持久化保存到磁盘上

具体修改操作方法为:insert、update、delete、select;

**第三层:DISK/CFG FILE**

持久化配置信息,重启时可以从磁盘快速加载回来;

ProxySQL不同层次间移动配置信息:

为了将配置持久化到磁盘或者应用到runtime,在管理接口下有一系列管理命令来实现相关功能配置:

​“`tiki wiki
# 01 user相关配置
LOAD MYSQL USERS TO RUNTIME;
— MEM加载到runtime
SAVE MYSQL USERS TO MEMORY;
— RUNTIME保存至MEM
LOAD MYSQL USERS FROM DISK;
— DISK加载到MEM
SAVE MYSQL USERS TO DISK;
— MEM保存至DISK
LOAD MYSQL USERS FROM CONFIG
— CFG加载到MEM

# 02 server相关配置
LOAD MYSQL SERVERS TO RUNTIME;
— MEM加载到RUNTIME
SAVE MYSQL SERVERS TO MEMORY;
— RUNTIME保存至MEM
LOAD MYSQL SERVERS FROM DISK;
— DISK加载到MEM
SAVE MYSQL SERVERS TO DISK;
— MEM保存至DISK
LOAD MYSQL SERVERS FROM CONFIG
— CFG加载到MEM

# 03 MYSQL QUERY RULES相关配置
LOAD MYSQL QUERY RULES TO RUNTIME;
— MEM加载到RUNTIME
SAVE MYSQL QUERY RULES TO MEMORY;
— RUNTIME保存至MEM
LOAD MYSQL QUERY RULES FROM DISK;
— DISK加载到MEM
SAVE MYSQL QUERY RULES TO DISK;
— MEM保存至DISK
LOAD MYSQL QUERY RULES FROM CONFIG
— CFG加载到MEM

# 03 MYSQL VARIABLES相关配置
LOAD MYSQL VARIABLES TO RUNTIME;
— MEM加载到RUNTIME
SAVE MYSQL VARIABLES TO MEMORY;
— RUNTIME保存至MEM
LOAD MYSQL VARIABLES FROM DISK;
— DISK加载到MEM
SAVE MYSQL VARIABLES TO DISK;
— MEM保存至DISK
LOAD MYSQL VARIABLES FROM CONFIG
— CFG加载到MEM
​“`

需要注意:只有load到runtime状态时才会验证配置,在保存到mem或disk时,都不会发生任何警告或错误;

当load到runtime时,如果出现了错误信息,将恢复为之前保存的状态,这时可以根据错误日志信息做检查;

> 总结:日常配置过程大部分时间是在mem中进行配置,然后load到runtime,或者save到disk中,对于cfg很少使用;

**ProxySQL基于SQL语句进行读写分离实践配置:**

①. 在mysql_replication_hostgroup表中,配置读写组编号:

proxySQL会根据server的read only的取值将服务器进行分组:

– read_only=0的server,即master会被分到编号为10的写组;
– read_only=1的server,即slave会被分到编号为20的读组;(`所以需要将从库设置:set global read_only=1`)

​“`SQL
db03 [(none)]>insert into mysql_replication_hostgroups(writer_hostgroup,reader_hostgroup,comment) values(10,20,’proxy’);
Query OK, 1 row affected (0.00 sec)

db03 [(none)]>save mysql servers to disk;
Query OK, 0 rows affected (0.01 sec)

db03 [(none)]>load mysql servers to runtime;
Query OK, 0 rows affected (0.00 sec)

db03 [(none)]>select * from mysql_replication_hostgroups\G
*************************** 1. row ***************************
writer_hostgroup: 10
reader_hostgroup: 20
check_type: read_only
comment: proxy
1 row in set (0.00 sec)
​“`

② 添加主机到ProxySQL

​“`sql
db03 [(none)]>insert into mysql_servers(hostgroup_id,hostname,port) values (10,’192.168.137.51′,3306);
Query OK, 1 row affected (0.00 sec)

db03 [(none)]>insert into mysql_servers(hostgroup_id,hostname,port) values (20,’192.168.137.52′,3306);
Query OK, 1 row affected (0.00 sec)

db03 [(none)]>insert into mysql_servers(hostgroup_id,hostname,port) values (20,’192.168.137.53′,3306);
Query OK, 1 row affected (0.00 sec)

db03 [(none)]>save mysql servers to disk;
Query OK, 0 rows affected (0.01 sec)

db03 [(none)]>load mysql servers to runtime;
Query OK, 0 rows affected (0.00 sec)

db03 [(none)]>select * from mysql_servers\G;
*************************** 1. row ***************************
hostgroup_id: 20
hostname: 192.168.30.102
port: 3306
gtid_port: 0
status: ONLINE
weight: 1
compression: 0
max_connections: 1000
max_replication_lag: 0
use_ssl: 0
max_latency_ms: 0
comment:
*************************** 2. row ***************************
hostgroup_id: 20
hostname: 192.168.30.103
port: 3306
gtid_port: 0
status: ONLINE
weight: 1
compression: 0
max_connections: 1000
max_replication_lag: 0
use_ssl: 0
max_latency_ms: 0
comment:
*************************** 3. row ***************************
hostgroup_id: 10
hostname: 192.168.30.110
port: 3306
gtid_port: 0
status: ONLINE
weight: 1
compression: 0
max_connections: 1000
max_replication_lag: 0
use_ssl: 0
max_latency_ms: 0
comment:
3 rows in set (0.00 sec)
​“`

③ 创建监控用户,并开启监控

利用监控用户对后端节点的运行情况进行监控数据同步,一旦后端节点出现数据同步异常,就不要再向故障节点发送相应业务请求;

​“`tiki wiki
# 主库创建监控用户
db01 [(none)]>create user monitor@’%’ identified with mysql_native_password by ‘123’;
Query OK, 0 rows affected (0.02 sec)
db01 [(none)]>grant replication client on *.* to monitor@’%’;
Query OK, 0 rows affected (0.01 sec)

# 在proxysql中修改variables表配置信息
db03 [(none)]>set mysql-monitor_username=’monitor’;
Query OK, 1 row affected (0.00 sec)

db03 [(none)]>set mysql-monitor_password=’123′;
Query OK, 1 row affected (0.00 sec)
— 以上变量信息修改为方法一

db03 [(none)]>update global_variables set variable_value=’monitor’ where variable_name=’mysql-monitor_username’;
Query OK, 1 row affected (0.00 sec)

db03 [(none)]>update global_variables set variable_value=’123′ where variable_name=’mysql-monitor_password’;
Query OK, 1 row affected (0.00 sec)
— 以上变量信息修改为方法二

db03 [(none)]>load mysql variables to runtime;
Query OK, 0 rows affected (0.01 sec)

db03 [(none)]>save mysql variables to disk;
Query OK, 154 rows affected (0.00 sec)

# 检查核实配置信息
db03 [(none)]>select @@mysql-monitor_username\G
*************************** 1. row ***************************
@@mysql-monitor_username: monitor
1 row in set (0.00 sec)

db03 [(none)]>select @@mysql-monitor_password\G
*************************** 1. row ***************************
@@mysql-monitor_password: 123
1 row in set (0.00 sec)

# 查询监控日志信息
db03 [(none)]>select * from mysql_server_connect_log;
+——————-+——+————————-+———————————+—————————————-+
| hostname | port | time_start_us | connect_success_time_us | connect_error |
+——————-+——+————————-+———————————+—————————————-+
| 192.168.30.110 | 3306 | 1674026545375939 | 2831 | NULL |
| 192.168.30.103 | 3306 | 1674026546137911 | 1480 | NULL |
| 192.168.30.102 | 3306 | 1674026546899730 | 3781 | NULL |
— 检查确认所有节点的连接访问情况

db03 [(none)]>select * from mysql_server_ping_log;
+——————-+——+————————-+—————————–+—————+
| hostname | port | time_start_us | ping_success_time_us | ping_error |
+——————-+——+————————-+—————————–+—————+
| 192.168.30.102 | 3306 | 1674026696004217 | 1139 | NULL |
| 192.168.30.103 | 3306 | 1674026696095455 | 194 | NULL |
| 192.168.30.110 | 3306 | 1674026696186794 | 1466 | NULL |
— 检查确认所有节点的网络连通情况

db03 [(none)]>select * from mysql_server_read_only_log limit 3;
+——————-+——+————————-+———————-+————-+——–+
| hostname | port | time_start_us | success_time_us | read_only | error |
+——————-+——+————————-+———————-+————-+——–+
| 192.168.30.110 | 3306 | 1674027579464285 | 1325 | 0 | NULL |
| 192.168.30.102 | 3306 | 1674027579479777 | 1743 | 1 | NULL |
| 192.168.30.103 | 3306 | 1674027579494993 | 308 | 1 | NULL |
— 检查确认所有节点的只读状态信息(获取主库或从库主机信息)

db03 [(none)]>select * from mysql_server_replication_lag_log;
Empty set (0.00 sec)
— 检查确认所有节点的主从延时情况
​“`

④ 创建应用用户信息

创建数据库应用用户信息,利用应用用户,可以使proxySQL进行数据库节点的操作管理;

​“`tiki wiki
# 主库创建应用用户
db01 [(none)]>create user root@’%’ identified with mysql_native_password by ‘123’;
Query OK, 0 rows affected (0.00 sec)

db01 [(none)]>grant all on *.* to root@’%’;
Query OK, 0 rows affected (0.00 sec)

# 在proxysql中添加数据库节点的管理用户信息
db03 [(none)]>insert into mysql_users(username,password,default_hostgroup) values(‘root’,’123′,10);
Query OK, 1 row affected (0.00 sec)

db03 [(none)]>load mysql users to runtime;
Query OK, 0 rows affected (0.00 sec)

db03 [(none)]>save mysql users to disk;
Query OK, 0 rows affected (0.00 sec)

# 早期版本,需要开启事务的持续化(忽略)
update mysql_users set transaction_persistent=1 where username=’root’;
load mysql users to runtime;
save mysql users to disk;
— 事务路由分配持续性,同一个事务的语句不会被分配到不同的组
​“`

⑤ 实用的读写规则配置

​“`sql
> insert into mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) values (1,1,’^select.*for update$’,10,1);
> insert into mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) values (2,1,’^select’,20,1);
— 其余数据库操作语句信息,默认路由放置到主节点进行执行

> load mysql query rules to runtime;
> save mysql query rules to disk;
​“`

select … for update规则的rule_id必须要小于普通的select规则的rule_id,proxySQL是根据rule_id的顺序进行规则匹配的;

⑥ 测试读写分离效果

​“`tiki wiki
[root@xiaoQ-03 ~]# mysql -uroot -p123 -P6033 -h127.0.0.1 -e “begin;select @@server_id;commit”
+—————–+
| @@server_id |
+—————–+
| 51 |
+—————–+
— 非查询操作走的是主节点

[root@xiaoQ-03 ~]# mysql -uroot -p123 -P6033 -h127.0.0.1 -e “select @@server_id;”
+—————–+
| @@server_id |
+—————–+
| 52 |
+—————–+
[root@xiaoQ-03 ~]# mysql -uroot -p123 -P6033 -h127.0.0.1 -e “select @@server_id;”
+—————–+
| @@server_id |
+—————–+
| 53 |
+—————–+
— 查询操作走的是从节点

>select * from stats_mysql_query_digest\G
— 这个表对于分析SQL语句至关重要,是分析语句性能、定制路由规则指标的最主要来源
​“`

读写分离配置过程总结:

| 步骤 | 操作说明 | 涉及数据表信息 | 涉及操作信息 |
| —- | ——————————— | ——————————- | ———— |
| 01 | 设置从库只读模式 | | read_only=1 |
| 02 | 添加主机组信息 | mysql_replication_hostgroups | |
| 03 | 添加主机组节点信息 | mysql_servers | |
| 04 | 添加用户信息(监控用户 应用用户) | global_variables
mysql_users | |
| 05 | 添加读写分离规则 | mysql_query_rules | |

步骤四:读写分离架构软件配置扩展

① 基于端口进行读写分离路由

​“`tiki wiki
# 修改proxySQL监听SQL流量的端口号,监听多端口信息
> set mysql-interfaces=’0.0.0.0:6033;0.0.0.0:6034′

# 使监听端口配置信息生效
> save mysql variables to disk;
[root@xiaoQ-03 ~]# systemctl restart proxysql

# 设定相应读写分离路由规则
> delete from mysql_query_rules;
— 为了测试效果,先清空已有规则信息
> insert into mysql_query_rules(rule_id,active,proxy_port,destination_hostgroup,apply) values(1,1,6033,10,1),(2,1,6034,20,1);
> load mysql query rules to runtime;
> save mysql query rules to disk;
— 除了基于端口进行分离,还可以基于监听地址(修改字段proxy_addr即可),也可以基于客户端地址(修改字段client_addr字段即可);
​“`

② 基于用户进行读写分离路由

​“`sql
> insert into mysql_users(username,password,default_hostgroup) values (‘write’,’123′,10),(‘reader’,’123′,20);
> load mysql users to runtime;
> save mysql users to disk;

> delete from mysql_query_rules;
— 为了测试效果,先清空已有规则信息
> insert into mysql_query_rules(rule_id,active,username,destination_hostgroup,apply) values (1,1,’write’,10,1),(2,1,’reader’,20,1);
> load mysql users to runtime;
> save mysql users to disk;
​“`

###
“`

![image-20250217124159058](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20250217124159058.png)

# 第十章 索引

## 1 概念

“`sh
#### 1.6.1 数据库索引相关概念

数据库中的索引类似一本书的目录,如果希望快速了解这本书特定的章节内容,则可以通过目录查找指定章节对应页码;

根据页码快速找到需要的信息。

索引是数据库中用来提高数据读取性能的常用工具,所有mysql列类型都可以被索引,对相关列使用索引;

可以是提高select操作性能的最佳途径,可以尽可能快的锁定要查询数据的范围,从而达到加速查询的目的(减少IO消耗);

一般索引设置都是应用在比较大的数据表上,比如百万级别、千万级别或亿级别的数据表中,从而完成一些针对性优化;

`可以简单理解:数据库索引相当于书的目录,可以借助索引有针对的查看相应数据的信息,避免了全盘检索带来的工作量;`

主要利用MySQL中的索引,可以快速锁定查询范围,mysql索引比较适合范围查找数据;

#### 1.6.2 数据库索引模型介绍

在使用常规索引类型时,对应的索引创建后,也会在内存中构建索引模型结构,可以利用模型结构来理解数据检索过程;

在MySQL数据库服务中,是有很多种索引类型模型的,但是比较常用的索引类型模型主要有:

索引树结构模型可视化网站:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

| 序号 | 类型 | 说明 |
| —— | ——– | ————————————– |
| 类型01 | `B+Tree` | 默认类型索引(擅于等值和区间查询数据) |
| 类型02 | Hash | 算法类型索引(擅于等值查询数据) |
| 类型03 | RTree | 空间类型索引(擅于区间查询数据) |
| 类型04 | Fulltext | 全文类型索引(擅于过滤查询数据) |

##### 01 索引模型类型一:B+Tree

在讲解B+Tree之前先了解一下树的整体结构,无非就是二叉树、红黑树、Btree、B+tree等;

而树的查找性能取决于树的高度,让树尽可能平衡是为了降低树的高度。

为什么MySQL会选用B+树的结构,可以先来看看其他的树形结构:

– **二叉树**

二叉树的每一个节点都只有两个子节点,当需要向其插入更多的数据的时候,就必须要增加树的高度,而增加树的高度会导致IO消耗大;

对于二叉树而言,它的查找操作的时间复杂度就是树的高度,树的高度越高查询性能就会随着数据的增多越来越低。

二叉树节点中,还存在非正常的倾斜(比如ID自增的情况)的二叉树,查询一次数据就相当于全表搜索,因此二叉树的查询性能特别差
“`

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day13-数据库服务存储引擎/课程笔记目录/MySQL中级数据库课程-07章-数据库索引知识介绍/课件配图资料/1682955300451.png)

“`sh
从磁盘物理硬件角度分析,利用二叉树的方案调取数据性能也是最差的,因为索引不止存在内存中,还要写到磁盘上。

你可以想象一下一棵 100 万节点的平衡二叉树,假设树高 20。一次查询可能需要访问 20 个数据块。

在机械硬盘时代,从磁盘随机读一个数据块需要 10 ms 左右的寻址时间。

也就是说,对于一个 100 万行的表,如果使用二叉树来存储,单独访问一个行可能需要 20 个 10 ms 的时间,这个查询可真够慢的。

为了让一个查询尽量少地读磁盘,就必须让查询过程访问尽量少的数据块。

那么,我们就不应该使用二叉树,而是要使用“N 叉”树。

N 叉树由于在读写上的性能优点,以及适配磁盘的访问模式,已经被广泛应用在数据库引擎中了。

一颗空树或者满足以下性质的二叉树被称之为二叉搜索树:

– 如果左子树不为空,则左子树所有节点值都小于根节点的值;
– 如果右子树不为空,则右子树所有节点值都大于根节点的值;
– 任意一棵子树也是一棵二叉搜索树

> 说明:查找时间复杂度是O(logn),但在极端情况下会出现时间复杂度是O(n)的情况

– **红黑树:**

在原本二叉搜索树结构中进行数据查询时,利用其结构特性可以更快的进行数据的查询;

但是一旦二叉树讲解为链表结构后,查询效率会严重降低;

因此,在二叉搜索树实际应用过程会有所缺陷,为了弥补这个缺陷,就出现了平衡二叉搜索树结构(AVL树-属于平衡树);

平衡树(Balance Tree BT)

– 任意节点的子树的高度差都小于等于1;
– 常见的平衡树包括B树(MySQL中的索引)、AVL树-作者Adelson-Velskii,Landis等;

平衡二叉搜索树(AVL树 作者Adelson-Velskii,Landis)

– 同时满足二叉搜索树以及平衡树特点
– 可以有效的减少二叉树的深度,从而提高了查询的效率
– 严格平衡,代价高

红黑树(Red Black Tree R-B Tree)

1972年的Rudolf Bayer发明,称为平衡二叉B树(symmetric binary B-trees)

1978年被Leo J.Guibas和Robert Sedgewick 修改为 “红黑树”

红黑树属于一种平衡二叉树,会应用复杂的定义和规则都是为了保证树的平衡性;

红黑树属于一种特化的AVL树,在插入和删除时通过特定操作保持二叉查找树的相对平衡,从而获得较高的查找性能;

不是严格的AVL树,只是黑色平衡

– 根节点P的左子树显然比右子树高
– 但左子树和右子树的黑节点的层数是相等的

简单理解:可以将插入的连续有序数值信息,在排序后进行逐一的颜色划分,并且连续的数据需要红黑交替标记颜色;
“`

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day13-数据库服务存储引擎/课程笔记目录/MySQL中级数据库课程-07章-数据库索引知识介绍/课件配图资料/Image 047.png)

“`sh
红黑树特点:(符合二叉搜索树基本特征)

– 节点非黑即红(每个节点要么是黑色,要么是红色)
– 其根节点是黑色
– 叶子节点是黑色(为了简单理解,一般会省略该节点)
– 相邻节点不同为红色(红色节点子节点必是黑色)
– 从一个节点到该节点的下叶子节点的所有路径上包含的黑节点数量相等(这一点是平衡的关键)

> 说明:黑根黑叶红不邻,同祖等高只数黑

– **BTree:改造后的二叉树**

MySQL的数据是存储在磁盘文件中的,查询处理数据时,需要先把磁盘中的数据加载到内存中,磁盘IO操作非常耗时;

因此需要日常对数据库优化的重点就是尽量减少磁盘IO操作,而访问二叉树的每个节点就会发生一次IO,如果想要减少IO(降低高度);

假设:key(字段)数据类型为bigint占用8字节,每个节点要有两个指针,每个指针占用4字节,一个节点占用的空间为16字节;

因为在MySQL中的InnoDB存储引擎一次IO会读取一页(默认1页16K)的数据量;

若二叉树一次IO有效数据量只有16字节,空间利用率较低;

为了最大化利用一次IO空间,一个简单的想法是在每个节点存储多个元素,即在每个节点尽可能多的存储数据;

每个节点可以存储1000个索引数据(16k/16=1000),这样就将二叉树改造成了多叉树,通过增加树的分支,将树的高瘦结构变为矮胖;

一般构建100万条记录,树的高度只需2层就可以(1000*1000=100万),也就是说只需要2次磁盘IO就可以查询到数据;

磁盘IO次数变少了,查询数据的效率也就提高了;

这种方式构造的存储数据结构称为BTree,B树是一种多叉平衡查找树,如下图所示;
“`

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day13-数据库服务存储引擎/课程笔记目录/MySQL中级数据库课程-07章-数据库索引知识介绍/课件配图资料/索引树结构-BTree.png)

“`sh
利用数据树形结构查询数据:

假如查询值等于10的数据。查询路径磁盘块10->磁盘块20->磁盘块31。

第一次磁盘IO:将磁盘块1加载到内存中,在内存中从头遍历比较,10<15,走左路,到磁盘寻址磁盘块2。 第二次磁盘IO:将磁盘块2加载到内存中,在内存中从头遍历比较,7<10,到磁盘中寻址定位到磁盘块5。 第三次磁盘IO:将磁盘块5加载到内存中,在内存中从头遍历比较,10=10,找到10,取出data; ① 如果data存储的行记录,取出data,查询结束。 ② 如果存储的是磁盘地址,还需要根据磁盘地址到磁盘中取出数据,查询终止。 相比二叉平衡查找树,在整个查找过程中,虽然数据的比较次数并没有明显减少,但是磁盘IO次数会大大减少。 同时,由于我们的比较是在内存中进行的,比较的耗时可以忽略不计。 B树的高度一般2至3层就能满足大部分的应用场景,所以使用B树构建索引可以很好的提升查询的效率。 此种树形结构特点: - B树的节点中存储着多个元素,每个内节点有多个分叉; - 节点中的元素包含键值和数据,节点中的键值从大到小排列。也就是说所有的节点都储存数据。 - 父节点当中的元素不会出现在子节点中。 - 所有的叶子结点都位于同一层,叶节点具有相同的深度,叶节点之间没有指针连接。 看到这里一定觉得B树很理想了,但是B树不支持范围查询的快速查找; 如果想查询15-30之间的数据,查到15之后,我们还要返回根节点重新遍历查找下一个数据,直到全部遍历找到。 如果data存储的是行记录,行的大小随着列数的增多,所占空间会变大。 这时一个页中可存储的数据量就会变少,树相应就会变高,磁盘IO次数就会变大。 **对于B+tree算法的底层算法逻辑理解:** 利用Btree算法还是快速锁定100个盒子中,有代金券的盒子编号,如下图所示: - 将需要存储的数据信息,均匀分配保存到对应页当中,最终数据信息的均匀存储(落盘) - 根据页节点存储的数据信息,取出页节节点最小数据信息,并将每个叶节点最小数据信息进行汇总整合,生成相应内部节点数据; 实质上存储的是下层页节点的区间范围,以及与之对应的指针信息,最后构建出内部节点信息; - 根据内部节点存储的数据信息,取出内部节点最小数据信息,并将每个内部节点最小值信息进行汇总整合,生成相应根节点数据; 根节点只能有占用一个页区域,如果一个页区域空间不够,需要进行内部节点层次扩展,但是尽量要保证层次越少越好; 实质上存储的是下层内部节点的区域范围,以及与之对应的指针信息,最后构建出独立且唯一的根节点信息; - 整个树形结构,越向上节点存储数据的范围越大,然后依次再分发数据到下面的小范围,最终形成多叉树; 由于出现了多叉树,就表示全部数据分布在多个链表上,避免了单条链表存储数据,同时可以实现并发的访问数据 - 对于加号表示增强,其中增强表示在整个链表上,增加了同级相邻节点之间的双向指针,从而实现相邻节点相互跳转 ``` ![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day13-数据库服务存储引擎/课程笔记目录/MySQL中级数据库课程-07章-数据库索引知识介绍/课件配图资料/1667936870251.png) ```sh 利用数据树形结构查询数据: 假如想要查找9和26之间的数据。查找路径是磁盘块1->磁盘块2->磁盘块6->磁盘块7。

① 查找值等于9的数据,将值等于9的数据缓存到结果集。这一步和前面等值查询流程一样,发生了三次磁盘IO。

② 查找到15之后,底层的叶子节点是一个有序列表,我们从磁盘块6,键值9开始向后遍历筛选所有符合筛选条件的数据。

③ 第四次磁盘IO:根据磁盘6后继指针到磁盘中寻址定位到磁盘块7,将磁盘7加载到内存中;

​ 在内存中从头遍历比较,9<25<26,9<26<=26,将data缓存到结果集。 主键具备唯一性(后面不会有<=26的数据),不需再向后查找,查询终止。将结果集返回给用户 此种树形结构特点: - B+树的节点中可存N个key,N个key划分出N个区间,树的高度是相对矮的。 - 所有父节点都会重复出现在子节点中,从左到右依次递增 - 非叶子结点只起索引作用, 叶子结点包含信息。 - 非叶子结点可能在内存中缓存 - 所有的叶子结点都位于最后一层,叶节点之间首尾连接,所有key值都在子节点中存在,且最大为key **根据以上B+Tree的结构说明,假设现在需要查找54这个数据值信息所在的数据页:等值查询** - 根据定义查找的数值信息,首先在根节点中获取数值所在的区间范围和相应指针信息,从而找到下层对应的内部节点信息; - 根据定义查找的数据信息,其次在枝节点中获取数值所在的区域范围和相应指针信息,从而找到下层对应的叶子节点信息; - 根据定义查找的数据信息,最后在叶子节点中获取最终的数据信息,结果结合上图经历三步完成了数据查找(3*16=48kB); 在利用BTree查找数据信息时,会结合树形层次结构,来决定查询数据的步骤过程,并且理论上每个数据查找过程步骤相同; > 总结:B代表的平衡含义就是,每次查找数据消耗的IO数量是一致的,并且读取的页数量也是一致的,查找时间复杂度是一致的;

**根据以上B+Tree的结构说明,假设现在需要查找大于90这个数据值信息所在的数据页:不等值查询**

– 根据定义查找的数值信息,首先在根节点中获取首个大于指定数值的区间范围和相应指针信息,从而找到下层对应的内部节点信息;
– 根据定义查找的数据信息,其次在枝节点中获取数值所在的区域范围和相应指针信息,并且结合双向指针进行预读;
– 根据定义查找的数据信息,最后在叶子节点中获取最终的数据信息,并且结合双向指针进行预读,查询其余大于90的数值;

在利用BTree查找数据信息时,由于存在双向指针概念,可以避免重复从根查找问题,减少IO消耗,结合预读快速调取数据到内存中

> 总结:在BTree中的双向链接增强特性和预读功能,可以根据簇(64page)读取数据,可以使数据信息的范围查找变得更加方便高效

##### **02 索引模型类型二:Hash(哈希表)**

此种索引方式是一种以键-值(key-value)存储数据的结构,只要输入待查找的键即key,就可以找到其对应的值即value;

哈希索引的思路很简单,就是把值放在数组里,用一个哈希函数把key换算成一个确定的位置,然后把value放在数组的这个位置;

不可避免地多个key值经过哈希函数的换算,会出现同一值的情况,处理方法是拉出一个链表;

假设,现在维护着一个身份证信息和姓名的表,需要根据身份证号查找对应的名字,这时对应的哈希索引的示意图如下所示:
“`

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day13-数据库服务存储引擎/课程笔记目录/MySQL中级数据库课程-07章-数据库索引知识介绍/课件配图资料/1692005069389.png)

“`sh
图中,User2 和 User4 根据身份证号算出来的值都是 N,但没关系,后面还跟了一个链表。

假设,这时候你要查 ID_card_n2 对应的名字是什么;

处理步骤就是:

– 首先,将 ID_card_n2 通过哈希函数算出 N;
– 然后,按顺序遍历,找到 User2。

> 需要注意:
>
> 图中四个 ID_card_n 的值并不是递增的,这样做的好处是增加新的 User 时速度会很快,只需要往后追加。
>
> 但缺点是,因为不是有序的,所以哈希索引做区间查询的速度是很慢的。

你可以设想下,如果你现在要找身份证号在[ID_card_X, ID_card_Y]这个区间的所有用户,就必须全部扫描一遍了。

所以,哈希表这种结构适用于只有等值查询的场景,比如 Memcached 及其他一些 NoSQL 引擎。

##### **03 索引模型类型三:R+Tree**

空间索引是检索空间数据集合的”目录”。它不同于图书的目录,在进行图书内容检索时,目录对应的书本内容是不变的;

而空间索引是根据空间数据的变化而变化的,包括数据的创建、修改、删除等基本操作都会重新建立新的索引;

R-tree的特点:

– 多维空间数据支持:R-tree支持多维空间数据的索引,适用于地理信息系统(GIS)等领域的应用。
– 空间查询效率高:R-tree可以快速定位和访问空间数据,提高查询效率。
– 空间数据动态更新:R-tree支持空间数据的动态更新,可以在线添加、删除和修改数据。

##### **04 索引模型类型四:FULLTEXT**

全文搜索的类型为FULLTEXT,表示在定义索引的列上支持值的全文查找,允许在这些索引列中插入重复值和空值;

全文搜索主要用来查找文本中的关键字,而不是直接与索引中的值相比较;

全文索引与其它索引大不相同,它更像是一个搜索引擎,而不是简单的WHERE语句的参数匹配。

#### 1.6.3 数据库索引应用类型

数据库服务在进行BTree索引构建时,是比较重要的知识点,因为最终还是会应用BTree算法知识进行索引的构建,常用方法有:

**索引方式一:聚簇索引(集群索引/聚集索引)**

聚簇索引主要是:将多个簇(区-64个数据页-1M)聚集在一起就构成了所谓聚簇索引,也可以称之为主键索引;

聚簇索引作用是:用来组织存储表的数据行信息的,也可以理解为数据行信息都是按照聚簇索引结构进行存储的,即按区分配空间的;

聚簇索引的存储:聚簇是多个簇,簇是多个连续数据页(64个),页是多个连续数据块(4个),块是多个连续扇区(8个);

> 总结:利用聚簇索引可以实现从物理上或逻辑上,都能满足数据存储的连续性关系,方便进行数据查找的有序性IO;(IOT组织表)

聚簇索引的构建方式:

– 数据表创建时,显示的构建了主键信息(pk),主键(pk)就是聚簇索引;
– 数据表创建时,没有显示的构建主键信息时,会将第一个不为空的UK的列做为聚簇索引;
– 数据表创建时,以上条件都不符合时,生成一个6字节的隐藏列作为聚簇索引;

结合下图信息,可以看出聚簇索引组织存储数据过程与加速查询过程原理:
“`

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day13-数据库服务存储引擎/课程笔记目录/MySQL中级数据库课程-07章-数据库索引知识介绍/课件配图资料/1668072355146.png)

“`sh
以上图信息为例,若显示创建ID列为pk自增列:

① 按照ID逻辑顺序,在同一个区中连续的数据页上,有序存储数据行;

② 数据行所在的数据页,作为聚簇索引的叶子节点(叶子节点就是所有数据行);

③ 叶子节点构建完后,可以构建no-left(支节点),用于保存的是leaf节点中的ID范围和指针信息;

④ 支节点构建完后,可以构建root(根节点),用于保存的是no-leaf节点中的ID范围和指针信息;

⑤ 并且leaf节点和no-leaf相邻数据页之间都具有双向指针,从而加速数据的范围查找;

**索引方式二:辅助索引**

辅助索引主要是:主要用于辅助聚簇索引查询的索引,一般按照业务查找条件,建立合理的索引信息,也可以称之为一般索引;

辅助索引作用是:主要是将需要查询的列信息可以和聚合索引信息建立有效的关联,从而使数据查询过程更高效,节省IO和CPU消耗

辅助索引的存储:调取需要建立的辅助索引列信息,并加上相应主键列的所有信息,存储在特定的数据页中;

> 总结:利用辅助索引与聚合索引建立的关联,先经过辅助索引的查询获取对应聚簇索引,在经过聚簇索引回表查询获取详细数据;

辅助索引的构建方式:

– 数据表创建时,显示的构建了一般索引信息(mul),一般索引信息(mul)就是辅助索引;

– 数据表创建时,没有显示的构建一般索引信息时,在查询检索指定数据信息,会进行全表扫描查找数据;

结合下图信息,可以看出辅助索引组织存储数据过程与加速查询过程原理:
“`

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day13-数据库服务存储引擎/课程笔记目录/MySQL中级数据库课程-07章-数据库索引知识介绍/课件配图资料/1668091064864.png)

“`sh
以上图信息为例,若显示创建name列为mul查询列:

① 调取需要建立的辅助索引列信息,并加上相应主键列的所有信息,存储在特定的内存区域中;

② 根据调取的辅助索引列信息,进行字符的顺序排序,便于形成范围查询的区间,并将排序后的数据信息存储在特定数据页中;

③ 叶子节点构建完后,可以构建no-left(支节点),用于保存的是leaf节点中的字符范围和指针信息;

④ 支节点构建完后,可以构建root(根节点),用于保存的是no-leaf节点中的字符范围和指针信息;

⑤ 找到相应辅助索引的数据信息后,在根据辅助索引与聚簇索引的对应关系,获取到相应的主键信息,从而获取相应其他数据信息

​ 在利用聚簇索引获取其他数据信息的过程,也可以称之为回表查询过程;

#### 1.6.4 数据库索引应用方法

##### **01 数据库索引信息创建**

索引的创建方式有多种,主要由数据库的功能决定,索引由DBA或表的拥有者负责创建和撤销;

创建索引类型一:普通索引、唯一索引和主键索引

– **普通索引:**

普通索引是MySQL中的最基础索引类型,允许对创建索引的列中插入重复值和空值;

创建普通索引的操作方法:

01 直接创建索引,语句格式如下:

​“`mysql
mysql> create index index_name on table(column(length))
​“`

02 以修改表结构的方式添加索引,语句格式如下:

​“`mysql
mysql> alter table table_name add index idx_name(column(length));
mysql> alter table table_name add index idx_name on (column(length));
​“`

03 创建表的同时创建索引,语句格式如下:

​“`mysql
mysql> create table table_name (….,index index_name(column(length))
mysql> create table author(
id char(6) not null,
name char(6) not null,
brief varchar(128),
primary key(id),index author_name(name(length))
);
​“`

– **唯一索引:**

唯一索引指索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一;

创建唯一索引的操作方法:

01 直接创建索引,语句格式如下:

​“`mysql
mysql> create unique index index_name on table(column(length)) ;
​“`

02 以修改表结构的方式添加索引,语句格式如下:

​“`mysql
mysql> ALTER TABLE `table_name` ADD UNIQUE index_name on (column(length))
​“`

03 创建表的同时创建索引,语句格式如下:

​“`mysql
mysql> create table table_name (….,unique index index_name(column(length))
mysql> create table press(
id char(6) not null,
name char(6) not null,
brief varchar(128),
primary key(id),UNIQUE index author_name(name(length))
);
​“`

– **主键索引:**

主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值;

创建主键索引的操作方法:

01 以修改表结构的方式添加索引,语句格式如下:

​“`mysql
mysql> ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` );
​“`

02 创建表的同时创建索引,语句格式如下:

​“`mysql
mysql> create table table_name (….,primary key (column));
​“`

> 说明:一般主键索引是在建表时同时创建主键索引的

单列索引创建特殊情况:前缀索引

创建前缀索引方法:

​“`sql
mysql> alter table city add index ix_n(name(10));
​“`

创建索引类型二:单列索引和组合索引

单列索引即一个索引只包含单个列,一个表可以多个单列索引;

组合索引(联合索引)是指组合表的多个字段创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用;

使用组合索引时需要遵循最左前缀匹配原则;

单列索引一般特指的就是之前说明的普通索引、唯一索引或主键索引,所以创建过程不再进行赘述;

创建联合索引的操作方法:

01 直接创建索引,语句格式如下:

​“`mysql
mysql> create index index_name on table(column01,column02);
​“`

02 以修改表结构的方式添加索引,语句格式如下:

​“`sql
mysql> alter table city add index ix_na_po(name,population);
​“`

03 创建表的同时创建索引,语句格式如下:

​“`mysql
mysql> create table table_name (….,index index_name(column01,column02));
​“`

##### **02 数据库索引信息查询**

索引信息简单查看:

​“`sql
mysql> use world;
mysql> desc city;
— 查询表结构信息,获取索引配置
​“`

索引信息的展示形式:

| 序号 | 索引标识 | 解释说明 |
| —- | ——— | ———————————— |
| 01 | PK(PRI) | 表示为聚簇索引,也可以理解为主键索引 |
| 02 | K(MUL) | 表示为辅助索引,也可以理解为一般索引 |
| 03 | UK(UNI) | 表示唯一键索引 |

参考链接博文:https://blog.csdn.net/weixin_71438279/article/details/126945736

索引信息详细查看:

​“`mysql
# 查看索引详细信息
SHOW INDEX FROM table_name [FROM db_name] [WHERE condition];
— table_name:需要查询索引的表名
— db_name:(可选)数据库名。如果你已经在某个数据库上下文中,可以省略此参数。
— condition:(可选)可以添加WHERE子句来筛选索引信息,比如根据索引名称、索引类型等进行筛选。

mysql> use world;
mysql> show index from city;
— 查询表索引信息,获取索引详细信息
​“`

命令输出列信息说明:

| 序号 | 列信息 | 解释说明 |
| —- | ———— | ———————————————————— |
| 01 | table | 表示查询索引信息对应的表名称 |
| 02 | non_unique | 表示是否允许重复值;如果值为1,表示允许重复值;如果值为0,表示不允许重复值(唯一索引) |
| 03 | key_name | 索引名称。主键索引名通常为PRIMARY。 |
| 04 | seq_in_index | 索引中的列的序号。对于组合索引,这表示列在索引中的位置。 |
| 05 | column_name | 表示索引对应表中的列名,索引涉及的列 |
| 06 | collation | 排序规则。A表示升序,D表示降序,NULL表示不可排序 |
| 07 | cardinality | 索引的基数。这是一个估算值,表示索引中唯一值的数量。这个值对于查询优化器选择索引非常重要。 |
| 08 | sub_part | 索引的前缀长度。对于部分索引,这表示索引的前缀长度。 |
| 09 | packed | 索引是否被压缩。如果索引未被压缩,该列的值为NULL。 |
| 10 | null | 列是否允许包含NULL值 |
| 11 | index_type | 索引类型。常见的类型有BTREE、HASH、FULLTEXT等。 |
| 12 | comment | 表示索引的备注信息 |

索引选择度参考链接:https://blog.csdn.net/weigeshikebi/article/details/80214965

##### **03 数据库索引信息删除**

– **单列索引信息删除方法:**

删除普通索引信息:

​“`mysql
mysql> alter table 表名 drop index 索引名;
mysql> alter table t100w drop index idx_name;
​“`

删除唯一索引信息:

​“`mysql
mysql> alter table 表名 drop index 索引名;
mysql> alter table t100w drop index id;
​“`

删除主键索引信息:

​“`mysql
mysql> alter table 表名 drop primary key;
mysql> alter table t100w drop primary key;
​“`

– **组合索引信息删除方法:**

​“`mysql
mysql> alter table 表名 drop index 索引名;
mysql> alter table t100w drop index oldboy;
​“`

#### 1.6.5 数据库索引创建原则

传统的查询方法是按照表的顺序遍历查询的,无论查询几条数据,MySQL需要从头开始遍历表,直到找到该数据;

创建索引后,MySQL一般通过BTREE算法生成一个索引文件,在查询数据库时,首先找到索引文件并遍历索引记录;

在其中找到对应的键后就可以获取对应值的数据,查询效率会提高很多;

然后,使用索引是有代价的,索引设计得不合理,或者缺少索引都会对数据库和应用程序的性能造成影响;

高效的索引对于良好性能的获取非常重要,因此,只要遵循创建索引的有效原则建立索引,才能真正达到事半功倍的效果;

**01 创建索引要有专人完成**

– 索引由DBA或表的拥有者负责创建和撤销,其他用户不能随意操作;
– 索引由系统自动选择,或由用户打开,用户可执行重建索引操作;

**02 是否创建索引取决于表的数据量**

– 基本表中的记录的数据量越多,记录越长,越有必要创建索引。创建索引后,查询速度的提升效果会很明显;

要避免对经常更新的表创建过多的索引,索引中的列也要尽可能少;

– 数据量小的表最好不要使用索引,由于数据较少,查询花费的时间可能比遍历索引的时间还要短,创建索引可能不会产生优化效果

对经常用于查询的字段应该创建索引,但要避免添加不必要的字段;

– 索引要根据数据查询或数据处理的要求确定是否创建,对于查询频率高,实时性要求高的数据一定要建立索引

**03 索引数据量要适度**

– 索引文件占用文件目录和存储空间,因此索引过多会加重系统负担;

– 索引需要自身维护,当基本表的数据增加、删除或修改时,索引也会进行调整和更新,索引文件也要随之变化,以保持与基本表一致;

– 索引过多会影响数据增、删、改的速度;

索引并非越多越好,一张表中如果有大量的索引,不仅占用磁盘空间,而且还会影响insert、delete、update等操作的性能;

**04 避免使用索引的情况**

– 包含太多重复值的字段;
– 查询中很少被引用的字段;
– 值特别长的字段;
– 查询返回率很高的字段;
– 具有很多NULL值的字段;
– 需要经常增、删、改的字段;
– 记录较少的表;
– 需要频繁、大批量进行数据更新的基本表;

**辅助索引检索数据产生回表问题分析:**(回表次数越少越高)

产生问题:

① 在回表过程中,有可能会出现多次的回表,从而造成磁盘IOPS的升高;(因为是随机IO操作过程)

② 在回表过程中,有可能会出现多次的回表,从而造成磁盘IO量的增加;

解决方法:

① 可以建立联合索引,调整查询条件,使辅助索引过滤出更精细主键ID信息,从而减少回表查询的次数;

② 可以控制查询信息,实现覆盖索引,辅助索引完全覆盖查询结果;

③ 优化器算法做调整???(MRR-多路读功能 ICP-索引下推功能 )

**构建索引树高度问题分析:**(索引树高度越低越好)

影响索引树高度因素:

① 数据行数量会对高度产生影响;(3层BTREE — 可以实现一般2000万行数据索引的存储-20~30列表)

​ 解决方法:可以拆分表 拆分库 或者实现分布式存储;

② 索引字段长度过大会对高度产生影响;

​ 解决方法:利用前缀索引解决问题

③ 数据类型设定会对高度产生影响;

​ 解决方法:列定义时,选择简短合适的数据类型;

#### 1.6.6 数据库索引应用压测

在进行索引操作之前,可以进行一个压力测试,将一个100W数据量的数据库备份数据进行备份恢复:

​“`tiki wiki
# 进行测试数据恢复操作:
mysql> source ~/t100w_oldboy.sql

# 进行数据库程序服务压测:
mysqlslap –defaults-file=/etc/my.cnf –concurrency=100 –iterations=1 –create-schema=’oldboy’ –query=”select * from oldboy.t100w where k2=’VWlm'” engine=innodb –number-of-queries=2000 -uroot -p123456 -h192.168.30.101 -verbose
— concurrency=100 模拟同时100会话连接;
— iterations=1 测试执行的迭代次数,代表要在不同并发环境下,各自运行测试多少次
— create-schema=’test’ 指定操作的数据库信息;
— query=”select * from test.100w where k2=’780P'” 指定压测过程具体执行了什么语句操作
— number-of-queries=2000 指定一共做了多少次查询,总的测试查询次数(并发客户数×每客户查询次数)
​“`

数据库压力测试结果情况:

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day13-数据库服务存储引擎/课程笔记目录/MySQL中级数据库课程-07章-数据库索引知识介绍/课件配图资料/1668098473569.png)

进行索引建立优化:

​“`sql
mysql> alter table oldboy.t100w add index idx_k2(k2);
​“`

在进行压测检查确认:

“`

## 2 索引总结

“`sh
01 数据库服务索引知识

– 数据索引功能作用
– 数据索引算法结构
– 数据索引构建方式(聚簇索引-主键索引 辅助索引-普通索引 唯一索引 前缀索引 联合索引)
– 数据索引管理方式(创建索引 查看索引 删除索引)
– 数据索引应用测试(压力测试)
02 数据库服务执行计划
– 数据库执行计划概述
– 数据库执行计划查看
– 数据库索引应用类型(慢查询语句)

数据库服务处理SQL语句流程:
步骤一:需要确认客户端可以访问服务端
步骤二:在数据库服务层进行SQL语句处理

– 连接器:管理连接 权限验证

– 分析器:词法分析 语法分析

– 优化器:执行计划生成 索引选择

– 执行器:控制引擎 获取执行结果
步骤三:在数据库服务引擎层调取或存储数据

– 存储结构层次 (段 区-1M 页-16KB)

– 磁盘建立关联 (1页-4block-32扇区 – 顺序IO存储)

– 内存建立关联 (数据页加载数据 1个数据页-16KB-IO)

01 数据库服务索引知识

– 数据索引功能作用
1)利用索引可以加快数据查询效率(select update delete insert)
2)利用索引可以锁定调取数据范围

– 数据索引算法结构
早期:遍历算法 每个表的每个数据页都去查看 和指定条件信息最比较
select * from oldboy.t1 where id=10000
第一行数据调取 id-01 name age
第二行数据调取 id-02 name age
第三行数据调取
第十行数据调取 id-10 name age — 数据信息返回给server层 — 客户端
改进:二叉树算法 (红黑 均衡树)
可以减少一部分数据调取过程,提高查询效率
二叉树结构如果出现链式结构,查询效率也会降低
二叉树查询数据信息时,无法做到均衡查询的效率
目前:B+Tree结构 (B-Tree B+Tree) ***** 结构特点(根 支 页 — 锁定查询数据区域 查询数据消耗资源更均衡 因为有底层数据页横向适合范围查询)
具体查询数据的树形结构
优化二叉树层次结构,可以控制数的结构在3-4层即可
使查询数据时间成本和资源消耗成本更加均衡 (数据信息都存储在底层页节点)
由于B+Tree结构具有横向指针,也更适合于范围查询数据
https://blog.51cto.com/u_16213559/11217209

– 数据索引构建方式
聚簇索引结构 (利用主键列信息 构建根节点 支节点 叶节点-索引列+数据列)
聚簇索引的构建方式:
– 数据表创建时,显示的构建了主键信息(pk),主键(pk)就是聚簇索引;
– 数据表创建时,没有显示的构建主键信息时,会将第一个不为空的UK的列做为聚簇索引;
– 数据表创建时,以上条件都不符合时,生成一个6字节的隐藏列作为聚簇索引;

① 按照ID逻辑顺序,在同一个区中连续的数据页上,有序存储数据行;
② 数据行所在的数据页,作为聚簇索引的叶子节点(叶子节点就是所有数据行);
③ 叶子节点构建完后,可以构建no-left(支节点),用于保存的是leaf节点中的ID范围和指针信息;
④ 支节点构建完后,可以构建root(根节点),用于保存的是no-leaf节点中的ID范围和指针信息;
⑤ 并且leaf节点和no-leaf相邻数据页之间都具有双向指针,从而加速数据的范围查找;

辅助索引结构 (利用指定索引列信息 构建根节点 支节点 叶节点-主键列+辅助索引列)
辅助索引的构建方式:

– 数据表创建时,显示的构建了一般索引信息(mul),一般索引信息(mul)就是辅助索引;
– 数据表创建时,没有显示的构建一般索引信息时,在查询检索指定数据信息,会进行全表扫描查找数据;

① 调取需要建立的辅助索引列信息,并加上相应主键列的所有信息,存储在特定的内存区域中;
② 根据调取的辅助索引列信息,进行字符的顺序排序,便于形成范围查询的区间,并将排序后的数据信息存储在特定数据页中;
③ 叶子节点构建完后,可以构建no-left(支节点),用于保存的是leaf节点中的字符范围和指针信息;
④ 支节点构建完后,可以构建root(根节点),用于保存的是no-leaf节点中的字符范围和指针信息;
⑤ 找到相应辅助索引的数据信息后,在根据辅助索引与聚簇索引的对应关系,获取到相应的主键信息,从而获取相应其他数据信息
在利用聚簇索引获取其他数据信息的过程,也可以称之为【回表查询过程】;

– 数据索引管理方式
创建索引信息:
1)主键索引 (聚簇索引–调取数据)
— 创建表时创建
create table table_name (….,primary key (column));
create table xiaoA01 (id int,name char(10),age tinyint,gender char(1),primary key(id));
— 创建表后创建
ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` );
alter table xiaoA02 add primary key (id);

2)普通索引 (辅助索引–根据指定列查询数据)
— 创建表时创建
create table table_name (….,index index_name(column));
create table xiaoB01 (id int,name char(10),age tinyint,gender char(1),index idx_name(name));
— 创建表后创建
alter table table_name add index idx_name(column);
alter table xiaoB02 add index idx_name(name);

3)唯一索引 (辅助索引–索引列数据必须唯一,可以为空 保证/限制回表次数)
— 创建表时创建
create table table_name (….,unique index index_name(column))
create table xiaoC01 (id int,name char(10),age tinyint,gender char(1),unique index uidx_name(name));
— 创建表后创建
ALTER TABLE `table_name` ADD UNIQUE index index_name on (column)
alter table xiaoC02 add unique index uidx_name(name);

4)联合索引 (辅助索引–可以将多个列进行索引创建 减少回表次数)
— 创建表时创建
create table table_name (….,index index_name(column01,column02,..));
create table xiaoD01 (id int,name char(10),age tinyint,gender char(1),index idx_na_ag(name,age));
— 创建表后创建
alter table table_name add index idx_name(column01,column02,..);
alter table xiaoD02 add index idx_na_ag(name,age);

5)前缀索引 (辅助索引–可以截取数据列部分信息存储在索引数据页中 可以有效控制索引树高度)
— 创建表时创建
create table table_name (….,index index_name(column(length))
create table xiaoE01 (id int,name char(10),age tinyint,gender char(1),index idx_na(name(5)));
— 创建表后创建
ALTER TABLE `table_name` ADD index index_name (column(length))
ALTER TABLE xiaoE02 ADD index idx_na(name(5)))
“`

# 第十一章 服务执行计划

“`sh
#### 1.8.1 数据库执行计划概念

在介绍数据库服务程序运行逻辑时,在SQL层处理SQL语句时,会根据解析器生成解析树(多种处理方案);

然后在利用优化器生成最终的执行计划,然后在根据最优的执行计划进行执行SQL语句;

作为管理员,可以在某个语句执行前,将语句对应的执行计划提取出来进行分析,便可大体判断语句的执行行为,从而了解执行效果;

SQL语句只是告诉了数据库要做什么,并没有告诉数据库如何做,查看SQL语句的执行计划可以使SQL的执行过程从黑盒变白盒;

`可以简单理解:执行计划就是最优的一种执行SQL语句的方案,表示相应SQL语句是如何完成的数据查询与过滤,以及获取;`

#### 1.8.2 数据库执行计划获取

可以利用命令进行获取执行计划信息:explain/desc

​“`SQL
explain select * from oldboy.t100w where k2=’VWlm’;
或者
desc select * from oldboy.t100w where k2=’VWlm’;
​“`

命令执行输出信息:

“`

![](D:/学习//4_数据库/视频和笔记/系统运维95期-Day13-数据库服务存储引擎/课程笔记目录/MySQL中级数据库课程-08章-数据库服务执行计划/课件配图资料/1668100222339.png)

输出信息解释说明:

| 序号 | 字段 | 解释说明 |
| —- | ————— | ———————————————————— |
| 01列 | ID | 表示语句执行顺序,单表查询就是一行执行计划,多表查询就会多行执行计划;
显示表或子查询属于查询的哪一个部分 |
| 02列 | select_type | 表示语句查询类型,sipmle表示简单(普通)查询
primary表示嵌套查询最外层的查询块,subquery代表select语句的子查询块 |
| 03列 | `table` | 表示语句针对的表,单表查询就是一张表,多表查询显示多张表; |
| 04列 | partitions | 表示匹配的分区信息 |
| 05列 | `type***` | 表示索引应用类型,通过类型可以判断有没有用索引,其次判断有没有更好的使用索引 |
| 06列 | `possible_keys` | 表示可能使用到的索引信息,因为列信息是可以属于多个索引的,如果为null表示不考虑使用索引 |
| 07列 | `key` | 表示确认使用到的索引信息,如果为null表示不使用索引 |
| 08列 | `key_len***` | 表示索引覆盖长度,对联合索引是否都应用做判断 |
| 09列 | ref | 表示当使用索引列等值查询时,与索引列进行等值匹配的对象信息 |
| 10列 | `rows` | 表示查询扫描的数据行数(尽量越少越好),尽量和结果集行数匹配,从而使查询代价降低
估算的找到所需记录所需要读取的行数 |
| 11列 | fltered | 表示查询的匹配度,返回结果的行占需要读到行(rows字段)的百分比 |
| 12列 | Extra | 表示额外的情况或额外的信息,其他字段中不包含的额外说明都放在该字段 |

特殊说明:

实际工作中,如果发现一个正在执行的SQL语句耗时长,若想查询它的执行计划,通常的做法是使用explain生成该SQL语句执行计划;

但因为统计信息等原因,生成的执行计划和正在执行的执行计划可能不完全相同,更好的做法是使用explain for connection

查询当前正在使用的执行计划

例如:如下的SQL语句查询出了当前会话号:

“`mysql
mysql> select connection_id();
“`

在当前会话中执行一个慢SQL语句,如下所示;

“`mysql
mysql> select sleep(60),* from oldboy.t100w where k2=’VWlm’;
“`

根据会话号在其他会话里查询正在执行的SQL语句的执行计划;

“`mysql
mysql> explain for connection xx\G
“`

#### 1.8.3 数据库索引应用类型

利用类型信息,来判断确认索引的扫描方式,常见的索引扫描方式类型:

| 序号 | 类型 | 解释说明 |
| —- | —————— | ———————————————————— |
| 01 | ALL – ok | 表示全表扫描方式,没用利用索引扫描类型; |
| 02 | index | 表示全索引扫描方式,需要将索引树全部遍历,才能获取查询的信息(主键index=全表扫描) |
| 03 | range | 表示范围索引方式,按照索引的区域范围扫描数据,获取查询的数据信息; |
| 04 | ref | 表示辅助索引等值(常量)查询,精准定义辅助索引的查询条件 |
| 05 | eq_ref | 表示多表连接查询时,被驱动表的连接条件是主键或者唯一键时,获取的数据信息过程; |
| 06 | const(constants) | 表示主键或者唯一键等值(常量)查询,精准定义索引的查询条件 |
| 07 | system | 这是最高级别的访问类型,表示 MySQL只有一行数据,这行数据是从系统表中读取的。 |

# 第十二章 数据库服务引擎

## 1 概念

“`sh
存储引擎就是数据库服务中的文件系统,用户可以根据应用的需要选择如何存储和索引数据,是否使用事务等;

存储引擎作用:(磁盘管理器 — 引擎)

1)可以利用数据库存储引擎,去管理磁盘调取或存储数据;

2)可以将磁盘调取数据和内存进行交互,可以将内存中修改或添加的数据存储到磁盘中
“`

## 2 引擎种类

“`sh
#### 数据库存储引擎种类

在各种版本的数据库服务中,是有多种存储引擎可以应用的,以MySQL数据库服务为例,可以使用命令查看可以应用存储引擎:

​“`sql
mysql> show engines;
​“`

引擎类型信息输出:
db02 [(none)]>show engines;
+——————–+———+—————————————————————-+————–+——+————+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+——————–+———+—————————————————————-+————–+——+————+
| ndbcluster | NO | Clustered, fault-tolerant tables | NULL | NULL | NULL |
| FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
| ndbinfo | NO | MySQL Cluster system information storage engine | NULL | NULL | NULL |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
+——————–+———+—————————————————————-+————–+——+————+

在实际场景中,99.9%都是使用innodb存储引擎,并且在最新版8.0数据库中,所有mysql数据库中的表对应的引擎也都改为了innodb;

**如果在面试环节中,面试官询问你:**

– 列举出mysql中支持的存储引擎种类:InnoDB、MyISAM、CSV、MEMORY;

– 列举出mysql分支产品的存储引擎种类:在percone、mariadb数据库中,可能还会应用TokuDB MyRocks Rocksdb存储引擎

从特点上可以支持innodb引擎的特性(支持事务),并且数据压缩比比较高(15倍),数据插入性能比较强(5~6倍);

以上存储引擎就比较适合于zabbix监控类的平台,归档数据、历史数据存储业务等,数据量级比较大的情况;

监控服务部署tokuDB存储引擎参考链接:https://www.cnblogs.com/oldboy-heqing/articles/16891210.html

#### 1.11.3 数据库存储引擎特性

在数据库服务领域,大部分场景下都会使用innodb存储引擎,是因为innodb存储引擎具有一定优秀特性:

| 序号 | 特性 | 解释说明 |
| —- | ———— | ———————————————————- |
| 01 | 数据访问特性 | 支持多版本并发控制特性(MVCC),支持行级锁控制并发 |
| 02 | 数据索引特性 | 支持聚簇索引/辅助索引特性,可以组织存储数据和优化查询(IOT) |
| 03 | 数据事务特性 | 支持事务概念特性,可以实现数据的安全保证 |
| 04 | 数据缓冲特性 | 支持多缓冲区功能,自适应hash索引(AHI) |
| 05 | 数据迁移特性 | 支持复制数据中的高级功能特性,支持数据备份恢复的热备 |
| 06 | 服务自愈特性 | 支持自动故障恢复(CR-Crash Recovery) |
| 07 | 数据存储特性 | 支持数据双写机制(Double write) 数据存储有关的安全机制 |

**如果在面试环节中,面试官询问你:**

– InnoDB核心特性有哪些,以及与MyISAM存储引擎之间的区别:

InnoBD支持:事务、mvcc、聚簇索引、外键、缓冲区、AHI、DW;MyISAM均不支持

InnoDB支持:行级锁,MyISAM只支持表级锁;

InnoDB支持:数据热备,可以保证业务正常运行,对业务影响低,MyISAM只支持温备份,需要锁表备份;

InnoDB支持:支持CR自动故障恢复,宕机自动恢复,数据安全和一致性可以得到保证;MyISAM不支持,宕机可能丢失当前数据;

**企业实际场景案例分析说明:**

案例说明01:某期学员负责运维+MySQL相关工作;

平台环境:zabbix 3.2 + centos 7.3 + mariadb 5.5(InnoDB引擎),利用监控平台,监控了2000多个节点服务;

问题现象:每隔一段时间zabbix服务就会运行操作很卡,每隔3-4个月,都要重新部署zabbix,存储空间经常爆满(ibdata1 400~500G);ibdate1 ibdata1-01

异常分析:

– zabbix版本过低,建议将zabbix程序进行升级更新;
– zabbix使用的数据版本过低,建议将数据库版本进行升级,因为新版本数据库的原生态环境就比旧版本好些;
– 在数据库5.5版本中,在没有做数据存储调配时,数据库所有数据都会保存到ibdata1文件中;
– 在ibdata1文件中的数据空间,不会因为数据库中的数据删除,产生数据回缩的效果,即空间不释放;

优化建议:

– 数据库版本升级到percona 5.7+(mariadb 10.x+),zabbix软件升级更高版本;
– 数据库服务存储引擎改为tokudb;
– 监控数据最好按月份进行数据切割(二次开发zabbix程序,数据保留机制功能重写,并且数据库分表存储)
– 将数据库服务的binlog功能关闭(双1);
– 数据库服务相关内存优化参数调整;

优化思路:

– zabbix程序原生态支持TokuDB,经过压力测试,5.7要比5.5数据库版本性能高出 2~3倍;
– 使用TokuDB作为数据库存储引擎,insert数据比innodb要快的多,数据压缩比也要比Innodb高;
– 监控数据按月份进行切割(分区),为了能够truncate每个分区表,以便立即释放存储空间;
– 将数据库服务binlog关闭,是为了减少无关日志的记录,避免磁盘IO的消耗,以及节省磁盘空间的使用;
– 参数优化调整,主要是对安全性参数或内存相关参数调整,提高数据库服务运行性能;

企业案例资料参考:

https://mariadb.com/kb/en/installing-tokudb

https://docs.percona.com/percona-server/5.7/tokudb/tokudb_intro.html

https://www.percona.com/doc/percona-server/5.7/tokudb/tokudb_installation.html

案例说明02:企业客户实际数据库需求

平台环境:centos 5.8+mysql 5.0版本,MyISAM存储引擎+网站架构LNMP,数据量50G左右

问题现象:业务并发压力大的时候,整体网站访问非常卡,还会出现数据库服务宕机情况,造成部分数据丢失

问题分析:

– MyISAM存储引擎具有表级锁,在高并发访问时,会频繁出现锁等待情况;
– MyISAM存储引擎不支持事务机制,在断电或宕机时,会有可能丢失数据信息;

优化建议:

– 数据库服务版本进行升级,从5.0升级到更高的版本;ok
– 数据库服务升级后,迁移所有表数据到新环境(表空间迁移),调整存储引擎为InnoDB;
– 数据库服务开启双1安全参数;
– 数据库服务进行重构主从架构
“`

## 3 存储引擎应用

“`sh
##### 01 引擎基本操作说明:

**数据库存储引擎信息查看:**

​“`tiki wiki
# 查看数据库可用存储引擎
mysql> show engines;

# 查看数据库默认存储引擎
mysql> select @@default_storage_engine;
+———————————-+
| @@default_storage_engine |
+———————————-+
| InnoDB |
+———————————-+
1 row in set (0.00 sec)
​“`

**数据库存储引擎配置修改:**

​“`tiki wiki
# 永久修改存储引擎配置
[root@xiaoQ-01 ~]# vim /etc/my.cnf
default_storage_engine=InnoDB
— 重启数据库服务生效
​“`

**数据表存储引擎信息查看:(具体表的存储引擎)**

​“`tiki wiki
# 查看建表语句获取存储引擎信息
mysql > show create table city;

# 查看information_schema数据库获取存储引擎信息
mysql > select table_schema,table_name,engine from information_schema.tables where table_schema not in(‘sys’,’mysql’,’information_schema’,’performance_schema’)
​“`

**数据表存储引擎配置修改:(具体表的存储引擎)**

​“`tiki wiki
# 创建表时设置存储引擎
mysql > create table xxx (id int) engine=innodb charset=utf8mb4;

# 修改表示设置存储引擎
mysql > alter table world.xxx engine=myisam;
mysql > alter table world.xxx engine=innodb;
​“`

#### 1.11.5 数据库存储引擎结构

对于InnoDB存储引擎来说,数据时存储在磁盘上,而执行引擎想要操作数据,必须先将磁盘的数据加载到内存中才能操作;

当数据从磁盘中取出后,缓存内存中,下次查询同样的数据的时候,直接从内存中读取,这样大大提高了查询性能;

数据库服务存储引擎结构的介绍,可以依据官方图示参考说明:

https://dev.mysql.com/doc/refman/8.0/en/innodb-architecture.html

结合官方存储引擎结构图,可以看出存储引擎结构分为两个部分:

内存部分 / 磁盘部分(表空间数据 日志文件数据)

##### **01 On-Disk Structures(磁盘结构部分)**

在磁盘存储结构中,会使用表空间模式进行数据信息的管理,经常提到的段 区 页概念也是属于表空间的逻辑结构;

表空间用来存储表结构和数据,表空间可以分为系统表空间、独立表空间、通用表空间、临时表空间等多种类型;

表空间的概念源于oracle数据库,最初的目的是为了能够更好的做存储的扩容;因此数据库的表空间技术类似磁盘管理的LVM技术;

InnoDB Data Dictionary:数据字典

InnoDB数据字典由内部系统表组成,这些表包含用于查找表、索引和表字段等对象的元数据。

在数据库服务中所使用的表空间也被划分出几种种类:

**类型一:共享(系统)表空间**

属于数据库服务5.5版本时默认的表空间应用,具体数据存储数据方式为:ibdata1~ibdataN

ibdata共享表空间在各个版本之间的作用区别:

| 数据库版本 | 存储数据 | 解释说明 |
| ————- | ———— | ———————————————————— |
| MySQL 5.5版本 | 系统相关数据 | 全局数据字典信息(表基本结构信息、状态系统参数、属性)、undo回滚日志(记录撤销操作);
Double write buffer信息、临时表信息、changer buffer |
| | 用户相关数据 | 业务表数据行、表的索引数据均统一存储在ibdata1中,实现集中管理
数据表中数据清理后,ibdata1也不会释放磁盘空间 |
| MySQL 5.6版本 | 系统相关数据 | 全局数据字典信息(表基本结构信息、状态系统参数、属性)、undo回滚日志(记录撤销操作);
Double write buffer信息、临时表信息、changer buffer |
| | 用户相关数据 | `共享表空间只存储系统数据,用户相关数据被独立管理了(独立表空间管理)` |
| MySQL 5.7版本 | 系统相关数据 | 全局数据字典信息 undo回滚日志 Double write buffer信息、changer buffer
`临时表信息被独立出来了,undo也可以设定为独立` |
| | 用户相关数据 | 共享表空间只存储系统数据,用户相关数据被独立管理了(独立表空间管理) |
| MySQL 8.0.11 | 系统相关数据 | Double write buffer信息、changer buffer
`undo回滚日志信息被独立出来了,数据字典信息也不再集中存储管理了` |
| | 用户相关数据 | 共享表空间只存储系统数据,用户相关数据被独立管理了(独立表空间管理) |
| MySQL 8.0.20 | 系统相关数据 | changer buffer
`Double write buffer信息被独立出来了` |

**共享表空间管理:**

– 扩容共享表空间操作:

扩容前共享表空间信息查看:

​“`SQL
mysql> select @@innodb_data_file_path;
+————————————-+
| @@innodb_data_file_path |
+————————————-+
| ibdata1:12M:autoextend |
+————————————-+
1 row in set (0.00 sec)
— 可以在初始安装好数据库服务后,进行修改配置为两个ibdate文件,每个共享表空间文件占用2G,总共占用4个G空间

mysql> select @@innodb_autoextend_increment;
+———————————————+
| @@innodb_autoextend_increment |
+———————————————+
| 64 |
+———————————————+
1 row in set (0.00 sec)
— 查看参数信息说明:ibdata1文件,默认初始大小12M,不够用会自动扩展,默认每次扩展64M
​“`

共享表空间的扩容操作方法:

​“`tiki wiki
# 编写数据库配置文件信息
vim /etc/my.cnf
[mysqld]
innodb_data_file_path=ibdata1:12M;ibdata2:100M;ibdata3:100M:autoextend
— 需要注意的是ibdata1文件大小必须和实际数据库要存储的数据相匹配,否则会出现如下报错信息
[ERROR] [MY-012264] [InnoDB] The innodb_system data file ‘./ibdata1’ is of a different size 768 pages (rounded down to MB) than the 4864 pages specified in the .cnf file!
— 表示ibdate1指定大小超过了原有ibdata1实际的大小尺寸

# 查看配置信息是否生效
mysql> select @@innodb_data_file_path;
+———————————————————————+
| @@innodb_data_file_path |
+———————————————————————+
| ibdata1:12M;ibdata2:100M;ibdata3:100M:autoextend |
+———————————————————————+
1 row in set (0.00 sec)
​“`

数据库初始化时设置共享表空间容量建议:

| 序号 | 版本信息 | 建议说明 |
| —- | ——— | ———————————————————— |
| 01 | MySQL 5.7 | 设置共享表空间2~3个,大小建议512M或1G,最后一个定制为自动扩展 |
| 02 | MySQL 8.0 | 设置共享表空间1个即可,大小建议512M或1G |

共享表空间的初始设置方法:

​“`tiki wiki
# 模拟初始化清理数据
[root@xiaoQ-01 ~]# /etc/init.d/mysqld stop
[root@xiaoQ-01 ~]# rm -rf /data/3306/data/*

# 模拟初始化配置文件
[root@xiaoQ-01 ~]# vim /etc/my.cnf
[mysqld]
innodb_data_file_path=ibdata1:100M;ibdata2:100M;ibdata3:100M:autoextend

# 模拟初始化操作命令
[root@xiaoQ-01 ~]# mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data

# 模拟初始化重启服务
[root@xiaoQ-01 ~]# /etc/init.d/mysqld start
​“`

**类型二:独立表空间**

在数据库服务5.6版本中,针对用户数据,可以进行单独的存储管理,存储表的数据行和索引等相关信息;

**独立表空间在各个版本之间的作用区别:**

– 在数据库服务8.0版本前

用户表包含三个部分组成(表.ibd 表.frm ibdata1-全局数据字典信息存储);

所以在8.0之前,如果想修改表数据结构信息(元数据修改),都会修改frm和ibdata文件信息,每次更新都会锁表(元数据锁);

因为要保证数据一致性,并且两个表均更新完,才能释放解锁,因此在8.0前修改元数据信息,要避开业务繁忙时间段;

– 在数据库服务8.0版本后

用户表数据进行统一存储(表.ibd); 如果想修改表数据结构信息(元数据修改),只会修改ibd文件信息;

此时不需要对两个表文件均更新,只要更新一个文件即可,因此对文件锁的代价降低了,降低了对业务的影响;

**独立表空间管理:**

– 表空间配置参数信息查看

​“`tiki wiki
mysql > select @@innodb_file_per_table;
+———————————+
| @@innodb_file_per_table |
+———————————+
| 1 |
+———————————+
1 row in set (0.00 sec)
— 表示每个表就是一个独立文件,进行数据信息的独立存储,不建议进行修改,如果改为0就是所有数据统一存储在共享表空间

[root@xiaoQ-01 ~]# ibd2sdi city.ibd
— 可以看到文件中存储的元数据信息(数据字典信息),并且数据库8.0之后不再有表对应的frm文件信息了
— 在数据库5.7环境中,每个表数据信息会存储生成两个表 frm ibd
— frm文件中存储数据表的数据字典信息(元数据信息)
— ibd文件中存储数据行信息和索引信息
​“`

– 表空间配置参数信息修改

​“`tiki wiki
mysql > set global innodb_file_per_table=0
— 设置为0表示利用共享表空间存储用户数据 1表示利用独立表空间存储用户数据
​“`

**表空间企业应用案例:** 数据库服务宕机 — 重新部署数据库 —

案例01:利用独立表空间进行快速数据迁移(源端 3306/test/t100w –> 目标端 3307/test/t100w)

说明:可以在需要某个表中的数据信息时,可以将数据表的独立表空间数据信息做迁移,在另一个数据库中进行恢复即可

**操作步骤一:锁定源端t100w表**

​“`tiki wiki
mysql > lock tables oldboy.t100w write;
— 给t100w表加写数据锁
mysql > show create table oldboy.t100w;
CREATE TABLE `t100w` (
`id` int DEFAULT NULL,
`num` int DEFAULT NULL,
`k1` char(2) DEFAULT NULL,
`k2` char(4) DEFAULT NULL,
`dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY `idx` (`k1`,`k2`,`num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
— 获取创建表结构数据信息
​“`

**操作步骤二:目标端创建oldboy库和t100w空表**

​“`tiki wiki
mysql> create database oldboy;
— 创建新的数据库
mysql > CREATE TABLE `t100w` (
`id` int DEFAULT NULL,
`num` int DEFAULT NULL,
`k1` char(2) DEFAULT NULL,
`k2` char(4) DEFAULT NULL,
`dt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
KEY `idx` (`k1`,`k2`,`num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
— 创建新的数据表
​“`

**操作步骤三:单独删除空的表空间文件**

​“`tiki wiki
mysql> alter table oldboy.t100w discard tablespace;
— 删除t100w表的ibd数据文件信息,但是保留t100w的frm,ibdata1中关于t100w的系统数据
​“`

**操作步骤四:拷贝源端ibd文件到目标端目录,并设置权限**

​“`tiki wiki
[root@xiaoQ-01 ~]# cp /data/3306/data/oldboy/t100w.ibd /data/3307/data/oldboy/
[root@xiaoQ-01 ~]# chown -R mysql.mysql /data/*
​“`

**操作步骤五:导入表空间**

​“`tiki wiki
mysql> alter table oldboy.t100w import tablespace;
— 在目标端加载识别迁移过来的数据文件信息
mysql> select count(*) from t100w;
+———-+
| count(*) |
+———-+
| 1000000 |
+———-+
1 row in set (0.04 sec)
— 查看数据表中是否有迁移古来的数据信息
​“`

**操作步骤六:解锁源端数据表**

​“`tiki wiki
mysql> unlock tables;
​“`

案例02:利用表空间迁移功能实现数据损坏恢复

说明:操作系统突然断电了,启动完成后 / 变为只读了,fsck修复文件系统,系统再次重新启动后,mysql启动不了了

结果:造成confulence库在、jira库不见了(备份没有 日志也没开)

服务:jira(bug追踪)、confluence(内部知识库) 、mysql 5.6.33(innodb引擎 使用独立表空间) — LNMT架构

硬件:联想服务器(8核 16G内存 500G存储空间 没有raid),centos 6.8系统

========================================================================================

对话描述:

学生:这种情况怎么恢复?

老师:有备份吗?

学生:连二进制日志都没有,没有备份,没有主从

老师:jira数据库数据没什么办法了,只能进行硬盘数据恢复了

学生:jira数据库数据先不用关注,数据磁盘已经拉到中关村处理了

​ 主要是confulence库还想使用,但将生成中的库目录,导入到其他主机上(var/lib/mysql),无法直接访问数据库中数据?

​ 老师有没有工具能直接读取数据库目录中的ibd文件内容

老师:我查查,最后发现没有

​ 我们可以尝试下独立表空间迁移

​ create table xx

​ alter table coufulence.t1 discard tablespace;

​ alter table coufulence.t1 import tablespace;

​ 虚拟环境测试可行

问题:confulence库中总共有107张表

困惑:如何创建107张和原来一模一样的表

解决:学生环境中有2016年的历史库,让学生利用mysqldump命令备份confulence历史库

​ mysqldump -uroot -ppassw0rd -B confulence –no-date > test.sql 只获取所有表结构信息

​ 如果是自研数据库,没有备份怎么办

​ mysql工具包中,拥有mysqlfrm工具也可以读取frm文件获取表结构

========================================================================================

**操作步骤一:备份历史数据库的所有表结构信息,并进行恢复**

​“`tiki wiki
[root@xiaoQ-01 ~]# mysqldump -uroot -ppassw0rd -B confulence –no-date > test.sql
mysql > create database confulence
mysql > source test.sql

# 如果没有提前没有进行数据表的结构备份,也可以通过读取表结构文件信息,获取数据表结构
mysqlfrm –diagnostic gis.frm >gis.txt
— MySQL 5.7利用frm文件获取表结构信息
— mysqlfrm工具命令没有需要进行下载安装:https://downloads.mysql.com/archives/utilities/

ibd2sdi –dump-file name.txt name.ibd
— MySQL 8.0利用ibd文件获取表结构信息
​“`

> 说明:原ibd文件时的数据库版本要和当前数据库版本一致(如8.0.11和8.0.23版本不同会导致复原失败)

根据ibd文件解析的json信息,可以根据以下代码获取创建表语句:

​“`php
request->param();
// 替换为你的 JSON 数据
$filename = “city.txt”;
$jsonData = file_get_contents($filename);
// 解析 JSON 数据
$data = json_decode($jsonData, true);
// 提取表名和字段信息
$tableName = $data[1][‘object’][‘dd_object’][‘name’];
$columns = $data[1][‘object’][‘dd_object’][‘columns’];
// 构建创建表的 SQL 查询语句
$sql = “CREATE TABLE $tableName (“;

foreach ($columns as $column) {
$columnName = $column[‘name’];
// 如果字段名不是 DB_TRX_ID 和 DB_ROLL_PTR,则构建该字段的 SQL
if ($columnName !== ‘DB_TRX_ID’ && $columnName !== ‘DB_ROLL_PTR’ && $columnName !== ‘DB_ROW_ID’) {
$columnType = $column[‘column_type_utf8’];
$isNullable = $column[‘is_nullable’] ? ‘NULL’ : ‘NOT NULL’;
$default = ($column[‘default_value_null’] || $column[‘default_value_utf8_null’]) ? ” : “DEFAULT ‘{$column[‘default_value’]}'”;

// 构建列的注释,如果注释为空,不包含 COMMENT 部分
$comment = isset($column[‘comment’]) && !empty($column[‘comment’]) ? “COMMENT ‘{$column[‘comment’]}'” : ”;

// 构建列的 SQL
$sql .= “$columnName $columnType $isNullable $default $comment, “;
}
}
// 去除最后的逗号和空格
$sql = rtrim($sql, ‘, ‘) . “);”;
echo $sql;
}
test()
?>
​“`

**操作步骤二:删除空表的独立表空间**

​“`sql
select concat(“alter table “,table_schema,”.”,table_name,” discard tablespace;”) from information_schema.tables where table_schema=’confulence’; into outfile ‘/tmp/discard.sql’;
source /tmp/discard.sql
​“`

实际执行过程发现,有20-30张表无法成功,主外键关系问题,如果一个表一个表分析表结构,处理外键关系很痛苦

​“`sql
set foreign_key_checks=0
— 跳过外键检查,从而把有问题的20-30张表的独立表空间也删除了
​“`

**操作步骤三:拷贝生成中confulence库下的所有表的ibd文件到准备好的环境中并加载识别**

​“`sql
select concat(“alter table “,table_schema,”.”,table_name,” import tablespace;”) from information_schema.tables where table_schema=’confulence’ into outfile ‘/tmp/import.sql’;
source /tmp/import.sql
​“`

**操作步骤四:进行数据信息验证**

表都可以访问了,数据挽回了出现问题时刻的状态

案例03:mysql 5.7中误删除了ibdata1数据文件,导致数据库服务无法启动;(作业)

说明:如何恢复t100w表中数据,并且假设库中有100张表,而且表结构无法通过show create table获得;

提示:有可能是自研数据库,并且没有数据备份

思路:先获取表结构信息,然后重新建表,删除空表的独立表空间,导入表的数据文件,加载识别表数据信息

操作步骤一:mysql工具包中含有mysqlfrm工具,可以读取frm文件获得表结构;

​“`tiki wiki
[root@xiaoQ-01 ~]# ./mysqlfrm /data/3306/data/test/t100w.frm –diagnostic
​“`

操作步骤二:将新库中所有独立表空间进行删除

​“`sql
select concat(‘alter table ‘,table_schema,’.’table_name,’ discard tablespace;’) from informatin_schema.tables where table_schema=’confluence’ into outfile ‘/tmp/discard.sql’;
source /tmp/discard.sql
​“`

**类型三:undo表空间**

利用undo表空间主要用来完成撤销工作(回滚操作);

在数据库5.7版本中,默认存储在共享表空间中(ibdata);在数据库8.0版本后,默认就是独立存储了(undo_001-undo_002);

在实际生产环境中,建议在5.7版本之后,都将undo表空间进行独立文件存储;

**undo表空间管理:**

– 表空间配置参数信息查看

​“`tiki wiki
mysql> select @@innodb_undo_tablespaces;
+——————————————-+
| @@innodb_undo_tablespaces |
+——————————————-+
| 2 |
+——————————————-+
1 row in set (0.00 sec)
— 确认是否打开独立undo模式,并设置undo表空间文件个数(3-5个)

mysql> select @@innodb_max_undo_log_size;
+——————————————-+
| @@innodb_max_undo_log_size |
+——————————————-+
| 1073741824 |
+——————————————-+
1 row in set (0.00 sec)
— 表示undo日志信息的大小,默认1G

mysql> select @@innodb_undo_log_truncate;
+——————————————-+
| @@innodb_undo_log_truncate |
+——————————————-+
| 1 |
+——————————————-+
1 row in set (0.00 sec)
— 表示开启undo自动回收的机制(undo purge)

mysql> select @@innodb_purge_rseg_truncate_frequency;
+———————————————————-+
| @@innodb_purge_rseg_truncate_frequency |
+———————————————————-+
| 128 |
+———————————————————-+
1 row in set (0.00 sec)
— 触发自动回收的条件,单位是检测次数
​“`

> 官方参数使用说明(important):
>
> The number of undo tablespaces can only be configured when initializing a MySQL instence and is fixed for the
>
> life of the instance;
>
> undo表空间的数量只能在初始化MySQL实例时配置,并且在实例生命周期内是固定的

– 表空间配置参数修改调整

修改数据库5.7版本服务的undo表空间,实现undo表空间的独立存储;

查看获取数据库5.7版本的默认undo配置情况:

​“`tiki wiki
mysql> select @@innodb_undo_tablespaces;
+————————————–+
| @@innodb_undo_tablespaces |
+————————————–+
| 0 |
+————————————–+
1 row in set (0.00 sec)
— 在数据库5.7版本中,undo表空间默认并未实现独立存储;
​“`

关闭数据库服务程序,对undo表空间进行独立存储配置:

​“`tiki wiki
# 关闭数据库服务程序,清理数据库服务数据目录
[root@xiaoQ-01 ~]# systemctl stop mysqld3357
[root@xiaoQ-01 ~]# systemctl is-active mysqld3357
unknown
— 关闭数据库服务程序
[root@xiaoQ-01 ~]# rm -rf /data/3357/data/*
— 删除清空数据库数据目录

# 编写修改数据库服务配置文件
[root@xiaoQ-01 ~]# vim /data/3357/my.cnf
[mysqld]
innodb_undo_tablespaces=3
innodb_max_undo_log_size=128M
innodb_undo_log_truncate=ON
innodb_purge_rseg_truncate_frequency=32
— 在数据库服务端添加以上参数信息
​“`

重新初始化数据库服务程序:

​“`tiki wiki
[root@xiaoQ-01 ~]# /usr/local/mysql57/bin/mysqld –defaults-file=/data/3357/my.cnf –initialize-insecure –basedir=/usr/local/mysql57 –datadir=/data/3357/data –user=mysql

[root@xiaoQ-01 ~]# ll /data/3357/data/undo*
-rw-r—– 1 mysql mysql 10485760 11月 15 10:19 /data/3357/data/undo001
-rw-r—– 1 mysql mysql 10485760 11月 15 10:19 /data/3357/data/undo002
-rw-r—– 1 mysql mysql 10485760 11月 15 10:19 /data/3357/data/undo003
​“`

重新启动数据库服务程序:

​“`tiki wiki
[root@xiaoQ-01 ~]# systemctl start mysqld3357
[root@xiaoQ-01 ~]# systemctl is-active mysqld3357
active
​“`

实现undo表空间文件指定目录存储

​“`tiki wiki
# 将数据库服务进行关闭
[root@xiaoQ-01 ~]# systemctl stop mysqld3357

# 编写数据库服务配置文件
[root@xiaoQ-01 ~]# vim /data/3357/my.cnf
[mysqld]
innodb_undo_directory=/data/3357/undologs

# 创建存储undo表空间文件目录
[root@xiaoQ-01 ~]# mkdir -p /data/3357/undologs
[root@xiaoQ-01 ~]# chown -R mysql.mysql /data/*
[root@xiaoQ-01 ~]# cp -a /data/3357/data/undo* /data/3357/undologs/

# 将数据库服务进行启动
[root@xiaoQ-01 ~]# mysql -S /tmp/mysql3357.sock
mysql > select @@innodb_undo_directory;
+————————————-+
| @@innodb_undo_directory |
+————————————-+
| /data/3357/undologs |
+————————————-+
1 row in set (0.00 sec)
​“`

> 说明:对于数据库8.0版本,在进行undo表空间配置信息调整的时候,可以进行在线调整;

数据库8.0 undo表空间与数据库5.7undo表空间区别资料:

https://dev.mysql.com/doc/refman/8.0/en/innodb-undo-tablespaces.html

===============================================================================================

数据库8.0独立表空间设置扩展:Adding Undo Tablespaces

Because undo logs can become large during long-running transactions, creating additional undo tablespaces

can help prevent individual undo tablespaces from becoming too large.

As of MySQL 8.0.14, additional undo tablespaces can be created at runtime using [`CREATE UNDO TABLESPACE`](https://dev.mysql.com/doc/refman/8.0/en/create-tablespace.html) syntax.

​“`sql
# 创建新的独立的undo表空间文件
CREATE UNDO TABLESPACE tablespace_name ADD DATAFILE ‘file_name.ibu’;

# 查看已经创建的独立的undo表空间文件
mysql> select tablespace_name,file_name from information_schema.files where file_type like ‘undo log’;
+—————————+—————-+
| TABLESPACE_NAME | FILE_NAME |
+—————————+—————-+
| innodb_undo_001 | ./undo_001 |
| innodb_undo_002 | ./undo_002 |
| tablespace_name | ./xiaoQ1.ibu |
+—————————+—————-+
3 rows in set (0.00 sec)

# 删除已有的独立的undo表空间文件
ALTER UNDO TABLESPACE tablespace_name SET INACTIVE;
— 将指定的undo表空间信息设置为失效
DROP UNDO TABLESPACE tablespace_name;
— 删除指定的undo表空间信息

# 查看已有的独立的undo表空间状态
SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES
WHERE NAME LIKE ‘tablespace_name’;
​“`

===============================================================================================

**类型四:temp表空间**

临时表空间主要用于存储临时表信息,主要是在使用group by,order by,having,unique all,子查询等情况都会使用临时表;

临时表可以存储在内存和磁盘上;

**临时表空间管理:**

– 扩容临时表空间操作:

扩容前临时表空间信息查看:

​“`SQL
mysql> select @@innodb_temp_data_file_path;
+——————————————–+
| @@innodb_temp_data_file_path |
+——————————————–+
| ibtmp1:12M:autoextend |
+——————————————–+
1 row in set (0.00 sec)

mysql> select @@innodb_autoextend_increment;
+———————————————+
| @@innodb_autoextend_increment |
+———————————————+
| 64 |
+———————————————+
1 row in set (0.00 sec)
— 查看参数信息说明:ibtmp1文件,默认初始大小12M,不够用会自动扩展,默认每次扩展64M
​“`

临时表空间的扩容操作方法:

​“`tiki wiki
# 编写数据库配置文件信息
vim /etc/my.cnf
[mysqld]
innodb_temp_data_file_path=ibtmp1:12M;ibtmp2:120M:autoextend:max:500M
— 需要注意的是ibdata1文件大小必须和实际数据库要存储的数据相匹配,否则会出现如下报错信息

# 查看配置信息是否生效
mysql> select @@innodb_temp_data_file_path;
+———————————————————————+
| @@innodb_temp_data_file_path |
+———————————————————————+
| ibtmp1:12M;ibtmp2:120M:autoextend:max:500M |
+———————————————————————+
1 row in set (0.00 sec)
​“`

> 说明:建议数据初始化之前设定好临时表空间,建议2~3个,大小512M~1G;

数据库初始化时设置临时表空间容量建议:

| 序号 | 版本信息 | 建议说明 |
| —- | ——— | ———————————————————— |
| 01 | MySQL 5.7 | 设置共享表空间2~3个,大小建议512M或1G,最后一个定制为自动扩展 |
| 02 | MySQL 8.0 | 设置共享表空间1个即可,大小建议512M或1G |

临时表空间的初始设置方法:

​“`tiki wiki
# 模拟初始化清理数据
[root@xiaoQ-01 ~]# /etc/init.d/mysqld stop
[root@xiaoQ-01 ~]# rm -rf /data/3306/data/*

# 模拟初始化配置文件
[root@xiaoQ-01 ~]# vim /etc/my.cnf
[mysqld]
innodb_temp_data_file_path=ibtmp1:12M;ibtmp2:120M:autoextend:max:500M

# 模拟初始化操作命令
[root@xiaoQ-01 ~]# mysqld –initialize-insecure –user=mysql –basedir=/usr/local/mysql –datadir=/data/3306/data

# 模拟初始化重启服务
[root@xiaoQ-01 ~]# /etc/init.d/mysqld start
​“`

**类型五:redo事务日志**

redo log属于事务重做日志文件,主要用于记录内存数据页的变化(记录在内存中对数据页的操作信息),都会以日志文件方式记录;

可以实现”前进”(WAL-write ahead log)的功能,数据库会保证redo操作日志优先于数据写入磁盘,加快了事务提交效率(提高并发);

在进行日志信息记录时,采用的是顺序IO,而数据存储时采用的异步IO(随机IO);

重做日志,记录了所有缓冲池修改的数据,修改数据的时候先写日志,后修改的缓冲区;

假设修改写入操作的时候数据库崩溃了或停电了,等下次启动通过重做日志来保持数据的正确性。

redo事务日志的存储路径为:默认存储在数据库服务的数据目录下,默认大小为48M

​“`tiki wiki
[root@xiaoQ-01 data]# pwd
/data/3306/data
[root@xiaoQ-01 data]# ll ib_log*
-rw-r—– 1 mysql mysql 50331648 11月 15 11:34 ib_logfile0
-rw-r—– 1 mysql mysql 50331648 11月 15 11:27 ib_logfile1
​“`

**redo事务日志管理:**

– 进行redo事务日志操作:

redo事务日志配置信息查看:

​“`SQL
mysql> show variables like ‘%innodb_log_file%’;
+———————————–+—————+
| Variable_name | Value |
+———————————–+—————+
| innodb_log_file_size | 50331648 |
| innodb_log_files_in_group | 2 |
+———————————–+—————+
2 rows in set (0.00 sec)
— 在实际生产环境中,建议大小为512M~4G,应用组数为2~4组(写入数据过程轮询写入)
​“`

redo事务日志配置操作方法:

​“`tiki wiki
# 编写数据库配置文件信息
vim /etc/my.cnf
[mysqld]
innodb_log_file_size=100M
innodb_log_files_in_group=3

# 确认配置信息是否已经生效
[root@xiaoQ-01 data]# /etc/init.d/mysqld restart
[root@xiaoQ-01 data]# ll /data/3306/data/ib_log*
-rw-r—– 1 mysql mysql 104857600 11月 15 15:12 /data/3306/data/ib_logfile0
-rw-r—– 1 mysql mysql 104857600 11月 15 15:12 /data/3306/data/ib_logfile1
-rw-r—– 1 mysql mysql 104857600 11月 15 15:12 /data/3306/data/ib_logfile2
​“`

**类型六:ib_buffer_pool预热文件**

ib_buffer_pool预热文件可以用于缓冲和缓存,可以存储”热”数据页(经常查询或修改的数据页),从而减少物理IO消耗;

从数据库5.7版本开始,数据库正常关闭后,内存中存储的数据页缓冲或缓存信息均会失效,重新启动后还会消耗IO获取相应数据页信息;

为了可以尽量减少磁盘IO的消耗,可以将内存中的热数据页信息存储在ib_buffer_pool文件中;

数据库服务再次启动后,会直接读取ib_buffer_pool文件中信息,并将读取的信息加载到内存中,最终减少随机IO数量;

> 说明:存在ib_buffer_pool预热文件后,有可能在数据库服务关闭时比较耗费一些时间,但实际环境数据库服务关闭情况较少;

**类型七:Doublewrite Buffer(DWB)文件**

双写缓冲区,我们知道数据修改先修改的Page页后又刷到磁盘的,在刷到磁盘前这些数据会先存放在双写缓存区中;

双写缓存区是用来保障数据写入磁盘时候出现问题的备份。

DWB文件主要作用是:mysql process crash in the middle of a page write(在数据库服务存储时,数据页写了一半);

数据库Innodb可以找到一个好的数据页副本从Doublewrite Buffer文件中,主要是避免数据信息出现损坏;

MySQL数据库最小IO存储单元是page(16kB),OS系统中最小的IO存储单元是block(4kB),OS也可以称为存储子系统;

会出现一个问题:数据库系统与操作系统的存储关系问题,在数据库中写入一个数据页时,在文件系统层面可能只是写入了2个block;

在数据库8.0.19之前,默认在ibdataN文件中进行存储,在数据库8.0.20以后,可以进行独立文件存储;

​“`tiki wiki
[root@xiaoQ-01 data]# ll *ib_16384*
-rw-r—– 1 mysql mysql 196608 11月 15 15:14 #ib_16384_0.dblwr
-rw-r—– 1 mysql mysql 8585216 11月 15 11:27 #ib_16384_1.dblwr
​“`

##### **02 In-Memory Structures(内存结构部分)**

在内存结构中也是包含很多的组成部分,主要的组成部分有:

**组成部分一:InnoDB Buffer Pool(IBP)**

Buffer Pool内存存储区域主要用来缓冲或缓存数据库服务的数据页和索引页,是MySQL中最大的、最重要的内存区域;

缓冲池,数据缓冲池里面不是直接存放数据,而是存放page页,将数据存放在了page页中,在缓冲池page页是通过链表形式来存放的;

**Buffer Pool内存空间管理:**

– Buffer Pool配置参数信息查看:

​“`tiki wiki
mysql> select @@innodb_buffer_pool_size;
+—————————————+
| @@innodb_buffer_pool_size |
+—————————————+
| 134217728 |
+—————————————+
1 row in set (0.00 sec)
— buffer pool默认内存空间大小为128M,生产建议大小可以设置为物理内存总量的50%~80%
​“`

– Buffer Pool配置参数修改方法:

​“`tiki wiki
# 配置信息临时调整
mysql > set global innodb_buffer_pool_size=268435456;
— 配置调整后,重新登录mysql数据库生效

# 配置信息永久调整
[root@xiaoQ-01 ~]# vim /etc/my.cnf
[mysqld]
innodb_buffer_pool_size=256M
— 配置调整后,重新启动mysql数据库生效
​“`

**组成部分二:Change Buffer**

写缓冲区,正常情况下修改数据是先修改的缓冲池中Page的数据,但是缓冲池肯定不是所有的数据;

而修改数据没有对应的Page数据的时候并不会直接把数据加载到缓冲池中去,而是放在了写缓冲区中记录;

等到数据被读取的时候再把数据合并到缓冲池中。

**组成部分三:Adaptive Hash Index**

自适应Hash索引,InnoDB存储引擎会根据Page页的访问频率和模式建立对应的Hash索引,这个索引是根据查询情况自动建立的;

称为自适应Hash索引。

**组成部分四:InnoDB Log Buffer(ILB)**

Log Buffer内存存储区域主要用来缓冲 redo/undo log日志信息;

日志缓冲区,主要用来保存写入磁盘的(Redo/Undo)日志文件,日志缓冲区会定期刷新到磁盘log文件中;

这样不用每次日志都进行磁盘IO操作,提高效率。

**Log Buffer内存空间管理:**

– Log Buffer配置参数信息查看:

​“`tiki wiki
mysql> select @@innodb_log_buffer_size;
+————————————–+
| @@innodb_log_buffer_size |
+————————————–+
| 16777216 |
+————————————–+
1 row in set (0.00 sec)
— log_buffer默认内存空间大小为16M,生产建议大小可以设置为innodb_log_file_size文件大小的 1-N倍(后续说明)
​“`

– Log Buffer配置参数修改方法:

​“`tiki wiki
# 配置信息临时调整
mysql > set global innodb_log_buffer_size=33554432;
— 配置调整后,重新登录mysql数据库生效

# 配置信息永久调整
[root@xiaoQ-01 ~]# vim /etc/my.cnf
[mysqld]
innodb_log_buffer_size=32M
— 配置调整后,重新启动mysql数据库生效
​“`

“`

# 第十三章 事务机制

#### 1.12.1 数据库存储事务机制概念

事务(Transaction)可以更通俗的理解为交易,所以事务会伴随着交易类的业务类型出现的概念(工作模式);

现实生活中存在很多的交易行为,比如:物换物的等价交换、货币换物的等价交换、虚拟货币换物(虚拟物品)的等价交换;

因此就需要考虑如何保证现实生活中交易过程的和谐,一般会有法律、道德等方面规则进行约束;

而在数据库服务中为了保证线上交易的”和谐”,便加入了”事务”工作机制

#### 1.12.2 数据库存储事务机制特性

数据事务设计遵循ACID的原则:原子性(Atomicity)一致性(Consistency)隔离性(Isolation)持久性(Durability)

一个支持事务(Transaction)的数据库,必须要具有这四个特性,否则在事务过程当中无法保证数据的正确性;

在数据库服务中引入事务机制概念,主要是为了应用事务机制的相关特性处理安全一致性问题,其中事务机制主要包含的特性有:

**特性一:原子性(Atomicity)**

原子性表示一个事务生命周期中的DML语句,要么全成功要么全失败,不可以出现中间状态;

事务是数据库的逻辑工作单位,事务中包括的所有操作要么都做,要么都不做。

语句要么全执行,要么全不执行,是事务最核心的特性,事务本身就是以原子性来定义的;`实现主要基于undo log`

“`
Begin:DML01 DML02 DML03 Commit;
“`

**特性二:一致性(Consistency)**

数据库事务的一致性是指事务必须确保数据库从一个一致性状态变换到另一个一致性状态。

一致性表示一个事务发生前、中、后,数据都最终保持一致,即读和写都要保证一致性;

事务追求的最终目标,一致性的实现既需要数据库层面的保障,也需要应用层面的保障;

“`
CR + Double write
“`

举例说明补充说明:

事务开始之前和结束之后,数据库的完整性约束(如字段约束、外键约束、触发器等)没有被破坏;

在转账操作中,无论转账操作是否成功,账户A和账户B的存款总额应保持不变。

一致性还涉及业务逻辑上的完整性;

比如转账操作中,账户A减少的金额应与账户B增加的金额相等。

事务的原子性、隔离性和持久性是实现一致性的手段:

– 原子性确保事务中的所有操作要么全部完成,要么全部不执行;

– 隔离性确保并发事务之间的操作相互隔离,不会相互干扰;

– 持久性则确保事务对数据库的改变一旦提交,就不会丢失,即使在系统故障的情况下也能恢复。

**特性三:隔离性(Isolation)**

隔离性表示一个事务操作数据行的时候,不会受到其他事务的影响,主要利用锁机制来保证隔离性;

即一个事务内部的操作及使用的数据对其他事务是隔离的,并发执行的各个事务之间互相不干扰;

**特性四:持久性(Durability)**

持久性表示一旦事务进行了提交,即可永久生效(落盘)

保证事务提交后不会因为宕机等原因导致数据丢失;`实现主要基于redo log`

事务ACID相关知识官方说明:https://dev.mysql.com/doc/refman/8.0/en/mysql-acid.html

#### 1.10.3 数据库存储事务生命周期

在运用事务机制完成相关工作任务时,对于事务使用是存在生命周期概念的,标准显示的事务生命周期控制语句有:

“`tiki wiki
# 开启事务机制
begin;
start transaction;
#事务周期内存,完成事件操作
DML01
DML02
DML03

# 提交事务任务(事务结束 在binlog中记录事件信息)
commit;
# 回滚事务操作(事务结束)
rollback;
“`

> 说明:事务生命周期中,只能使用DML语句,其中包括:select、update、delete、insert;DDL语句会隐式进行提交

事务的生命周期操作演示:

“`tiki wiki
# 进行测试数据库查询数据
mysql> use world;
mysql> select * from city limit 10;

# 进行测试数据库数据撤销修改
mysql> begin;
mysql> update city set population=10 where id=1;
mysql> update city set population=10 where id=2;
— 由于是采用事务进行的修改,所以只是在内存层面进行的修改,并没有对磁盘上的数据进行修改;
mysql> select * from city limit 10;
— 由于是采用事务进行的修改,此时看到的数据信息只是内存层面的修改信息
mysql> rollback;
— 由于是采用事务进行的撤销,会读取undo文件信息,将事务操作撤回到事务开始前的状态
mysql> select * from city limit 10;
— 由于是采用事务进行的修改,当撤销操作执行完,看到数据信息还是原来的;

# 进行测试数据库数据永久修改
mysql> begin;
mysql> update city set population=10 where id=1;
mysql> update city set population=10 where id=2;
— 由于是采用事务进行的修改,所以只是在内存层面进行的修改,并没有对磁盘上的数据进行修改;
mysql> select * from city limit 10;
— 由于是采用事务进行的修改,此时看到的数据信息只是内存层面的修改信息
mysql> commit;
— 由于是采用事务进行的提交,会加载redo文件信息,将事务内存层面的修改同步到磁盘中(完成了D特性)
mysql> select * from city limit 10;
— 由于是采用事务进行的修改,当执行操作执行完,看到数据信息将永久保存下载;
“`

#### 1.10.4 数据库存储事务提交方式

**方式一:在事务生命周期管理过程中,事务的提交机制可以采用自动提交方式(auto_commit)**

**事务自动提交方式作用说明:**

事务自动提交表示在没有显示的使用`begin`语句的时候,执行DML操作语句时,会在DML操作语句前自动添加`begin`;

并在DML操作语句执行后自动添加`commit`;

在生产环境中,若处于频繁事务业务场景中,建议关闭autocommit自动提交功能,或者每次事务执行的时候;

都进行显示的执行`begin`和`commit`

**事务自动提交方式参数信息:**

“`tiki wiki
mysql> select @@autocommit;
+———————+
| @@autocommit |
+———————+
| 1 |
+———————+
1 row in set (0.00 sec)
— 在事务自动提交功能设置修改时,设置为1表示开启自动提交,设置为0表示关闭自动提交
“`

**事务自动提交方式参数修改:**

“`tiki wiki
# 临时关闭事务自动提交功能
mysql> set global autocommit=0;
— 配置调整后,重新登录mysql数据库生效

# 永久关闭事务自动提交功能
[root@xiaoQ-01 ~]# vim /etc/my.cnf
[mysqld]
autocommit=0
— 配置调整后,重新启动mysql数据库生效
“`

事务自动提交方式设置方式优点缺点说明:

| 序号 | 参数配置 | 优势 |
| —— | ———————————- | ———————————————————— |
| 情况01 | autocommit=0
关闭事务自动提交 | 可以编写多个关联的DML,进行一次性提交操作,若出现异常可以回滚
符合原子特性 |
| | | **劣势** |
| | | 可能出现多个关联的DML,只是完成了部分操作,这时就可能等待状态
基于隔离特性,操作的数据表或数据行就会进入锁定状态 |
| | **参数配置** | **优势** |
| 情况02 | autocommit=1
开启事务自动提交 | 可以出现多个关联的DML,逐行操作自动提交,就可以不用处于锁等待状态 |
| | | **劣势** |
| | | 可能出现多个关联的DML,,每执行一条就进行提交,会造成多个语句执行不符合原子性 |

**方式二:在事务生命周期管理过程中,事务的提交机制可以采用隐式提交方式:**

在进行事务操作时,需要注意操作语句必须都是DML语句,如果中间插入了DDL语句,也会造成之前的事务操作自动提交;

“`tiki wiki
begin; DML1; DML2; DDL1; COMMIT; DML3; COMMIT;
— 这种情况出现会破坏原本事务的原子性
“`

**隐式自动提交方式语句:**

在出现隐式自动提交时,可能导致提交的非事务语句有:

| 序号 | 语句类型 | 涉及命令 |
| —- | ———— | ————————————————— |
| 01 | DDL语句类型 | alter、create、drop |
| 02 | DCL语句类型 | grant、revoke、set password |
| 03 | 锁定语句类型 | lock tables、unlock tables |
| 04 | 其他语句类型 | truncate table、load data infile、select for update |

> 说明:在多个数据库会话窗口中,A窗口的所有事务性DML操作,不会受到B窗口的非事务语句影响,同一会话窗口会有影响;

**隐式自动回滚情况分析:**

– 情况一:在事务操作过程中,会话窗口自动关闭了,会进行隐式自动回滚;
– 情况二:在事务操作过程中,数据库服务被停止了,会进行隐式自动回滚;
– 情况三:在事务操作过程中,出现事务冲突死锁了,会进行隐式自动回滚;

#### 1.10.5 数据库存储事务隔离级别

数据库事务隔离级别主要作用是实现事务工作期间,数据库操作读的隔离特性,所谓读的操作就是将数据页可以调取到内存;

然后可以读取数据页中相应数据行的能力,并且不同事务之间的数据页读操作相互隔离;

可以简单理解为:一个事务在对数据页中数据行做更新操作时,在没有更新提交前,另一个事务此时是不能读取数据页中数据行内容的;

对于数据库存储事务隔离级别包括4种,可以通过操作命令查看获取当前使用的隔离级别:

“`tiki wiki
mysql> select @@transaction_isolation;
+———————————+
| @@transaction_isolation |
+———————————+
| REPEATABLE-READ |
+———————————+
1 row in set (0.00 sec)
“`

**常用的事务隔离级别类型:**

**类型一:RU(READ-UNCOMMITTED 表示读未提交)**

可以读取到事务未提交的数据,隔离性差,会出现脏读(当前内存读),不可重复读,幻读问题;

**类型二:RC(READ-COMMITTED 表示读已提交)**`可用`

可以读取到事务已提交的数据,隔离性一般,不会出现脏读问题,但是会出现不可重复读,幻读问题;

**类型三:RR(REPEATABLE-READ 表示可重复读)**`默认`

可以防止脏读(当前内存读),防止不可重复读问题,防止会出现的幻读问题,但是并发能力较差;

会使用next lock锁进制,来防止幻读问题,但是引入锁进制后,锁的代价会比较高,比较耗费CPU资源,占用系统性能;

**类型四:SR(SERIALIZABLE 可串行化)**

隔离性比较高,可以实现串行化读取数据,但是事务的并发度就没有了;

这是事务的最高级别,在每条读的数据上,加上锁,使之不可能相互冲突

事务隔离级别官方链接:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html

**常用的事务隔离级别名词:**

在解释分析说明相应的隔离级别名词前,需要对数据库事务隔离级别进行调整,以及关闭自动提交功能:

“`tiki wiki
# 设置事务隔离级别
mysql> set global transaction_isolation=’READ-UNCOMMITTED’; 隔离级别最弱(并行读取数据问题) 并行处理能力最强
mysql> set global transaction_isolation=’READ-COMMITTED’; RC
mysql> set global transaction_isolation=’REPEATABLE-READ’; 抑制写并行操作 RR
mysql> set global transaction_isolation=’SERIALIZABLE’; 抑制读/写并行操作 隔离级别最强 并行处理会话/处理事务能力 弱

# 查看事务隔离级别
mysql> select @@transaction_isolation;
+———————————+
| @@transaction_isolation |
+———————————+
| READ-UNCOMMITTED |
+———————————+
mysql> select @@transaction_isolation;
+———————————+
| @@transaction_isolation |
+———————————+
| READ-COMMITTED |
+———————————+
mysql> select @@transaction_isolation;
+———————————+
| @@transaction_isolation |
+———————————+
| REPEATABLE-READ |
+———————————+

# 临时关闭自动提交功能:
mysql> set global autocommit=0;
mysql> select @@autocommit;
+———————+
| @@autocommit |
+———————+
| 0 |
+———————+
“`

创建隔离级别测试数据表:

“`sql
mysql> use oldboy
mysql> create table t1 (
id int not null primary key auto_increment,
a int not null,
b varchar(20) not null,
c varchar(20) not null
) charset=utf8mb4 engine=innodb;

mysql> begin;
mysql> insert into t1(a,b,c)
values
(5,’a’,’aa’),
(7,’c’,’ab’),
(10,’d’,’ae’),
(13,’g’,’ag’),
(14,’h’,’at’),
(16,’i’,’au’),
(20,’j’,’av’),
(22,’k’,’aw’),
(25,’l’,’ax’),
(27,’o’,’ay’),
(31,’p’,’az’),
(50,’x’,’aze’),
(60,’y’,’azb’);
mysql> commit;
— 确认两个SQL会话窗口,即不同的事务查看的数据是否一致的;
“`

– **名词解读分析一:脏读**

脏读主要表示在一个事务窗口中,没有数据修改提交操作前,另一个事务就可以看到内存中数据页的修改;

简单理解:在一个事务窗口中,可以读取到别人没有提交的数据信息;

**利用隔离级别RU解读:**

“`tiki wiki
# 设置事务隔离级别
mysql> set global transaction_isolation=’READ-UNCOMMITTED’;
mysql> select @@transaction_isolation;
+———————————+
| @@transaction_isolation |
+———————————+
| READ-UNCOMMITTED |
+———————————+
mysql> set global autocommit=0;
mysql> select @@autocommit;
— 重新开启两个SQL会话窗口

# 数据库A会话窗口操作
mysql> begin;
mysql> update t1 set a=10 where id=1;
— 只是在内存层面进行数据页中数据修改
mysql> rollback;
— 进行事务回滚操作

# 数据库B会话窗口操作
mysql> begin;
mysql> select * from t1 where id=1;
+—-+—-+—+—-+
| id | a | b | c |
+—-+—-+—+—-+
| 1 | 10 | a | aa |
+—-+—-+—+—-+
1 row in set (0.01 sec)
— 在A会话窗口没提交的事务修改,被B会话窗口查询到了
mysql> select * from t1 where id=1;
+—-+—-+—+—-+
| id | a | b | c |
+—-+—-+—+—-+
| 1 | 5 | a | aa |
+—-+—-+—+—-+
1 row in set (0.01 sec)
— 在A会话窗口进行回滚后,在B窗口查询的数据又恢复了
“`

– **名词解读分析二:不可重复读**

不可重复读表示在一个事务中,利用相同的语句多次查询,获取的数据信息是不同的;

**利用隔离级别RU解读:**

“`tiki wiki
# 数据库B会话窗口操作
mysql> begin;
mysql> select * from t1 where id=1;
+—-+—-+—+—-+
| id | a | b | c |
+—-+—-+—+—-+
| 1 | 10 | a | aa |
+—-+—-+—+—-+
1 row in set (0.01 sec)
— 在B会话事务窗口进行数据第一次查询看到数据信息:a=10
mysql> select * from t1 where id=1;
+—-+—-+—+—-+
| id | a | b | c |
+—-+—-+—+—-+
| 1 | 5 | a | aa |
+—-+—-+—+—-+
1 row in set (0.01 sec)
— 在B会话事务窗口进行数据第二次查询看到数据信息:a=5
“`

**利用隔离级别RC解读:**

“`tiki wiki
# 设置事务隔离级别
mysql> set global transaction_isolation=’READ-COMMITTED’;
mysql> select @@transaction_isolation;
+———————————+
| @@transaction_isolation |
+———————————+
| READ-COMMITTED |
+———————————+
mysql> set global autocommit=0;
mysql> select @@autocommit;
— 重新开启两个SQL会话窗口

# 数据库A会话窗口操作
mysql> use oldboy;
mysql> begin;
mysql> select * from t1 where id=1;
+—-+—+—+—-+
| id | a | b | c |
+—-+—+—+—-+
| 1 | 5 | a | aa |
+—-+—+—+—-+
1 row in set (0.00 sec)
— A窗口事务查询信息 = B窗口事务查询信息
mysql> update t1 set a=10 where id=1;
— A窗口事务进行修改
mysql> commit;
— A窗口事务进行提交

# 数据库B会话窗口操作
mysql> use oldboy;
mysql> begin;
mysql> select * from t1 where id=1;
+—-+—+—+—-+
| id | a | b | c |
+—-+—+—+—-+
| 1 | 5 | a | aa |
+—-+—+—+—-+
1 row in set (0.00 sec)
— A窗口事务查询信息 = B窗口事务查询信息
mysql> select * from t1 where id=1;
+—-+—+—+—-+
| id | a | b | c |
+—-+—+—+—-+
| 1 | 5 | a | aa |
+—-+—+—+—-+
1 row in set (0.00 sec)
— B窗口事务查询信息,不能看到A窗口事务未提交的数据变化,避免了脏数据问题;
mysql> select * from t1 where id=1;
+—-+—+—+—-+
| id | a | b | c |
+—-+—+—+—-+
| 1 | 10 | a | aa |
+—-+—+—+—-+
1 row in set (0.00 sec)
— A窗口事务提交之后,B窗口事务查询信息和之前不同了
“`

**利用隔离级别RR解读:**

“`tiki wiki
# 设置事务隔离级别
mysql> set global transaction_isolation=’REPEATABLE-READ’;
mysql> select @@transaction_isolation;
+———————————+
| @@transaction_isolation |
+———————————+
| REPEATABLE-READ |
+———————————+
mysql> set global autocommit=0;
mysql> select @@autocommit;
— 重新开启两个SQL会话窗口

# 数据库A会话窗口操作
mysql> use oldboy;
mysql> begin;
mysql> select * from t1;
— 确认初始数据信息
mysql> update t1 set a=10 where id=1;
— A窗口事务进行修改
mysql> commit;
— A窗口事务进行提交

# 数据库B会话窗口操作
mysql> use oldboy;
mysql> begin;
mysql> select * from t1;
— 确认初始数据信息
mysql> select * from t1 where id=1;
+—-+—+—+—-+
| id | a | b | c |
+—-+—+—+—-+
| 1 | 5 | a | aa |
+—-+—+—+—-+
1 row in set (0.00 sec)
— B窗口事务查询信息,不能看到A窗口事务未提交的数据变化,避免了脏数据问题;
mysql> select * from t1 where id=1;
+—-+—+—+—-+
| id | a | b | c |
+—-+—+—+—-+
| 1 | 5 | a | aa |
+—-+—+—+—-+
1 row in set (0.00 sec)
— A窗口事务提交之后,B窗口事务查询信息和之前是相同的;
— 在RR级别状态下,同一窗口的事务生命周期下,每次读取相同数据信息是一样,避免了不可重复读问题
mysql> commit;
mysql> select * from t1 where id=1;
— 在RR级别状态下,同一窗口的事务生命周期结束后,看到的数据信息就是修改的了
“`

– **名词解读分析三:幻读**

**利用隔离级别RC解读:**

“`tiki wiki
# 设置事务隔离级别
mysql> set global transaction_isolation=’READ-COMMITTED’;
mysql> select @@transaction_isolation;
+———————————+
| @@transaction_isolation |
+———————————+
| READ-COMMITTED |
+———————————+
mysql> set global autocommit=0;
mysql> select @@autocommit;
— 重新开启两个SQL会话窗口

# 数据库A会话窗口操作(重新进入)
mysql> use oldboy;
mysql> select * from t1;
+—-+—-+—+—–+
| id | a | b | c |
+—-+—-+—+—–+
| 1 | 10 | a | aa |
| 2 | 7 | c | ab |
| 3 | 10 | d | ae |
| 4 | 13 | g | ag |
| 5 | 14 | h | at |
| 6 | 16 | i | au |
| 7 | 20 | j | av |
| 8 | 22 | k | aw |
| 9 | 25 | l | ax |
| 10 | 27 | o | ay |
| 11 | 31 | p | az |
| 12 | 50 | x | aze |
| 13 | 60 | y | azb |
+—-+—-+—+—–+
13 rows in set (0.00 sec)
— 查看获取A窗口表中数据
mysql> alter table t1 add index idx(a);
— 在A窗口中,添加t1表的a列为索引信息
mysql> begin;
— 在A窗口和B窗口中,同时做开始事务操作;
mysql> update t1 set a=20 where a<20; -- 在A窗口中,将a<20的信息均调整为20 mysql> commit;
— 在A窗口中,进行事务提交操作,是在B窗口事务没有提交前
mysql> select * from t1;
— 在A窗口中,查看数据信息,希望看到的a是没有小于20的,但是结果看到了a存在等于10的(即出现了幻读)

# 数据库B会话窗口操作(重新进入)
mysql> use oldboy;
mysql> select * from t1;
+—-+—-+—+—–+
| id | a | b | c |
+—-+—-+—+—–+
| 1 | 10 | a | aa |
| 2 | 7 | c | ab |
| 3 | 10 | d | ae |
| 4 | 13 | g | ag |
| 5 | 14 | h | at |
| 6 | 16 | i | au |
| 7 | 20 | j | av |
| 8 | 22 | k | aw |
| 9 | 25 | l | ax |
| 10 | 27 | o | ay |
| 11 | 31 | p | az |
| 12 | 50 | x | aze |
| 13 | 60 | y | azb |
+—-+—-+—+—–+
13 rows in set (0.00 sec)
— 查看获取B窗口表中数据
mysql> begin;
mysql> insert into t1(a,b,c) values(10,’A’,’B’)
— 在B窗口中,插入一条新的数据信息 a=10
mysql> commit;
— 在B窗口中,进行事务提交操作
“`

**利用隔离级别RR解读:**

“`tiki wiki
# 设置事务隔离级别
mysql> set global transaction_isolation=’REPEATABLE-READ’;
mysql> select @@transaction_isolation;
+———————————+
| @@transaction_isolation |
+———————————+
| REPEATABLE-READ |
+———————————+
mysql> set global autocommit=0;
mysql> select @@autocommit;
— 重新开启两个SQL会话窗口

# 数据库A会话窗口操作
mysql> use oldboy;
mysql> select * from t1;
— 查看获取A窗口表中数据
mysql> alter table t1 add index idx(a);
— 在A窗口中,添加t1表的a列为索引信息
mysql> begin;
mysql> update t1 set a=20 where a>20;
— 在A窗口中,将a>20的信息均调整为20

# 数据库B会话窗口操作
mysql> use oldboy;
mysql> select * from t1;
— 查看获取B窗口表中数据
mysql> begin;
mysql> insert into t1(a,b,c) values(30,’sss’,’bbb’);
— 在B窗口中,插入一条新的数据信息 a=30,但是语句执行时会被阻塞,没有反应;
mysql> show processlist;
— 在C窗口中,查看数据库连接会话信息,insert语句在执行,等待语句超时(默认超时时间是50s)
— 因为此时在RR机制下,创建了行级锁(阻塞修改)+间隙锁(阻塞区域间信息插入)=next lock
— 区域间隙锁 < 左闭右开(可用临界值) ; 区域间隙锁 > 左开右闭(不可用临界值)
“`

利用MVCC机制隔离(只能保证读的隔离)

**MVCC技术概念简述:**

MVCC(multi-version-concurrent-control)即多版本并发控制,是一种并发控制的方法;

可以类别成Git进行并发处理的机制,其实就是每个事务在发生更新的过程中,维护发生更新事务的各个版本;

`各个事务版本通过undo的日志(前镜像)实现快照的技术(read view),从而可以保存多个事务版本;`

在MySQL InnoDB存储引擎下RC、RR基于MVCC(多版本并发控制)进行并发事务控制;

MVCC是基于”数据版本”对并发事务进行访问。

**MVCC技术引入说明:**

根据之前的事务隔离级别,可以等到以下结论信息:

对于RR级别在innodb引擎下,可以实现解决不可重复读和幻读等相关问题,需要使用Innodb引擎中的MVCC机制;

| 隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
| ————— | ———- | —————- | ——————– | —— |
| READ UNCOMMITED | 是 | 是 | 是 | 否 |
| READ COMMITTED | 否 | 是 | 是 | 否 |
| REPEATABLE READ | 否 | 否 | 否(引擎innodb除外) | 否 |
| SERIALIZABLE | 否 | 否 | 否 | 是 |

**MVCC技术原理详述:**

假设一个数据库服务中,出现了以下4个并发事务操作信息:

RR隔离级别查看信息:select 01 查看信息为张三;select 02 查看信息为张三;

RC隔离级别查看信息:select 01 查看信息为张三;select 02 查看信息为张小三;

> 说明:RC级别下出现”不可重复读情况”,RR级别下实现了”可重复读情况

为什么在MySQL数据库中,应用Innodb存储引擎时,两种不同的隔离级别(RR/RC)会出现不同的读取结果?

这就需要了解MVCC的工作原理机制,其中首先需要掌握undo log的链接表机制(版本链):

以上信息就是通过undo log回滚日志所产生的版本链,和每个版本对应的数据镜像信息;

但是新的问题又出现了:

undo log回滚日志不是会被删除吗?特别是中间数据万一被删除了版本链不就断了吗?

`undo log版本链并不是立即删除清理的;`

MySQL数据库服务要确保版本链上数据镜像不再被其他并行事务”引用”后才进行删除;

了解完什么是undo log版本链相关知识后,那么版本链和MVCC有什么关系呢,下面需要再了解一个新的知识”Read View”;

ReadView是”`快照读`”SQL执行时MVCC提取数据的依据;

– `快照读`就是最普通的select查询SQL语句;(应用MVCC读取数据)

– `当前读`指代执行下列语句时进行数据读取的方式;(应用next key lock锁进制读取数据)

insert、update、delete、select…for update、select…lock in share mode

ReadView是一个数据结构,包含4个组成字段:

| 字段 | 结构 | 解释说明 |
| —- | ————– | ———————————————— |
| 01 | m_ids | 当前活跃的事务编号集合,即没有提交的事务编号汇总 |
| 02 | min_trx_id | 最小活跃事务编号 |
| 03 | max_trx_id | 预分配事务编号,当前最大事务编号+1 |
| 04 | creator_trx_id | readview创建者的事务编号 |

根据以上readview的数据结构信息,可以得到以上数据业务的readview信息:

**场景01:读已提交(RC)-在每一次执行快照读时生成新的readview**

生成的readview数据结构信息如下:

**第一次数据读取过程分析:(RC级别下的第一次readview信息读取)**

版本链数据访问规则:

– 判断当前版本链中数据镜像事务trx_id = creator_trx_id(4)

若成立说明数据就是自己这个事务更改的,可以访问;

– 判断当前版本链中数据镜像事务trx_id < min_trx_id(2) 若成立说明数据已经提交了,可以访问 - 判断当前版本链中数据镜像事务trx_id > max_trx_id(5)

若成立说明该事务是在readview生成以后才开启的,不允许访问数据

– 判断当前版本链中数据镜像事务min_trx_id<=trx_id<=max_trx_id(5) 若成立需要在m_ids事务集合中进行对比,当前事务trx_id若未出现在集合中,则代表数据已经提交了,可以访问 > 结论:因此根据以上的版本链数据访问规则,readview可以读取到事务trx_id=1提交的数据,即数据”张三”;

**第二次数据读取过程分析:(RC级别下的第二次readview信息读取)**

版本链数据访问规则:

– 判断当前版本链中数据镜像事务trx_id = creator_trx_id(4)

若成立说明数据就是自己这个事务更改的,可以访问;

– 判断当前版本链中数据镜像事务trx_id < min_trx_id(2) 若成立说明数据已经提交了,可以访问 - 判断当前版本链中数据镜像事务trx_id > max_trx_id(5)

若成立说明该事务是在readview生成以后才开启的,不允许访问数据

– 判断当前版本链中数据镜像事务 min_trx_id<=trx_id<=max_trx_id(5) 若成立需要在m_ids事务集合中进行对比,当前事务trx_id若未出现在集合中,则代表数据已经提交了,可以访问 > 结论:因此根据以上的版本链数据访问规则,readview可以读取到事务trx_id=1提交的数据,即数据”张小三”;

**场景02:可重复读(RR)-仅在第一次执行快照读时生成readview,后续快照读复用(但有例外情况)**

生成的readview数据结构信息如下:

**第一次数据读取过程分析:(RR级别下的第一次readview信息读取)**

版本链数据访问规则:

– 判断当前版本链中数据镜像事务trx_id = creator_trx_id(4)

若成立说明数据就是自己这个事务更改的,可以访问;

– 判断当前版本链中数据镜像事务trx_id < min_trx_id(2) 若成立说明数据已经提交了,可以访问 - 判断当前版本链中数据镜像事务trx_id > max_trx_id(5)

若成立说明该事务是在readview生成以后才开启的,不允许访问数据

– 判断当前版本链中数据镜像事务 min_trx_id<=trx_id<=max_trx_id(5) 若成立需要在m_ids事务集合中进行对比,当前事务trx_id若未出现在集合中,则代表数据已经提交了,可以访问 > 结论:因此根据以上的版本链数据访问规则,readview可以读取到事务trx_id=1提交的数据,即数据”张三”;

**第二次数据读取过程分析:(RR级别下的第二次readview信息读取)**

由于RR级别下,会复用第一次读的readview信息,所以读取的的数据是一致的,从而实现了可重复读情况;

> 结论:因此根据以上的版本链数据访问规则,readview可以读取到事务trx_id=1提交的数据,即数据”张三”;

RR级别下使用MVCC可以解决幻读问题吗?

可以解决,但不能完全解决;

因为在MVCC机制中,并不是采用锁的机制,完全的对事务数据操作进行了隔离,而是采用版本控制的方式,变相的解决了幻读问题;

对于RR隔离级别中,由于连续多次快照读,ReadView会产生复用情况,所以在两个快照读之间,就算其他事务产生了数据变化;

对于当前事务来说,变化的数据信息都是不可见的,所以没有幻读问题;

但是会有一种特例情况:当两次快照读之间存在当前读,ReadView会重新生成,导致产生幻读;

![1695354305898](D:/学习//4_数据库/视频和笔记/系统运维95期-Day14-数据库服务事务机制/课程笔记目录/MySQL中级数据库课程-11章-数据库服务事务机制/课件配图资料/1695354305898.png)

对于隔离级别而言,只有RC和RR级别可以使用到MVCC机制的,实现一种快照读机制,而RU和SR级别是不会使用到MVCC机制的;

– RC:应用MVCC的快照读机制,是基于语句级别的;`(不可重复读 ture)`

在事务期间,执行每个查询语句的时候,都会检查MVCC版本(快照列表),获取最新的已提交事务的快照;

– RR:应用MVCC的快照读机制,是基于事务级别的;`(不可重复读 false)`

在事务期间,执行首条查询语句的时候,就会生成MVCC版本(相应快照),将会一直读取此快照数据信息,直到事务生命周期结束;

以上的RR隔离级别利用MVCC的快照读机制,又称为一致性快照读;

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注