如何快速注释掉大文件前两行

一.扯淡
最近遇到这么一茬事儿,某租户的DB需要在线升级,我们给出的方案是使用搭建从库的方式来缩短割接时间。正当我兴致勃勃做割接准备的时候,出了这么个幺蛾子。
生产环境有150GB的数据,我是通过mysqldump方式导出来,传到备库,准备把数据灌到备库的时候,爆出命令错误,不能执行,这TM什么鬼。第一反应肯定是查看dump文件,于是head的了前50行,发现多出了两行数据库登录的warming,这特么的.......果断vim,噩梦就此开始,等我反应过来为时已晚,索性等一下吧,足足等了十来分钟,vim控制界面出现了,于是dd了两行记录。老规矩:x保存退出。尼玛石化了,也卡了十几分钟,卡了十几分钟也就算了,最后100多GB的文件变为了8GB。这TM什么鬼......无奈只有重新导数据。
这次变聪明了,不用vim,用sed神器,哈哈,不就删除前两行嘛,简单:sed -i "1,2d" dumpfile.sql。一个回车下去,一二十分钟没反应,我工作目录下发现了一个以sed开头,几个随机字符结尾文件名的文件,真TM日了狗了,又TM把文件读进来,写出去。无奈,当时也没相处更好的办法,等了差不多一个小时,终于跑出来了。
可能还有同学想到用awk等等。我们要明白,awk sed都是基于行去编辑文件副本,最后将编辑好的文件落盘合并,也就是说,我们不能只修改文件的某几个字节,必须读取文件的全部。对于以上案例,我们仅仅只需要注释掉前几行就可以了,完全没有必要把文件IO一次。那么有没有更好的办法呢,做DBA的应该很容易想到,数据库读写数据文件都是以块为单位,我们可以只修改某个块,而不用读取整个文件。

二.聊聊文件
这里我们不聊linux的vfs,也不聊inode那些东西,我们只单纯的看看文件这个东西。说到文件,大家就想到txt文本,没错他就是一个非常纯正的文件。那么打开文件,大家又看到一行又一行,潜意思里就感觉文件就是一行下面再一行,这样叠在一起的。其实呢,我们只要把文件想做一段连续的数据流(你也可以说字节哟)就好了,c++一般都聊stream嘛。我们只要知道文件就是一个连续不断的东西,而不是肉眼看到的那个一行一行的,行只是文本编辑软件将换行符\n显示出来了。
绘图1
那么数据库里面的数据块是什么意思呢?难道我们的数据真的是一块一块的,在硬盘上面也是一个一个的物理小方块?不要被忽悠了,数据块说白了就是一个逻辑概念,所谓数据块就是人为规定以一定步长为基准,将一个文件连续不断的数据流切成一段一段的,那么一段就是一个数据块。这么说来我们数据库修改一个数据块就是修改一段固定长度的数据流。oracle里面规定常规数据块8KB,innodb里面规定一个page是16KB。
有了这个概念就好了,我们要修改某个数据块,只需要将文件读写指针seek到相应的偏移位置,然后读写一个数据块长度的数据流就OK了。
绘图2

三.快速注释掉大文件的前两行
聊到文件,要说的东西太多太多了,针对本次问题,我们需要了解哪些知识才能解决呢。首先去网上扒绝对不是上上策,我们肯定要自己写个小程序来解决。那么选择什么语言呢,Python?go?js?我想都可以吧。这里我选择C++,既然用c++是不是一定要非常熟悉open(),read(),write(),lseek(),pread(),pwrite(),readv()writev()这些系统调用呢,不必了,选择C++就图轻松,我们用用c++的fstream就解决了。非常简单,我们只需要明白fstream控制的几个文件指针就好了。
fstream控制了两个文件,一个读指针,一个写指针,分别代表我们即将从哪里开始读取或者写入文件流;其次我们还要使用fstream的getline()方法,该方法从指定的读指针开始读取,直到遇到一个换行符为止为一行,这时读写指针的位置都被更新到行末;最后还要用到就是fstream的输出,我们直接输出注释符即可,此时读写指针也会被修改为写操作的结束位置。

想了解以下文件IO的奥秘,推荐大家读读《Linux_UNIX系统编程手册(上)》的第4第5章,或者《UNIX环境高级编程(中文第三版》的第3 4 5章。

以下贴上一个DBA的蹩脚代码,实在太久没搞代码了,献丑了。

/*************************************************************************
        > File Name: px_mysql.cpp
        > Author: V8power
        > Mail: landcruiser5700@outlook.com 
        > Created Time: Fri 10 Feb 2017 11:51:26 AM CST
 ************************************************************************/

#include
#include
#include 
#include 

using namespace std;

bool parse_char(char );
int parse_int(char*);

int main(int argc,char *argv[])
{
        if((argc!=3)&||parse_char(argv[1][0]))
        {
                cout<<"usage:note -|#|/ line_num"<>num;
        return num;
}

以下是使用demo

[rongwei.deng@JumperBox-Deploy code]$ cat out.txt 
this is line1
this is line2
this is line3
this is line4
this is line5
this is line6
this is line7
this is line8
this is line9
[rongwei.deng@JumperBox-Deploy code]$ ./px_mysql - 2   //注释符是  -   注释两行
pos for put:0
pos for gut:0
pos for put:2
pos for gut:2
pos for put:14
pos for gut:14
pos for put:16
pos for gut:16
[rongwei.deng@JumperBox-Deploy code]$ cat out.txt 
-- s is line1
-- s is line2
this is line3
this is line4
this is line5
this is line6
this is line7
this is line8
this is line9

Write a Reply or Comment

电子邮件地址不会被公开。 必填项已用*标注