飞天班第48节:数据库高级应用-1

2020/06/17

1、MySQL 5.7安装

Linux

1、下载rpm包
wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.28-1.el7.x86_64.rpm-bundle.tar

# 如果提示需要账号密码,就用这个方式下载
# wget --http-user=youremail@email.com --http-passwd=yourpassword https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.28-1.el7.x86_64.rpm-bundle.tar

2、查看系统是否自带mariadb
rpm -qa | grep mariadb
[root@alibaba local]# rpm -qa | grep mariadb
mariadb-libs-5.5.60-1.el7_5.x86_64

3、将查出的mariadb进行卸载
rpm -e --nodeps mariadb-libs-5.5.60-1.el7_5.x86_64

4、把刚刚下载的mysql tar解压
tar -xvf mysql-5.7.28-1.el7.x86_64.rpm-bundle.tar

5、在解压目录安装如下4个mysql核心包
rpm -ivh mysql-community-common-5.7.28-1.el7.x86_64.rpm
rpm -ivh mysql-community-libs-5.7.28-1.el7.x86_64.rpm
rpm -ivh mysql-community-client-5.7.28-1.el7.x86_64.rpm
rpm -ivh mysql-community-server-5.7.28-1.el7.x86_64.rpm

6、安装到server时缺少依赖报错
[root@alibaba mysql5.7]# rpm -ivh mysql-community-server-5.7.28-1.el7.x86_64.rpm
warning: mysql-community-server-5.7.28-1.el7.x86_64.rpm: Header V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY
error: Failed dependencies:
        libaio.so.1()(64bit) is needed by mysql-community-server-5.7.28-1.el7.x86_64
        libaio.so.1(LIBAIO_0.1)(64bit) is needed by mysql-community-server-5.7.28-1.el7.x86_64
        libaio.so.1(LIBAIO_0.4)(64bit) is needed by mysql-community-server-5.7.28-1.el7.x86_64

7、安装缺少的依赖
yum -y install libaio

8、再次安装server
rpm -ivh mysql-community-server-5.7.28-1.el7.x86_64.rpm

9、启动mysql服务
[root@alibaba ~]# service mysqld start
Redirecting to /bin/systemctl start mysqld.service

10、查看v5.7版本的默认登录密码,只能本地登录
[root@alibaba ~]# grep password /var/log/mysqld.log
2020-06-19T04:30:50.288394Z 1 [Note] A temporary password is generated for root@localhost: =GDs4dKhDTJT

11、登录到mysql命令行,修改默认密码
[root@alibaba ~]# mysql -uroot -p
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '12345678';

12、会告诉你密码不符合规范,可以修改校验等级和长度后再次执行上面的语句
mysql> set global validate_password_policy=LOW;
mysql> set global validate_password_length=6; 

13、账户授权
mysql> use mysql;
mysql> select host,user from user;
# 所有ip都可以访问数据库
mysql> grant all privileges on *.* to gavin@'%' identified by '123456';
# 也可以设置只有内网网段ip才可访问,并授权账号可以授权其他人,%不限制访问的ip
# mysql> grant all privileges on *.* to gavin@'192.168.%' identified by '123456' with grant option;
mysql> flush privileges;
  • yum安装的mysql

    yum remove mysql mysql-server mysql-libs compat-mysql51
    # 删除数据目录
    rm -rf /var/lib/mysq
    # 删除配置文件
    rm /etc/my.cnf
    
  • rpm安装的mysql

rpm -qa | grep -i mysql
# 1、将查出来的包名卸载
rpm -e –nodeps 包名
# 查看mysql是否开启启动
chkconfig --list | grep -i mysql
# 2、删除mysql开启启动服务
chkconfig --del mysql
# 3、然后找出OS中分散的mysql文件夹,并删除
[root@helloworld mysql]# find / -name mysql
/etc/selinux/targeted/active/modules/100/mysql
/etc/rc.d/init.d/mysql
/run/lock/subsys/mysql
/var/spool/mail/mysql
/usr/bin/mysql
/usr/local/mysql
/usr/local/mysql/bin/mysql
/usr/local/mysql/data/mysql
/usr/local/mysql/include/mysql
/usr/lib64/mysql
/usr/lib64/python2.7/site-packages/sqlalchemy/dialects/mysql
/usr/share/mysql
/home/mysql
/www/aikomj.github.io/assets/images/2020/icoding/mysql
/www/aikomj.github.io/_posts/2020/mysql
/www/jekyll-blog/mysql
/www/jekyll-blog/assets/images/2020/icoding/mysql
/root/mysql
/root/mysql/mysql

Window

安装mysql8.0

1、下载免安装版,解压缩

2、配置环境变量

  • 变量名:MYSQL_HOME
  • 变量值:D:\software\mysql-8.0.13-winx64(这里填写MySQL的安装路径)

在path变量的变量值末尾加上:;%MYSQL_HOME%\bin

