使用svndumpfilter exclude來清理svn庫的廢棄文件實現差別備份


 

先啰嗦下為什么要使用svndumpfilter…

 

svn庫用久了以后就會越來越大,進行整體文件打包備份的時候,發現壓力山大…尤其是美術團隊也在使用svn進行重要美術資源管理的時候…….幾百g的資源同步備份的盛況對於機器來說是難以承受的壓力,尤其是當存在多種備份方式(比如本地多機器拷貝、遠程機器上傳備份)。幾十kb的速度去承擔幾百g的備份是痛苦的事情。

 

公司的運維又不給力,圍觀了下他們部署在我們服務器的批處理備份代碼,就一個簡單的對svn的倉庫目錄直接用winrar壓縮,然后用rsync同步,這是多么粗糙的方案,當然也不是說錯誤,只是說過於暴力了點,且可控性太差,當想要過濾某些文件的時候就無法實現了。

 

一開始我想的方案也是壓縮打包,用python實現,這樣過濾文件的時候,可以在邏輯上加處理,這樣可以屏蔽下想要過濾的東西,代碼如下:

#-*- coding=gbk -*-

import tarfile
import os
import time
import threading
import datetime
import shutil
import subprocess
from myEmail import Mailer

ignoreNames = ["ArtGroup", ".svn"]
TargetDir = "D:\\02_ucgame_Svn_Repositories"
BackUp = "svn_backup_%s.tar.gz"
ShareDir = "z:\\backup"
mailTitle = "[UCCDTest] svn backup analyze %s"
receivers = [
            "account@qq.com",
            "account@qq.com",             "account@qq.com",             "account@qq.com",              ]
def delShareFile():
    # 刪除共享文件夾里的備份文件
    # 該文件夾路徑為z:\\backup,里面均是壓縮文件包
    # 故,刪除策略為反向排序,取文件的修改時間作為排序的關鍵字處理
    # 刪除時,保留最新的3個文件
    files = [os.path.join(ShareDir, x) for x in os.listdir(ShareDir)]
    if len(files) > 2:
        sorted(files, key = lambda file:os.stat(file).st_mtime, reverse = True)
        DelFiles =  files[:-2]
        for nfile in DelFiles:
            os.remove(nfile)
    
def delLocalFile():
    # 刪除本地文件夾里的備份文件
    # 該文件夾路徑為當前腳本所在路徑,里面均是壓縮文件包
    # 故,刪除策略為反向排序,取文件的修改時間作為排序的關鍵字處理
    # 刪除時,保留最新的1個文件(不刪除該文件的目的是,避免誤操作的文件丟失,同時,為后續操作保留一個緩存文件)
    files = [os.path.join(os.getcwd(), x) for x in os.listdir(".") if "svn_backup" in x]
    if len(files) > 1:
        sorted(files, key = lambda file:os.stat(file).st_mtime, reverse = True)
        DelFiles = files[:-1]
        for nfile in DelFiles:
            os.remove(nfile)
    
def addFile(tf, files, root):
    # 往壓縮包里面添加文件,在添加時,均處理過文件的地址,添加了替換的replace方法的目的是相對路徑時,避免出現多余的符號
    st = time.time()
    length = len(files)
    for ids, nfile in enumerate(files):
        if time.time() - st >= 3:
            print "\radding file at root [%s]: [%s / %s], package is [%s MB]" % (root, ids, length, os.stat(tarPackage).st_size / (1024 * 1024))
            st = time.time()
        fp = os.path.join(root, nfile).replace(".\\","")
        key = root.split("\\")
        if len(key) > 1:
            key = key[1]
            if key not in ret:
                ret[key] = os.stat(fp).st_size
            else:
                ret[key] += os.stat(fp).st_size
        tf.add(fp)

def checkDir(Names):
    # 檢測需要過濾得文件名或路徑名,這里主要用來過濾svn主路徑下的特殊文件夾名字
    for nName in ignoreNames:
        if nName not in Names:
            pass
        else:
            return False
    return True

def maketar(dist, tar):
    # 掃描緩存文件夾,對待處理的文件夾進行過濾,並調用添加方法,將待壓縮文件添加到壓縮包內
    st = time.time()
    cur = os.getcwd()
    os.chdir(dist)

    for root, dir, files in os.walk("."):
        if not checkDir(root):
            continue
        addFile(tar, files, root)
    tar.close()
    os.chdir(cur)

