2013-03-30 30 views
3

我想使用一個相當大的語料庫。它以Web 1T-gram的名字命名。它有大約3萬億令牌。這是我第一次使用redis,我試圖編寫所有的關鍵值對,但其耗時太長。我的最終目標是使用多個redis實例來存儲語料庫,但現在,我堅持將它們全部寫入單個實例。寫入3,795,790,711唯一鍵:值對redis

我不確定,但有什麼方法可以加快寫作過程嗎?截至目前,我只在一臺裝有64G內存的機器上寫入一個redis實例。我在考慮是否有一些緩存大小設置可以最大限度地用於redis。或者在這些線上的東西?

謝謝。

僅供參考,我已經寫了下面的代碼:

import gzip 
import redis 
import sys 
import os 
import time 
import gzip 
r = redis.StrictRedis(host='localhost',port=6379,db=0) 
startTime = time.time() 
for l in os.listdir(sys.argv[1]): 
     infile = gzip.open(os.path.join(sys.argv[1],l),'rb') 
     print l 
     for line in infile: 
       parts = line.split('\t') 
       #print parts[0],' ',parts[1] 
       r.set(parts[0],int(parts[1].rstrip('\n'))) 
r.bgsave() 
print time.time() - startTime, ' seconds ' 

UPDATE:

我也讀到了大量插入,並一直在努力做到這一點,但是,保持迷失方向。這裏是腳本的變化:

def gen_redis_proto(*args): 
    proto = '' 
    proto += '*' + str(len(args)) + '\r\n' 
    for arg in args: 
     proto += '$' + str(len(arg)) + '\r\n' 
     proto += str(arg) + '\r\n' 
    return proto 
import sys 
import os 
import gzip 
outputFile = open(sys.argv[2],'w') 



for l in os.listdir(sys.argv[1]): 
     infile = gzip.open(os.path.join(sys.argv[1],l),'rb') 
     for line in infile: 
       parts = line.split('\t') 
       key = parts[0] 
       value = parts[1].rstrip('\n') 
       #outputFile.write(gen_redis_proto('SET',key,value)) 
       print gen_redis_proto('SET',key,value) 

     infile.close() 
     print 'done with file ',l 

生成方法的功勞歸於github用戶。我沒有寫。

如果我運行此,

ERR wrong number of arguments for 'set' command 
ERR unknown command '$18' 
ERR unknown command 'ESSPrivacyMark' 
ERR unknown command '$3' 
ERR unknown command '225' 
ERR unknown command ' *3' 
ERR unknown command '$3' 
ERR wrong number of arguments for 'set' command 
ERR unknown command '$25' 
ERR unknown command 'ESSPrivacyMark' 
ERR unknown command '$3' 
ERR unknown command '157' 
ERR unknown command ' *3' 
ERR unknown command '$3' 

這正好和。輸入形式爲

「string」\ t count。

謝謝。

月2日更新:

我用流水線,但這並給我一個提升。但是很快它就耗盡了內存。作爲參考,我有一個64G RAM的系統。我認爲它不會耗盡內存。代碼如下:

import redis 
import gzip 
import os 
import sys 
r = redis.Redis(host='localhost',port=6379,db=0) 
pipe = r.pipeline(transaction=False) 
i = 0 
MAX = 10000 
ignore = ['3gm-0030.gz','3gm-0063.gz','2gm-0008.gz','3gm-0004.gz','3gm-0022.gz','2gm-0019.gz'] 
for l in os.listdir(sys.argv[1]): 
     if(l in ignore): 
       continue 
     infile = gzip.open(os.path.join(sys.argv[1],l),'rb') 
     print 'doing it for file ',l 
     for line in infile: 
       parts = line.split('\t') 
       key = parts[0] 
       value = parts[1].rstrip('\n') 
       if(i<MAX): 
         pipe.set(key,value) 
         i=i+1 
       else: 
         pipe.execute() 
         i=0 
         pipe.set(key,value) 
         i=i+1 
     infile.close() 

難道要走的路嗎?我認爲64場演出就足夠了。我只給了它20億個關鍵值對的一小部分,而不是全部。

+0

哦,我的。 Redis僅支持43億個密鑰。你的意思是30億? –

+0