3、生成data文件

打开cmd窗口,切换到%MYSQL_HOME%/bin下,输入命令:

mysqld --initialize-insecure --user=mysql

等待执行完毕,会在mysql根目录下生成data文件夹

4、启动msyql服务

# 以管理员身份运行CMD 命令,否则没有权限
net start mysql

5、登录msyql

mysql -u root -p
# 此时会提示输入密码,由于初次登录MySQL,无需输入密码,所以直接回车即可

# 修改 root用户的密码
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
# 刷新权限
mysql> flush privileges;
# 退出
mysql> quit;

学习的基础数据导入

create database icoding_admin;
use icoding_admin;

DROP TABLE IF EXISTS `ad_role`;

CREATE TABLE `ad_role` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `role_name` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `ad_role` (`id`, `role_name`)
VALUES
	(1,'vip1'),
	(2,'vip2'),
	(3,'vip3');
	
DROP TABLE IF EXISTS `ad_user`;

CREATE TABLE `ad_user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL DEFAULT '',
  `password` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `ad_user` (`id`, `username`, `password`)
VALUES
	(1,'arry','123456'),
	(2,'gavin','1234567'),
	(3,'coding','123456');
	
DROP TABLE IF EXISTS `ad_user_role`;

CREATE TABLE `ad_user_role` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `ad_user_role` (`id`, `user_id`, `role_id`)
VALUES
	(1,1,1),
	(2,1,2),
	(3,1,3),
	(4,2,2),
	(5,3,3),
	(7,2,3);

2、基础知识回顾

sql语句

  • where条件解析顺序
    • MySQL:自左向右,所以考虑写sql语句时把筛选条件强的写在最左边,算是sql语句优化的第一步
    • Oralce:自右向左
  • SQL执行顺序
    • FROM
    • ON
    • JOIN
    • WHERE
    • GROUP BY
    • HAVING
    • SELECT
    • DISTINCT 去重复
    • ORDER BY
    • LIMIT 限制返回结果条数
  • 全文索引
    • 只有在MyISAM的引擎才可以用

      ,只能使用在CHAR、VARCHAR、TEXT字段使用使用,如like,not like

  • MySQL中SQL执行的过程-MySQL 5.7
    • 连接器
    • 查询缓存,注意8.0后把缓存去掉了
    • 分析器(词法分析(如select from等关键字是否正确)、语法分析(sql语句的前后逻辑是否正常,如group by不能写在select 后面))
    • 优化器(表的索引是否命中, 挑选最优化的索引,完成执行计划)
    • 执行器(开始执行sql语句查询数据 ,会判断客户端连接的用户是否查询表的权限 )
    • 数据引擎(有查询权限,通过数据引擎查询数据 )

# 声明使用查询缓存,但是不建议这样使用
select SQL_CACHE * from ad_user;
# 怎么保证数据一致性, 查询缓存失效是非常频繁的,查询缓存被清空:1.表结构变化,2.增加数据,3.修改数据 
# mysql底层会监控commit事件和ddl,判断查询缓存是否应该被清空。 
# 注意:
	1、MySQL 8.0 把查询缓存这个模块去掉了,这说明查询缓存的作用不大
	2、判断用户查询权限是在执行器阶段开始的
	3、查询缓存会随着结果定期更新
# 核心三步
- 分析
- 优化
- 执行 

MySQL体系结构

如下图

  • Connectors 连接的客户端
  • Connection Pool 连接池
  • Management Service & Utlities 管理服务和工具组件
  • SQL Interface SQL接口组件
  • Parser 查询分析组件
  • Optimizer SQL语句优化器组件
  • Caches & Buffers 缓冲组件
  • Pluggable Storage Engines 插件式存储引擎
  • Files & Logs 物理文件(数据索引文件、日志文件)

MySQL数据库区别于其他数据库的最重要的一个特点就是其插件式的表存储引擎。但是我们要注意一个最重要的,那就是存储引擎是基于表的,而不是数据库。

MySQL数据引擎

存储引擎是MySQL区别于其他数据库的一个最重要特性

# 看MySQL支持的数据引擎8个
mysql> show engines;

存储引擎 说明
MyISAM 高速查询引擎,不支持事务,写入时对表加排他锁
InnoDB v5.5以后是MySQL的默认引擎,写入时对行加排他锁,只有它支持事务
Archive 数据压缩存储引擎,便于数据归档
Memory 内存存储引擎,只要断电了,数据就没了

对比MyISAM和InnoDB

对比 InnoDB MyISAM
存储文件的形式 .frm表定义文件,.ibd存放数据和索引的 .frm表定义文件,.myd数据文件,.myi索引文件
表、页、行
事务 支持 不支持
CRUD 可同时读、写 只可同时读,不可同时写
全文索引 不支持 支持
# 查询表的引擎,一般客户端工具也可以查询,如navicat
show table status like '%ad_user%' \G;
# 创建表的时候可以选择数据引擎

MySQL的9种存储引擎都是各自是各自的特点,然后根据需求的不同,我们在建表的时候可以选择一下,

1.FEDERATED存储引擎

这个引擎不是存放数据的引擎,而是一个指向远程MySQL数据库服务器的,那是什么意思呢,其实一句大白话:“我这里不存表结构文件和数据文件,我是在远程端存的”

就像图上说的,FEDERATED存储引擎分成了2部分,一部分是本地服务,另外一部分就是远程服务,那么如果在你切换到这个引擎的时候,他在执行CRUD的时候,就会把执行操作的命令发到远程服务器,然后执行完了之后,在发回本地,然后从本地服务器中返回匹配的行即可。

2.InnoDB存储引擎

v5.5以后是MySQL的默认引擎,唯一支持事务

  • 支持事务。默认的事务隔离级别为可重复读
  • 支持外键,这个外键大家肯定也都清楚,有利有弊,毕竟外键的作用在那里放着(利:增加可读性,若出现宕机,最大限度的保证数据的一致性和完整性,弊:降低了表的查询速度,如果数据了过大,那么你插入数据库数据的时长可能是不增加外键的十倍)
  • 行锁设计,这样可以支持更高的并发,这也是为什么有时候面试官说你们上ES有点大材小用,因为MySQL自己也能处理那么多。
  • 使用多版本并发控制(MVCC)来获得高并发性,并且实现了SQL标准的4种隔离级别,默认为REPEATABLE READ级别(可重复读)。
  • 使用一种被称为next-key locking(有人称它为间隙锁)的策略来避免幻读(phantom)现象的产生
  • 数据存储采用了聚集(clustered)的方式,每张表的存储都是按主键的顺序进行存放。所以主键不要uuid导致数据无序,当插入新的行,会触发索引重排,消耗CPU性能。
  • InnoDB的索引结构和MySQL其他的存储引擎不同,聚簇索引对主键查询性能非常高,这时候就得有个限制要求,如果表上的索引较多,主键就尽可能的小
  • 数据存储在表空间(tablespace)中,这个表空间实际上是由InnoDB管理的一个黑盒,由一系列的文件组成。

我们从上面的图中能看到,InnoDB存储引擎有许多的内存块,可以认为这些内存块其实就相当于是一个大的内存池,就是线程池类似的那种,

InnoDB存储引擎是多线程的模型,因此其后台有多个不同的后台线程,负责处理不同的任务。可以把它分为4种线程

  • 核心线程Master Thread

    将缓冲池中的数据异步的刷新到磁盘上,来保证数据的一致性

  • IO线程 IO Thread

    主要就是用来IO请求的回调处理,在做主从复制,从库会启动一个IO线程读取主库的binlog写入自己的relaylog,开启一条核心线程读取relaylog在自己数据库上重放操作

  • 净化线程Purge Thread

    主要作用就是事务提交之后回收已经使用并分配的undo页

  • 清理线程Page Cleaner Thread

    他的作用是将之前版本中脏页的刷新操作都放入到单独的线程中来完成

3、Memory 存储引擎

Memory 存储引擎实际上就是将表中的数据存放在内存中,如果数据库重启或发生崩溃,表中的数据都将消失

  • 速度非常快,只支持表锁,并发性能较差

  • 不支持TEXT和BLOB类型,对于字符串类型的数据,只支持固定长度的行,VARCHAR会被自动存储为CHAR类型
  • 存储变长字段(varchar)时是按照定常字段(char)的方式进行的,因此会浪费内存

4、CSV 存储引擎

CSV 存储引擎实际上操作的就是一个标准的CSV 文件,而且他的特点就是不支持索引,也就说,不支持索引,那么效率必然会很低,这个相信很多人都不会选择去使用它。

MySQL默认存储数据的位置

cd /var/lib/mysql
# 这个目录下存放的是数据库对应的各个数据库的数据文件,直接把数据库文件icoding_admin备份到别的服务器,实现数据库冷备份/数据库迁移

MySQL默认配置文件路径

linux下Mysql启动时读取配置文件的顺序为
1./etc/my.cnf 
2./etc/mysql/my.cnf
3./usr/local/mysql/etc/my.cnf(自己安装mysql的目录)
4.~/my.cnf 

# 查找my.cnf文件
sudo find / -name my.cnf
#第二种
sudo mysql --help | grep my.cnf

总结:

  • MyISAM:默认的MySQL插件式存储引擎,它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。注意,通过更改STORAGE_ENGINE配置变量,能够方便地更改MySQL服务器的默认存储引擎。

  • InnoDB:用于事务处理应用程序,具有众多特性,包括ACID事务支持。(提供行级锁)

  • BDB:可替代InnoDB的事务引擎,支持COMMIT、ROLLBACK和其他事务特性。

  • Memory:将所有数据保存在RAM中,在需要快速查找引用和其他类似数据的环境下,可提供极快的访问。

  • Merge:允许MySQL DBA或开发人员将一系列等同的MyISAM表以逻辑方式组合在一起,并作为1个对象引用它们。对于诸如数据仓储等VLDB环境十分适合。

  • Archive:为大量很少引用的历史、归档、或安全审计信息的存储和检索提供了完美的解决方案。

  • Federated:能够将多个分离的MySQL服务器链接起来,从多个物理服务器创建一个逻辑数据库。十分适合于分布式环境或数据集市环境。

  • Cluster/NDB:MySQL的簇式数据库引擎,尤其适合于具有高性能查找要求的应用程序,这类查找需求还要求具有最高的正常工作时间和可用性。

  • Other:其他存储引擎包括CSV(引用由逗号隔开的用作数据库表的文件),Blackhole(用于临时禁止对数据库的应用程序输入),以及Example引擎(可为快速创建定制的插件式存储引擎提供帮助)。

一般来说不使用事务的话,请使用MyISAM引擎(mysql的一个默认数据库mysql就是使用MyISAM引擎的),使用事务的话,一般使用InnoDB

3、MySQL内部的日志类型(重点)

MySQL常用的日志有下面几个

错误日志

查看mysql是否正常启动,默认路径/var/log/mysqld.log

mysql> show variables like '%log_error%';
# mysql配置文件my.cnf配置如下,修改log_error的文件路径,错误级别
log_error=/var/log/mysqld.log
log_warnings=2
# log_warnings= 0| 1| 2 
# 0 关闭
# 1 开启-default
# >1 大于1,失败的连接,拒绝访问的错误也会记录到log_error

查询日志

查询日志会将所有数据库的操作(ddl,dml)都会记录(general log 通用日志),消耗I/O,默认不开

mysql> show variables like '%general_log%';
# mysql配置文件/etc/my.cnf配置
log_output=FILE
FILE、TABLE、FILE,TABLE、NONE(不输出)

慢查询日志

mysql> show variables like '%slow%';

Mysql默认是关闭慢查询日志的,也可以使用druid 作为数据库连接池的同时监控慢查询sql语句

测试慢查询

# 1、mysql配置文件/etc/my.cnf配置慢查询相关参数
[root@alibaba mysql]# vi /etc/my.cnf
slow_query_log=ON  # 开启
slow_launch_time=3  
# slow_query_log_file=/usr/local/mysql/slow.log 慢sql语句日志文件,修改后要授权mysql用户
# 修改/usr/local目录的拥有者是mysql用户
[root@alibaba mysql]# chown -R mysql:mysql /usr/local/mysql/slow.log
# 2、重启mysql
[root@alibaba mysql]# service mysqld restart
Redirecting to /bin/systemctl restart mysqld.service
# 3、tail -f 命令刷新查看慢sql日志文件
# 4、慢查询sql语句
mysql> use icoding_admin;
mysql> select sleep(4),username from ad_user; # 每条记录睡4秒

3、直接查看慢查询日志

[root@alibaba mysql]# tail -f -n100 /var/lib/mysql/alibaba-slow.log 
/usr/sbin/mysqld, Version: 5.7.28-log (MySQL Community Server (GPL)). started with:
Tcp port: 0  Unix socket: /var/lib/mysql/mysql.sock
Time                 Id Command    Argument
# Time: 2020-06-19T08:38:17.726855Z
# User@Host: root[root] @ localhost []  Id:     6
# Query_time: 12.000825  Lock_time: 0.000176 Rows_sent: 3  Rows_examined: 3
use icoding_admin;
SET timestamp=1592555897;
select sleep(4),username from ad_user;

发现查询用了12秒,远远超过4秒,返回了3条记录,3*4=12秒。mysql的默认慢查询时间是10秒

# 查看sql语句慢查询时间
mysql> show variables like 'long_query_time';

mysqldumpslow 慢查询命令

我们也可以使用mysql提供的慢查询命令mysqldumpslow来查看慢sql语句,更方便

# 帮助手册
mysqldumpslow --help
# 根据时间降序
mysqldumpslow -s -t /var/lib/mysql/alibaba-slow.log 
# 根据记录数降序
mysqldumpslow -s -r /var/lib/mysql/alibaba-slow.log 
# 根据执行次数降序
mysqldumpslow -s -c /var/lib/mysql/alibaba-slow.log 

Count:执行了多少次

二进制日志binlog

这个是数据库中最重要的日志,会记录所有DML,不会记录select,只记录数据的修改。trancate table属于ddl语句,不会被binlog记录。

事务日志

中继日志relay log

主从复制模式,从节点一定要配置relay log

4、MySQL数据备份恢复

查询binlog是否开启

 # 默认关闭 
 mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | OFF   |
+---------------+-------+

# 查询mysql当前使用的binlog模式
mysql> show variables like '%binlog_format%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+

# 查询binlog单个文件的最大大小,默认是1g,文件达到1g,创建新的binlog文件
mysql> show variables like '%max_binlog%';
| max_binlog_size      | 1073741824 

# 查看当前正在记录操作的日志log_bin文件名称
mysql > show master status;

# my.cnf开启binlog和binlog文件大小,一般设置500m,根据公司要求数据库情况自己调整
# 开启binlog看下面的两种模式

Binlog的statement模式

statement(v5.6 及以下是statement模式)

纯粹的记录DML的语句,记录sql语句本身,如:

update ad_user set username=’gavin.huang’ where id=1;

delete from ad_user where id=1;

如果你误删了数据,但你有不知道删除数据的内容是什么,你要恢复的话,statement模式下只能通过binlog跑一遍到删除前(你要知道删除的时间),那样你就知道被删除的数据是什么了,当然你不能在生产库上跑statement脚本,要到一个空数据库上跑。

# 新建文件夹binlog
[root@alibaba local]# mkdir binlog
# 给mysql用户授权
[root@alibaba local]# chown -R mysql:mysql /usr/local/binlog/
# 修改mysql配置文件my.cnf,开启statement模式
[root@alibaba local]# vi /etc/my.cnf 
[mysqld]
server-id=213   # mysql5.7版本一定要加server-id,5.6可不加
log_bin=/usr/local/binlog/mysql-bin   # MySQL会自动生成一个mysql-bin-00001.log
binlog_format=statement
# binlog日志切割的大小
max_binlog_size=500m
# binlog过期清理时间,有效期3天
expire_logs_days=3

# 重启mysql
[root@alibaba local]# service mysqld restart

binlog已开启

已创建binlog文件

[root@alibaba binlog]# ls
mysql-bin.000001  mysql-bin.index

修改数据测试

update ad_user set username = 'gavin_wang' where id=2
delete from ad_user  where id =1 

查看binlog日志,发现已经记录了修改的sql语句.

# 查看
mysql> show binlog events in 'mysql-bin.000001';

小结:statement模式只会记录sql语句,不记录数据。

Binlog的row模式

row(v5.7版本默认是row模式)

记录过去的历史值和现在的新值,记录数据的变化

# 修改my.cnf 将statement模式修改为row模式
[root@alibaba binlog]# vi /etc/my.cnf
binlog_format=row
# 重启mysql
[root@alibaba binlog]# service mysqld restart

# 重启后发现多了个mysql-bin.000002文件,因为模式不同
[root@alibaba binlog]# ls
mysql-bin.000001  mysql-bin.000002  mysql-bin.index

# row模式的日志查询, -v 显示更新语句 -vv 显示更新语句+显示字段
mysqlbinlog --base64-output=decode-rows -vv mysql-bin.000002
  • 删除数据测试
delete from ad_user where id =3

通过mysql命令行查看,不明确

通过mysqlbinlog命令查看binlog日志,发现已经记录了被删除的记录

[root@alibaba binlog]# mysqlbinlog --base64-output=decode-rows -vv mysql-bin.000002

  • 修改数据测试
update ad_user set username = 'gavin' where id=2

再查看binlog日志

[root@alibaba binlog]# mysqlbinlog --base64-output=decode-rows -vv mysql-bin.000002

看到被修改的数据,恢复就方便多啦。

  • truancate table测试
truncate table ad_user;

再查看binlog日志

[root@alibaba binlog]# mysqlbinlog --base64-output=decode-rows -vv mysql-bin.000002

怎么快速找到误操作的语句

row模式的定位,根据时间范围定位

mysqlbinlog --base64-output=decode-rows --start-datetime='2020-06-19 22:00' --stop-datetime='2020-06-19 22:05' -vv mysql-bin.000002

定位到了我们刚刚删除的记录

mysqlbinlog也可以查询statement模式的数据,得到时间区间

# 查看帮助命令
mysqlbinlog --help

当前会话的所有操作不记录binlog日志

如果进行大批量的数据操作,这个时候数据库是安全的,不让MySQL记录

mysql> set sql_log_bin=0; #临时关闭binlog

测试

查看binlog日志

[root@alibaba binlog]# mysqlbinlog --base64-output=decode-rows -vv mysql-bin.000002

mixed 混合模式

# 混合模式
statement:95%

数据备份

备份的场景和分析

  • 全量备份
  • 差异备份,全量+周二是完整的,全量+周三是完整的

  • 增量备份,丢了一天的数据就不完整

  • 时间点备份

备份类型

  • 热备:热备是不能通过简单的copy命令执行备份的,在备份的过程中mysql能读能写,比如使用mysqldump备份就是热备。
  • 温备:只能进行读操作,备份的过程中可以进行读操作,不能进行写操作,锁表就可以达到这种效果
  • 冷备:备份的过程中不能读也不能写
  • 物理备份:copy 数据库文件
  • 逻辑备份:把数据导出来的方式来备份数据,如导到另外一个数据库,或者mysqldump

常用备份工具

  • mysqldump
  • Percona提供的xtrabackup,支持热备、温备多种备份方式
# 查看帮助命令
mysqldump --help

# --master-data 0(不记录position) 1(记录position位置) 2(记录position位置并注释该条解析的语句)
# routines 存储过程
# triggers 触发器
# events 事件
# single-transaction 避免导出数据的过程中mysql卡顿 ,在开始备份前会对导出数据内容做镜像再导出,在导出的过程中,独立一个事务是隔离的,保证数据的一致性
# --ignore-table=icoding_admin.ad_user_role --ignore-table=icoding_admin.ad_user  忽略多表
# 基于innodb引擎 ,-p密码 -P端口
# --flush-logs 刷新,生成新的binlog日志
mysqldump -uroot -p123456 -h127.0.0.1 --master-data=2 --routines --triggers --events --single-transaction --databases icoding_admin --ignore-table=icoding_admin.ad_user_role > mydb.sql

已经备份了数据库文件在当前目录下进入mysql命令行

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| icoding_admin      |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

# 删除数据库
mysql> drop database icoding_admin;
Query OK, 3 rows affected (0.04 sec)

# 查看
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

# 恢复数据库
mysql> source mydb.sql;

# 查看数据库,icoding_admin又回来啦
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| icoding_admin      |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

为什么一定要加–single-transaction

场景:有一个电商系统,小明有200积分,我们24 :00 点备份,假如积分表有200w数据,数据库有300张表,积分表是比较靠后才导出的(mysqldump是按表名字母顺序排序导出的 ),因为是热备所以能写能读,如果在导出的过程中,小明买了东西积分被提升为400,没有加–single-transaction 开启一个事务来隔离的话,那么24点备份的数据中小明的积分就变为400,而不是200,很明显这是错误的,因为积分400是24点后的数据,不是24点的数据。这就会造成备份的业务数据在某个时间点是不准确的。

如果表的数据引擎是MyISAM不支持事务怎么办

添加 –lock-tables 锁表把所有表锁了,能读不让写就可以了(温备),保证数据的一致性。

mysqldump -uroot -p123456 -h127.0.0.1 --master-data=2 --routines --triggers --events --lock-tables --databases icoding_admin --ignore-table=icoding_admin.ad_user_role > mydb.sql

crontab定时任务

cron介绍

我们经常使用的是crontab命令是cron table的简写,它是cron的配置文件,也可以叫它作业列表,我们可以在以下文件夹内找到相关配置文件。

  • /var/spool/cron/ 目录下存放的是每个用户包括root的crontab任务,每个任务以创建者的名字命名
  • /etc/crontab 这个文件负责调度各种管理和维护任务。
  • /etc/cron.d/ 这个目录用来存放任何要执行的crontab文件或脚本。
  • 我们还可以把脚本放在/etc/cron.hourly、/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthly目录中,让它每小时/天/星期、月执行一次。

crontab的使用

我们常用的命令如下:

[root@alibaba binlog]# crontab --help
crontab [-u username]    //省略用户表表示操作当前用户的crontab
    -e      (编辑工作表)
    -l      (列出工作表里的命令)
    -r      (删除工作表)

我们用crontab -e进入当前用户的工作表编辑,是常见的vim界面。每行是一条命令。

crontab的命令构成为 时间+动作,其时间有分、时、日、月、周五种,操作符有

  • ***** 取值范围内的所有数字
  • / 每过多少个数字
  • - 从X到Z
  • 散列数字

实例

# 每1分钟执行一次myCommand
* * * * * myCommand

# 每小时的第3和第15分钟执行
3,15 * * * * myCommand

# 在上午8点到11点的第3和第15分钟执行
3,15 8-11 * * * myCommand

# 每隔两天的上午8点到11点的第3和第15分钟执行
3,15 8-11 */2  *  * myCommand

# 每周一上午8点到11点的第3和第15分钟执行
3,15 8-11 * * 1 myCommand

# 每晚的21:30重启smb
30 21 * * * /etc/init.d/smb restart

# 每月1、10、22日的4 : 45重启smb
45 4 1,10,22 * * /etc/init.d/smb restart

# 每周六、周日的1 : 10重启smb
10 1 * * 6,0 /etc/init.d/smb restart

# 每天18 : 00至23 : 00之间每隔30分钟重启smb
0,30 18-23 * * * /etc/init.d/smb restart

# 每星期六的晚上11 : 00 pm重启smb
0 23 * * 6 /etc/init.d/smb restart

# 每一小时重启smb
* */1 * * * /etc/init.d/smb restart

# 晚上11点到早上7点之间,每隔一小时重启smb
* 23-7/1 * * * /etc/init.d/smb restart

数据恢复

备份如何设计

  • 全量备份的方式

    使用crontab结合mysqldump来做定时备份

  • 增量时间点补偿

    • 如何补偿
    • 考虑修改的变化:update、delete
    • 借助我们的binlog
# 1、先做全量备份,可以定时处理
# 编写备份的脚本
[root@alibaba binlog]# vim icoding_admin_bak.sh
backupdir=/usr/local/binlog/bak
time=` date +%Y%m%d%H%M `
# --master-data=2 记录position位置
mysqldump -uroot -p123456 -h127.0.0.1 --master-data=2 --single-transaction --databases icoding_admin  > $backupdir/icoding_admin_bak_$time.sql
# 删除过期的备份,这里是超过6天
find $backupdir -name "icoding_admin_bak_*.sql" -type f -mtime +6 -exec rm {} \; > /dev/null 2>&1

# crontab添加定时执行脚本的任务
[root@alibaba binlog]# crontab -e
13 14 * * * sh /usr/local/binlog/icoding_admin_bak.sh

看到脚本已经定时备份了

binlog日志我已开启row模式,备份完后修改数据

 # 添加一条数据
 insert into ad_user VALUES(4,'allen','1234567')

删除数据库,模拟恢复数据

mysql> drop database icoding_admin;  
Query OK, 3 rows affected (0.03 sec)

我们的全量备份并没有记录新增的allen老师的记录,所以要结合binlog一起恢复数据

查看全量备份文件,发现它备份的数据库记录位置是 6246,对应binlog的

[root@alibaba bak]# cat icoding_admin_bak_202006211413.sql 

查看binlog ,我们应该从6246的下一个位置6311到6542作为增量恢复的数据,恢复要看增量的数据大小

mysql> show binlog events in 'mysql-bin.000002';

# 2、开启binlong,statement/row/mixed三种模式,默认使用row模式
# 全量恢复数据,也可考虑是否binlog
mysql> source icoding_admin_bak_202006211413.sql.sql
# 对binlog做数据导出
# 场景1:如果数据量小,比如关键的某一条数据,row模式下可以直接手工处理,row模式可以看被修改删除的数据,直接自己拿出来重新写回
mysqlbinlog --base64-output=decode-rows --start-position=6311 --stop-position=6542  -vv mysql-bin.000002

# 场景2:数据量特别大,需要导出进行处理
mysqlbinlog --start-position=6311 --stop-position=6542 mysql-bin.000002 > ist.sql

# 3、增量数据的恢复根据需要把当前会话binlog记录关掉,避免记录没必要的操作,提高恢复效率
mysql> set sql_log_bin=0;
mysql> source ist.sql

好了,现在知道了增量恢复的位置,使用全量备份+增量备份恢复数据

# 导出增量备份的数据
[root@alibaba binlog]# mysqlbinlog --start-position=6311 --stop-position=6542 mysql-bin.000002 > bak/ist.sql
[root@alibaba binlog]# cd bak
[root@alibaba bak]# ls
icoding_admin_bak_202006211413.sql  ist.sql
# 进入mysql命令行模式恢复数据
mysql> source icoding_admin_bak_202006211413.sql
# 全量备份恢复完毕查看数据 
mysql> use icoding_admin;
Database changed
mysql> select * from ad_user;
+----+------------+----------+
| id | username   | password |
+----+------------+----------+
|  1 | arry       | 1234567  |
|  2 | gavin      | 1234567  |
|  3 | coding_qin | 1234567  |
+----+------------+----------+
# 增量恢复,不记录binlog
mysql> set sql_log_bin=0;
mysql> source ist.sql
# 增量备份恢复完毕查看数据,allen老师也恢复了
mysql> select * from ad_user;
+----+------------+----------+
| id | username   | password |
+----+------------+----------+
|  1 | arry       | 1234567  |
|  2 | gavin      | 1234567  |
|  3 | coding_qin | 1234567  |
|  4 | allen      | 1234567  |
+----+------------+----------+
# 恢复完成
mysql> exit;
Bye

全量恢复的时候,当前会话是并没有关闭binlog的,也就是说恢复的操作也会被binlog记录(整体来说记录了重复的dml,ddl操作),所以可以考虑恢复前把当前会话的binlog先关闭。

可以看出,binlog的数据库位置已发生变化。

总结:数据一定要定时全量备份,开启binlog

作业:全量备份+增量备份

参考博客:https://www.jianshu.com/p/d3f77f7da512

全量备份

步骤流程:

  • 先创建相应目录
  • 使用mysqldump进行全量备份
  • 对备份文件进行压缩
  • 使用scp复制备份文件到其他服务器
  • 清理过期的全量备份文件
  • 定时任务自动进行全量备份

备份命令

# --quick : 该选项在导出大表时很有用,它强制 mysqldump 从服务器查询取得记录直接输出而不是取得所有记录后将它们缓存到内存中。
# --all-databases 备份所有数据库
# --databases 指定备份的数据库
# --flush-logs 生成新的二进制日志
# --delete-master-logs 
# --single-transaction 单事务,为了确保数据的一致性,为了确保使用--single-transaction命令时,保证dump文件的有效性。需没有下列语句ALTER TABLE, CREATE TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE,因为一致性读不能隔离上述语句。所以如果在dump过程中,使用上述语句,可能会导致dump出来的文件数据不一致或者不可用。
mysqldump -uroot -p123456 --quick --events --all-databases --flush-logs --delete-master-logs --single-transaction > data.sql

全量备份脚本

mysqlFullBack.sh

# 初始化时,创建相应目录
# mkdir -p /home/mysql/backup/daily 

# 定时任务
# 每个星期日凌晨3:00执行全量备份脚本 
# 0 3 * * 0 /bin/bash -x /root/mysqlFullBack.sh >/dev/null 2>&1
# 周一到周六凌晨3:00执行增量备份脚本
# 0 3 * * 1-6 /bin/bash -x /root/mysqlPartBack.sh >/dev/null 2>&1

BakDir=/home/mysql/backup
LogFile=/home/mysql/backup/bak.log
Date=`date +%Y%m%d`
Begin=`date +"%Y年%m月%d日 %H:%M:%S"`
cd $BakDir
DumpFile=$Date.sql
GZDumpFile=$Date.sql.tgz
mysqldump -uroot -p123456 --quick --events --all-databases --flush-logs --delete-master-logs --single-transaction > $DumpFile
/bin/tar -zvcf $GZDumpFile $DumpFile
/bin/rm $DumpFile
Last=`date +"%Y年%m月%d日 %H:%M:%S"`
echo [FullBack] 开始:$Begin 结束:$Last $GZDumpFile successful >> $LogFile
# scp重复全量备份文件到其他服务器
# scp $GZDumpFile root@xxxx:/usr/mysql/backup/$GZDumpFile
# 删除30天前的全量备份文件
find $BakDir -mtime +30 -type f -name "*.sql.tgz" | xargs rm -f
# 删除增量备份文件
cd $BakDir/daily
/bin/rm -f *

恢复全量备份数据

mysql > source /home/data.sql;

增量备份

步骤流程

  • 刷新日志
  • 遍历日志索引文件
  • 备份新日志文件,忽略已经备份的文件

增量备份命令

# 在/var/lib/mysql下或/var/lib/mysql/mysql-bin下可查看到增量备份日志
mysqladmin -uroot -p123456 flush-logs

增量备份脚本

mysqlPartBack.sh

#!/bin/bash
#在使用之前,请提前创建以下各个目录
backupDir=/home/mysql/backup/daily
#增量备份时复制mysql-bin.00000*的目标目录,提前手动创建这个目录
mysqlDir=/var/lib/mysql
#mysql的数据目录
logFile=/home/mysql/backup/bak.log
BinFile=/var/lib/mysql/binlog.index
#mysql的index文件路径,放在数据目录下的

mysqladmin -uroot -p123456 flush-logs
#这个是用于产生新的mysql-bin.00000*文件
# wc -l 统计行数
# awk 简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
Counter=`wc -l $BinFile |awk '{print $1}'`
NextNum=0
#这个for循环用于比对$Counter,$NextNum这两个值来确定文件是不是存在或最新的
for file in `cat $BinFile`
do
    base=`basename $file`
    echo $base
    #basename用于截取mysql-bin.00000*文件名,去掉./mysql-bin.000005前面的./
    NextNum=`expr $NextNum + 1`
    if [ $NextNum -eq $Counter ]
    then
        echo $base skip! >> $logFile
    else
        dest=$backupDir/$base
        if(test -e $dest)
        #test -e用于检测目标文件是否存在,存在就写exist!到$logFile去
        then
            echo $base exist! >> $logFile
        else
            cp $mysqlDir/$base $backupDir
            echo $base copying >> $logFile
         fi
     fi
done
echo [PartBack] `date +"%Y年%m月%d日 %H:%M:%S"` $Next Bakup successful! >> $logFile

恢复增量备份数据

mysqlbinlog /home/mysql/backup/daily/binlog.000008 | mysql -uroot -p123456;

Crontab定时任务

每个星期日凌晨3:00执行全量备份脚本

0 3 * * 0 /bin/bash -x /root/mysqlFullBack.sh >/dev/null 2>&1

周一到周六凌晨3:00执行增量备份脚本

0 3 * * 1-6 /bin/bash -x /root/mysqlPartBack.sh >/dev/null 2>&1

常见问题

当通过linux定时任务crontab去执行备份脚本时,你会发现全量备份后的sql文件是个空文件。然而,你手动执行命令却是正常的。

原因:mysqldump命令在crontab执行环境中并不存在,要使用绝对路径,才能执行此命令。 查看mysqldump所在位置

[root@alibaba ~]# which mysqldump
/usr/bin/mysqldump

然后把全量备份脚本中的mysqldump替换为/usr/bin/mysqldump

Post Directory