数据库介绍
什么是数据
数据(data)是事实或观察的结果,是对客观事物的逻辑归纳,是用于表示客观事物的未经加工的的原始素材。
数据可以是连续的值,比如声音、图像,称为模拟数据。也可以是离散的,如符号、文字,称为数字数据。
在计算机系统中,数据以二进制信息单元0,1的形式表示。
数据的定义:数据是指对客观事件进行记录并可以鉴别的符号,是对客观事物的性质、状态以及相互关系等进行记载的物理符号或这些物理符号的组合。它是可识别的、抽象的符号。
什么是数据库
数据库就是存储和管理数据的仓库,数据按照一定的格式进行存储,用户可以对数据库中的数据进行增加修改、删除、查询等操作。数据库的分为关系型数据库和非关系型数据库。
关系型数据库是指采用了关系模型来组织数据的数据库,简单来说就是二维表格模型,好比Excel文件中的表格,强调使用表格的方式存储数据。关系型数据库核心元素:数据行、数据列、数据表、数据库(数据表的集合),非关系型数据库强调Key-Value的方式存储数据。
常用关系数据库:MySQL、SQLite、Oracle等 常见非关系数据库:MongoDB、Redis
数据库特点:持久化存储、读写速度极高、保证数据的有效性
关系型数据库管理系统是为管理关系型数据库而设计的软件系统,如果要使用关系型数据库就需要安装数据库管理系统,其实就是一个应用软件。关系型数据库管理系统分为关系型数据库服务端软件和关系型数据库客户端软件。 关系型数据库服务端软件主要负责管理不同的数据库,而每个数据库里面会有一系列数据文件,数据文件是用来存储数据的,其实数据库就是一系列数据文件的集合。 关系型数据库客户端软件主要负责和关系型数据库服务端软件进行通信,向服务端传输数据或者从服务端获取数据。
RDMS与NoSQL对比
功能对比
关系型数据库(sql/RDBMS) | 非关系型数据库 | |
---|---|---|
强大的查询功能 | √ | × |
强一致性 | √ | × |
二级索引(目录) | √ | × |
灵活模式 | × | √ |
扩展性 | × | √ |
性能 | × | √ |
特点对比
关系型数据库(RDBMS)的特点:
- 二维表
- 典型产品Oracle传统企业,MySQL互联网企业
- 数据存取是通过SQL语句(Structured Query Language结构化查询语言)
- 最大特点数据安全性方面强(ACID)
非关系型数据库(NoSQL:Not only SQL)的特点:
- 不是否定关系型数据库,而是做关系型数据库的补充
数据库市场
MySQL的市场应用
- 中、大型互联网公司
- 市场空间:互联网领域第一
- 趋势明显
- 同源产品:MariaDB、PerconaDB
其他产品
- 微软:SQLserver
- 微软和sysbase合作开发的产品,后来自己开发,windows平台
- 三、四线小公司,传统行业在用
- IBM:DB2
- 市场占有量小
- 目前只有:国有银行(人行,中国银行,工商银行等)、中国移动应用
- PostgreSQL
- MongoDB
- Redis
MySQL数据库
MySQL是一个关系型数据库管理系统由瑞典 MySQL AB公司开发,属于Oracle旗下产品。MySQL是最流行的关系型数据库管理系统之一,在WEB应用方面,MySQL是最好的RDBMS (Relational Database Management System,关系数据库管理系统)应用软件之一。
数据库管理员(Database Administrator,简称DBA):是从事管理和维护数据库管理系统(DBMS)的相关工作人员的统称,属于运维工程师的一个分支,主要负责业务数据库从设计、测试到部署交付的全生命周期管理。
DBA工作内容
如下图所示,分别是开发DBA和运维DBA:
包括但不限于以下工作内容:
数据管理
对库和表的创建
基本的数据增删改查
用户管理
- root,运维用户ops,程序连接用户(只读用户,读写用户)
- 以及对于不同用户的权限管理
集群管理
数据备份、恢复
- 逻辑备份、物理备份、冷备、热备、温备、全备、增量备份、差异备份等
健康及状态监控
- 进程,端口、集群状态、主从复制 延时情况、SQL读写速率等
MySQL发展史
- 1979年,报表工具Unireg出现。
- 1985年,以瑞典David Axmark为首,成立了一家公司(AB前身),ISAM引擎出现。
- 1990年,提供SQL支持。
- 1999年-2000年,MySQL AB公司成立,并公布源码,开源化。
- 2000年4月BDB引擎出现,支持事务。
- 2008年1月16日 MySQL被Sun公司收购。
- 2009年4月20日Oracle收购Sun公司,MySQL转入Oracle门下。
MySQL的安装
安装方式
- rpm、yum安装
- 安装方便、安装速度快、无法定制
- 二进制
- 不需要安装,解压即可使用,不能定制功能
- 编译安装
- 可定制,安装慢
- 四个步骤
- 解压(tar)
- 生成(./configure) cmake
- 编译(make)
- 安装(make install)
源码安装
- MySQL版本选择:
- https://downloads.mysql.com/archives/community/
- 5.6:GA(稳定版) 6-12个月 小版本是偶数版是稳定版,奇数版本是开发版
- 5.7:选择5.17版本以上,支持MGR(MySQL自带的高可用)
- 下载源码,并且配置编译环境
wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.40.tar.gz
tar xzvf mysql-5.6.40.tar.gz
cd mysql-5.6.40
yum install -y ncurses-devel libaio-devel cmake gcc gcc-c++ glibc
- 创建mysql用户
useradd mysql -s /sbin/nologin -M
- 编译并安装
[root@localhost mysql-5.6.40]# mkdir /application
[root@localhost mysql-5.6.40]# cmake . -DCMAKE_INSTALL_PREFIX=/application/mysql-5.6.38 \
-DMYSQL_DATADIR=/application/mysql-5.6.38/data \
-DMYSQL_UNIX_ADDR=/application/mysql-5.6.38/tmp/mysql.sock \
-DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci \
-DWITH_EXTRA_CHARSETS=all \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_FEDERATED_STORAGE_ENGINE=1 \
-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
-DWITHOUT_EXAMPLE_STORAGE_ENGINE=1 \
-DWITH_ZLIB=bundled \
-DWITH_SSL=bundled \
-DENABLED_LOCAL_INFILE=1 \
-DWITH_EMBEDDED_SERVER=1 \
-DENABLE_DOWNLOADS=1 \
-DWITH_DEBUG=0
[root@localhost mysql-5.6.40]# echo $? #查看上条命令是否运行正确,0表示正确
[root@localhost mysql-5.6.40]# make
[root@localhost mysql-5.6.40]# make install
- 创建配置文件
ln -s /application/mysql-5.6.38/ /application/mysql
cd /application/mysql/support-files/
cp my-default.cnf /etc/my.cnf
cp:是否覆盖"/etc/my.cnf"? y
- 创建启动脚本
cp mysql.server /etc/init.d/mysqld
- 初始化数据库
cd /application/mysql/scripts/
yum -y install autoconf
./mysql_install_db --user=mysql --basedir=/application/mysql --datadir=/application/mysql/data/
- 启动数据库
mkdir /application/mysql/tmp
chown -R mysql.mysql /application/mysql*
/etc/init.d/mysqld start
- 配置环境变量
vim /etc/profile.d/mysql.sh
export PATH="/application/mysql/bin:$PATH"
source /etc/profile
- systemd管理mysql
vim /usr/lib/systemd/system/mysqld.service
[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=https://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
ExecStart=/application/mysql/bin/mysqld --defaults-file=/etc/my.cnf
LimitNOFILE = 5000
vim /etc/my.cnf
basedir = /application/mysql/
datadir = /application/mysql/data
systemctl daemon-reload
- 设置mysql开机启动,并且开始mysql服务
systemctl start mysqld
systemctl enable mysqld
- 设置mysql密码,并且登录测试
mysqladmin -uroot password '123456'
mysql -uroot -p123456
mysql> show databases;
mysql> \q
Bye
二进制安装
当前运维和开发最常见的做法是二进制安装mysql
- 下载二进制包并且解压
wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz
tar xzvf mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz
- 然后的步骤就和编译安装一样
mkdir /application
mv mysql-5.6.40-linux-glibc2.12-x86_64 /application/mysql-5.6.40
ln -s /application/mysql-5.6.40 /application/mysql #为了后续写的脚本,方便监控mysql
cd /application/mysql/support-files #该文件夹有mysql初始化(预设)配置文件,覆盖文件是因为注释更全。
cp my-default.cnf /etc/my.cnf
cp:是否覆盖"/etc/my.cnf"? y
cp mysql.server /etc/init.d/mysqld #mysql.server包含如何启动mysql的脚本命令,让系统知道通过该命令启动mysql时的动作,该目录存放系统中各种服务的启动/停止脚本
cd /application/mysql/scripts
useradd mysql -s /sbin/nologin -M
yum -y install autoconf #不然会报错
./mysql_install_db --user=mysql --basedir=/application/mysql --data=/application/mysql/data
vim /etc/profile.d/mysql.sh #写到环境变量的子配置文件内,未修改前只能使用/bin/mysql的命令才能启用,而不能全局使用
export PATH="/application/mysql/bin:$PATH"
source /etc/profile #否则要重启系统才生效
- 需要注意,官方编译的二进制包默认是在
/usr/local
目录下的,我们需要修改配置文件
sed -i 's#/usr/local#/application#g' /etc/init.d/mysqld /application/mysql/bin/mysqld_safe
此时不可以通过systemctl命令启动,只能通过/etc/init.d/mysql start启动(nginx也是,如果此时通过这样的命令启动Nginx,会导致systemctl start Nginx失败,因为冲突。)
- 创建systemd管理文件,并且测试是否正常使用
vim /usr/lib/systemd/system/mysqld.service
[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=https://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
ExecStart=/application/mysql/bin/mysqld --defaults-file=/etc/my.cnf #强制从my.cnf读配置,不然会从多个路径读配置
LimitNOFILE = 5000
server_id = 1 #用作主从的时候生效
vim /etc/my.cnf
basedir = /application/mysql/
datadir = /application/mysql/data
systemctl daemon-reload
systemctl start mysqld
systemctl enable mysqld
#此时ss -ntl 可以看到3306端口
mysqladmin -uroot password '123456'
mysql -uroot -p123456
客户端工具
mysql
用来连接和管理数据库
mysqladmin
- “强制回应 (Ping)”服务器
- 关闭服务器
- 创建和删除数据库
- 显示服务器和版本信息
- 显示或重置服务器状态变量
- 设置口令
- 重新刷新授权表
- 刷新日志文件和高速缓存
- 启动和停止复制
[root@localhost ~]# mysqladmin -uroot -p123456 create hellodb
[root@localhost ~]# mysqladmin -uroot -p123456 drop hellodb
[root@localhost ~]# mysqladmin -uroot -p123456 ping # 检查服务端状态的
[root@localhost ~]# mysqladmin -uroot -p123456 status # 服务器运行状态
[root@localhost ~]# mysqladmin -uroot -p123456 status # 服务器状态 --sleep 2 --count 10 每两秒钟显示⼀次。服务器实时状态⼀共显示10次
Uptime:是mysql正常运行的时间。
Threads:指开启的会话数。
Questions: 服务器启动以来客户的问题(查询)数目 (应该是只要跟mysql作交互:不管你查询表,还是查询服务器状态都问记一次)。
Slow queries:按字面意思是慢查询的意思,不知道mysql认为多久才足够算为长查询,这个先放着。
Opens: 服务器已经打开的数据库表的数量
Flush tables: 服务器已经执行的flush ...、refresh和reload命令的数量。
open tables:通过命令是用的 数据库的表的数量,以服务器启动开始。
Queries per second avg:select语句平均查询时间
[root@localhost ~]# mysqladmin -uroot -p123456 extended-status 显示状态变量
[root@localhost ~]# mysqladmin -uroot -p123456 variables 显示服务器变量
[root@localhost ~]# mysqladmin -uroot -p123456 flush-privileges 数据库重读授权表,等同于reload
[root@localhost ~]# mysqladmin -uroot -p123456 flush-tables 关闭所有已经打开的表
[root@localhost ~]# mysqladmin -uroot -p123456 flush-threds 重置线程池缓存
[root@localhost ~]# mysqladmin -uroot -p123456 flush-status 重置⼤多数服务器状态变量
[root@localhost ~]# mysqladmin -uroot -p123456 flush-logs ⽇志滚动。主要实现⼆进制和中继⽇志滚动
[root@localhost ~]# mysqladmin -uroot -p123456 flush-hosts 清楚主机内部信息
[root@localhost ~]# mysqladmin -uroot -p123456 kill 杀死线程
[root@localhost ~]# mysqladmin -uroot -p123456 refresh 相当于同时执⾏flush-hosts flush-logs
[root@localhost ~]# mysqladmin -uroot -p123456 shutdown 关闭服务器进程
[root@localhost ~]# mysqladmin -uroot -p123456 version 服务器版本以及当前状态信息
[root@localhost ~]# mysqladmin -uroot -p123456 start-slave 启动复制,启动从服务器复制线程
[root@localhost ~]# mysqladmin -uroot -p123456 stop-slave 关闭复制线程
mysqldump
- 备份数据库和表的内容
mysqldump -uroot -p --all-databases > /backup/mysqldump/all.db
# 备份所有数据库
mysqldump -uroot -p test > /backup/mysqldump/test.db
# 备份指定数据库
mysqldump -uroot -p mysql db event > /backup/mysqldump/2table.db
# 备份指定数据库指定表(多个表以空格间隔)
mysqldump -uroot -p test --ignore-table=test.t1 --ignore-table=test.t2 > /backup/mysqldump/test2.db
# 备份指定数据库排除某些表
- 还原的方法
mysqladmin -uroot -p create db_name
mysql -uroot -p db_name < /backup/mysqldump/db_name.db
# 注:在导入备份数据库前,db_name如果没有,是需要创建的; 而且与db_name.db中数据库名是一样的才可以导入。
mysql > use db_name
mysql > source /backup/mysqldump/db_name.db
# source也可以还原数据库
图形化工具Navicate
在windows上使用navicate工具的时候,需要先登录到MySQL中,授权来自windows的IP访问:
[root@localhost ~]# grant all privileges on *.* to root@"192.168.88.%" identified by "123";
然后打开navicate,输入数据库的连接信息,连接到数据库中
MySQL架构
客户端与服务器模型
mysql是一个典型的C/S服务结构
- mysql自带的客户端程序(/application/mysql/bin)
- mysql
- mysqladmin
- mysqldump
mysqld一个二进制程序,后台的守护进程
- 单进程
- 多线程
应用程连接MySQL方式
TCP/IP的连接方式,远程方式,PHP应用可安装于另一台机器
测试需要服务器数据库
mysql> grant all privileges on . to root@'192.168.128.%' identified by '123456';
客户端数据库
yum -y install mariadb #安装客户端
mysql -uroot -p123456 -h192.168.128.X -P3306
socket,默认连接方式,本地连接,快速无需建立TCP连接,通过/application/mysql/tmp下的mysql.sock文件
mysql -uroot -p123456 -h127.0.0.1 #TCP连接方式
[root@localhost ~]# mysql -uroot -p123456 -h127.0.0.1
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 4
Server version: 5.6.40 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> status
--------------
mysql Ver 14.14 Distrib 5.6.40, for linux-glibc2.12 (x86_64) using EditLine wrapper
Connection id: 4
Current database:
Current user: root@localhost
SSL: Not in use
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 5.6.40 MySQL Community Server (GPL)
Protocol version: 10
Connection: 127.0.0.1 via TCP/IP
Server characterset: latin1
Db characterset: latin1
Client characterset: utf8
Conn. characterset: utf8
TCP port: 3306
Uptime: 1 hour 55 min 9 sec
Threads: 2 Questions: 18 Slow queries: 0 Opens: 67 Flush tables: 1 Open tables: 60 Queries per second avg: 0.002
* 套接字连接方式
* `mysql -uroot -p123456 -S /application/mysql/tmp/mysql.sock`
MySQL服务器构成
mysqld服务结构
- 实例=mysqld后台守护进程+Master Thread +干活的Thread+预分配的内存
- 公司=老板+经理+员工+办公室
- 连接层
- 验证用户的合法性(ip,端口,用户名)
- 提供两种连接方式(socket,TCP/IP)
- 验证操作权限
- 提供一个与SQL层交互的专用线程
- SQL层
- 接受连接层传来的SQL语句
- 检查语法
- 检查语义(DDL,DML,DQL,DCL)
- 解析器,解析SQL语句,生成多种执行计划
- 优化器,根据多种执行计划,选择最优方式
- 执行器,执行优化器传来的最优方式SQL
- 提供与存储引擎交互的线程
- 接收返回数据,优化成表的形式返回SQL
- 将数据存入缓存
- 记录日志,binlog
- 存储引擎
- 接收上层的执行结构
- 取出磁盘文件和相应数据
- 返回给SQL层,结构化之后生成表格,由专用线程返回给客户端
mysql逻辑结构
MySQL的逻辑对象:做为管理人员或者开发人员操作的对象
- 库
- 表:元数据+真实数据行
- 元数据:列+其它属性(行数+占用空间大小+权限)
- 列:列名字+数据类型+其他约束(非空、唯一、主键、非负数、自增长、默认值)
最直观的数据:二维表,必须用库来存放
mysql逻辑结构与Linux系统对比
use test;
create table db01(id int);
MySQL | Linux |
---|---|
库 | 目录 |
show databases; | ls -l / |
use mysql | cd /mysql |
表 | 文件 |
show tables; | ls |
二维表=元数据+真实数据行 | 文件=文件名+文件属性 |
mysql的物理结构
- MySQL的最底层的物理结构是数据文件,也就是说,存储引擎层,打交道的文件,是数据文件。
- 存储引擎分为很多种类
- 不同存储引擎的区别:存储方式、安全性、性能
myisam:
- mysql自带的表部分就是使用的myisam
mysql> show create table mysql.user\G;
...
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges'
innodb:
- 自己创建一个表,在编译的时候已经默认指定使用innodb
段、区、页(块)
- 段:理论上一个表就是一个段,由多个区构成,(分区表是一个分区一个段)
- 区:物理内存管理的基本单位,操作系统将物理内存划分为多个区块
- 页:页是虚拟内容管理的基本单位,与
区
构成映射关系
举例:假设有一个简单的 C 程序,它包含以下几个部分:
- 程序代码
- 全局变量
- 局部变量
- 栈
在内存中,这个程序会被划分成几个段:
- 代码段: 存放程序的机器指令
- 数据段: 存放全局变量
- BSS段: 存放未初始化的全局变量
- 栈段: 存放函数的局部变量和调用信息
这些段是由编译器或链接器根据程序的结构划分的逻辑内存单元。
接下来,操作系统会将这些段映射到物理内存的区域上。操作系统会把物理内存划分成很多个相同大小(通常为 4KB)的区块,称为区。操作系统会根据程序的内存需求,将程序的段映射到这些区块上。
当程序执行时,它会访问虚拟内存地址。操作系统会将虚拟内存地址转换为物理内存地址,并将相应的页(通常也是 4KB)加载到物理内存中。这就是页的概念。
例如,当程序访问一个全局变量时,操作系统会先将包含该变量的页加载到物理内存的某个区中,然后程序就可以访问这个变量了。
总结一下:
- 段: 程序的逻辑内存单元,由编译器或链接器划分
- 区: 物理内存的基本单位,由操作系统管理
- 页: 虚拟内存的基本单位,由操作系统管理并映射到物理内存的区上
Mysql用户权限管理
Mysql用户基础操作
Linux用户的作用
- 登录系统
- 管理系统文件
Linux用户管理
- 创建用户:useradd adduser
- 删除用户:userdel
- 修改用户:usermod
Mysql用户的作用
- 登录Mysql数据库
- 管理数据库对象
Mysql用户管理
- 创建用户:create user
- 删除用户:delete user drop user
- 修改用户:update
用户的定义
- username@'主机域'
- 主机域:可以理解为是Mysql登录的白名单
- 主机域格式:
- 10.1.1.12
- 10.1.0.1%
- 10.1.0.%
- 10.1.%.%
- %
- localhost
- 192.168.1.1/255.255.255.0
刚装完mysql数据库该做的事情
先truncate mysql.user;再重启mysql发现进不去了
- 设定初始密码
mysqladmin -uroot password '123456'
* 忘记root密码
/etc/init.d/mysqld stop
mysqld_safe --skip-grant-tables --skip-networking #skip-networking禁止掉3306端口,不允许网络登录
# 修改root密码
update user set password=PASSWORD('123456') where user='root' and host='localhost';
flush privileges;
用户管理
- 创建用户
mysql> create user user01@'192.168.175.%' identified by '123456';
- 查看用户
mysql> select user,host from mysql.user;
查看当前用户
mysql> select user();
查看当前数据库
mysql> select database();
删除用户
mysql> drop user user01@'192.168.175.%';
- 修改密码
mysql> set password=PASSOWRD('123456')
mysql> update user set password=PASSWORD('user01') where user='root' and host='localhost';
mysql> grant all privileges on *.* to user01@'192.168.175.%' identified by '123456';
用户权限介绍
- 权限
INSERT,SELECT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE
- INSERT: 用于向数据库表中插入新数据。
- SELECT: 用于从数据库表中查询和检索数据。
- UPDATE: 用于更新数据库表中的现有数据。
- DELETE: 用于从数据库表中删除数据。
- CREATE: 用于创建数据库对象,如表、视图、存储过程等。
- DROP: 用于删除数据库对象,如表、视图、存储过程等。
- RELOAD: 用于重新加载 MySQL 服务器的配置文件。
- SHUTDOWN: 用于关闭 MySQL 服务器。
- PROCESS: 用于查看和管理 MySQL 服务器的进程。
- FILE: 用于读写服务器上的文件。
- REFERENCES: 用于创建外键约束。
- INDEX: 用于创建和管理数据库索引。
- ALTER: 用于修改数据库对象的结构,如表、视图、存储过程等。
- SHOW DATABASES: 用于查看当前 MySQL 服务器上的所有数据库。
- SUPER: 是一种特殊的权限,允许用户执行一些高级操作,如查看和杀死其他用户的进程。
- CREATE TEMPORARY TABLES: 用于创建临时表。
- LOCK TABLES: 用于手动锁定数据库表。
- EXECUTE: 用于执行存储过程和函数。
- REPLICATION SLAVE: 用于配置数据库复制时的从库权限。
- REPLICATION CLIENT: 用于获取数据库复制状态信息。
- CREATE VIEW: 用于创建数据库视图。
- SHOW VIEW: 用于查看数据库视图的定义。
- CREATE ROUTINE: 用于创建存储过程和函数。
- ALTER ROUTINE: 用于修改存储过程和函数。
- CREATE USER: 用于创建新的数据库用户账号。
- EVENT: 用于创建和管理数据库事件。
- TRIGGER: 用于创建和管理数据库触发器。
- CREATE TABLESPACE: 用于创建数据库表空间。
- 每次设定只能有一个属主,没有属组或其他用户的概念
grant all privileges on *.* to user01@''192.168.175.%'' identified by ''123'';
权限 作用对象 归属 密码
作用对象分解
- . [当前MySQL实例中所有库下的所有表]
- wordpress.* [当前MySQL实例中wordpress库中所有表(单库级别)]
- wordpress.user [当前MySQL实例中wordpress库中的user表(单表级别)]
企业中权限的设定
- 开发人员说:给我开一个用户
- 沟通
- 你需要对哪些库、表进行操作
- 你从哪里连接过来
- 用户名有没有要求
- 密码要求
- 发邮件
- 一般给开发创建用户权限
grant select,update,delete,insert on *.* to 'user01'@'192.168.175.%' identified by '123456';
实验思考问题
#创建wordpress数据库
create database wordpress;
#使用wordpress库
use wordpress;
#创建t1、t2表
create table t1 (id int);
create table t2 (id int);
#创建blog库
create database blog;
#使用blog库
use blog;
#创建t1表
create table tb1 (id int);
授权
1、grant select on *.* to wordpress@’10.0.0.5%’ identified by ‘123’;
2、grant insert,delete,update on wordpress.* to wordpress@’10.0.0.5%’ identified by ‘123’;
3、grant all on wordpress.t1 to wordpress@’10.0.0.5%’ identified by ‘123’;
- 一个客户端程序使用wordpress用户登陆到10.0.0.51的MySQL后,
- 对t1表的管理能力?
- 对t2表的管理能力?
- 对tb1表的管理能力?
- 解
- 同时满足1,2,3,最终权限是1+2+3
- 同时满足了1和2两个授权,最终权限是1+2
- 只满足1授权,所以只能select
- 结论
- 如果在不同级别都包含某个表的管理能力时,权限是相加关系。
- 但是我们不推荐在多级别定义重复权限。
- 最常用的权限设定方式是单库级别授权,即:wordpress.*
Mysql启动关闭流程
启动
/usr/lib/systemd/system/mysqld.service告知系统从哪执行mysqld
mysld_safe是个脚本,mysqld才是binary
/etc/init.d/mysqld start ------> mysqld_safe ------> mysqld
- 关闭
/etc/init.d/mysqld stop
mysqladmin -uroot -p123456 shutdown
kill -9 pid ?
killall mysqld ?
pkill mysqld ?
pkill mysqld_safe
出现问题:
- 如果在业务繁忙的情况下,数据库不会释放pid和sock文件
- 号称可以达到和Oracle一样的安全性,但是并不能100%达到
- 在业务繁忙的情况下,丢数据(补救措施,高可用)
Mysql实例初始化配置
- 在启动一个实例的时候,必须要知道如下的问题
- 我不知道我的程序在哪?
- 我也不知道我将来启动后去哪找数据库?
- 将来我启动的时候启动信息和错误信息放在哪?
- 我启动的时候sock文件pid文件放在哪?
- 我启动,你们给了我多少内存?
- ......若干问题
- 预编译:cmake去指定,硬编译到程序当中去
- 在命令行设定启动初始化配置
--skip-grant-tables
--skip-networking
--datadir=/application/mysql/data
--basedir=/application/mysql
--defaults-file=/etc/my.cnf
--pid-file=/application/mysql/data/db01.pid
--socket=/application/mysql/data/mysql.sock
--user=mysql
--port=3306
--log-error=/application/mysql/data/db01.err
- 初始化配置文件(/etc/my.cnf)
- --defaults-file:默认配置文件
- 如果使用./bin/mysqld_safe 守护进程启动mysql数据库时,使用了 --defaults-file=<配置文件的绝对路径>参数,这时只会使用这个参数指定的配置文件。
#cmake:
socket=/application/mysql/tmp/mysql.sock
#命令行:
--socket=/tmp/mysql.sock
#配置文件:
/etc/my.cnf中[mysqld]标签下:socket=/opt/mysql.sock
#default参数:
--defaults-file=/tmp/a.txt配置文件中[mysqld]标签下:socket=/tmp/test.sock
- 优先级结论
- 命令行
- defaults-file
- 配置文件
- 预编译
- 初始化配置文件功能
- 影响实例的启动(mysqld)
- 影响到客户端
- 配置标签分类
- [client]所有客户端程序
- [server]所有服务器程序
MySQL多实例配置
多实例
- 多套后台进程+线程+内存结构
- 多个配置文件
- 多端口
- 多socket文件
- 多个日志文件
- 多个server_id
- 多套数据
实战配置
#创建数据目录
mkdir -p /data/330{7..9}
#创建配置文件
touch /data/330{7..9}/my.cnf
touch /data/330{7..9}/mysql.log
#编辑3307配置文件
vim /data/3307/my.cnf
[mysqld]
basedir=/application/mysql
datadir=/data/3307/data
socket=/data/3307/mysql.sock
log_error=/data/3307/mysql.log
log-bin=/data/3307/mysql-bin
server_id=7
port=3307
[client]
socket=/data/3307/mysql.sock
#编辑3308配置文件
vim /data/3308/my.cnf
[mysqld]
basedir=/application/mysql
datadir=/data/3308/data
socket=/data/3308/mysql.sock
log_error=/data/3308/mysql.log
log-bin=/data/3308/mysql-bin
server_id=8
port=3308
[client]
socket=/data/3308/mysql.sock
#编辑3309配置文件
vim /data/3309/my.cnf
[mysqld]
basedir=/application/mysql
datadir=/data/3309/data
socket=/data/3309/mysql.sock
log_error=/data/3309/mysql.log
log-bin=/data/3309/mysql-bin
server_id=9
port=3309
[client]
socket=/data/3309/mysql.sock
#初始化3307数据,每条命令需要间隔若干秒,否则失败
/application/mysql/scripts/mysql_install_db \
--user=mysql \
--defaults-file=/data/3307/my.cnf \
--basedir=/application/mysql --datadir=/data/3307/data
#初始化3308数据,每条命令需要间隔若干秒,否则失败
/application/mysql/scripts/mysql_install_db \
--user=mysql \
--defaults-file=/data/3308/my.cnf \
--basedir=/application/mysql --datadir=/data/3308/data
#初始化3309数据,每条命令需要间隔若干秒,否则失败
/application/mysql/scripts/mysql_install_db \
--user=mysql \
--defaults-file=/data/3309/my.cnf \
--basedir=/application/mysql --datadir=/data/3309/data
#修改目录权限
chown -R mysql.mysql /data/330*
#启动多实例
mysqld_safe --defaults-file=/data/3307/my.cnf &
mysqld_safe --defaults-file=/data/3308/my.cnf &
mysqld_safe --defaults-file=/data/3309/my.cnf &
#设置每个实例的密码
#mysqladmin -S /data/3307/mysql.sock -uroot password '123456'
#查看server_id
mysql -S /data/3307/mysql.sock -e "show variables like 'server_id'"
mysql -S /data/3308/mysql.sock -e "show variables like 'server_id'"
mysql -S /data/3309/mysql.sock -e "show variables like 'server_id'"
# 进入单独的mysql实例
mysql -S /data/3307/mysql.sock -uroot
# 关闭实例,如果设置了密码登录就需要密码了 用参数-p
mysqladmin -S /data/3307/mysql.sock -uroot shutdown
mysqladmin -S /data/3308/mysql.sock -uroot shutdown
mysqladmin -S /data/3309/mysql.sock -uroot shutdown
SQL语句
- SQL是结构化的查询语句
- SQL的种类
- DDL:数据定义语句
- DCL:数据控制语言
- DML:数据操作语言
- DQL:数据查询语言
DDL数据定义语句
- 对库或者表进行操作的语句
- 创建数据库
create database db01;
# 创建数据库
create database DB01;
# 数据库名区分大小写(注意windows里面不区分)
show variables like 'lower_case_table_names';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| lower_case_table_names | 0 |
+------------------------+-------+
具体lower_case_table_names 取值详解如下:
0:区分大小写
1:不区分大小写,存储的时候,将表名都转换成小写
2:不区分大小写,存储的时候,表名跟建表时大小写保持一致
show databases;
# 查看数据库(DQL)
show create database db01;
# 查看创建数据库语句
help create database;
# 查看创建数据库语句帮助
create database db02 charset utf8;
# 创建数据库的时候添加属性
- 删除数据库
drop database db02;
# 删除数据库db02
- 修改定义库
alter database db01 charset utf8;
show create database db01;
- 创建表
help create table;
# 查看创表语句的帮助
use db01;
# 进入库中
create table student(
sid int,
sname varchar(20),
sage tinyint,
sgender enum('m','f'),
comtime datetime
);
# 创建表,并且定义每一列
数据类型(下面有完整的)
| int | 整数 -231~230 | | :------- | :-------------------- | | tinyint | 整数 -128~127 | | varchar | 字符类型(可变长) | | char | 字符类型(定长) | | enum | 枚举类型 | | datetime | 时间类型 年月日时分秒 |
# 删除表
drop table student;
# 带数据属性创建学生表
create table student(
sid int not null primary key auto_increment comment '学号',
sname varchar(20) not null comment '学生姓名',
sgender enum('m','f') not null default 'm' comment '学生性别',
cometime datetime not null comment '入学时间'
)charset utf8 engine innodb;
# 查看建表语句
show create table student;
# 查看表
show tables;
# 查看表中列的定义信息
desc student;
数据属性
| not null | 不允许是空 | | :------------- | :---------------------------------------- | | primary key | 主键(唯一且非空) | | auto_increment | 自增,此列必须是primary key或者unique key | | unique key | 单独的唯一的 | | default | 默认值 | | unsigned | 非负数 | | comment | 注释 |
删除表
drop table student;
- 修改表的定义
alter table student rename stu;
# 修改表名
alter table stu add age int;
# 添加列和列数据类型的定义
alter table stu add test varchar(20),add qq int;
# 添加多个列
alter table stu add classid varchar(20) first;
# 指定位置进行添加列(表首)
alter table stu add phone int after age;
# 指定位置进行添加列(指定列)
alter table stu drop qq;
# 删除指定的列及定义
alter table stu modify sid varchar(20);
# 修改列及定义(列属性)
alter table stu change phone telphone char(20);
# 修改列及定义(列名及属性)
DCL数据控制语言
- DCL是针对权限进行控制
- 授权
grant all on *.* to root@'192.168.175.%' identified by '123456'
# 授予root@'192.168.175.%'用户所有权限(非超级管理员)
grant all on *.* to root@'192.168.175.%' identified by '123456' with grant option;
# 授权一个超级管路员
max_queries_per_hour:一个用户每小时可发出的查询数量
max_updates_per_hour:一个用户每小时可发出的更新数量
max_connections_per_hour:一个用户每小时可连接到服务器的次数
max_user_connections:允许同时连接数量
- 收回权限
revoke select on *.* from root@'192.168.175.%';
# 收回select权限
show grants for root@'192.168.175.%';
# 查看权限
DML数据操作语言
- 操作表中的数据
- 插入数据
insert into stu valus('linux01',1,NOW(),'zhangsan',20,'m',NOW(),110,123456);
# 基础用法,插入数据
insert into stu(classid,birth.sname,sage,sgender,comtime,telnum,qq) values('linux01',1,NOW(),'zhangsan',20,'m',NOW(),110,123456);
# 规范用法,插入数据
insert into stu(classid,birth.sname,sage,sgender,comtime,telnum,qq) values('linux01',1,NOW(),'zhangsan',20,'m',NOW(),110,123456),
('linux02',2,NOW(),'zhangsi',21,'f',NOW(),111,1234567);
# 插入多条数据
insert into stu(classid,birth.sname,sage,sgender,comtime,qq) values('linux01',1,NOW(),'zhangsan',20,'m',NOW(),123456);
#少了电话
- 更新数据
update student set sgender='f';
# 不规范
update student set sgender='f' where sid=1;
# 规范update修改
update student set sgender='f' where 1=1;
# 如果非要全表修改
update mysql.user set password=PASSWORD('123456') where user='root' and host='localhost';
# 修改密码,需要刷新权限flush privileges
- 删除数据`
delete from student;
# 不规范
delete from student where sid=3;
# 规范删除(危险)
truncate table student;
# DDL清空表中的内容,恢复表原来的状态,自增重新从1开始,否则delete依旧正常增长
- 使用伪删除
- 有时候一些重要数据不能直接删除,只能伪删除,因为以后还得使用呢
- 使用update代替delete,将状态改成删除状态,在查询的时候就可以不显示被标记删除的数据
alter table student add status enum(1,0) default 1;
# 额外添加一个状态列
update student set status='0' where sid=1;
# 使用update
select * from student where status=1;
# 应用查询存在的数据
DQL数据查询语言
- select:基础用法
- 演示用的SQL文件下载:https://download.s21i.faiusr.com/23126342/0/0/ABUIABAAGAAgzcXwhQYozuPv2AE?f=world.sql&v=1622942413
mysql -uroot -p123 < world.sql
或者mysql> \. /root/world.sql
# 常用用法
select countrycode,district from city;
# 常用用法
select countrycode from city;
# 查询单列
select countrycode,district from city limit 2;
select id,countrycode,district from city limit 2,2; #从第二个开始继续显示2个
# 行级查询
select name,population from city where countrycode='CHN';
# 条件查询
select name,population from city where countrycode='CHN' and district='heilongjiang';
# 多条件查询
select name,population,countrycode from city where countrycode like '%H%' limit 10;
# 模糊查询
select id,name,population,countrycode from city order by countrycode limit 10;
# 排序查询(顺序)
select id,name,population,countrycode from city order by countrycode desc limit 10;
# 排序查询(倒序)
select * from city where population>=1410000;
# 范围查询(>,<,>=,<=,<>)
select * from city where countrycode='CHN' or countrycode='USA';
# 范围查询OR语句
select * from city where countrycode in ('CHN','USA');
# 范围查询IN语句
select country.name,city.name,city.population,country.code from city,country where city.countrycode=country.code and city.population < 100;
# 多表查询
字符集定义
- 什么是字符集(Charset)
- 字符集:是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。
- MySQL数据库的字符集
- 字符集(CHARACTER)
- 校对规则(COLLATION)
- MySQL中常见的字符集
- UTF8
- LATIN1
- GBK
- 常见校对规则
- ci:大小写不敏感
- cs或bin:大小写敏感
- 我们可以使用以下命令查看
show charset;
show collation;
字符集设置
- 操作系统级别
source /etc/sysconfig/i18n
echo $LANG
- Mysql实例级别
cmake .
-DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci \
-DWITH_EXTRA_CHARSETS=all \
# 在编译的时候指定
[mysqld]
character-set-server=utf8
# 在配置文件中指定
mysql> create database db01 charset utf8 default collate = utf8_general_ci;
# 建库的时候
mysql> CREATE TABLE `test` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`name` char(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
# 建表的时候
mysql> alter database db01 CHARACTER SET utf8 collate utf8_general_ci;
mysql> alter table t1 CHARACTER SET utf8;
# 修改字符集
MySQL数据类型
数据类型介绍
四种主要类别
- 数值
- 字符
- 二进制
- 时间
数据类型的ABC要素
- Appropriate(适当)
- Brief(简洁)
- Complete(完整)
数值数据类型
使用数值数据类型时的注意事项
- 数据类型所标识的值的范围
- 列值所需的空间量
- 列精度和范围(浮点数和定点数)
数值数据类型的类
整数:整数
浮点数:小数
定点数:精确值数值
BIT:位字段值
| 类 | 类型 | 说明 | | :----- | :-------- | :----------------------------------------------- | | 整数 | tinyint | 极小整数数据类型(0-255) | | 整数 | smallint | 较小整数数据类型(-2^15到2^15-1) | | 整数 | mediumint | 中型整数数据类型 | | 整数 | int | 常规(平均)大小的整数数据类型(-2^31到2^31-1) | | 整数 | bigint | 较大整数数据类型 | | 浮点数 | float | 小型单精度(四个字节)浮点数 | | 浮点数 | double | 常规双精度(八个字节)浮点数 | | 定点数 | decimal | 包含整数部分、小数部分或同时包括二者的精确值数值 | | BIT | BIT | 位字段值 |
字符串数据类型
表示给定字符集中的一个字母数字字符序列
用于存储文本或二进制数据
几乎在每种编程语言中都有实现
支持字符集和整理
属于以下其中一类
| 类 | 类型 | 说明 | | :--- | :--------- | :----------------------------------- | | 文本 | char | 固定长度字符串,最多为255个字符 | | 文本 | varchar | 可变长度字符串,最多为65535个字符 | | 文本 | tinytext | 可变长字符串,最多为255个字符 | | 文本 | text | 可变长字符串,最多为65535个字符 | | 文本 | mediumtext | 可变长字符串,最多为16777215个字符 | | 文本 | longtext | 可变长字符串,最多为4294967295个字符 | | 整数 | enum | 由一组固定的合法值组成的枚举 | | 整数 | set | 由一组固定的合法值组成的集 |
文本:真实的非结构化字符串数据类型
整数:结构化字符串类型
二进制字符串数据类型
字节序列
- 二进制位按八位分组
- 存储二进制值
- 编译的计算机程序和应用程序
- 图像和声音文件
字符二进制数据类型的类
二进制:固定长度和可变长度的二进制字符串
BLOB:二进制数据的可变长度非结构化集合
| 类 | 类型 | 说明 | | :----- | :---------- | :----------------------------------------------------------- | | 二进制 | binary | 类似于char(固定长度)类型,但存储的是二进制字节字符串,而不是非二进制字符串 | | 二进制 | varbinary | 类似于varchar(可变长度)类型, | | BLOB | tinyblob | 最大长度为255个字节的BLOB列 | | BLOB | blob | 最大长度为65535个字节的BLOB列 | | BLOB | MEDIUDMBLOB | 最大长度为16777215个字节的BLOB列 | | BLOB | longblob | 最大长度为4294967295个自己的blob列 |
时间数据类型
列属性介绍
列属性的类别
数值:适用于数值数据类型(BIT 除外)
字符串:适用于非二进制字符串数据类型
常规:适用于所有数据类型
| 数据类型 | 属性 | 说明 | | :------- | :------------- | :------------------------------------- | | 数值 | unsigned | 禁止使用负值 | | 仅整数 | auto_increment | 生成包含连续唯一整数值的序列 | | 字符串 | character set | 指定要使用的字符集 | | 字符串 | collate | 指定字符集整理 | | 字符串 | binary | 指定二进制整理 | | 全部* | Null或not Null | 指定列是否可以包含NULL值 | | 全部 | Default | 如果未为新记录指定值,则为其提供默认值 |
索引介绍
- 索引就好比一本书的目录,它能让你更快的找到自己想要的内容。
- 让获取的数据更有目的性,从而提高数据库检索数据的性能。
索引类型介绍
- BTREE:B+树索引
- HASH:HASH索引
- FULLTEXT:全文索引
- RTREE:R树索引
- B+树
- B*树
索引管理
- 索引建立在表的列上(字段)的。
- 在where后面的列建立索引才会加快查询速度。
- pages<---索引(属性)<----查数据。
- 索引分类:
- 主键索引:
- 就像是一本书的目录页,可以快速定位到某一页的内容。
- 每一行数据都有一个独一无二的标识(主键),建立主键索引可以迅速找到对应的行。
- 主键索引是最重要的索引类型,能大大提高查询效率。
- 普通索引:
- 就像是在一本书的页边做了一些标记,方便以后快速找到感兴趣的内容。
- 普通索引不要求值必须唯一,可以建立在单个列或多个列上。
- 普通索引可以提高查询速度,但更新和插入数据时会稍微损耗一些性能。
- 唯一索引:
- 可以认为是一种特殊的主键,它要求索引列的值必须是唯一的。
- 唯一索引既可以提高查询效率,又能确保数据的完整性,防止出现重复数据。
- 不过,与主键不同的是,唯一索引允许出现空值。
- 主键索引:
- 添加索引:
alter table test add index index_name(name);
#创建索引
create index index_name on test(name);
#创建索引
desc table;
#查看索引
show index from table;
#查看索引
alter table test drop key index_name;
#删除索引
alter table student add unique key uni_xxx(xxx);
#添加主键索引(略)
#添加唯一性索引
select count(*) from city;
#查看表中数据行数
select count(distinct(name)) from city;
#查看去重数据行数
前缀索引和联合索引
前缀索引
- 根据字段的前N个字符建立索引
alter table test add index idx_name(name(10));
#比如表里很多城市以D开头,Dalas Des'Moine Denver Detroit,以字母D建立的索引会加快检索速度
- 避免对大列建索引
- 如果有,就使用前缀索引
联合索引
- 多个字段建立一个索引
- 原则:把最常用来做为条件查询的列放在最前面
create table people (id int,name varchar(20),age tinyint,money int ,gender enum('m','f'));
#创建people表
alter table people add index idx_gam(gender,age,money);
#创建联合索引
explain详解
EXPLAIN
是 SQL 中一个非常有用的关键字,它可以帮助我们分析和优化 SQL 查询的执行计划。使用 EXPLAIN
语句可以获取 SQL 查询的执行计划,了解查询是如何执行的,从而帮助我们识别并优化查询性能瓶颈。
下面我们来详细讲解 EXPLAIN
语句的用法和输出含义:
使用方法:
- 在 SQL 语句前加上
EXPLAIN
关键字即可,例如:EXPLAIN SELECT * FROM users WHERE id = 1;
mysql> explain select name,countrycode from city where id=1;
- 在 SQL 语句前加上
输出信息:
id
: 查询编号,表示查询执行的顺序。如果有子查询,每个子查询都会分配一个单独的 id。select_type
: 查询类型,包括 SIMPLE、PRIMARY、SUBQUERY 等。table
: 正在访问的数据表名称。partitions
: 匹配的分区情况。type
: 访问类型,包括 system、const、eq_ref、ref、range、index、ALL 等,访问类型由快到慢依次排列。possible_keys
: 可能使用的索引。key
: 实际使用的索引名称。key_len
: 使用索引的长度。ref
: 列上的比较操作。rows
: 估计要读取的行数。filtered
: 条件过滤后剩余的行占原始行的比例。Extra
: 一些额外的信息,如 Using index、Using where 等。
应用场景:
- 查看查询计划,了解 MySQL 是如何执行 SQL 查询的。
- 分析查询瓶颈,如果
type
是ALL
表示全表扫描,可能需要优化索引。 - 检查是否使用了正确的索引,
possible_keys
和key
列可以告诉你 MySQL 选择了哪个索引。 - 估算查询的行数,
rows
列给出了 MySQL 估计的行数,可以帮助判断查询的效率。
MySQL查询数据的方式
- 全表扫描(在explain语句结果中type为ALL,例如select * from XXX)
- 业务确实要获取所有数据
- 不走索引导致的全表扫描
- 没索引
- 索引创建有问题
- 语句有问题
- 生产中,mysql在使用全表扫描时的性能是极其差的,所以MySQL尽量避免出现全表扫描
- 索引扫描
- 常见的索引扫描类型
- index
- range
- ref
- eq_ref
- const
- system
- null
- 从上到下,性能从最差到最好,我们认为至少要达到range级别
- 常见的索引扫描类型
index
- Full Index Scan,index与ALL区别为index类型只遍历索引树。
range
- 索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行。显而易见的索引范围扫描是带有between或者where子句里带有<,>查询。
mysql> alter table city add index idx_city(population);
mysql> explain select * from city where population>30000000;
ref
- 用非唯一索引扫描或者唯一索引的前缀扫描,返回匹配某个单独值的记录行。
mysql> alter table city drop key idx_code;
mysql> explain select * from city where countrycode='chn';
mysql> explain select * from city where countrycode in ('CHN','USA');
mysql> explain select * from city where countrycode='CHN' union all select * from city where countrycode='USA';
eq_ref
- 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件A
join B
on A.sid=B.sid
const、system
- 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。
- 如将主键置于where列表中,MySQL就能将该查询转换为一个常量
mysql> explain select * from city where id=1000;
NULL
- MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
mysql> explain select * from city where id=1000000000000000000000000000;
Extra(扩展)
在 MySQL 中,当你执行一条 SQL 语句时,结果集通常包含一个名为 "Extra" 的列,它提供了一些额外的信息,可以帮助我们更好地理解 SQL 语句的执行过程。下面是一些常见的 "Extra" 信息及其含义:
- No index used
- 表示在执行 SQL 语句时,数据库没有使用任何索引。这可能会降低查询性能。
- Using index
- 表示在执行 SQL 语句时,数据库使用了索引来优化查询。这通常意味着查询性能较好。
- Using index condition
- 表示在执行 SQL 语句时,数据库使用了索引条件下推(Index Condition Pushdown)技术来优化查询。这可以减少不必要的行访问。
- Using index for group-by
- 表示在执行 GROUP BY 语句时,数据库使用了索引来优化分组操作。
- Using temporary
- 表示在执行 SQL 语句时,数据库创建了一个临时表来存储中间结果。这可能会降低查询性能。
- Using filesort
- 表示在执行 SQL 语句时,数据库需要对结果集进行额外的排序操作。这可能会降低查询性能。
- Using join buffer (Block Nested Loop)
- 表示在执行 JOIN 查询时,数据库使用了连接缓冲区来优化连接操作。
- Impossible WHERE noticed after reading const tables
- 表示在执行 SQL 语句时,数据库发现 WHERE 条件永远为 false,因此直接返回空结果集。
- No tables used
- 表示在执行 SQL 语句时,数据库没有访问任何表,例如执行
SELECT 1;
。
- 表示在执行 SQL 语句时,数据库没有访问任何表,例如执行
- Select tables optimized away
- 表示在执行 SQL 语句时,数据库优化掉了部分表的访问,例如在某些情况下,数据库可以直接从索引中获取所需的数据,而无需访问表本身。
这些 "Extra" 信息可以帮助我们了解 SQL 语句的执行过程,识别潜在的性能问题,并针对性地优化查询。在优化 SQL 语句时,我们应该关注那些可能降低性能的信息,例如 "Using temporary"、"Using filesort" 等
- 如果出现Using filesort请检查order by ,group by ,distinct,join 条件列上没有索引
mysql> explain select * from city where countrycode='CHN' order by population;
- 当order by语句中出现Using filesort,那就尽量让排序值在where条件中出现
mysql> explain select * from city where population>30000000 order by population;
mysql> select * from city where population=2870300 order by population;
* key_len: 越小越好
* 前缀索引去控制,rows: 越小越好
建立索引的原则(规范)
- 为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引。
- 选择唯一性索引
- 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
例如: 学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。 如果使用姓名的话,可能存在同名现象,从而降低查询速度。 主键索引和唯一键索引,在查询中使用是效率最高的。
select count(*) from world.city;
select count(distinct countrycode) from world.city;
select count(distinct countrycode,population ) from world.city;
* 注意:如果重复值较多,可以考虑采用联合索引
- 为经常需要排序、分组和联合操作的字段建立索引
- 经常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作会浪费很多时间。
- 如果为其建立索引,可以有效地避免排序操作
- 为常作为查询条件的字段建立索引
- 如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。
- 因此,为这样的字段建立索引,可以提高整个表的查询速度。
- 如果经常作为条件的列,重复值特别多,可以建立联合索引
- 尽量使用前缀来索引
- 如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。
- 限制索引的数目
- 索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。
- 修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。
- 删除不再使用或者很少使用的索引
- 表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。
重点关注:
- 没有查询条件,或者查询条件没有建立索引
select * from table;
select * from tab where 1=1;
# 全表扫描
- 在业务数据库中,特别是数据量比较大的表,是没有全表扫描这种需求。
- 对用户查看是非常痛苦的。
- 对服务器来讲毁灭性的。
- SQL改写成以下语句
# 情况1
select * from table;
#全表扫描
selec * from tab order by price limit 10;
#需要在price列上建立索引
# 情况2
select * from table where name='zhangsan';
#name列没有索引
1、换成有索引的列作为查询条件
2、将name列建立索引
- 查询结果集是原表中的大部分数据,应该是25%以上
mysql> explain select * from city where population>3000 order by population;
* 如果业务允许,可以使用limit控制
* 结合业务判断,有没有更好的方式。如果没有更好的改写方案就尽量不要在mysql存放这个数据了,放到redis里面。
- 索引本身失效,统计数据不真实
- 索引有自我维护的能力。
- 对于表内容变化比较频繁的情况下,有可能会出现索引失效。
- 重建索引就可以解决
- 查询条件使用函数在索引列上或者对索引列进行运算,运算包括(+,-,*等)
错误的例子:select * from test where id-1=9;
正确的例子:select * from test where id=10;
- 隐式转换导致索引失效.这一点应当引起重视,也是开发中经常会犯的错误
mysql> create table test (id int ,name varchar(20),telnum varchar(10));
# 例如列类型为整形,非要在检索条件中用字符串
mysql> insert into test values(1,'zs','110'),(2,'l4',120),(3,'w5',119),(4,'z4',112);
mysql> explain select * from test where telnum=120;
mysql> alter table test add index idx_tel(telnum);
mysql> explain select * from test where telnum=120;
mysql> explain select * from test where telnum=120;
mysql> explain select * from test where telnum='120';
- <> ,not in 不走索引
mysql> select * from tab where telnum <> '1555555';
mysql> explain select * from tab where telnum <> '1555555';
- 单独的>,<,in 有可能走,也有可能不走,和结果集有关,尽量结合业务添加limit
- or或in尽量改成union
EXPLAIN SELECT * FROM teltab WHERE telnum IN ('110','119');
#改写成
EXPLAIN SELECT * FROM teltab WHERE telnum='110'
UNION ALL
SELECT * FROM teltab WHERE telnum='119'
- like "%_" 百分号在最前面不走索引
#走range索引扫描
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%';
#不走索引
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110';
%linux%类的搜索需求,可以使用Elasticsearch -------> ELK
- 单独引用联合索引里非第一位置的索引列
CREATE TABLE t1 (id INT,NAME VARCHAR(20),age INT ,sex ENUM('m','f'),money INT);
ALTER TABLE t1 ADD INDEX t1_idx(money,age,sex);
DESC t1
SHOW INDEX FROM t1
#走索引的情况测试
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30 AND sex='m';
#部分走索引
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30;
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND sex='m';
#不走索引
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=20
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=30 AND sex='m';
EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE sex='m';
MySQL的存储引擎
存储引擎简介
Mysql的数据用不同的技术存储在文件(或内存)中,不同的技术拥有不同的存储机制、索引技巧、锁定水平,通过选择不同的技术获得不同的功能,从而改善应用的整体功能,而这些不同的技术和功能就是存储引擎(也称作表引擎)
Mysql支持的存储引擎:InnoDB、MyISAM、MEMORY、CSV、BLACKHOLE、FEDERATED、MRG_MYISAM、 ARCHIVE、PERFORMANCE_SCHEMA。其中NDB和InnoDB提供事务安全表,其他存储引擎都是非事务安全表
- 文件系统:
- 操作系统组织和存取数据的一种机制。
- 文件系统是一种软件。
- 文件系统类型:ext2 3 4 ,xfs 数据
- 不管使用什么文件系统,数据内容不会变化
- 不同的是,存储空间、大小、速度。
- MySQL引擎:
- 可以理解为,MySQL的“文件系统”,只不过功能更加强大。
- MySQL引擎功能:
- 除了可以提供基本的存取功能,还有更多功能事务功能、锁定、备份和恢复、优化以及特殊功能
- 总之,存储引擎的各项特性就是为了保障数据库的安全和性能设计结构。
MySQL自带的存储引擎类型
- MySQL 提供以下存储引擎:
- InnoDB
- MyISAM
- MEMORY
- ARCHIVE
- FEDERATED
- EXAMPLE
- BLACKHOLE
- MERGE
- NDBCLUSTER
- CSV
- 还可以使用第三方存储引擎:
- MySQL当中插件式的存储引擎类型
- MySQL的两个分支
- perconaDB
- mariaDB
不同存储引擎介绍
InnoDB
MySql 5.6 版本默认的存储引擎。InnoDB 是一个事务安全的存储引擎,它具备提交、回滚以及崩溃恢复的功能以 保护用户数据。InnoDB 的行级别锁定以及 Oracle 风格的一致性无锁读提升了它的多用户并发数以及性能。
InnoDB 将用户数据存储在聚集索引中以减少基于主键的普通查询所带来的 I/O 开销。为了保证数据的完整性,
InnoDB 还支持外键约束。
应用场景:并发高、数据一致、更新和删除操作较多、事务的完整提交和回滚
innodb体系结构:
innodb主要包括 内存池、后台线程以及存储文件
内存池是由多个内存块组成的,主要包括缓存磁盘数据,redolog 缓冲等
后台线程则包括了: Master Thread、IO Thread、Purge Thread等
由 InnoDB 存储引擎实现的表的存储结构文件一般包括表结构文件(.frm)、共享表空间文件(ibdata1)、独占表空间文件(ibd)以及日志文件(redo 文件等)等。
MyISAM(读、插入操作)
MyISAM既不支持事务、也不支持外键、其优势是访问速度快,但是表级别的锁定限制了它在读写负载方面的性能, 因此它经常应用于只读或者以读为主的数据场景。
应用场景:读操作和插入操作为主
Memory(内存存储数据,快速定位记录)
在内存中存储所有数据,应用于对非关键数据由快速查找的场景。Memory类型的表访问数据非常快,因为它的数据 是存放在内存中的,并且默认使用HASH索引,但是一旦服务关闭,表中的数据就会丢失
应用场景:快速定位记录
- MEMORY是MySQL中一类特殊的存储引擎。它使用存储在内存中的内容来创建表,而且数据全部放在内存中。这些特性与前面的两个很不同。
- 每个基于MEMORY存储引擎的表实际对应一个磁盘文件。该文件的文件名与表名相同,类型为frm类型。该文件中只存储表的结构。而其数据文件,都是存储在内存中,这样有利于数据的快速处理,提高整个表的效率。值得注意的是,服务器需要有足够的内存来维持MEMORY存储引擎的表的使用。如果不需要了,可以释放内存,甚至删除不需要的表。
- MEMORY默认使用哈希索引。速度比使用B型树索引快。当然如果你想用B型树索引,可以在创建索引时指定。
- 注意,MEMORY用到的很少,因为它是把数据存到内存中,如果内存出现异常就会影响数据。如果重启或者关机,所有数据都会消失。因此,基于MEMORY的表的生命周期很短,一般是一次性的。
BLACKHOLE(只接收不保存数据)
黑洞存储引擎,类似于 Unix 的 /dev/null,Archive 只接收但却并不保存数据。对这种引擎的表的查询常常返 回一个空集。这种表可以应用于 DML 语句需要发送到从服务器,但主服务器并不会保留这种数据的备份的主从配置 中。
CSV
它的表真的是以逗号分隔的文本文件。CSV 表允许你以 CSV 格式导入导出数据,以相同的读和写的格式和脚本和应用交互数据。由于 CSV 表没有索引,你最好是在普通操作中将数据放在 InnoDB 表里,只有在导入或导出阶段 使用一下 CSV 表。
NDB
(又名 NDBCLUSTER)——这种集群数据引擎尤其适合于需要最高程度的正常运行时间和可用性的应用。注意:NDB 存 储引擎在标准 MySql 5.6 版本里并不被支持。
目前能够支持 MySql 集群的版本有:基于 MySql 5.1 的 MySQL Cluster NDB 7.1;基于 MySql 5.5 的 MySQL Cluster NDB 7.2;基于 MySql 5.6 的 MySQL Cluster NDB 7.3。同样基于 MySql 5.6 的 MySQL Cluster NDB 7.4
目前正处于研发阶段。
Merge(超大规模数据场景)
允许 MySql DBA 或开发者将一系列相同的 MyISAM 表进行分组,并把它们作为一个对象进行引用。适用于超大规 模数据场景,如数据仓库。
Federated(分布式)
提供了从多个物理机上联接不同的 MySql 服务器来创建一个逻辑数据库的能力。适用于分布式或者数据市场的场景。
Example(保存例子)
这种存储引擎用以保存阐明如何开始写新的存储引擎的 MySql 源码的例子。它主要针对于有兴趣的开发人员。这 种存储引擎就是一个啥事也不做的 "存根"。你可以使用这种引擎创建表,但是你无法向其保存任何数据,也无法从 它们检索任何索引。
经典面试题:InnoDB和MyISAM区别
两种存储引擎的大致区别表现在:
- InnoDB支持事务,MyISAM不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以了。
- MyISAM适合查询以及插入为主的应用。
- InnoDB适合频繁修改以及涉及到安全性较高的应用。
- InnoDB支持外键,MyISAM不支持。
- 从MySQL5.5.5以后,InnoDB是默认引擎。
- InnoDB不支持FULLTEXT类型的索引。
- InnoDB中不保存表的行数,如select count(*) from table时,InnoDB需要扫描一遍整个表来计算有多少行,但是 MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含where条件时MyISAM也需要扫描整个表。
- 对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引。
- DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的 删除,效率非常慢。MyISAM则会重建表。
- InnoDB支持行锁(某些情况下还是锁整表,如 update table set a=1 where user like '%lee%'。
查看当前MySQL的存储引擎
mysql> show engines
#查看当前MySQL支持的存储引擎类型
mysql> select table_schema,table_name,engine from information_schema.tables where engine='innodb';
#查看innodb的表有哪些
mysql> select table_schema,table_name,engine from information_schema.tables where engine='myisam';
#查看myisam的表有哪些
- innodb和myisam的区别
#进入mysql目录
[root@localhost~l]# cd /application/mysql/data/mysql
#查看所有user的文件
[root@localhost mysql]# ll user.*
-rw-rw---- 1 mysql mysql 10684 Mar 6 2017 user.frm
-rw-rw---- 1 mysql mysql 960 Aug 14 01:15 user.MYD
-rw-rw---- 1 mysql mysql 2048 Aug 14 01:15 user.MYI
#进入word目录
[root@localhost world]# cd /application/mysql/data/world/
#查看所有city的文件
[root@localhost world]# ll city.*
-rw-rw---- 1 mysql mysql 8710 Aug 14 16:23 city.frm
-rw-rw---- 1 mysql mysql 688128 Aug 14 16:23 city.ibd
innodb存储引擎的简介
在MySQL5.5版本之后,默认的存储引擎,提供高可靠性和高性能。
优点:
事务安全(遵从 ACID)
MVCC(Multi-Versioning Concurrency Control,多版本并发控制)
InnoDB 行级别锁定,另一种DB是表级别的
Oracle 样式一致非锁定读取
表数据进行整理来优化基于主键的查询
支持外键引用完整性约束
大型数据卷上的最大性能
将对表的查询与不同存储引擎混合
出现故障后快速自动恢复
用于在内存中缓存数据和索引的缓冲区池
| 功能 | 支持 | 功能 | 支持 | | :----------- | :--- | :----------------- | :--- | | 存储限制 | 64TB | 索引高速缓存 | 是 | | MVCC | 是 | 数据高速缓存 | 是 | | B树索引 | 是 | 自适应散列索引 | 是 | | 群集索引 | 是 | 复制 | 是 | | 压缩数据 | 是 | 更新数据字典 | 是 | | 加密数据 | 是 | 地理空间数据类型 | 是 | | 查询高速缓存 | 是 | 地理空间索引 | 否 | | 事务 | 是 | 全文搜索索引 | 是 | | 锁定粒度 | 行 | 群集数据库 | 否 | | 外键 | 是 | 备份和恢复 | 是 | | 文件格式管理 | 是 | 快速索引创建 | 是 | | 多个缓冲区池 | 是 | performance_schema | 是 | | 更改缓冲 | 是 | 自动故障恢复 | 是 |
innodb核心特性
- MVCC
- 事务
- 行级锁
- 热备份
- Crash Safe Recovery(自动故障恢复)
查看存储引擎
- 使用 SELECT 确认会话存储引擎
SELECT @@default_storage_engine;
# 查询默认存储引擎
- 使用 SHOW 确认每个表的存储引擎
SHOW CREATE TABLE City\G
SHOW TABLE STATUS LIKE 'CountryLanguage'\G
# 查看表的存储引擎
- 存储引擎的设置
- 在启动配置文件中设置服务器存储引擎
[mysqld]
default-storage-engine=<Storage Engine>
# 在配置文件的[mysqld]标签下添加
- 使用 SET 命令为当前客户机会话设置
SET @@storage_engine=<Storage Engine>
# 在MySQL命令行中临时设置
- 在 CREATE TABLE 语句指定
CREATE TABLE t (i INT) ENGINE = <Storage Engine>;
# 建表的时候指定存储引擎
【实战】存储引擎切换
- 项目背景:
- 公司原有的架构:一个展示型的网站,LAMT,MySQL5.1.77版本(MYISAM),50M数据量。
- 小问题不断:
- 表级锁:对表中任意一行数据修改类操作时,整个表都会锁定,对其他行的操作都不能同时进行。
- 不支持故障自动恢复(CSR):当断电时有可能会出现数据损坏或丢失的问题。
- 解决方案:
- 提建议将现有的MYISAM引擎替换为Innodb,将版本替换为5.6.38
- 如果使用MYISAM会产生”小问题”,性能安全不能得到保证,使用innodb可以解决这个问题。
- 5.1.77版本对于innodb引擎支持不够完善,5.6.38版本对innodb支持非常完善了。
- 实施过程和注意要素
- 备份生产库数据(mysqldump)
#[root@db01 ~]# mysqldump -uroot -p123 -A --triggers -R --master-data=2 >/tmp/full.sql
#由于没有开启bin logging所以去掉 --master-data=2
[root@db01 ~]# mysqldump -uroot -p123 -A --triggers -R >/tmp/full.sql
- 准备一个5.6.38版本的新数据库
- 对备份数据进行处理(将engine字段替换)
[root@db01 ~]# sed -i 's#ENGINE=MYISAM#ENGINE=INNODB#g' /tmp/full.sql
- 将修改后的备份恢复到新库
- 应用测试环境连接新库,测试所有功能
- 停应用,将备份之后的生产库发生的新变化,补偿到新库
- 应用割接到新数据库
表空间介绍
- 5.5版本以后出现共享表空间概念
- 表空间的管理模式的出现是为了数据库的存储更容易扩展
- 5.6版本中默认的是独立表空间
- 共享表空间
[root@localhost ~]# ll /application/mysql/data/
-rw-rw----. 1 mysql mysql 12582912 6月 8 09:43 ibdata1
# 物理查看
mysql> show variables like '%path%';
innodb_data_file_path = ibdata1:12M:autoextend
- 5.6版本中默认存储
- 系统数据
- undo
- 临时表
- 5.7版本中默认会将undo和临时表独立出来,5.6版本也可以独立,只不过需要在初始化的时候进行配置
- 共享表空间扩展配置方法
#编辑配置文件
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
#注意,idata1文件已存在,可能超过50M导致mysqld重启不成功,建议重命名了再重启。
innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend
- 独立表空间
- 对于用户自主创建的表,会采用此种模式,每个表由一个独立的表空间进行管理
[root@localhost ~]# ll /application/mysql/data/world/
-rw-rw----. 1 mysql mysql 589824 6月 6 10:23 city.ibd
# 物理查看
mysql> show variables like '%per_table%';
innodb_file_per_table = ON
【实战】数据库服务损坏
在没有备份数据的情况下,突然断电导致表损坏,打不开数据库。
拷贝库目录到新库中
[root@db01 ~]# cp -r /application/mysql/data/world/ /data/3307/data/
[root@db01 ~]# chown -R mysql.mysql /application/mysql/data/world
- 启动新数据库
#pkill mysqld 先干掉mysql
[root@db01 ~]# mysqld_safe --defaults-file=/data/3307/my.cnf &
- 登陆数据库查看
mysql> show databases;
- 查询表中数据
mysql> select * from city;
ERROR 1146 (42S02): Table 'world.city' doesn't exist
#先 use world;
- 找到以前的表结构在新库中创建表,此处演示用的show命令实际需要从原来的开发文档查看
mysql> show create table world.city;
#删掉外键创建语句
CREATE TABLE `city` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` char(35) NOT NULL DEFAULT '',
`CountryCode` char(3) NOT NULL DEFAULT '',
`District` char(20) NOT NULL DEFAULT '',
`Population` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`),
KEY `CountryCode` (`CountryCode`),
KEY `idx_city` (`Population`,`CountryCode`),
CONSTRAINT `city_ibfk_1` FOREIGN KEY (`CountryCode`) REFERENCES `country` (`Code`)
) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1;
mysql> CREATE TABLE `city_new` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` char(35) NOT NULL DEFAULT '',
`CountryCode` char(3) NOT NULL DEFAULT '',
`District` char(20) NOT NULL DEFAULT '',
`Population` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`),
KEY `CountryCode` (`CountryCode`)
# KEY `idx_city` (`Population`,`CountryCode`),
#干掉外键的约束,否则失败
#CONSTRAINT `city_ibfk_1` FOREIGN KEY (`CountryCode`) REFERENCES `country` (`Code`)
) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1;
- 删除表空间文件
mysql> alter table city_new discard tablespace;
- 拷贝旧表空间文件
[root@db01 world]# cp /data/3307/data/world/city.ibd /data/3307/data/world/city_new.ibd
- 授权
[root@db01 world]# cd /data/3307/data/world
[root@db01 world]# chown -R mysql.mysql *
- 导入表空间
mysql> alter table city_new import tablespace;
mysql> alter table city_new rename city;
MySQL事物
事务
- 事务的定义
- 主要针对DML语句(update,delete,insert)一组数据操作执行步骤,这些步骤被视为一个工作单元
- 用于对多个语句进行分组
- 可以在多个客户机并发访问同一个表中的数据时使用
- 所有步骤都成功或都失败
- 如果所有步骤正常,则执行
- 如果步骤出现错误或不完整,则取消
- 主要针对DML语句(update,delete,insert)一组数据操作执行步骤,这些步骤被视为一个工作单元
- 交易的概念
- 物与物的交换(古代)
- 货币现金与实物的交换(现代1)
- 虚拟货币与实物的交换(现代2)
- 虚拟货币与虚拟实物交换(现代3)
- 事务ACID特性
- Atomic(原子性)
- 所有语句作为一个单元全部成功执行或全部取消。
- Consistent(一致性)
- 如果数据库在事务开始时处于一致状态,则在执行该事务期间将保留一致状态。
- Isolated(隔离性)
- 事务之间不相互影响。
- Durable(持久性)
- 事务成功完成后,所做的所有更改都会准确地记录在数据库中。所做的更改不会丢失。
- Atomic(原子性)
- 事务的控制语句
START TRANSACTION(或 BEGIN):显式开始一个新事务
SAVEPOINT:分配事务过程中的一个位置,以供将来引用
COMMIT:永久记录当前事务所做的更改
ROLLBACK:取消当前事务所做的更改
ROLLBACK TO SAVEPOINT:取消在 savepoint 之后执行的更改
RELEASE SAVEPOINT:删除 savepoint 标识符
SET AUTOCOMMIT:为当前连接禁用或启用默认 autocommit 模式
- 一个成功事务的生命周期
begin;
sql1
sql2
sql3
...
commit;
- 一个失败事务的生命周期
begin;
sql1
sql2
sql3
...
rollback;
- 自动提交
#默认自动提交,关闭后需要命令commit才生效,此时另一个客户端除非重新登录才能看到变化(隔离性)
#开启两个窗口,不同update,不同时间commit演示
mysql> show variables like 'autocommit';
#查看自动提交
mysql> set autocommit=0;
#临时关闭
[root@db01 world]# vim /etc/my.cnf
[mysqld]
autocommit=0
#永久关闭
事务演示
成功事务
需要打开另一个mysql终端查看,分别在执行完语句以及commit后查看
mysql> create table stu(id int,name varchar(10),sex enum('f','m'),money int);
mysql> begin;
mysql> insert into stu(id,name,sex,money) values(1,'zhang3','m',100), (2,'zhang4','m',110);
mysql> commit;
* 事务回滚
mysql> begin;
mysql> update stu set name='zhang3';
mysql> delete from stu;
mysql> rollback;
- 事务隐式提交情况
- 现在版本在开启事务时,不需要手工begin,只要你输入的是DML语句,就会自动开启事务。
- 有些情况下事务会被隐式提交
- 在事务运行期间,手工执行begin的时候会自动提交上个事务
- 在事务运行期间,加入DDL、DCL操作会自动提交上个事务
- 在事务运行期间,执行锁定语句(lock tables、unlock tables)
- load data infile
- select for update
- 在autocommit=1的时候
事务日志redo
- redo,顾名思义“重做日志”,是事务日志的一种。
- 在事务ACID过程中,实现的是“D”持久化的作用。
- 特性:WAL(Write Ahead Log)日志优先写
- REDO:记录的是,内存数据页的变化过程
- REDO工作过程
update t1 set num=2 where num=1;
- 执行步骤
- 首先将t1表中num=1的行所在数据页加载到内存中buffer page
- MySQL实例在内存中将num=1的数据页改成num=2
- num=1变成num=2的变化过程会记录到,redo内存区域,也就是redo buffer page中 commit;
- 提交事务执行步骤
- 当敲下commit命令的瞬间,MySQL会将redo buffer page写入磁盘区域redo log
- 当写入成功之后,commit返回ok
事务日志undo
- undo,顾名思义“回滚日志”,是事务日志的一种。
- 在事务ACID过程中,实现的是“A”原子性的作用。当然CI的特性也和undo有关
- redo和undo的存储位置
[root@db01 data]# ll /application/mysql/data/
-rw-rw---- 1 mysql mysql 50331648 Aug 15 06:34 ib_logfile0
-rw-rw---- 1 mysql mysql 50331648 Mar 6 2021 ib_logfile1
# redo位置
[root@db01 data]# ll /application/mysql/data/
-rw-rw---- 1 mysql mysql 79691776 Aug 15 06:34 ibdata1
-rw-rw---- 1 mysql mysql 79691776 Aug 15 06:34 ibdata2
# undo位置
- 在MySQL5.6版本中undo是在ibdata文件中,在MySQL5.7版本会独立出来。
事务中的锁
- “锁”顾名思义就是锁定的意思
- 在事务ACID特性过程中,“锁”和“隔离级别”一起来实现“I”隔离性的作用。
- 排他锁:保证在多事务操作时,数据的一致性。
- 共享锁:保证在多事务工作期间,数据查询时不会被阻塞。
- 多版本并发控制(MVCC)
- 只阻塞修改类操作,不阻塞查询类操作
- 乐观锁的机制(谁先提交谁为准)
- 锁的粒度
- MyIsam:低并发锁(表级锁)
- Innodb:高并发锁(行级锁)
- 事务的隔离级别
- 四种隔离级别
- READ UNCOMMITTED(独立提交)
- 允许事务查看其他事务所进行的未提交更改,一个用户update命令后未commit,另一个用户看到的是修改的内存里的,即使没有写入硬盘
- READ COMMITTED
- 允许事务查看其他事务所进行的已提交更改
- REPEATABLE READ**
- 确保每个事务的 SELECT 输出一致,一个用户update命令即使commit。另一个用户看到的未修改的,除非重新登录或者commit+
- InnoDB 的默认级别
- SERIALIZABLE
- 将一个事务的结果与其他事务完全隔离,一个用户update命令后未commit,另一个用户即使select都看不到。
- READ UNCOMMITTED(独立提交)
mysql> show variables like '%iso%';
#查看隔离级别
[mysqld]
transaction_isolation=read-uncommit
#修改隔离级别为RU
mysql> use test
mysql> select * from stu;
mysql> insert into stu(id,name,sex,money) values(2,'li4','f',123);
[mysqld]
transaction_isolation=read-commit
#修改隔离级别为RC
MySQL日志管理
日志简介
日志文件 | 选项 | 文件名,表名 | 程序 |
---|---|---|---|
错误 | --log-error | host_name.err | |
常规 | --general_log | host_name.log general_log | |
慢速查询 | --slow_query_log --long_query_time | host_name-slow.log | mysqldumpslow |
二进制 | --log-bin --expire-logs-days | host_name-bin.000001 | mysqlbinlog |
审计 | --audit_log --audit_log_file ... | audit.log |
错误日志
- 记录mysql数据库的一般状态信息及报错信息,是我们对于数据库常规报错处理的常用日志。
- 默认位置
- $MYSQL_HOME/data/
- 开启方式
- MySQL安装完后默认开启
[root@db01 ~]# vim /etc/my.cnf
# 编辑配置文件
[mysqld]
log_error=/application/mysql/data/$hostname.err
mysql> show variables like 'log_error';
# 查看方式
一般查询日志
- 记录mysql所有执行成功的SQL语句信息,可以做审计用,但是我们很少开启。
- 默认位置
- $MYSQL_HOME/data/
- y开启方式
- MySQL安装完之后默认不开启
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
general_log=on
general_log_file=/application/mysql/data/$hostnamel.log
# 编辑配置文件
mysql> show variables like '%gen%';
# 查看方式
二进制日志
记录已提交的DML事务语句,并拆分为多个事件(event)来进行记录
记录所有DDL、DCL等语句
总之,二进制日志会记录所有对数据库发生修改的操作
二进制日志模式
- statement:语句模式
- row:行模式,即数据行的变化过程
- mixed:以上两者的混合模式。
- 企业推荐使用row模式
二进制日志模式优缺点
statement模式
- 优点:简单明了,容易被看懂,就是sql语句,记录时不需要太多的磁盘空间
- 缺点:记录不够严谨
row模式
记录了底层操作的所有事情
- 优点:记录更加严谨
- 缺点:有可能会需要更多的磁盘空间,不太容易被读懂
binlog的作用
- 如果我拥有数据库搭建开始所有的二进制日志,那么我可以把数据恢复到任意时刻
- 数据的备份恢复
- 数据的复制
二进制日志的管理操作实战
- 开启方式
[root@db01 data]# vim /etc/my.cnf
[mysqld]
log-bin=mysql-bin
binlog_format=row
注意:在mysql5.7中开启binlog必须要加上server-id。
[root@db01 data]# vim /etc/my.cnf
[mysqld]
log-bin=mysql-bin
binlog_format=row
server_id=1
- 二进制日志的操作
[root@db01 data]# ll /application/mysql/data/
-rw-rw---- 1 mysql mysql 285 Mar 6 2021 mysql-bin.000001
#物理查看
mysql> show binary logs;
mysql> show master status;
#命令行查看
mysql> show binlog events in 'mysql-bin.000007';
#查看binlog事件
- 事件介绍
- 在binlog中最小的记录单元为event
- 一个事务会被拆分成多个事件(event)
- 事件(event)特性
- 每个event都有一个开始位置(start position)和结束位置(stop position)。
- 所谓的位置就是event对整个二进制的文件的相对位置。
- 对于一个二进制日志中,前120个position是文件格式信息预留空间。
- MySQL第一个记录的事件,都是从120开始的。
- row模式下二进制日志分析及数据恢复
mysql> show master status;
# 查看binlog信息
mysql> create database binlog;
# 创建一个binlog库
mysql> use binlog
# 使用binlog库
mysql> create table binlog_table(id int);
# 创建binglog_table表
mysql> show master status;
# 查看binlog信息
mysql> insert into binlog_table values(1);
# 插入数据1
mysql> show master status;
# 查看binlog信息
mysql> commit;
# 提交
mysql> show master status;
# 查看binlog信息
mysql> insert into binlog_table values(2);
# 插入数据2
mysql> insert into binlog_table values(3);
#插入数据3
mysql> show master status;
# 查看binlog信息
mysql> commit;
# 提交
mysql> delete from binlog_table where id=1;
# 删除数据1
mysql> show master status;
# 查看binlog信息
mysql> commit;
# 提交
mysql> update binlog_table set id=22 where id=2;
# 更改数据2为22
mysql> show master status;
# 查看binlog
mysql> commit;
# 提交
mysql> show master status;
# 查看binlog信息
mysql> select * from binlog_table;
# 查看数据
mysql> drop table binlog_table;
# 删表
mysql> drop database binlog;
# 删库
- 恢复数据之前
mysql> show binlog events in 'mysql-bin.000013';
# 查看binlog事件
# 使用mysqlbinlog来查看
[root@db01 data]# mysqlbinlog /application/mysql/data/mysql-bin.000001
[root@db01 data]# mysqlbinlog /application/mysql/data/mysql-bin.000001|grep -v SET
[root@db01 data]# mysqlbinlog --base64-output=decode-rows -vvv mysql-bin.000013
# 查看二进制日志后,发现删除开始位置是858
# binlog某些内容是以二进制放进去的,加入base64-output用于解码
[root@db01 data]# mysqlbinlog --start-position=120 --stop-position=858 /application/mysql/data/mysql-bin.000013 > /tmp/binlog.sql
mysql> set sql_log_bin=0;
#临时关闭binlog,必须关闭,否则source倒入的内容也会被记录进binlog,binlog就脏了。
mysql> source /tmp/binlog.sql
#执行sql文件
mysql> show databases;
#查看删除的库
mysql> use binlog
#进binlog库
mysql> show tables;
#查看删除的表
mysql> select * from binlog_table;
#查看表中内容
- 只查看某个数据库的binlog文件
mysql> flush logs;
#刷新一个新的binlog,原来的bin000X.log作废
mysql> create database db1;
mysql> create database db2;
#创建db1、db2两个库
mysql> use db1
#库db1操作
mysql> create table t1(id int);
#创建t1表
mysql> insert into t1 values(1),(2),(3),(4),(5);
#插入5条数据
mysql> commit;
#提交
mysql> use db2
#库db2操作
mysql> create table t2(id int);
#创建t2表
mysql> insert into t2 values(1),(2),(3);
#插入3条数据
mysql> commit;
#提交
mysql> show binlog events in 'mysql-bin.000014';
#查看binlog事件
[root@db01 data]# mysqlbinlog -d db1 --base64-output=decode-rows -vvv /application/mysql/data/mysql-bin.000014
#查看db1的操作
- 刷新binlog日志
- flush logs;
- 重启数据库时会刷新
- 二进制日志上限(max_binlog_size)
- 删除二进制日志
- 原则
- 在存储能力范围内,能多保留则多保留
- 基于上一次全备前的可以选择删除
- 删除方式
- 根据存在时间删除日志
#临时生效
SET GLOBAL expire_logs_days = 7;
#永久生效
[root@db01 data]# vim /etc/my.cnf
[mysqld]
expire_logs_days = 7
* 使用purge命令删除
PURGE BINARY LOGS BEFORE now() - INTERVAL 3 day;
* 根据文件名删除
PURGE BINARY LOGS TO 'mysql-bin.000010';
* 用reset master
mysql> reset master;
慢查询日志
- 是将mysql服务器中影响数据库性能的相关SQL语句记录到日志文件
- 通过对这些特殊的SQL语句分析,改进以达到提高数据库性能的目的
- 默认位置:
- MYSQL_HOME/data/MYSQLHOME/data/hostname-slow.log
- 开启方式(默认没有开启)
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
#指定是否开启慢查询日志
slow_query_log = 1
#指定慢日志文件存放位置(默认在data)
slow_query_log_file=/application/mysql/data/slow.log
#设定慢查询的阀值(默认10s)
long_query_time=0.05
#不使用索引的慢查询日志是否记录到索引
log_queries_not_using_indexes
#查询检查返回少于该参数指定行的SQL不被记录到慢查询日志
min_examined_row_limit=100(鸡肋)
- 模拟慢查询语句
mysql> use world
#进入world库
mysql> show tables;
#查看表
mysql> create table t1 select * from city;
#将city表中所有内容加到t1表中
mysql> desc t1;
#查看t1的表结构
mysql> insert into t1 select * from t1;
mysql> insert into t1 select * from t1;
mysql> insert into t1 select * from t1;
mysql> insert into t1 select * from t1;
#将t1表所有内容插入到t1表中(多插入几次)
mysql> commit;
#提交
mysql> delete from t1 where id>2000;
#删除t1表中id>2000的数据
[root@db01 ~]# cat /application/mysql/data/mysql-db01
#查看慢日志
- 使用mysqldumpslow命令来分析慢查询日志
$PATH/mysqldumpslow -s c -t 10 /database/mysql/slow-log
#输出记录次数最多的10条SQL语句
- 参数说明:
- -s:
- 是表示按照何种方式排序,c、t、l、r分别是按照记录次数、时间、查询时间、返回的记录数来排序,ac、at、al、ar,表示相应的倒叙;
- -t:
- 是top n的意思,即为返回前面多少条的数据;
- -g:
- 后边可以写一个正则匹配模式,大小写不敏感的;
- -s:
$PATH/mysqldumpslow -s r -t 10 /database/mysql/slow-log
#得到返回记录集最多的10个查询
$PATH/mysqldumpslow -s t -t 10 -g "left join" /database/mysql/slow-log
#得到按照时间排序的前10条里面含有左连接的查询语句
- 第三方推荐(扩展):
yum install -y percona-toolkit-3.0.11-1.el6.x86_64.rpm
* 使用percona公司提供的pt-query-digest工具分析慢查询日志
[root@mysql-db01 ~]# pt-query-digest /application/mysql/data/mysql-db01-slow.log
pt-query-digest 的分析结果:
总体统计数据:
Queries examined: 总共分析的查询语句数量
Query time: 总查询时间
Min/Max/Avg query time: 最短、最长和平均查询时间
Variance: 查询时间的方差
Aggregated profile: 按查询类型统计的信息,如 SELECT、INSERT、UPDATE 等
Top Queries:
这部分列出了执行时间最长的前 10 个查询语句。
Rank: 查询在整体中的排名
Query ID: 查询的唯一标识
Response Time: 查询的总响应时间
Calls: 查询被执行的次数
R/Call: 每次调用的平均响应时间
V/M: 查询时间的方差与平均值的比率,反映了查询时间的离散程度
Item: 查询语句的文本
查询语句的详细信息:
Query ID: 查询的唯一标识
Database: 查询所针对的数据库
Users: 执行查询的用户
Hosts: 执行查询的主机
Query: 查询语句的文本
Exec time: 查询的总执行时间
Lock time: 查询获取的锁定时间
Rows sent: 查询返回的行数
Rows examined: 查询扫描的行数
Rows affected: 查询影响的行数
Bytes sent: 查询返回的字节数
Tmp tables: 查询使用的临时表数量
Tmp disk tables: 查询使用的临时磁盘表数量
Scan/Join tables: 查询扫描的表数量
有能力的可以做成可视化界面: Anemometer基于pt-query-digest将MySQL慢查询可视化
可视化代码下载 https://github.com/box/Anemometer
备份与恢复
- 备份的原因
- 第一个是保护公司的数据.
- 第二个是让网站能7*24小时提供服务(用户体验)。
- 备份就是为了恢复。
- 尽量减少数据的丢失(公司的损失)
备份的类型
- 冷备份:
- 这些备份在用户不能访问数据时进行,因此无法读取或修改数据。这些脱机备份会阻止执行任何使用数据的活动。这些类型的备份不会干扰正常运行的系统的性能。但是,对于某些应用程序,会无法接受必须在一段较长的时间里锁定或完全阻止用户访问数据。
- 温备份:
- 这些备份在读取数据时进行,但在多数情况下,在进行备份时不能修改数据本身。这种中途备份类型的优点是不必完全锁定最终用户。但是,其不足之处在于无法在进行备份时修改数据集,这可能使这种类型的备份不适用于某些应用程序。在备份过程中无法修改数据可能产生性能问题。
- 热备份:
- 这些动态备份在读取或修改数据的过程中进行,很少中断或者不中断传输或处理数据的功能。使用热备份时,系统仍可供读取和修改数据的操作访问。
备份的方式
逻辑备份
SQL软件自带的功能
- 基于SQL语句的备份
- binlog
- into outfile
- mysqldump
- 基于SQL语句的备份
[root@localhost ~]# vim /etc/my.cnf
[mysqld]
secure_file_priv=/tmp
mysql> select * from world.city into outfile '/tmp/world_city.data';
* mysqldump
* replication
物理备份
通过二进制方式直接拖走所有数据、配置文件
- 基于数据文件的备份
- Xtrabackup(percona公司)
- 基于数据文件的备份
备份工具
- 备份策略
- 全量备份 full
- 增量备份 increamental
- 备份工具
- mysqldump(逻辑)
- mysql原生自带很好用的逻辑备份工具
- mysqlbinlog(逻辑)
- 实现binlog备份的原生态命令
- xtrabackup(物理)
- precona公司开发的性能很高的物理备份工具
- mysqldump(逻辑)
- 备份工具使用
- mysqldump
- 连接服务端参数(基本参数):-u -p -h -P -S
- -A, --all-databases:全库备份
- mysqldump
[root@db01 ~]# mysqldump -uroot -p123456 -A > /backup/full.sql
* 不加参数:单库、单表多表备份
[root@db01 ~]# mysqldump -uroot -p123456 db1 > /backup/db1.sql
[root@mysql-db01 backup]# mysqldump -uroot -p123456 world city > /backup/city.sql
* -B:指定库备份
[root@db01 ~]# mysqldump -uroot -p123 -B db1 > /backup/db1.sql
[root@db01 ~]# mysqldump -uroot -p123 -B db1 db2 > /backup/db1_db2.sql
* -F:flush logs在备份时自动刷新binlog(不怎么常用)
[root@db01 backup]# mysqldump -uroot -p123 -A -R –triggers -F > /backup/full_2.sql
* -d:仅表结构
* -t:仅数据
- 备份额外扩展项
- -R, --routines:备份存储过程和函数数据
- --triggers:备份触发器数据
[root@db01 backup]# mysqldump -uroot -p123 -A -R --triggers > /backup/full_2.sql
- mysqldump特殊参数
- -x:锁表备份(myisam温备份)
- --single-transaction:快照备份
[root@db01 backup]# mysqldump -uroot -p123 -A -R --triggers --master-data=2 –-single-transaction>/backup/full.sql
# 加了文件末尾会多一行:CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=120;
# 不加master-data就不会记录binlog的位置,导致后续不利于增量备份
- gzip:压缩备份
[root@db01 ~]# mysqldump -uroot -p123 -A -R --triggers --master-data=2 –single-transaction|gzip>/backup/full.sql.gz
[root@db01 ~]# gzip -d /backup/full.sql.gz
[root@db01 ~]# zcat /backup/full.sql.gz > linshi.sql
- 常用的热备份备份语句
mysqldump -uroot -p3307 -A -R --triggers --master-data=2 --single-transaction |gzip > /tmp/full_$(date +%F).sql.gz
- mysqldump的恢复
mysql> set sql_log_bin=0;
#先不记录二进制日志
mysql> source /backup/full.sql
#库内恢复操作
[root@db01 ~]# mysql -uroot -p123 < /backup/full.sql
#库外恢复操作
- 注意
- mysqldump在备份和恢复时都需要MySQL实例启动为前提
- 一般数据量级100G以内,大约15-30分钟可以恢复(PB、EB就需要考虑别的方式)
- mysqldump是以覆盖的形式恢复数据的
【实战】企业故障恢复
- 背景:
- 正在运行的网站系统,MySQL数据库,数据量25G,日业务增量10-15M。
- 备份策略:
- 每天23:00,计划任务调用mysqldump执行全备脚本
- 故障时间点:
- 上午10点开发人员误删除一个核心业务表,需要恢复
- 思路
- 停业务避免数据的二次伤害!
- 找一个临时的库,恢复前一天的全备
- 截取前一天23:00到第二天10点误删除之间的binlog,恢复到临时库
- 测试可用性和完整性
- 开启业务前的两种方式
- 直接使用临时库顶替原生产库,前端应用割接到新库
- 将误删除的表单独导出,然后导入到原生产环境
- 开启业务
- 故障模拟
mysql> flush logs;
#刷新binlog使内容更清晰
mysql> show master status;
#查看当前使用的binlog
mysql> create database backup;
#创建backup库
mysql> use backup
#进入backup库
mysql> create table full select * from world.city;
#创建full表
mysql> create table full_1 select * from world.city;
#创建full_1表
mysql> show tables;
#查看表
- 全备
[root@db01 ~]# mysqldump -uroot -p123456 -A -R --triggers --master-data=2 --single-transaction|gzip > /backup/full_$(date +%F).sql.gz
- 模拟数据变化
mysql> use backup
#进入backup库
mysql> create table new select * from mysql.user;
#创建new表
mysql> create table new_1 select * from world.country;
#创建new_1表
mysql> show tables;
#查看表
mysql> select * from full;
#查看full表中所有数据
mysql> update full set countrycode='CHN' where 1=1;
#把full表中所有的countrycode都改成CHN
mysql> commit;
#提交
mysql> delete from full where id>200;
#删除id大于200的数据
mysql> commit;
#提交
- 模拟故障
mysql> drop table new;
#删除new表
mysql> show tables;
#查看表
- 恢复过程
- 准备临时数据库
#开启一个新的实例
[root@db02 ~]# mysqld_safe --defaults-file=/data/3307/my.cnf &
#拷贝数据到新库上
[root@db02 ~]# scp /backup/full_2018-08-16.sql.gz root@10.0.0.52:/tmp
#解压全备数据文件,如果用同一个机器的不同实例则不需要这步
[root@db02 ~]# cd /tmp/
#进入tmp目录
[root@db02 tmp]# gzip -d full_2018-08-16.sql.gz
#解压全备数据文件
截取二进制
[root@db02 tmp]# head -50 full_2018-08-16.sql |grep -i 'change master to'
#查看全备的位置点(起始位置点),忽略大小写,假设找到起始的位置为268002
mysql> show binlog events in 'mysql-bin.000017'\G
#生产环境db01找到drop语句执行的位置点(结束位置点)
[root@db01 tmp]#mysqlbinlog -uroot -p123 --start-position=268002 --stop-position=670891 /application/mysql/data/mysql-bin.000004 > /tmp/inc.sql
#截取二进制日志,把备份点和drop直接的信息倒入到inc.sql
[root@db01 tmp]# scp /tmp/inc.sql root@10.0.0.52:/tmp
#发送增量数据到新库
#在新库内恢复数据
mysql> set sql_log_bin=0;
#不记录二进制日志
mysql> source /tmp/full_2018-08-16.sql
#恢复全备数据
mysql> use backup
#进入backup库
mysql> show tables;
# 查看表,此时没有new表
mysql> source /tmp/inc.sql
#恢复增量数据
mysql> show tables;
#查看表,此时有new表
- 将故障表导出并恢复到生产
#将现在已经恢复好的数据库backup里的new表备份到/tmp/new.sql文件中
#此时/tmp/目录下有昨天23:00前完整备份文件full_2018-08-16.sql、昨天23:00到drop的增量文件inc.sql。
#通过这两个文件恢复了数据库出事前的所有内容
[root@db02 ~]# mysqldump -uroot -p123 -S /data/3307/mysql.sock backup new > /tmp/new.sql
#导出new表
[root@db02 ~]# scp /tmp/new.sql root@10.0.0.51:/tmp/
#发送到db01的库,此时db01的生产环境backup库里没有new表(被无良开发drop掉了);可直接从new.sql倒入
mysql> use backup
#进入backup库,此时生产环境的backup
mysql> source /tmp/new.sql
#在生产库恢复数据
mysql> show tables;
#查看表
物理备份(Xtrabackup)
- Xtrabackup安装
yum -y install epel-release
#安装epel源
yum -y install perl perl-devel libaio libaio-devel perl-Time-HiRes perl-DBD-MySQL
#安装依赖
wget httpss://www.percona.com/downloads/XtraBackup/Percona-XtraBackup-2.4.4/binary/redhat/6/x86_64/percona-xtrabackup-24-2.4.4-1.el6.x86_64.rpm
#下载Xtrabackup
yum localinstall -y percona-xtrabackup-24-2.4.4-1.el6.x86_64.rpm
# 安装
- 备份方式(物理备份)
- 对于非innodb表(比如myisam)是直接锁表cp数据文件,属于一种温备。
- 对于innodb的表(支持事务),不锁表,cp数据页最终以数据文件方式保存下来,并且把redo和undo一并备走,属于热备方式。
- 备份时读取配置文件/etc/my.cnf,需要注明socket=/application/mysql/tmp/mysql.sock文件的位置
- 全量备份
[root@db01 data]# innobackupex --user=root --password=123 /backup
#全备
[root@db01 ~]# innobackupex --user=root --password=123 --no-timestamp /backup/full
#避免时间戳,自定义路径名
[root@db01 backup]# ll /backup/full
#查看备份路径中的内容
-rw-r----- 1 root root 21 Aug 16 06:23 xtrabackup_binlog_info
#记录binlog文件名和binlog的位置点
-rw-r----- 1 root root 117 Aug 16 06:23 xtrabackup_checkpoints
#备份时刻,立即将已经commit过的内存中的数据页刷新到磁盘
#备份时刻有可能会有其他数据写入,已备走的数据文件就不会再发生变化了
#在备份过程中,备份软件会一直监控着redo和undo,一旦有变化会将日志一并备走
-rw-r----- 1 root root 485 Aug 16 06:23 xtrabackup_info
#备份汇总信息
-rw-r----- 1 root root 2560 Aug 16 06:23 xtrabackup_logfile
#备份的redo文件
* 准备备份
* 将redo进行重做,已提交的写到数据文件,未提交的使用undo回滚,模拟CSR的过程
[root@db01 full]# innobackupex --user=root --password=123 --apply-log /backup/full
- 恢复备份
- 前提1:被恢复的目录是空的
- 前提2:被恢复的数据库的实例是关闭的
[root@db01 full]# /etc/init.d/mysqld stop
#停库
[root@db01 full]# cd /application/mysql
#进入mysql目录
[root@db01 mysql]# rm -fr data/
#删除data目录(在生产中可以备份一下)
[root@db01 mysql]# innobackupex --copy-back /backup/full
#拷贝数据
[root@db01 mysql]# chown -R mysql.mysql /application/mysql/data/
#授权
[root@db01 mysql]# /etc/init.d/mysqld start
#启动MySQL
- 增量备份及恢复
- 基于上一次备份进行增量
- 增量备份无法单独恢复,必须基于全备进行恢复
- 所有增量必须要按顺序合并到全备当中
[root@mysql-db01 ~]# innobackupex --user=root --password=123 --no-timestamp /backup/full
#不使用之前的全备,执行一次全备
- 模拟数据变化
mysql> create database inc1;
mysql> use inc1
mysql> create table inc1_tab(id int);
mysql> insert into inc1_tab values(1),(2),(3);
mysql> commit;
mysql> select * from inc1_tab;
- 第一次增量备份
[root@db01 ~]# innobackupex --user=root --password=123 --no-timestamp --incremental --incremental-basedir=/backup/full/ /backup/inc1
参数说明:
--incremental:开启增量备份功能
--incremental-basedir:上一次备份的路径
- 再次模拟数据变化
mysql> create database inc2;
mysql> use inc2
mysql> create table inc2_tab(id int);
mysql> insert into inc2_tab values(1),(2),(3);
mysql> commit;
- 第二次增量备份
[root@db01 ~]# innobackupex --user=root --password=123 --no-timestamp --incremental --incremental-basedir=/backup/inc1/ /backup/inc2
- 增量恢复
[root@db01 ~]# rm -fr /application/mysql/data/
#破坏数据
- 准备备份
- full+inc1+inc2
- 需要将inc1和inc2按顺序合并到full中
- 分步骤进行--apply-log
- 第一步:在全备中apply-log时,只应用redo,不应用undo
[root@db01 ~]# innobackupex --apply-log --redo-only /backup/full/
- 第二步:合并inc1合并到full中,并且apply-log,只应用redo,不应用undo
[root@db01 ~]# innobackupex --apply-log --redo-only --incremental-dir=/backup/inc1/ /backup/full/
- 第三步:合并inc2合并到full中,redo和undo都应用
[root@db01 ~]# innobackupex --apply-log --incremental-dir=/backup/inc2/ /backup/full/
- 第四步:整体full执行apply-log,redo和undo都应用
[root@db01 mysql]# innobackupex --apply-log /backup/full/
copy-back
[root@db01 ~]# innobackupex --copy-back /backup/full/
[root@db01 ~]# chown -R mysql.mysql /application/mysql/data/
[root@db01 ~]# /etc/init.d/mysqld start
MySQL的主从复制
主从复制原理
- 复制是 MySQL 的一项功能,允许服务器将更改从一个实例复制到另一个实例。
- 主服务器将所有数据和结构更改记录到二进制日志中。
- 从属服务器从主服务器请求该二进制日志并在本地应用其内容。
- IO:请求主库,获取上一次执行过的新的事件,并存放到relaylog
- SQL:从relaylog中将sql语句翻译给从库执行
- 主从复制的前提
- 两台或两台以上的数据库实例
- 主库要开启二进制日志
- 主库要有复制用户
- 主库的server_id和从库不同
- 从库需要在开启复制功能前,要获取到主库之前的数据(主库备份,并且记录binlog当时位置)
- 从库在第一次开启主从复制时,时必须获知主库:ip,port,user,password,logfile,pos
- 从库要开启相关线程:IO、SQL
- 从库需要记录复制相关用户信息,还应该记录到上次已经从主库请求到哪个二进制日志
- 从库请求过来的binlog,首先要存下来,并且执行binlog,执行过的信息保存下来
- 主从复制涉及到的文件和线程
- 主库:
- 主库binlog:记录主库发生过的修改事件
- dump thread:给从库传送(TP)二进制日志线程
- 从库:
- relay-log(中继日志):存储所有主库TP过来的binlog事件,在SQL thread执行完毕,数据持久化后清空,但写入relaylog.info文件
- relaylog.info:类似于master的binlog文件
- master.info:存储复制用户信息,上次请求到的主库binlog位置点
- IO thread:接收主库发来的binlog日志,也是从库请求主库的线程
- SQL thread:执行主库TP过来的日志
- 主库:
- 原理
- 通过change master to语句告诉从库主库的ip,port,user,password,file,pos
- 从库通过start slave命令开启复制必要的IO线程和SQL线程
- 从库通过IO线程拿着change master to用户密码相关信息,连接主库,验证合法性
- 从库连接成功后,会根据binlog的pos问主库,有没有比这个更新的
- 主库接收到从库请求后,比较一下binlog信息,如果有就将最新数据通过dump线程给从库IO线程
- 从库通过IO线程接收到主库发来的binlog事件,存储到TCP/IP缓存中,并返回ACK更新master.info
- 将TCP/IP缓存中的内容存到relay-log中
- SQL线程读取relay-log.info,读取到上次已经执行过的relay-log位置点,继续执行后续的relay-log日志,执行完成后,更新relay-log.info
【实战】MySQL主从复制
本实例中db01是master,db02是slave
主库操作
- 修改配置文件
[root@db01 ~]# vim /etc/my.cnf
#编辑mysql配置文件
[mysqld]
#在mysqld标签下配置
server_id =1
#主库server-id为1,从库不等于1
log_bin=mysql-bin
#开启binlog日志
- 导出一份全备数据给从库
[root@db01 ~]# mysqldump -uroot -p123456 -A -R --triggers --master-data=2 --single-transaction > /tmp/full.sql
[root@db01 ~]# scp /tmp/full.sql root@192.168.88.140:/tmp/
- 创建主从复制用户
[root@db01 ~]# mysql -uroot -p123456
#登录数据库
mysql> grant replication slave on *.* to slave@'192.168.88.%' identified by '123456';
#创建slave用户
从库操作(db02)
- 修改配置文件
[root@db02 ~]# vim /etc/my.cnf
#修改db02配置文件
[mysqld]
#在mysqld标签下配置
server_id =5
#主库server-id为1,从库不等于1
log_bin=mysql-bin
#开启binlog日志
[root@db02 ~]# systemctl restart mysqld
- 记录主库当前的binlog位置
[root@db01 ~]# mysql -uroot -p123456
mysql> show master status;
- 再从库上配置主库等信息
[root@db02 ~]# mysql -uroot -p123456
#登陆数据库
mysql> change master to
-> master_host='192.168.88.136',
-> master_user='slave',
-> master_password='123456',
-> master_log_file='mysql-bin.000001',
-> master_log_pos=120;
#head -50 /tmp/full.sql内的master_log_file与master_log_pos
-># master_auto_position=1; # 与前两句冲突,表示自动适配master的file和position,MariaDB5.5不支持GTID特性。此时未开启GTID
# master_auto_position=1 表示开启 MySQL 的基于 GTID 的主从复制功能。
# GTID(Global Transaction Identifier)是一种基于事务的复制方式,相比传统的基于文件和位置的复制方式,GTID 复制更加灵活和可靠。
# 当 master_auto_position=1 时,slave 会自动找到主库上最新的 GTID 位置,而不需要手动指定日志文件和位置。这样可以避免主从同步时由于位置不匹配而导致的问题。
开启主从复制
#执行change master to 语句
mysql> start slave;
mysql> show slave status\G
#看到Slave_IO_Running: Yes Slave_SQL_Running: Yes表示运行成功
#从数据库:/application/mysql/data目录下出现master.info文件,记录了同步的索引号
#测试方法:在主里面库建表插入内容,从里面可以看到主新增的内容表示同步成功。
主从复制基本故障处理
- IO线程报错
- user password ip port
- 网络:不同,延迟高,防火墙
- 没有跳过反向解析[mysqld]里面加入skip-name-resolve
- 请求binlog
- binlog不存在或者损坏
- 更新relay-log和master.info
- SQL线程
- relay-log出现问题
- 从库做写入了
- 操作对象已存在(create)
- 操作对象不存在(insert update delete drop truncate alter)
- 约束问题、数据类型、列属性
处理方法一
mysql> stop slave;
#临时停止同步
mysql> set global sql_slave_skip_counter=1;
#将同步指针向下移动一个(可重复操作),告诉这个执行不了的binlog条目不执行,去执行下一条。
mysql> start slave;
#开启同步
处理方法二
[root@db01 ~]# vim /etc/my.cnf
#编辑配置文件
slave-skip-errors=1032,1062,1007
#在[mysqld]标签下添加以下参数
但是以上操作都是有风险存在的
处理方法三
set global read_only=1;
#在命令行临时设置,在从库设置
read_only=1
#在配置文件中永久生效[mysqld]
延时从库
- 普通的主从复制可能存在不足
- 逻辑损坏怎么办?
- 不能保证主库的操作,从库一定能做
- 高可用?自动failover?
- 过滤复制
- 企业中一般会延时3-6小时
- 延时从库配置方法
mysql>stop slave;
#停止主从
mysql>CHANGE MASTER TO MASTER_DELAY = 180;
#设置延时为180秒
mysql>start slave;
#开启主从
mysql> show slave status \G
SQL_Delay: 60
#查看状态
mysql> stop slave;
#停止主从
mysql> CHANGE MASTER TO MASTER_DELAY = 0;
#设置延时为0
mysql> start slave;
#开启主从
- 恢复数据
- 停止主从
- 导出从库数据
- 主库导入数据
半同步复制
从MYSQL5.5开始,支持半自动复制。之前版本的MySQL Replication都是异步(asynchronous)的,主库在执行完一些事务后,是不会管备库的进度的。如果备库不幸落后,而更不幸的是主库此时又出现Crash(例如宕机),这时备库中的数据就是不完整的。简而言之,在主库发生故障的时候,我们无法使用备库来继续提供数据一致的服务了。
半同步复制(Semi synchronous Replication)则一定程度上保证提交的事务已经传给了至少一个备库。
IO在收到了读写后会发送ACK报告已经收到了。
出发点是保证主从数据一致性问题,安全的考虑。
- 半同步复制开启方法
- 安装(主库)
[root@db01 ~]# mysql -uroot -p123456
#登录数据库
mysql> show global variables like 'have_dynamic_loading';
#查看是否有动态支持 have_dynamic_loading=YES
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME'semisync_master.so';
#安装自带插件
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
#启动插件
mysql> SET GLOBAL rpl_semi_sync_master_timeout = 1000;
#设置超时
[root@db01 ~]# vim /etc/my.cnf
#修改配置文件
[mysqld]
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000 #多长时间认为超时,单位ms
#在[mysqld]标签下添加如下内容(不用重启库)
mysql> show variables like'rpl%';
mysql> show global status like 'rpl_semi%';
#检查安装
从库安装:
[root@mysql-db02 ~]# mysql -uroot -p123456
#登录数据库
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME'semisync_slave.so';
#安装slave半同步插件
mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;
#启动插件
mysql> stop slave io_thread;
mysql> start slave io_thread;
#重启io线程使其生效
[root@mysql-db02 ~]# vim /etc/my.cnf
#编辑配置文件(不需要重启数据库)
[mysqld]
rpl_semi_sync_slave_enabled =1
#在[mysqld]标签下添加如下内容
注:相关参数说明:
- rpl_semi_sync_master_timeout=milliseconds
- 设置此参数值(ms),为了防止半同步复制在没有收到确认的情况下发生堵塞,如果Master在超时之前没有收到任何确认,将恢复到正常的异步复制,并继续执行没有半同步的复制操作。
- rpl_semi_sync_master_wait_no_slave={ON|OFF}
- 如果一个事务被提交,但Master没有任何Slave的连接,这时不可能将事务发送到其它地方保护起来。默认情况下,Master会在时间限制范围内继续等待Slave的连接,并确认该事务已经被正确的写到磁盘上。
- 可以使用此参数选项关闭这种行为,在这种情况下,如果没有Slave连接,Master就会恢复到异步复制。
测试半同步:
mysql> create database test1;
Query OK, 1 row affected (0.04 sec)
mysql> create database test2;
Query OK, 1 row affected (0.00 sec)
#创建两个数据库,test1和test2
mysql> show global status like 'rpl_semi%';
#查看复制状态,Rpl_semi_sync_master_status状态是ON
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 768 |
| Rpl_semi_sync_master_net_wait_time | 1497 |
| Rpl_semi_sync_master_net_waits | 2 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 884 |
| Rpl_semi_sync_master_tx_wait_time | 1769 |
| Rpl_semi_sync_master_tx_waits | 2 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
#此行显示2,表示刚才创建的两个库执行了半同步
| Rpl_semi_sync_master_yes_tx | 2 |
+--------------------------------------------+-------+
14 rows in set (0.06 sec)
mysql> show databases;
#从库查看
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
| test1 |
| test2 |
+--------------------+
mysql> SET GLOBAL rpl_semi_sync_master_enabled = 0;
#关闭半同步(1:开启 0:关闭)
mysql> show global status like 'rpl_semi%';
#查看半同步状态
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 768 |
| Rpl_semi_sync_master_net_wait_time | 1497 |
| Rpl_semi_sync_master_net_waits | 2 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | OFF | #状态为关闭
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 884 |
| Rpl_semi_sync_master_tx_wait_time | 1769 |
| Rpl_semi_sync_master_tx_waits | 2 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 2 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
mysql> create database test3;
Query OK, 1 row affected (0.00 sec)
mysql> create database test4;
Query OK, 1 row affected (0.00 sec)
#再一次创建两个库
mysql> show global status like 'rpl_semi%';
#再一次查看半同步状态,此时Rpl_semi_sync_master_status变成OFF
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 768 |
| Rpl_semi_sync_master_net_wait_time | 1497 |
| Rpl_semi_sync_master_net_waits | 2 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | OFF |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 884 |
| Rpl_semi_sync_master_tx_wait_time | 1769 |
| Rpl_semi_sync_master_tx_waits | 2 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
#此行还是显示2,则证明,刚才的那两条并没有执行半同步否则应该是4
| Rpl_semi_sync_master_yes_tx | 2 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
注:不难发现,在查询半同步状态是,开启半同步,查询会有延迟时间,关闭之后则没有
过滤复制
- 主库:
- 白名单:只记录白名单中列出的库的二进制日志
- binlog-do-db
- 黑名单:不记录黑名单列出的库的二进制日志
- binlog-ignore-db
- 白名单:只记录白名单中列出的库的二进制日志
- 从库:
- 白名单:只执行白名单中列出的库或者表的中继日志
- --replicate-do-db=test
- --replicate-do-table=test.t1
- --replicate-wild-do-table=test.t2
- 黑名单:不执行黑名单中列出的库或者表的中继日志
- --replicate-ignore-db
- --replicate-ignore-table
- --replicate-wild-ignore-table
- 白名单:只执行白名单中列出的库或者表的中继日志
- 复制过滤配置
[root@db01 data]# vim /data/3307/my.cnf
replicate-do-db=world
#在[mysqld]标签下添加
mysqladmin -S /data/3307/mysql.sock shutdown
#关闭MySQL
mysqld_safe --defaults-file=/data/3307/my.cnf &
#启动MySQL
#设置主从的server_id,如法炮制设置主从关系,记得change master to时候加上参数master_port = ‘3306’,
- 测试复制过滤:
- 第一次测试:
- 主库:
[root@db02 ~]# mysql -uroot -p123 -S /data/3308/mysql.sock
mysql> use world
mysql> create table t1(id int);
* 从库查看结果
[root@db02 ~]# mysql -uroot -p123 -S /data/3307/mysql.sock
mysql> use world
mysql> show tables;
- 第二次测试
- 主库
[root@db02 ~]# mysql -uroot -p123 -S /data/3308/mysql.sock
mysql> use test
mysql> create table tb1(id int);
* 从库查看结果
[root@db02 ~]# mysql -uroot -p123 -S /data/3307/mysql.sock
mysql> use test
mysql> show tables;
MHA高可用架构
MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
MHA能够在较短的时间内实现自动故障检测和故障转移,通常在10-30秒以内;在复制框架中,MHA能够很好地解决复制过程中的数据一致性问题,由于不需要在现有的replication中添加额外的服务器,仅需要一个manager节点,而一个Manager能管理多套复制,所以能大大地节约服务器的数量;另外,安装简单,无性能损耗,以及不需要修改现有的复制部署也是它的优势之处。
MHA还提供在线主库切换的功能,能够安全地切换当前运行的主库到一个新的主库中(通过将从库提升为主库),大概0.5-2秒内即可完成。
MHA由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以独立部署在一台独立的机器上管理多个Master-Slave集群,也可以部署在一台Slave上。当Master出现故障时,它可以自动将最新数据的Slave提升为新的Master,然后将所有其他的Slave重新指向新的Master。整个故障转移过程对应用程序是完全透明的。
工作流程
- 把宕机的master二进制日志保存下来。
- 找到binlog位置点最新的slave。
- 在binlog位置点最新的slave上用relay log(差异日志)修复其它slave。(因为relay log修复比bin log快,所以不用master的bin log,slave没有bin log)
- 将宕机的master上保存下来的二进制日志恢复到含有最新位置点的slave上。
- 将含有最新位置点binlog所在的slave提升为master。
- 将其它slave重新指向新提升的master,并开启主从复制。
MHA工具介绍
MHA软件由两部分组成,Manager工具包和Node工具包
Manager工具包主要包括以下几个工具
| masterha_check_ssh | 检查MHA的ssh-key | | :----------------------- | :---------------------- | | masterha_check_repl | 检查主从复制情况 | | masterha_manger | 启动MHA | | masterha_check_status | 检测MHA的运行状态 | | masterha_master_monitor | 检测master是否宕机 | | masterha_master_switch | 手动故障转移 | | masterha_conf_host | 手动添加server信息 | | masterha_secondary_check | 建立TCP连接从远程服务器 | | masterha_stop | 停止MHA |
Node工具包主要包括以下几个工具
| save_binary_logs | 保存宕机的master的binlog | | :-------------------- | :----------------------- | | apply_diff_relay_logs | 识别relay log的差异 | | filter_mysqlbinlog | 防止回滚事件 | | purge_relay_logs | 清除中继日志 |
MHA优点总结
- Masterfailover and slave promotion can be done very quickly
- 自动故障转移快
- Mastercrash does not result in data inconsistency
- 主库崩溃不存在数据一致性问题
- Noneed to modify current MySQL settings (MHA works with regular MySQL)
- 不需要对当前mysql环境做重大修改
- Noneed to increase lots of servers
- 不需要添加额外的服务器(仅一台manager就可管理上百个replication)
- Noperformance penalty
- 性能优秀,可工作在半同步复制和异步复制,当监控mysql状态时,仅需要每隔N秒向master发送ping包(默认3秒),所以对性能无影响。你可以理解为MHA的性能和简单的主从复制框架性能一样。
- Works with any storage engine
- 只要replication支持的存储引擎,MHA都支持,不会局限于innodb
- Masterfailover and slave promotion can be done very quickly
MHA实验环境
- 搭建三台mysql数据库
wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz
tar xzvf mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz
mkdir /application
mv mysql-5.6.40-linux-glibc2.12-x86_64 /application/mysql-5.6.40
ln -s /application/mysql-5.6.40 /application/mysql
cd /application/mysql/support-files
cp my-default.cnf /etc/my.cnf
cp:是否覆盖"/etc/my.cnf"? y
cp mysql.server /etc/init.d/mysqld
cd /application/mysql/scripts
useradd mysql -s /sbin/nologin -M
yum -y install autoconf
./mysql_install_db --user=mysql --basedir=/application/mysql --data=/application/mysql/data
vim /etc/profile.d/mysql.sh
export PATH="/application/mysql/bin:$PATH"
source /etc/profile
sed -i 's#/usr/local#/application#g' /etc/init.d/mysqld /application/mysql/bin/mysqld_safe
vim /usr/lib/systemd/system/mysqld.service
[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=https://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf
LimitNOFILE = 5000
systemctl start mysqld
systemctl enable mysqld
mysqladmin -uroot password '123456'
mysql -uroot -p123456
基于GTID的主从复制
- 先决条件
- 主库和从库都要开启binlog
- 主库和从库server-id不同
- 要有主从复制用户
主库操作
- 修改配置文件
[root@mysql-db01 ~]# vim /etc/my.cnf
#编辑mysql配置文件
[mysqld]
#在mysqld标签下配置
server_id =1
#主库server-id为1,从库不等于1
log_bin=mysql-bin
#开启binlog日志
#如果3台设备是在装了mysql后克隆的,mysql的UUID则相同,需要修改为不同值
[root@mysql-db01 ~]# vim /application/mysql/data/auto.cnf
server-uuid=8108d02e-be0a-11ec-8a15-000c2956d2e2
- 创建主从复制用户,每台设备都要配置,因为slave有可能变成master
[root@mysql-db01 ~]# mysql -uroot -p123456
#登录数据库
mysql> grant replication slave on *.* to slave@'192.168.88.%' identified by '123456';
#创建slave用户
从库操作
- 修改配置文件
[root@mysql-db02 ~]# vim /etc/my.cnf
#修改mysql-db02配置文件
[mysqld]
#在mysqld标签下配置
server_id =5
#主库server-id为1,从库必须大于1
log_bin=mysql-bin
#开启binlog日志
[root@mysql-db02 ~]# systemctl restart mysqld
#重启mysql
[root@mysql-db03 ~]# vim /etc/my.cnf
#修改mysql-db03配置文件
[mysqld]
#在mysqld标签下配置
server_id =10
#主库server-id为1,从库必须大于1
log_bin=mysql-bin
#开启binlog日志
[root@mysql-db03 ~]# systemctl restart mysqld
#重启mysql
注:在以往如果是基于binlog日志的主从复制,则必须要记住主库的master状态信息。
mysql> show master status;
+------------------+----------+
| File | Position |
+------------------+----------+
| mysql-bin.000002 | 120 |
+------------------+----------+
主、从库都要开启GTID
mysql> show global variables like '%gtid%';
#没开启之前先看一下GTID的状态
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| enforce_gtid_consistency | OFF |
| gtid_executed | |
| gtid_mode | OFF |
| gtid_owned | |
| gtid_purged | |
+--------------------------+-------+
[root@mysql-db01 ~]# vim /etc/my.cnf
#编辑mysql配置文件(主库从库都需要修改)
[mysqld]
#在[mysqld]标签下添加
gtid_mode=ON
log_slave_updates
#重要:开启slave的binlog同步
enforce_gtid_consistency
#开启GTID特性
[root@mysql-db01 ~]# /etc/init.d/mysqld restart
#重启数据库
mysql> show global variables like '%gtid%';
#检查GTID状态
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| enforce_gtid_consistency | ON | #执行GTID一致
| gtid_executed | |
| gtid_mode | ON | #开启GTID模块
| gtid_owned | |
| gtid_purged | |
+--------------------------+-------+
注:主库从库都需要开启GTID否则在做主从复制的时候就会报错,因为slave可能变成master
[root@mysql-db02 ~]# mysql -uroot -123456
mysql> change master to
-> master_host='10.0.0.51',
-> master_user='rep',
-> master_password='123456',
-> master_auto_position=1;
#如果GTID没有开的话
ERROR 1777 (HY000): CHANGE MASTER TO MASTER_AUTO_POSITION = 1 can only be executed when @@GLOBAL.GTID_MODE = ON.
配置主从复制
[root@mysql-db02 ~]# mysql -uroot -p123456
#登录数据库
mysql> change master to
#配置复制主机信息
-> master_host='10.0.0.51',
#主库IP
-> master_user='rep',
#主库复制用户
-> master_password='123456',
#主库复制用户的密码
-> master_auto_position=1;
#GTID位置点
mysql> start slave;
#开启slave
mysql> show slave status\G
#查看slave状态
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.51
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000003
Read_Master_Log_Pos: 403
Relay_Log_File: mysql-db02-relay-bin.000002
Relay_Log_Pos: 613
Relay_Master_Log_File: mysql-bin.000003
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 403
Relay_Log_Space: 822
Until_Condition: None
- 从库设置
[root@mysql-db02 ~]# mysql -uroot -p123456
#登录从库
mysql> set global relay_log_purge = 0;
#禁用自动删除relay log 功能
mysql> set global read_only=1;
#设置只读
[root@mysql-db02 ~]# vim /etc/my.cnf
#编辑配置文件
[mysqld]
#在mysqld标签下添加
relay_log_purge = 0
#禁用自动删除relay log 永久生效
部署MHA
环境准备(所有节点)
下载MHA工具包:
cd && wget https://download.s21i.faiusr.com/23126342/0/0/ABUIABBPGAAg3OHUiAYolpPt7AQ.zip?f=mysql-master-ha.zip&v=1628778716
[root@mysql-db01 ~]# yum install perl-DBD-MySQL -y
#安装依赖包
[root@mysql-db01 ~]# unzip mysql-master-ha.zip /home/user1/tools/
[root@mysql-db01 ~]# cd /home/user1/tools/
#进入安装包存放目录
[root@mysql-db01 tools]# ll
mha4mysql-manager-0.56-0.el6.noarch.rpm
mha4mysql-manager-0.56.tar.gz
mha4mysql-node-0.56-0.el6.noarch.rpm
mha4mysql-node-0.56.tar.gz
[root@mysql-db01 tools]# yum install -y mha4mysql-node-0.56-0.el6.noarch.rpm
Preparing... ########################################### [100%]
1:mha4mysql-node ########################################### [100%]
#安装node包,所有节点都要安装node包
[root@mysql-db01 tools]# mysql -uroot -p123456
#登录数据库,主库,从库会自动同步
mysql> grant all privileges on *.* to mha@'192.168.88.%' identified by 'mha';
#添加mha管理账号
mysql> select user,host from mysql.user;
#查看是否添加成功
mysql> select user,host from mysql.user;
#主库上创建,从库会自动复制(在从库上查看)
- 命令软连接(所有节点)
[root@mysql-db01 ~]# ln -s /application/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog
[root@mysql-db01 ~]# ln -s /application/mysql/bin/mysql /usr/bin/mysql
#如果不创建命令软连接,检测mha复制情况的时候会报错,写入环境变量
- 部署管理节点(mha-manager:mysql-db03)最好用第四个节点安装mha-manager
[root@mysql-db03 ~]# yum -y install epel-release
#使用epel源
[root@mysql-db03 ~]# yum install -y perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes
#安装manager依赖包
[root@mysql-db03 tools]# yum install -y mha4mysql-manager-0.56-0.el6.noarch.rpm
Preparing... ########################################### [100%]
1:mha4mysql-manager ########################################### [100%]
#安装manager包
- 编辑配置文件
[root@mysql-db03 ~]# mkdir -p /etc/mha
#创建配置文件目录
[root@mysql-db03 ~]# mkdir -p /var/log/mha/app1
#创建日志目录
[root@mysql-db03 ~]# vim /etc/mha/app1.cnf
#编辑mha配置文件
[server default]
manager_log=/var/log/mha/app1/manager
manager_workdir=/var/log/mha/app1
master_binlog_dir=/application/mysql/data
user=mha
password=mha
ping_interval=2
repl_password=123456
repl_user=rep
ssh_user=root
[server1]
hostname=10.0.0.51
port=3306
[server2]
candidate_master=1
check_repl_delay=0
hostname=10.0.0.52
port=3306
[server3]
hostname=10.0.0.53
port=3306
配置文件详解
[server default]
manager_workdir=/var/log/masterha/app1
#设置manager的工作目录
manager_log=/var/log/masterha/app1/manager.log
#设置manager的日志
master_binlog_dir=/data/mysql
#设置master 保存binlog的位置,以便MHA可以找到master的日志,我这里的也就是mysql的数据目录
master_ip_failover_script= /usr/local/bin/master_ip_failover
#设置自动failover时候的切换脚本
master_ip_online_change_script= /usr/local/bin/master_ip_online_change
#设置手动切换时候的切换脚本
password=123456
#设置mysql中root用户的密码,这个密码是前文中创建监控用户的那个密码
user=root
#设置监控用户root
ping_interval=1
#设置监控主库,发送ping包的时间间隔,尝试三次没有回应的时候自动进行failover
remote_workdir=/tmp
#设置远端mysql在发生切换时binlog的保存位置
repl_password=123456
#设置复制用户的密码
repl_user=rep
#设置复制环境中的复制用户名
report_script=/usr/local/send_report
#设置发生切换后发送的报警的脚本
#一旦MHA到server02的监控之间出现问题,MHA Manager将会尝试从server03登录到server02
secondary_check_script= /usr/local/bin/masterha_secondary_check -s server03 -s server02 --user=root --master_host=server02 --master_ip=192.168.0.50 --master_port=3306
shutdown_script=""
#设置故障发生后关闭故障主机脚本(该脚本的主要作用是关闭主机防止发生脑裂,这里没有使用)
ssh_user=root
#设置ssh的登录用户名
[server1]
hostname=10.0.0.51
port=3306
[server2]
hostname=10.0.0.52
port=3306
candidate_master=1
#设置为候选master,如果设置该参数以后,发生主从切换以后将会将此从库提升为主库,即使这个主库不是集群中事件最新的slave。
check_repl_delay=0
#默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间,通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master
- 配置ssh信任(所有节点)
[root@mysql-db01 ~]# ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa >/dev/null 2>&1
#创建秘钥对
[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.88.136
[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.88.140
[root@mysql-db01 ~]# ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.88.142
#发送公钥,包括自己
- 启动测试
[root@mysql-db03 ~]# masterha_check_ssh --conf=/etc/mha/app1.cnf
#测试ssh
#看到如下字样,则测试成功
Tue Mar 7 01:03:33 2017 - [info] All SSH connection tests passed successfully.
[root@mysql-db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
#测试复制
#看到如下字样,则测试成功
#若不在slave库上创建用户会失败,按理说应该slave会同步master的库但创建slave用户是创建主从关系前
#mysql> grant replication slave on *.* to rep@'10.0.0.%' identified by '123456';
MySQL Replication Health is OK.
- 启动MHA
[root@mysql-db03 ~]# 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@mysql-db03 ~]# masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover --shutdown
#关闭
[root@mysql-db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
#查看mha是否运行正常,正常会显示master的IP
- 切换master测试
[root@mysql-db02 ~]# mysql -uroot -p123456
#登录数据库(db02)
mysql> show slave status\G
#检查复制情况
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.51
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000006
Read_Master_Log_Pos: 191
Relay_Log_File: mysql-db02-relay-bin.000002
Relay_Log_Pos: 361
Relay_Master_Log_File: mysql-bin.000006
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
[root@mysql-db03 ~]# mysql -uroot -p123456
#登录数据库(db03)
mysql> show slave status\G
#检查复制情况
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.51
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000006
Read_Master_Log_Pos: 191
Relay_Log_File: mysql-db03-relay-bin.000002
Relay_Log_Pos: 361
Relay_Master_Log_File: mysql-bin.000006
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
[root@mysql-db01 ~]# /etc/init.d/mysqld stop
#停掉主库
Shutting down MySQL..... SUCCESS!
[root@mysql-db02 ~]# mysql -uroot -p123456
#登录数据库(db02)
mysql> show slave status\G
#查看slave状态
Empty set (0.00 sec)
#db02的slave已经为空
[root@mysql-db03 ~]# mysql -uroot -p123456
#登录数据库(db03)
mysql> show slave status\G
#查看slave状态
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.52
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000006
Read_Master_Log_Pos: 191
Relay_Log_File: mysql-db03-relay-bin.000002
Relay_Log_Pos: 361
Relay_Master_Log_File: mysql-bin.000006
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
此时停掉主库后再开启db01,db01正常了只能通过手动方式以从库身份加入
[root@mysql-db01 ~]# mysql -uroot -123456
change master to
master_host='192.168.88.136',
master_user='slave',
master_password='123456',
master_auto_position=1;
-> start slave;
配置vIP漂移
- VIP漂移的两种方式
- 通过keepalived的方式,管理虚拟IP的漂移,与后面Nginx负载均衡有关
- 通过MHA自带脚本方式,管理虚拟IP的漂移
- MHA脚本方式
[root@mysql-db03 ~]# vim /etc/mha/app1.cnf
#编辑配置文件
[server default]
#在[server default]标签下添加
master_ip_failover_script=/etc/mha/master_ip_failover
#使用MHA自带脚本,在下载的mha文件mysql-master-ha.zip解压后的文件里
#tar xzvf mha4mysql-manager-0.56.tar.gz
#cd mha4mysql-manager-0.56/sample/script
#master-ip-failover文件就是自带的
#编辑脚本,该文件从wget而来
[root@mysql-db03 ~]# vim /etc/mha/master_ip_failover
#根据配置文件中脚本路径编辑
my $vip = '10.0.0.55/24';
my $key = '0';
#网卡名要改对,可能是ens33
my $ssh_start_vip = "/sbin/ifconfig ens33:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig ens33:$key down";
#修改以下几行内容
[root@mysql-db03 ~]# chmod +x /etc/mha/master_ip_failover
#添加执行权限,否则mha无法启动
[root@mysql-db03 ~]# yum install net-tools
#安装IFconfig,每台设备都要安装否则脚本执行失败
* 手动绑定vIP,假设db01是master
[root@mysql-db01 ~]# ifconfig ens33:0 192.168.88.88/24
#绑定vip,第一次要在master上手工配置,后面不需要了
[root@mysql-db01 ~]# ip a |grep eth0
#查看vip
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
inet 10.0.0.51/24 brd 10.0.0.255 scope global eth0
inet 10.0.0.55/24 brd 10.0.0.255 scope global secondary eth0:0
*安装dos2unix,因为从wget获取的master_ip_failover在windows下编辑,换行符与linux不一样,需要转换
[root@mysql-db03 mha]# yum install dos2unix
[root@mysql-db03 mha]# dos2unix master_ip_failover
*重启mha
[root@mysql-db03 ~]#masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover --shutdown
#关闭
[root@mysql-db03 ~]# 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@mysql-db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
#查看mha是否运行正常,正常会显示master的IP
* 测试ip漂移
#登录db02
[root@mysql-db02 ~]# mysql -uroot -p123456
#查看slave信息
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.51
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000007
Read_Master_Log_Pos: 191
Relay_Log_File: mysql-db02-relay-bin.000002
Relay_Log_Pos: 361
Relay_Master_Log_File: mysql-bin.000007
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
#停掉主库
[root@mysql-db01 ~]# /etc/init.d/mysqld stop
Shutting down MySQL..... SUCCESS!
#在db03上查看从库slave信息
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.52
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000006
Read_Master_Log_Pos: 191
Relay_Log_File: mysql-db03-relay-bin.000002
Relay_Log_Pos: 361
Relay_Master_Log_File: mysql-bin.000006
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
#在db01上查看vip信息
[root@mysql-db01 ~]# ip a |grep eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
inet 10.0.0.51/24 brd 10.0.0.255 scope global eth0
#在db02上查看vip信息
[root@mysql-db02 ~]# ip a |grep eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
inet 10.0.0.52/24 brd 10.0.0.255 scope global eth0
inet 10.0.0.55/24 brd 10.0.0.255 scope global secondary eth0:0