實際上,redis常見問題狀態'[redis]在實踐中已經過測試,每個實例至少需要處理2.5億個密鑰,所以這很可怕。 –

+2

您是否閱讀過[本文檔](http://redis.io/topics/mass-insert)? –

回答

2

你想要的是可能無法在您的情況。

根據this page,你的數據集是24 GB 壓縮gzip。 這些文件可能會包含很多類似的文本,如字典。

快速測試從dict程序words文件qields的3.12x壓縮:

> gzip -k -c /usr/share/dict/web2 > words.gz 
> du /usr/share/dict/web2 words.gz 
2496 /usr/share/dict/web2 
800 words.gz 
> calc '2496/800' 
3.12 /* 3.12 */ 
> calc '3.12*24' 
74.88 /* 7.488e1 */ 

所以,你的未壓縮數據的大小可以容易超過64 GB。因此,即使沒有任何針對Redis的開銷,即使您使用16位無符號整數來存儲計數,也不適合您的RAM。看看這個例子,大部分的鍵都是相對而言短;

serve as the incoming 92 
serve as the incubator 99 
serve as the independent 794 
serve as the index 223 
serve as the indication 72 
serve as the indicator 120 
serve as the indicators 45 
serve as the indispensable 111 
serve as the indispensible 40 
serve as the individual 234 
serve as the industrial 52 

你可以散列鍵,但它可能沒有太大將爲您平均節省:

In [1]: from hashlib import md5 

In [2]: data = '''serve as the incoming 92 
serve as the incubator 99 
serve as the independent 794 
serve as the index 223 
serve as the indication 72 
serve as the indicator 120 
serve as the indicators 45 
serve as the indispensable 111 
serve as the indispensible 40 
serve as the individual 234 
serve as the industrial 52''' 

In [3]: lines = data.splitlines() 

In [4]: kv = [s.rsplit(None, 1) for s in lines] 

In [5]: kv[0:2] 
Out[5]: [['serve as the incoming', '92'], ['serve as the incubator', '99']] 

In [6]: [len(s[0]) for s in kv] 
Out[6]: [21, 22, 24, 18, 23, 22, 23, 26, 26, 23, 23] 

In [7]: [len(md5(s[0]).digest()) for s in kv] 
Out[7]: [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16] 

對於任何鍵少於16個字節它實際上成本你更多的空間來散列它。

即使忽略標題,壓縮字符串通常也不會節省空間;

In [1]: import zlib 

In [2]: zlib.compress('foo')[:3] 
Out[2]: 'x\x9cK' 

In [3]: zlib.compress('bar')[:3] 
Out[3]: 'x\x9cK' 

In [4]: s = 'serve as the indispensable' 

In [5]: len(s) 
Out[5]: 26 

In [6]: len(zlib.compress(s))-3 
Out[6]: 31 
+0

謝謝。所以我可能應該考慮將它分佈在一臺機器中。謝謝。 – crazyaboutliv

+0

這取決於您計劃在語料庫存儲器中執行什麼操作。既然corpys已經被分成了(不重疊的,我假設)文件,那麼您是否可以每次使用不同的語料庫的一小部分來反覆進行研究? –

+0

不,我會讓「n」個客戶端詢問_any_表中的計數。所以,我認爲我不能。除非我首先在redis中進行搜索,如果沒有,則在磁盤上進行搜索或在這些行上進行搜索。 – crazyaboutliv

0

而不是寫一個命令文件,也許你應該使用流水線和可能的多處理。在redis-py中使用流水線非常簡單。您需要運行測試以找到理想的塊大小。

對於P-Redis的,多和流水線的一個例子,看看這個 example gist

+0

謝謝。我使用流水線,它確實給了我一個改進。我本來想問。我的數據集是靜態的,它將是隻讀的。在這種情況下,使pipe(transaction = False)是安全的嗎?這不會導致任何數據丟失,因爲我現在沒有閱讀,只能寫作。要確認是一個完整的redis新手 – crazyaboutliv

+0

它不應該導致任何數據丟失,但它可能會導致性能下降。我知道這聽起來並不直觀,但根據我已經完成的一些測試以及從其他人看到的情況,您可能會看到更好的性能。我建議運行一個相對較小的子集進行測試,看看您的特定模式在這兩種情況下的表現如何。 –