今天一不小心把公司的服务器上的数据库给删了,至于怎么会把服务器上的数据库给删了就不说了,说多了都是泪啊。删了以后我赶紧找备份(PS:还好之前有备份这下不怕啦,哈哈!)。赶紧用备份的sql恢复下,回复后打开程序看看,尼玛,数据不对!!!这是怎么回事,经过多番询问,同事告诉我他上周在这个服务器上又装了个数据库,我备份成他的那个了。顿时傻了,这下怎么办,数据没了,这下死定了。他说没事,今天早上他有把我的那个数据库备份了一次。突然感觉春天又来了,赶紧用他备份的那个文件试试。祈祷千万别出问题,结果真是怕什么来什么,运行快结束时候出错,错误信息又打不开。心情再次跌落谷底。这下只能打开这个50+M的sql文件看看是怎么回事,结果发现是没有备份完,语句不完整。这下真的要死了,不知道怎么办了。听说好像可以通过日志什么来恢复,问了所有的同事,没一个会弄的(因为是小公司,没有专业的dba),没办法了,只能硬着头皮自己来了,最后在几个好基友的帮助下总算恢复了。这个教训让我深刻体会到了人都是“逼”出来的,不逼自己永远不知道自己有多少潜力,在这之前我是从来没想过自己能通过日志把数据库恢复。
以上都是废话,敢时间的可以不看。
接下来来分享下这次数据恢复总结出来的经验吧,总的来说数据恢复大概是两种情况:
一、.误操作之前有备份直接恢复,这里包括备份的sql语句或者有备份mysql的data目录。。sql语句恢复就不说了,说说有data目录怎么恢复吧:
frm文件就是mysql的data目录下然再打开数据库名的文件夹就能看到了,一般来说,frm文件名就是表名
1) 这个其实很简单,先在你要恢复的数据库里面建一张同名的表,只要表名一样就可以了其他无所谓,
2)然后 关闭数据库服务,将之前备份的data目录下的数据库名目录下的frm文件考到需要恢复的mysql的data目录下的数据库名目录下就可以了。有点绕口,举个例子吧,比如我要恢复test数据下的user表,那么,在mysql的data/test目录下就会有一个user.frm文件,这个就是你这张表的数据结构,然后把备份的frm文件拷到现在要恢复的/data/test目录下,然后覆盖,再把备份的data目录下的ibdata1文件考到相应位置覆盖
3)重启服务再看看数据是不是恢复了,很简单吧。
二、.上面说的这个不是重点,重点是接下来的,基本上能搜到我这篇文章的都是要看接下来的这个方法。不卖关子了,一般来说误操作都是改之前没备份的,这个要怎么办呢?大家是不是首先想到通过日志回滚操作?没错,就是这么做,但是这么做首先要有两个前提:
1)数据库之前必须开启日志,开启日志的方法自己百度吧,这里就不多说了。
2)必须至少要有一个从开启日志后的备份过的版本
要是不满足上面两个条件,我也没办法了,只能请更专业的人来恢复。好了,有人肯定要问了,有了这两个东西要怎么用啊?我也是在网上研究了半天才弄清楚,其思路就是用以前备份的数据库,然后把日志里面执行的操作从以前的那个数据库开始操作一遍可能就能恢复了。之前拿到日志时老是想着怎么回滚回去,真是笨,其实转个弯倒过来想,将操作再执行一次就可以了。现在就把我总结后的方法步骤分享给大家吧。
1)找到mysql的data目录下的mysql-bin.00000X文件,类似这种的,应该有很多个,因为配置时候文件名可以配,所以不保证一定是这样的,要是不一样应该也很好找,就是有一组有规律XXXX.00000X文件就是了。
2)将日志文件导出成sql文件,方法是在命令行窗口下(其实就是cmd窗口)调用mysql的bin目录mysqlbinlog程序
mysqlbinlog --start-date="2013-10-01 00:00:00" --stop-date="2013-12-12 12:00:00" E:\mysql-5.5.21-win32\data\mysql-bin.000067 > e:\67.sql
上面这段将mysql-bin.000002日志文件中从2013-10-01 00:00:00到2013-12-12 00:00:00截止的sql语句导入到e:\67.sql文件中.如果有多个日志文件就执行多次,生成多个文件就好了。这些文件就是这段时间数据库执行的操作了。
3)哈哈,是不是以为把数据库还原下再直接执行上面导出的sql就OK了?当然没那么简单,不信你试试,我相信90%会出错。
因为导出的这个sql一般来说会有编码问题,还有一些七七八八的代码,因为本人不是专业的dba,搞不懂那些七七八八的东西有什么用,索性写个程序,把关键的update,delete,insert语句提取出来,程序本来想自己写但是突然想想别人可能有写过,就上网百度了一下,果然有,不过还是和我的需求有差就自己改了下,代码如下
public class SQLparser {
public static void main(String[] args) throws IOException {
File file = new File("e:\\iss68.sql");//这个是binlog导出的sql
if (!file.isFile()) {
System.out.println("error arg[0] not a file");
return;
}
System.out.println(file.getName() + "BEGIN!");
//这个为等等提取后的文件,这里注意编码
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(new File("e:\\iss68s1.sql")), "gbk"));
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(file), "gbk"/* 指定源文件的字符集编码 */));
String line = br.readLine();
int a =1;
while (line != null) {
line = br.readLine();
if(line!=null && line.length()>3 && line.substring(0, 3).equals("use")){
System.out.println(line);
//这个if是用来过滤数据库的,这里我只提取test数据库的相关操作,如果不用过滤数据库,可以把这个if注释掉 //让a=1就可以了
if(line.contains("test")){
a=1;
}else {
a=0;
}
}
if(a==1){
if(line!=null && line.length()>6 ){
String str = line.substring(0, 6);
if(str.equals("insert") || str.equals("update") || str.equals("delete") ){
//这个if是用来过滤表的,这里过滤了t_user和t_department表,不用的话可以将该if注释里面 //的bw.append(line + ";\n");要保留
if(line.contains("t_user")||line.contains("t_department")){
bw.append(line + ";\n");
}
}
}
}
}
br.close();
bw.close();
System.out.println(file.getName() + "OK!");
}
}
用以上方法导出的的sql放到mysql执行下就ok了,不过一定要注意编码问题。好了,这样就解决了,其实也不难吧。