擴充你的工具箱 - 大行文件的處理


前言

前幾天,從 DBA 手里接到一個 Redis RDB 文件,里面是 15G 約 660萬 的 Redis 鍵值對數據,想通過這些數據提取出當前 Redis 的 Key 和這些 Key 的類型。其文件的每行結構類似於:

KEY: IAmATestKey || TYPE: STRING || expiretime:-1 || value:IAmTheTestValue

下文就是此問題的跳坑和脫坑過程:

文章經常被人爬,而且還不注明原地址,我在這里的更新和糾錯沒法同步,這里注明一下原文地址:http://www.cnblogs.com/zhenbianshu/p/7486530.html 以防誤人子弟。


通用解決方案不好使

awk長時間處理中

對 linux 命令稍熟悉的同學可能就會說了:這些數據格式這么統一,數據字段間也有明顯的分隔符,不正是 awk 工具大顯身手的時刻嗎?

是的,awk 是 linux 下一個強大而又略復雜的命令,使用它的簡單語句也可以高效地處理大量文本,但是今天的主角不是它,我也不想復制粘貼網上到處都是的教程,就不再多介紹了。

原以為使用 awk -F ' ' '{printf $2","$6}' rdb.log >>keys.txt 將結果重定向到 keys.txt,也就一行命令的事,可是執行后,發現執行了兩個小時還沒有結束的意思。

是文件太大,awk 卡住了么?

切割文件也無法解決

文件太大處理不方便,這時,就要用到linux的另一個工具了:split 將文件以行數/大小平均分割; split [-b bytes][-l line] input_file output_prefix

由於每一行的大小是不同的,按照大小來分割的話可能會導致某一行被拆散,於是以每個文件 100 萬行,分割出7個文件,再對這些文件分別使用 awk 來處理, split -l 1000000 rdb.log rdb_split_

對分割后的文件使用awk,結果奇怪的是還是會在某個文件上執行很長時間沒反應。

這時使用 ls 命令查看文件大小的時候發現,15G 的文件分割成了 7 份,有一個竟然有 7G 大小,這時想到可能會有的 set 或 list 很大,占用幾 G 的內存也是有可能的。而 awk 長時間處理有可能是因為這些特別大的行。


用C來高效處理

既然如此,那就只好用別的方法先處理一下文件了,這里我考慮取出文件數據每行的前100個字符,由於鍵都很短,100個字符已經是足夠包括鍵名和類型了。

可是印象中沒有相關的工具或命令,於是找谷哥搜索一下文件的大行怎么處理。。。結果並沒有相應的解決方式,只好考慮自己來寫腳本了,由於其邏輯並不復雜,而且對效率要求高,就舍棄了 PHP,准備使用 C 來解決。

整體思想是:利用 C 文件操作函數 fgets(&res, length, file_hanler) 在每一行讀到換行符或讀到 length 個字符的特點。如果讀到100個字符還沒有讀到結尾(最后一個字符不是換行符),就說明此行是一個大行,那么就讀取單個字符並丟棄,直到讀到換行符,再繼續處理下一行。

這里貼上 C 腳本:

#include <stdio.h>
#include <string.h>

int main() {
    FILE *fp_in;
    FILE *fp_out;
    char ch;
    char line[100];
    
    fp_in = fopen("rdb.log", "r");
    fp_out = fopen("keys.txt", "w");

    while (!feof(fp_in)) {
        fgets(line, 100, fp_in);  //讀取一行
        fputs(line, fp_out);

        if (line[strlen(line) - 1] != '\n') {
            while ((ch = fgetc(fp_in)) != '\n') { 
                ;
            }
            fputc('\n', fp_out);
        }
    }
    fclose(fp_in);
    fclose(fp_out);
}

C 執行得還是很快的,大概三分鍾。執行結束后,再使用 awk 工具,果然很快就把鍵和類型拆了出來。

此時心情大好,果然多掌握一門像 C 這種高效語言就是有用啊。


還有更好的工具

當從 leader 口中得知 cut 命令時,我的表情是這樣的:

好吧,趕快了解一下 cut 命令:

cut [options] [file.name] :從每個文件中輸出指定部分到標准輸出。

其選項有:

  • -b n 輸出第n個字節;
  • -c n 輸出第n個字符,用於處理類似utf-8中文這種三個字節的字符;
  • -f n 輸出第n個字段,其字段分隔符用 -d 指定;

不光有我腳本取前 n 個字符的功能,還能直接取第 n 個字段。。

試了下,cut -b 100 rdb.log >>keys.log 取前 100 個字符用了 8 分鍾,雖然比 C 腳本要慢,可是它不用手寫腳本,而且適用范圍比我寫的腳本要大。


小結

問題是順利解決了,可是解決過程引起我的思考。這個問題應該會被更快更方便地解決的,搞得這么麻煩主要是因為 linux命令掌握不全

不知道 linux 還有 cut 命令。其實也不是不知道,事后發現我筆記里已經有了關於 cut 的簡單記錄了,可能是由於命令太過簡單,沒有很多參數,也沒想到太多應用場景,被我記入了 linux 的雜項。

同時也發現了跟 cut 一樣被遺忘的還有其他小知識點,是時候回憶一波了。

另外谷哥搜索命令的關鍵詞也大有問題,沒有抓住取文件前 n 字符的本質,竟然去搜索文件大行處理,因為急着要下班去接女朋友,失了智了,最后一個彌補工具問題的機會被拋棄了。

我一直認為:解決問題的能力 ~ 個人工具箱的大小。掌握的工具越多,面對問題就會有更多選擇,解決問題也就越得心應手。開發技能是基礎,工具用好才能快速高效地解決問題。

寫幾個常用自動化腳本、alias 簡化一下很長的命令、掌握一些常用的快捷鍵,開發效率就能這么一點點提升了。

關於本文有什么問題可以在下面留言交流,如果您覺得本文對您有幫助,可以點擊下面的 推薦 支持一下我。博客一直在更新,歡迎 關注


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM