MySQL 是如何恢复到某一天的某一秒的状态?,现在做 Java 开发有前途吗
内存资源是比较昂贵的,不用的话就要被清理。如果不做任何操作,在一定的时间之后(默认是 8 小时),连接器会自动断开,此时再查询就会报错。
一个比较好的方案是使用数据库连接池。Python 编程可以使用第三方库 DBUtils 来管理数据库连接池。
查询缓存
缓存可以快速返回命中的查询,在使用上的感受就是同一个 SQL,第二次查询时结果是立刻显示的。查询缓存中以 SQL 语句作为 KEY,查询结果作为 VALUE。
如果你的查询能够直接在这个缓存中找到 key,并且具有对该表的相应的权限,那么这个 value 就会被直接返回给客户端。
如果没有找到,会走接下来流程,一旦查到结果,结果还是会保存在查询缓存中。
分析器
如果没有命中查询缓存,SQL 语句就会传给分析器进行词法分析,分析是否有语法错误,解析中表名,字段名等等,其实不仅仅数据库有分析器,很多开源的工具也有分析 SQL 的功能,比如 Python 可以使用 python-sqlparse,JAVA 可以使用 druid(阿里巴巴开源)。
解析出表名之后,检查一下用户对表的权限,如果权限符合就进行下一步优化器。
优化器
经过了分析器,MySQL 就知道你要做什么了。
在开始执行之前,还要先经过优化器的处理。优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。
执行器
MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误。
也许你会问,权限验证前面不是已经做了吗? 为什么这里还要进行权限验证,因为除了 sql 还可能有存储引擎,触发器等,在这些对象中,也可能需要调用其它表去获取数据,也需要权限验证,前面的阶段对于触发器,存储引擎这种对象的执行是做不到的。
比如说:
select * from T where ID=10;
复制代码
如果 ID 字段没有索引,那么执行器的执行流程是这样的:调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。至此,这个语句就执行完成了。
对于有索引的表,执行的逻辑也差不多。第一次调用的是“取满足条件的第一行”这个接口,之后循环取“满足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。
说到存储引擎,MySQl 支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。也就是说,你执行 create table 建表的时候,如果不指定引擎类型,默认使用的就是 InnoDB。不过,你也可以通过指定存储引擎的类型来选择别的引擎,比如在 create table 语句中使用 engine=memory, 来指定使用内存引擎创建表。不同存储引擎的表数据存取方式不同,支持的功能也不同。
接下来,看一下写操作的执行过程,redo log 和 binlog 又起到了什么作用?
写操作
首先,可以确定的说,查询语句的那一套流程,更新语句也是同样会走一遍。
与查询流程不一样的是,更新流程还涉及两个重要的日志模块,它们正是 re
do log(重做日志)和 binlog(归档日志)。如果接触 MySQL,那这两个词肯定是绕不过的,redo log 和 binlog 在设计上有很多有意思的地方,这些设计思路也可以用到你自己的程序里。
以更新操作为例,假如 SQL 语句为:
update table_a set count = count + 1 where id = 2
复制代码
执行器先找引擎取 id=2 这一行。id 是主键,引擎直接用树搜索找到这一行。如果 id=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。
这里得说明一下,redo log 和 binlog 都是日志文件,为了防止异常重启、掉电、恢复数据等场景,这些日志文件都会持久化到磁盘上。为了防止频繁的访问磁盘,写 redo log 前会先写到内存中的 redo log buffer,会定期一起写到磁盘。
但是这两个 log 文件又有所区别:
redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 id=2 这一行的 c 字段加 1 ”。
redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
redo log 用于回滚,binlog 用于恢复。
如果将 MySQL 恢复到某一天的某一秒
要做到这一点有个前提,就是要对 MySQL 数据库定期做整库备份。这里的定期取决于系统的重要性,可以是一天一备,也可以是一周一备。
当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:
首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库;
然后,从备份的时间点开始,将备份的 binlog 依次取出来,重放到中午误删表之前的那个时刻。这样你的临时库就跟误删之前的线上库一样了。
最后,你可以把表数据从临时库取出来,按需要恢复到线上库去。
为什么要两阶段提交
前面写操作中的提到,写磁盘前先写 redo log,此时 redo log 状态为 prepare,然后再写 binlog,写完 binlog 后,再提交,redo log 才处于 commit 状态。
评论