def uploadFile(tar):
    args = ["rsync","-avzPu", "--password-file=passwd.pd","<p.txt", "svn_backup_*", "cwRsync@127.0.0.0::mysvnbackup"]
    os.system("".join(args))
    #p = subprocess.Popen(args, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)
    #print p.communicate()[1]
    #print p.stderr.read()
    
    

def getMailBody(rd):
    msg = '<table border="1"><tr><th>RootDirName</th><th>PackageSize</th></tr>'

    for nk in sorted(rd.items(), key = lambda pair: pair[1], reverse = True):
        if nk[1] < 1024:
            msg += '<tr><td>%s</td><td>%.2fByte</td></tr>' % (nk[0], float(nk[1]))            
        elif nk[1] < 1024 * 1024:
            msg += '<tr><td>%s</td><td>%.2fKB</td></tr>' % (nk[0], float(nk[1]) / 1024)
        elif nk[1] < 1024 * 1024 *1024:
            msg += '<tr><td>%s</td><td>%.2fMB</td></tr>' % (nk[0], float(nk[1]) / ( 1024 * 1024 ))
        else:
            msg += '<tr><td>%s</td><td><font color="red">%.2fGB</font></td></tr>' % (nk[0], float(nk[1]) / ( 1024 * 1024 * 1024 ))
        
    msg+= '</table>'
    return msg

global ret
ret = {}
# 獲取時間參數,用來評估多線程實現的模塊的性能,暫時無用,詳見makeTar_multipleThreads.py
st = time.time()
# 獲取當前日期,用於生成壓縮包的名字
ts = datetime.datetime.now().strftime("%Y-%m-%d")
mailTitle = mailTitle % ts
# 開始打包

print "start making package."
tarPackage = os.path.join(os.getcwd(),  BackUp % ts)
tar = tarfile.open(tarPackage, "w:gz")
maketar(TargetDir, tar)
print "finished making package. used time - %s" % (time.time() - st)
# 分析文件夾狀態,發送郵件
mailer = Mailer()
msg = getMailBody(ret)
for nmail in receivers:
    mailer.send(nmail, mailTitle, msg)
    print "send mail to ", nmail
    

# 刪除z盤的備份文件
delShareFile()
# 上傳備份文件到z盤
shutil.copy(tarPackage, ShareDir)
uploadFile(tarPackage)
# 刪除本地的文件
delLocalFile()

 

 

僅僅實現了過濾備份並不靠譜,因為svn里面的資源和版本其實仍然在,直接找到svn文件夾,刪除文件當然也可以,但是會導致svn庫里面出現空版本或其他問題,所以還是要利用svn本身的管理工具去處理。搜索了半天,發現了svndumpfilter,可以利用他的exclude指令去過濾文件,然后把過濾后的文件結構導入svn即可,但實際按網又介紹的又各種出錯,一般都是什么錯誤的文件頭,如下圖

image

反復研究和google嘗試(baidu查不到的只能找google了)后,終於確認下列的流程是可以操作的:

1. 使用svnadmin把整個要處理的svn庫的項目導出來

命令為:svnadmin dump repos1 > repos1.dump

repos1 為你的對應庫名,repos1.dump為導出的文件名,可以隨意命名,只要自己記得。

2. 使用svndumpfilter exclude命令過濾項目中廢棄的文件夾,可以填寫多個文件路徑,該命令是根據路徑名字過濾的。

命令為:svndumpfilter exlude directory1 directory2 directory3 <repos1.dump> filtered-repos1.dump

如上,路徑可以隨意填寫,但一定要正確存在,比如你導出的是/test/test1,你要過濾的是test1下面的test2,那么這里的directory1應該寫為test1/test2,如果還有其他要一並過濾的,可以空格添加其他directory2,directory3即可。

注意,路徑中如果包含有空格等,需要用雙引號包起來。避免識別錯誤。

路徑配置完成后,一定要用<>把之前第一步導出來的備份dump文件括起來,表示過濾是在該文件的結構中進行。

而最后一個filtered-repos1.dump文件就很好理解了,就是過濾出來的新的結構備份文件。

過濾后的效果如下圖:

image

3. 之后再新建一個庫,導進去即可,如果要繼續使用原來的庫名,將原來的庫刪除后重建,導入即可,導入命令用svnadmin load。

 

當然備份打包還可以用svn推薦的svnadmin dump或svnadmin hotcopy文件備份,但速度太慢,還是直接整體文件拷貝比較快,雖然不夠合理,但如果選擇完全沒人操作的時間還是可以的,這樣恢復也快。對於我們的項目來說,目前還是選用文件打包備份快點,也可以和運維那邊的管理方式結合起來,省的麻煩。。


免責聲明!

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



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