前言
周末逛知乎的時候,看到的一個經典面試題目:http://www.zhihu.com/question/26435483。非常經典的一道分而治之的題目。
題目描寫敘述例如以下:
有次面試遇到一個問題,10G的log里面每一行都保存着一個url,內存僅僅有250M,當輸入一個url時,假設高速查出日志里是否有這條記錄,假設有,有多少條?要求不能使用數據庫,僅僅能使用文本處理。
思路
之前我的研究生導師已經從project角度分析了這個問題。這里我是簡單的記錄思想,並寫出完整的演示樣例來解決問題。
這是一道很典型的使用分治法來解決這個問題的題目。思路例如以下:
- 首先。考慮將10G的log文件划分為多個小於250M的文件,這樣每一個小文件就能夠一次性加載內存了。
- 當小文件能夠一次性加載內存后,能夠直接grep搜索,也能夠對文件內容排序后,然后二分查找。
疑問:怎樣避免在切分文件的過程中。誤操作切斷url?
解答:能夠使用split按行來切分文件(注意:這里按行是依照“\n”切割,和你用vim編譯器打開看到的行是不一樣地)。因此,我們如果一個記錄幾乎相同是480byte(已經不少了),那么依照-l 500000 切割文件。那事實上每一個文件存儲500000行也才240M。全然能夠加載內存,也不存在url被截斷的問題了。
演示樣例
1. 通過一個4k大小的日志文件。構造一個1G大小的測試log文件。並添加特殊的url為“www.wangzhengyi.com”,這也是我們要查詢的特殊url。
思路:cat 4k大小的日志文件256000次,每次結束時添加這個特殊的url:"www.wangzhengyi.com“。演示樣例代碼例如以下:
#!/bin/bash BASE_LOG_PATH=/tmp/test/access.log.1 RES_LOG_PATH=/tmp/test/big.log if [ -f $RES_LOG_PATH ]; then rm -rf $RES_LOG_PATH fi touch $RES_LOG_PATH for i in `seq 1 256000`; do cat $BASE_LOG_PATH >> $RES_LOG_PATH echo "www.example.com|10.32.185.95|-[28/Oct/2014:12:34:39 +0800]|" >> $RES_LOG_PATH done建成之后,我們du -sh看一下文件大小確實是1G多。

2. 我們更苛刻一些,如果最多能用的內存是5k,那我們能夠依照1000行來對1G的文件進行分割,然后bash shell for循環遍歷分割后的文件,通過grep將特殊的url查找出來,存儲到特定的文件里。演示樣例腳本例如以下:
#!/bin/bash LOAD_DIR_PATH="/tmp/test/children" SOURCE_PATH="/tmp/test/big.log" if [ ! -d $LOAD_DIR_PATH ];then mkdir -p $LOAD_DIR_PATH fi cp $SOURCE_PATH $LOAD_DIR_PATH #1.split依照1000行來進行切分 NUMBER=1000 cd $LOAD_DIR_PATH split -l $NUMBER $LOAD_DIR_PATH/big.log #2.for循環遍歷查找 TARGET_URL="www.wangzhengyi.com" TARGET_PATH="/tmp/test/res.txt" for file in `ls $LOAD_DIR_PATH`; do if [[ $file != "big.log" ]]; then grep -i $TARGET_URL $file >> $TARGET_PATH fi done #3.統計行數(前提:總結果數不超過規定的內存限制) echo `cat $TARGET_PATH | wc -l`