你们好,我是Taoye,试图用玩世不恭过的态度对待生活的Coder。css
现现在咱们已然进入了大数据时代,不管是业内仍是业外的朋友,相信都有据说过数据库这个名词。数据是一个项目的精华,也扮演着为企业创造价值的重要角色,一个较为完善的公司通常都会有专门的DBA来管理数据库,以便更好的为用户服务。html
互联网的发展速度之快,以至大量的APP应用涌入用户的视野,在大多数APP中都会有“推荐”这一板块,而这个板块功能的核心正是基于用户以往的数据记录而实现的。再如《死亡笔记》中L·Lawliet这一角色所提到的大数定律,在众多繁杂的数据中必然存在着某种规律,偶然中必然包含着某种必然的发生。无论是咱们提到的大数定律,仍是最近火热的大数据亦或其余领域都离不开大量数据的支持。mysql
如上,咱们可初步体会到数据的重要性,而要想更好的管理数据,则避免不了与数据库打交道。对于数据库而言,操做数据库的用户就至关于一位老板,咱们须要向数据库发出命令,而后指望数据库给咱们返回想要的结果。web
咱们能够想象一下这么一个场景,老板给Taoye发布了这么一个任务:“Taoye啊,你做为一位专业的板砖工,我如今须要你在一小时的以内将工地的转搬回来。”是的,老板发布任务只注重结果和效率,而不在乎你板砖的过程。咱们在执行SQL语句的时候也是如此,通常只关心执行的结果和效率是否知足用户的需求。sql
最近,Taoye从新把以前学习数据库时候所记录的笔记复习了一下,而后又系统性的拜读了丁奇大大的《MySQL实战45讲》中的内容,因此想要把MySQL系列的知识内容单独整理出来,在进行自我提升的同时,也但愿能给予你们一点帮助。shell
咱们要想系统性的学习MySQL数据库,首先不得不了解MySQL的体系结构。在MySQL中,主要是由什么功能模块组成?每个功能模块在SQL语句执行的时候分别扮演了一个什么样的角色?MySQL的强大之处在哪,竟会受到如此之多的开发者的青睐?这些都是咱们每一位学习MySQL的朋友必须了解甚至掌握的内容,如下即是MySQL数据库的体系结构及其执行流程:数据库
从上图,咱们能够看出MySQL总体上主要分为了Server层和存储引擎层两个部分。缓存
在Server层内部又包括链接器、缓存、分析器、优化器、执行器等功能部件,主要负责了MySQL的大多数核心服务功能,好比存储过程、函数、触发器、视图等。安全
而存储引擎层主要的是负责数据的存储和提取,在MySQL中可支持MyISAM、InnoDB、Memory等多种存储引擎,其中最多见的是InnoDB和MyISAM,这也是咱们在学习存储引擎时候的一个重点。在MySQL 5.5.5版本以前默认使用的是MyISAM,而在此版本以后默认采用的是InnoDB存储引擎。咱们在实际建立数据表的时候,也能够经过ENGINE
来指定使用的存储引擎,以下所示。咱们能够对tb_comment
数据表指定使用MyISAM存储引擎:ruby
1CREATE TABLE `tb_comment` (
2 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
3 `content` varchar(255) DEFAULT NULL,
4 `user_name` varchar(255) DEFAULT NULL,
5 `openid` varchar(255) DEFAULT NULL,
6 `comment_time` int(11) DEFAULT NULL
7 PRIMARY KEY (`id`) USING BTREE
8) ENGINE=MyISAM AUTO_INCREMENT=175 DEFAULT CHARSET=utf8;
MyISAM和InnoDB这两种存储引擎最主要的区别是事务以及锁机制:
关于MySQL的事务和锁机制,这里只是简单的提一下,具体的细节咱们后面聊。下面咱们将MySQL的体系结构中每个功能模块单独的分离开来,依次看看每个功能模块所体现出的做用。
咱们要想正常的操做数据库,首先须要通过链接器这道大门。在正式引出链接器以前,各位看官不妨来看看下面一个例子:
金主大大整天担忧本身财产的安全,必然会将本身的money存储在银行金库中。某一天金主大大要想取出一部分的财产来维持公司的运营,而银行为了保障金库中money的安全性,金主必然要进行身份核验以及一系列防盗系统的检测。
在MySQL数据库中,链接器的做用其实就相似于上面的身份校验以及防盗系统,主要是负责与客户端创建链接、权限校验、维持和管理链接。咱们要想操做数据库,首先须要经过帐号、密码等信息来链接数据库,假如咱们想要以root帐户、密码为666666来链接192.168.31.100:3307的MySQL服务,则能够执行如下命令:
1mysql -h 192.168.31.100 -P 3307 -u root -p 666666
2
3# 若是是本地链接,则执行以下,密码也可回车后输入
4mysql -u root -p 666666
当数据匹配成功时,链接器就能容许用户与数据库创建链接。此外,链接器还须要验证该用户是否有权限对数据表进行操做,这个时候链接器会去权限表中查询链接用户的权限,只有在具备操做权限的前提下才能操做数据表。若是咱们在与数据库已经创建链接的前提下,可是不对数据库进行任何操做,这个时候链接就会处于空闲状态,咱们能够经过show processlist
命令来查看已经创建的链接数和处于空闲状态的链接,其中command字段为sleep表示链接空闲:
1mysql> show processlist;
2+----+------+-----------------+------+---------+------+----------+------------------+
3| Id | User | Host | db | Command | Time | State | Info |
4+----+------+-----------------+------+---------+------+----------+------------------+
5| 3 | root | localhost:53372 | NULL | Sleep | 28 | | NULL |
6| 4 | root | localhost:53378 | NULL | Query | 0 | starting | show processlist |
7+----+------+-----------------+------+---------+------+----------+------------------+
8
若是链接长期处于空闲状态而不作任何操做,当超过必定时间时,就会自动断开链接,而这个时间阈值主要是经过wait_timeout
属性来决定的,默认是28800,即8小时。show variables like '%wait_timeout%'"
可查看时间阈值,set @@session.wait_timeout=xxx
可修改当前会话下的时间阈值,具体操做以下:
1mysql> set @@session.wait_timeout=30000;
2Query OK, 0 rows affected (0.00 sec)
3
4mysql> show variables like "%wait_timeout%";
5+--------------------------+----------+
6| Variable_name | Value |
7+--------------------------+----------+
8| innodb_lock_wait_timeout | 50 |
9| lock_wait_timeout | 31536000 |
10| wait_timeout | 30000 |
11+--------------------------+----------+
123 rows in set, 1 warning (0.00 sec)
缓存这个概念,学习过《计算机组成原理》或是其余相关课程的朋友应该并不陌生,缓存通常使用的是SRAM(静态随机存储器)技术实现的,相较于DRAM(动态随机存储器)而言,它最主要的优点在于速度快,可以大大提升数据的查询效率。
关于缓存,咱们能够来作一个简答的计算题:
假设查询一次缓存须要1s,查询一次内存须要10s,缓存命中的几率为90%,一位用户想要查询100次,则使用缓存和不使用缓存的平均查询时间是多少?
可见,根据局部性原理,缓存的存在是能够大大提升数据的查询效率的。
由上方的执行流程图,咱们也能够知道,客户端经过链接器与MySQL服务器创建链接以后,这个时候就会来到缓存中查询用户所需的数据。假设用户向MySQL发出如下一条查询语句:
1mysql> select * from tb_comment where id=1;
MySQL收到用户发出的请求以后,就会先到缓存中看看以前是否有执行过相同的语句,并且以前执行的结果会以key-value键值对的形式直接进行存储。假如以前有执行过这条命令,则直接从缓存中取出数据并反馈给用户,若是没有执行过,则会继续走 分析器 -> 优化器 -> 执行器 -> 存储引擎这条链路。因此说,缓存的命中能够省去后面一系列操做所消耗的时间,这也是缓存提升查询效率的缘由。
按道理来说,缓存的引入应该是至关不错的,并且用户查询次数越多就越能体现缓存的强大,但为何在MySQL 8.0版本以后直接舍去了缓存部件呢?
在理想状况下,缓存引入确实是很是的完美,可是在数据库中,咱们除了查询操做以外,还有更新操做(增、删、改)。每当咱们执行过一次更新操做的时候,数据库中的数据就已然发生了改变,而当咱们再次发出命令从缓存中取出的数据就再也不是用户所指望的数据了。因此对MySQL而言,每当用户进行更新操做时,都会清空一次缓存,而后再次从新缓存新的数据,而这个过程给MySQL带来的压力是很大的,也大大削弱了SQL语句的执行效率。因此说,缓存对于一些查询多,更新少的数据表比较有用,而对那些更新比较频繁的数据表就会拔苗助长。
若是使用的是MySQL 8.0如下的版本,咱们能够根据实际需求来肯定是否开启缓存,主要是经过my.cnf
配置文件中的query_cache_type
来决定的,0表明禁用缓存,1表明开启缓存,2表明根据须要使用缓存。在执行SQL语句的时候能够附带SQL_CACHE和SQL_NO_CACHE
来肯定执行时是否使用缓存,而且能够经过show status like '%qcache%'
命令来查看缓存的总体状况:
1mysql> select SQL_CACHE * from tb_comment where id=1;
2mysql> show status like "%qcache%";
3+-------------------------+---------+
4| Variable_name | Value |
5+-------------------------+---------+
6| Qcache_free_blocks | 1 |
7| Qcache_free_memory | 1031872 |
8| Qcache_hits | 0 |
9| Qcache_inserts | 0 |
10| Qcache_lowmem_prunes | 0 |
11| Qcache_not_cached | 10 |
12| Qcache_queries_in_cache | 0 |
13| Qcache_total_blocks | 1 |
14+-------------------------+---------+
15
若是在执行查询SQL时没有命中缓存,则须要去数据表中查询用户所需的数据了,也就是开始进入分析器来对用户发出的SQL语句进行分析。
分析的时候主要是包括预处理和解析的过程,在这个阶段会解析SQL语句的语义,并对语句中的一些关键字和非关键字进行提取、解析,并组成一个解析树交给后面的执行器执行。具体的关键词包括但不限定于select/update/delete/or/in/where/group by/having/count/limit等,若是分析到语法或关键词错误,会直接给客户端抛出异常:ERROR:You have an error in your SQL syntax.
好比用户执行:select * from tb_comment where user_id=1
,在分析器中就会经过语义规则器将select、from、where
等提取和匹配出来,而后进行分析并校验,假如在校验的时候发现tb_comment
中并不存在user_id
字段,则会报错:Unknown column 'user_id' in 'where clause
1mysql> select * from tb_comment where user_id=1;
2ERROR 1054 (42S22): Unknown column 'user_id' in 'where clause'
分析器对用户的SQL语义进行分析以后,则说明SQL语句自己是没有任何问题,而且是能够被正常执行的。此时,MySQL已经知道了用户的意图,用户是想查询仍是更新,MySQL都心知肚明。
优化器主要是对SQL进行优化,会根据执行计划进行最优的选择来匹配合适的索引,并选择最佳的执行方案。在丁奇《MySQL实战45讲》中提到,一个语句有多表关联(join)的时候,会决定各个表的链接顺序。好比你执行下面这样的语句,这个语句是执行两个表的join:
1mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
这两种执行方式的逻辑结果是同样的,可是执行的效率会有不一样,而优化器的做用就是决定选择使用较佳的方案来执行。为了更好的理解丁奇大大的例子,咱们能够对其进行类别:咱们要想在中国上海找到年龄在20-30岁之间的人。第一种方案是先锁定上海,而后再查询年龄;而第二种是先在全国查询年龄区间在20-30的人,而后再锁定上海。至于哪一种方案较优,相信各位看官都明白吧。
固然了,优化器对SQL进行优化的地方不只仅局限于以上场景。在咱们建立联合索引,并对数据表进行查询的时候,优化器一样可能会对其进行优化。且看下面一个例子:
1mysql> create index oid_ctime_pid on tb_comment(openid, comment_time, problem_id);
2mysql> explain select * from tb_comment where openid="1" and problem_id=401 and comment_time=3;
3+----+-------------+------------+------+---------------+---------------+---------+-------------------+------+-----------------------+
4| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
5+----+-------------+------------+------+---------------+---------------+---------+-------------------+------+-----------------------+
6| 1 | SIMPLE | tb_comment | ref | oid_ctime_pid | oid_ctime_pid | 778 | const,const,const | 1 | Using index condition |
7+----+-------------+------------+------+---------------+---------------+---------+-------------------+------+-----------------------+
在上面,咱们首先根据tb_comment数据表中的openid、comment_time、problem_id创建一个组合索引oid_ctime_pid
,而后根据索引字段进行查询。然而在查询的时候,咱们有意地将筛选的字段顺序打乱,与索引字段的建立顺序不一致。(索引建立字段顺序:openid、comment_time、problem_id,查询条件字段顺序:openid、problem_id、comment_time)可是咱们采用explain
执行计划检索的时候发现key
属性为oid_ctime_pid
,换句话讲,即便咱们不遵循最左匹配原则,此时依然是走了oid_ctime_pid
组合索引的。之因此可以达到这样的效果,正式由于优化器的存在,在其内部对查询条件的顺序进行了优化,从而利用索引提升了数据的查询效率。
注意:以上内容提早涉及到了数据库当中索引的概念,索引是很是很是重要的,咱们往后会单独的详细讲讲索引。另外,还有一点值得注意的是,虽说优化器在必定程度上可以优化SQL,可是这种优化程度仅仅是MySQL所认为的一个较佳状态,而不必定可以知足DBA所须要的标准。
OK,MySQL已经对用户发出的SQL语句进行了分析,也进行了优化,如今就要进入执行阶段来执行SQL语句了。
执行器在对SQL语句进行执行的时候,会根据表的引擎定义,去使用这个引擎所提供的接口,这些接口都是引擎中已经定义好的,执行器直接经过“拿来主义”进行调用便可。引擎还有一个名字叫作“表处理器”,这个名字应该说更能体现出它所存在的意义。
咱们一样以一条简单的SQL语句来讲明下这个执行过程:
1mysql > select * from tb_comment where openid=123;
假设这张表采用的是InnoDB存储引擎,则首先InnoDB存储引擎接口会去查询的表数据中的第一行,对该行数据中的openid
字段所对应的值进行判断,假如该值为123,则将这行数据加入到结果集当中,不然引擎接口继续查询下一行,如此不断循环重复相同的判断逻辑,直至对数据表中最后一行的数据判断结束为止。最后,将结果集当中的全部知足条件的数据反馈给用户。
以上即是执行器在查询操做时候的大致功能,可是在进行更新操做(增、删、改)的时候,执行器还会将具体的操做记录到binlog日志当中,另外,update会采起两阶段的提交方式,记录到redolog中,也就是咱们接下来所要讲的MySQL日志系统。
日志日志,在生活中,顾名思义就是记录生活的点点滴滴,把本身的心里世界记录下来,更好的诠释本身当时写日志时候的心情感觉。待到未来的某一天,咱们回翻以前所写的日志时,咱们可以瞬间回忆起本身身边所发生的事以及心里世界。
同理,在计算机领域里,日志也是一个至关重要的概念。而MySQL的日志系统会将咱们对数据库的修改操做所有自动记录下来,这样就可以经过日志文件随时对误操做的数据进行恢复。说道这里,有了解过Git或是读过Taoye以前写的Git文章的朋友,应该会有点联想,在Git中,咱们能够在任意时间点对项目的版本进行恢复(回退),虽然结果与MySQL的日志系统有点相似,可是它们的实现原理是不同的。关于Git,想了解的读者能够暂且跳转学习:哪些年,咱们玩过的Git
redo log(重作日志)和binlog(归档日志)是MySQL当中很是重要的两种日志,尤为是binlog,在咱们以后进行数据恢复或是搭建主从的时候会频繁的接触到。
关于对redo log和binlog的理解,丁奇在《MySQL实战45讲》中已经讲解的很是明白了,其中以一种通俗易懂的方式来介绍了晦涩难懂的redo log和binlog。说实话,我记得第一次学习这两种日志的时候,老是对某些小的知识点细节理解的不够透彻,甚至会出现理解误差的状况,好在后来经过不断重复阅读和查询资料的形式一个个解决了当时的疑问。关于对redo log和binlog基本概念的理解,这里就再也不多说了,你们能够去看看《MySQL实战45讲》中该部分的内容,或是阅读:http://www.javashuo.com/article/p-zqydszwc-bs.html
下面主要是介绍下binlog二进制日志的常见操做,及如何经过binlog搭建主从和进行数据的恢复:
在MySQL中,binlog日志默认是处于关闭的状态,咱们要想开启并设置二进制日志,就须要修改MySQL的配置文件,即my.cnf
,配置完成以后再重启mysql便可生效systemctl restart mysql
:
1log-bin = /www/server/data/mysql-bin # binlog日志的存放路径
2expire_logs_days = 7 # 日志文件的过时时间,过时以后会自动删除,0表示不删除
3binlog_format = mixed # 日志的存储格式
4max_binlog_size = 100M # 每一个binlog日志文件的大小
该属性的值总共有三种:statement、row和mixed,当将日志格式配置为statement的时候,表示每一次修改数据的SQL语句自己都会记录到binlog日志当中,而row并不是记录SQL语句,而是记录被修改的那行数据。mixed格式则是前两总形式的混合,通常对库或表结构发生修改采用的是statement,而数据自己发生修改采用row格式。
以上关于binlog日志的配置只是一部分,其余的配置咱们用到的时候再来补充。当该部分配置完成以后,咱们就能够经过show variables like
命令来查看二进制文件的设置:
1mysql> show variables like "log_bin%";
2+---------------------------------+----------------------------------+
3| Variable_name | Value |
4+---------------------------------+----------------------------------+
5| log_bin | ON |
6| log_bin_basename | /www/server/data/mysql-bin |
7| log_bin_index | /www/server/data/mysql-bin.index |
8| log_bin_trust_function_creators | OFF |
9| log_bin_use_v1_row_events | OFF |
10+---------------------------------+----------------------------------+
查看目前所存在的全部二进制日志,以及当前正在使用的二进制日志:
1mysql> show master logs;
2+------------------+-----------+
3| Log_name | File_size |
4+------------------+-----------+
5| mysql-bin.000022 | 120 |
6| mysql-bin.000023 | 45529 |
7| mysql-bin.000024 | 24284 |
8+------------------+-----------+
9
10mysql> show master status;
11+------------------+----------+--------------+------------------+-------------------+
12| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
13+------------------+----------+--------------+------------------+-------------------+
14| mysql-bin.000028 | 120 | | | |
15+------------------+----------+--------------+------------------+-------------------+
每当咱们重启MySQL服务的时候,都会自动建立一个新的binglog二进制日志文件,而且还会生成mysql-bin.index文件,该文件存储全部二进制日志文件的清单,也就是二进制日志文件的索引。在上面,咱们配置在my.cnf
中配置binlog的时候,设置了expire_logs_days=7,也就是说超过7天的日志文件会被自动删除。可是若是咱们不对binlog日志文件进行处理的时候,大量的binlog文件会占据太多的磁盘空间,从而在必定程度上影响了磁盘的IO性能,因此按期清理binlog日志是颇有必要的。
咱们既能够清除全部的binlog日志文件,也能够对指定的日志文件进行清除,部分具体操做以下:
1# 清除mysql-bin.000023以前的全部日志文件
2mysql> purge master logs to "mysql-bin.000023";
3# 清除指定时间以前的日志文件
4mysql> purge master logs before "2020-06-12 12:12:12";
5# 清除全部的日志文件
6mysql> reset master;
7mysql> show master logs;
8+------------------+-----------+
9| Log_name | File_size |
10+------------------+-----------+
11| mysql-bin.000001 | 120 |
12+------------------+-----------+
咱们在执行reset master
命令的时候,会清除全部的binlog日志,而且会自动从新建立新的二进制日志文件,其其编号为000001开始。
此外,也能够经过flush logs
命令实现日志文件的滚动,即会生成的一个新的日志文件做为当前的记录日志,以后对数据库的修改操做都会记录在该日志文件当中:
1mysql> flush logs;
2Query OK, 0 rows affected (0.04 sec)
3
4mysql> show master logs;
5+------------------+-----------+
6| Log_name | File_size |
7+------------------+-----------+
8| mysql-bin.000001 | 167 |
9| mysql-bin.000002 | 120 |
10+------------------+-----------+
11
前面有提到,binlog中有statement、row和mixed三种存储格式,它们的存储内容都是二进制的形式,咱们是没法经过常规的方式浏览其内容的,通常能够以命令的形式或是工具来查看并分析二进制日志文件的内容:
1mysql> create database db_test;
2# 查看日志文件所记录的事件
3mysql> show binlog events in "mysql-bin.000002";
4+------------------+-----+-------------+-----------+-------------+---------------------------------------+
5| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
6+------------------+-----+-------------+-----------+-------------+---------------------------------------+
7| mysql-bin.000002 | 4 | Format_desc | 1 | 120 | Server ver: 5.6.47-log, Binlog ver: 4 |
8| mysql-bin.000002 | 120 | Query | 1 | 223 | create database db_test |
9+------------------+-----+-------------+-----------+-------------+---------------------------------------+
10
除了采用show binlog events
命令以外,咱们还可使用mysqlbinlog
来读取日志文件或转化为本身想要输出的文件形式,这样便于在误操做的状况下对日志文件进行分析。这里须要注意的一点是,在使用mysqlbinlog
工具的时候,通常要进入到其所在目录,默认存在于MySQL的bin目录当中,而且在指定具体解析的日志文件时,一样要定位文件所在位置。
1# 直接查看日志文件的内容
2$ ./mysqlbinlog --no-defaults ../../data/mysql-bin.000002
3
4# 将binlog日志文件内容以txt或log文件输出
5$ ./mysqlbinlog ../../data/mysql-bin.000002 > ./my-log.log
6
7# 将binlog日志文件转化为sql文件
8$ ./mysqlbinlog --base64-output=decode-rows -v ../../data/mysql-bin.000002 > ./my-log.sql
下面咱们从0开始建库、建表,并实现数据的增删改操做,来详细分析一下日志文件中所记录的信息:
1# 日志滚动、建库db_test、建表tb_test
2mysql> flush logs;
3mysql> create database db_test;
4mysql> use db_test;
5mysql> create table tb_test(id int primary key, age int, username varchar(25));
6
7# 对数据表执行增删改操做
8mysql> insert into tb_test values(1, 1, "me");
9mysql> update tb_test set username="you" where id=1;
10mysql> delete from tb_test where id=1;
1$ ./mysqlbinlog --no-defaults ../../data/mysql-bin.000003
2$ ./mysqlbinlog ../../data/mysql-bin.000003 > ./my-log.log
3$ ./mysqlbinlog --base64-output=decode-rows -v ../../data/mysql-bin.000003 > ./my-log.sql
下图是Taoye使用Sublime text打开my-log.sql
文件时所展示的内容(部份内容略过),从图中能够看到,binlog日志当中已经记录了咱们对数据库所作出的全部操做,包括建库、建表、增删改,而且该日志文件是存储在磁盘当中,换句话说,及时咱们的MySQL服务宕机了,可是重启以后依然能够根据该文件进行数据的恢复。
在上面,已经介绍了在实际使用过程当中binlog日志的常见操做,其中包括binlog日志的配置、查看、滚动、清除和分析等,接下来就是利用binlog日志实现数据的恢复了。
在一个伸手不见五指的黑夜,Taoye的同事Yetao坐在办公桌前,左手捧着一杯Java(咖啡),右手提着一块板砖。白天不断隐忍了产品经理的折磨以后,此时的他心情很是的愤懑,最终它作出了一个明智的选择:“删库,跑路!”
次日,产品经理发现数据库已然被Yetao这臭小子清空了,因而把心中的那把火所有洒在Taoye身上,而且命令Taoye在一天以内恢复数据库中全部的数据,不然滚蛋。
Shit,Yetao这臭小子竟然作了这么愚蠢的骚操做,竟然还让我背锅、擦屁股。好在个人binlog日志玩的贼溜,不然还真的得滚蛋。下面咱们来看看在实际的过程当中,假如出现了数据的误操做,咱们该如何利用binlog二进制日志文件对数据进行恢复?
1# 日志滚动、建库db_test、建表tb_test
2mysql> flush logs;
3mysql> create if not exists database db_test;
4mysql> use db_test;
5mysql> create table if not exists tb_test(id int primary key, age int, username varchar(25));
6
7mysql> insert into tb_test values(1, 1, "taoye");
8mysql> insert into tb_test values(2, 2, "yetao");
9mysql> delete from tb_test where id=1;
首先,咱们须要经过以上一些SQL语句来自定一个场景,以便咱们实现数据的恢复:flush logs;
进行日志文件滚动,使得以后对数据库的操做都能记录在一个新的binlog日志文件当中。以后建立db_test数据库以及tb_test数据表,并在表中的插入两条数据,最后删除id=1所对应的数据。
假如咱们最后删除的那一条数据是一个误操做,咱们应当如何经过binlog日志文件来恢复该条数据呢?
1.查看以上全部操做被记录在哪个binlog当中,也就是当前所使用的binlog日志
1mysql> show master status;
2+------------------+----------+--------------+------------------+-------------------+
3| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
4+------------------+----------+--------------+------------------+-------------------+
5| mysql-bin.000002 | 1074 | | | |
6+------------------+----------+--------------+------------------+-------------------+
2.由show master status
命令咱们能够知道,当前使用的是mysql-bin.000002
日志,而且以上全部操做都已经被记录在该日志文件当中。对此,咱们能够继续使用show binlog events
命令来指定该日志文件,查看一下该文件所发生的事件:
1mysql> show binlog events in "mysql-bin.000002";
经过执行以上命令,咱们能够获得日志文件所记录每一个事件的信息,其中包括日志文件名、起始位置、事件类型、服务id、终止位置、描述信息,其中比较重要的是log_name、pos、end_log_pos、info字段的信息,咱们进行数据的恢复也是的根据这些信息来实现的。
3.分析及数据的恢复
既然咱们是想对最后的delete
语句所删除的数据进行恢复,那么根据上面的执行结果能够发现,倒数第二行中的Info字段的值为:use 'db_test'; delete from tb_test where id=1
,也就是说咱们的需求是想要获得执行该行以前的数据库状态。
对此,咱们继续往上查找,发现倒数第四行的Info是一个commit
,也就是insert
事务的提交。至此,咱们基本能够肯定,咱们只须要将数据库恢复到从起始位置到该commit
为止便可,这样就完美的跳过了delete
操做。而指定数据恢复的位置即是pos和end_log_pos两个字段所决定的,从图中咱们就能明白:只须要执行binlog日志文件中pos=4,end_log_pos=848的内容便可恢复。
因此咱们能够经过以下方式实现该数据的恢复:
1# 删除以前旧的数据库
2mysql> drop database db_test;
3# 恢复数据
4$ ./mysqlbinlog --start-position 4 --stop-position 848 ../../data/mysql-bin.000002 | mysql -uroot -p;
以上是咱们经过mysqlbinlog工具实现数据恢复的通常步骤,在其中指定的了--start-position和stop-position两个参数来肯定恢复的起始位置和终止位置。固然,在前面咱们也有介绍过将binlog日志转化为sql文件方法,因此咱们一样能够基于sql文件来实现数据的恢复,这个比较简单,这里就很少说了。
以上是咱们基于单个binlog日志文件实现数据恢复的操做,可是假如咱们的数据操做信息被记录到多个binlog日志文件当中,在误操做的状况下应该如何实现日志的恢复呢?
1mysql> flush logs;
2mysql> insert into tb_test values(3, 3, "taoye03");
3mysql> insert into tb_test values(4, 4, "taoye04");
4mysql> insert into tb_test values(5, 5, "taoye05");
5
6mysql> flush logs;
7mysql> insert into tb_test values(6, 6, "taoye06");
8msyql> delete from tb_test where id=4;
9mysql> insert into tb_test values(7, 7, "taoye07");
10
11mysql> flush logs;
12mysql> insert into tb_test values(8, 8, "taoye08");
13mysql drop table tb_test;
14mysql drop database db_test;
在上面咱们执行了三次flush logs
命令,也就是滚动了三次,并生成了三个日志binlog日志文件。第一个文件中记录了三次insert
操做,第二个日志文件中记录了insert、delete、insert
,第三个日志记录了insert、删表、删库
的操做。假如咱们如今的需求是恢复全部delete以及删表删库的操做,咱们应该怎么作?
有了前一个例子的铺垫,该数据的恢复应该不难,思路就是:第一个日志文件进行所有恢复,第二个日志文件进行两次部分恢复(以delete为分界)、第三日志文件进行一次部分恢复(即除去删表、删库操做)。
具体的命令操做,你们能够参考删一个例子,基本是一致的,这里就不作过多赘述了。
参考资料:
[1] 丁奇.MySQL实战45讲
[2] 详细分析MySQL事务日志(redo log和undo log)
https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html#auto_id_11
[3] MySQL二进制日志详解
https://www.cnblogs.com/jkin/p/10124182.html
至此,本文基本就结束了。
该文详细讲解了MySQL的体系结构,其中包括链接器、缓存、分析器、优化器和执行器等各个功能部件的做用及所扮演的角色。再者,介绍了MySQL的日志系统,日志系统在MySQL当中是很是重要的,其应用场景主要体如今主从搭建以及数据恢复,在实际生产过程当中使用的仍是很是频繁的,毕竟谁也保证不了咱们的数据库没有出现问题的时候。
这篇文章是《大话数据库》的第一篇,也是从宏观的角度来对MySQL进行一个总体性的分析,这样对后面的事务、索引等知识的理解都是很是有帮助的,咱们只有了解了MySQL的底层工做原理,才能在出现问题时直击问题的本质。
认真读到这里的读者,相信都是和Taoye同样怀揣着一颗想要不断提升本身的心,想要成为一位优秀的Coder。原创不易啊,若是本文对各位有所帮助,还请关注+转发+再看,【三连】走一走啊,就当是给Taoye坚持肝文的鼓励和支持了。
《大话数据库》,咱们下期再见!