O Redis importa rapidamente grandes quantidades de dados

1. Descrição do problema

Existem muitos dados armazenados como "key '\ t'value''value' '……", os exemplos são os seguintes:

0000    0475_48070 0477_7204 0477_7556 0480_33825 0481_206660 0482_76734 0436_33682 0484_13757 0538_217492 0727_83721 0910_39874 0436_82813 0421_24138 0433_
113233 0425_67342 0475_56710 0438_83702 0421_14436 0451_15490 0456_51031 0475_126541 0459_64108 0475_28238 0475_73706 0425_67481 0481_70285 0482_40188 0482_
95188 0484_13346 0484_13940 0538_164341 0538_183629 0545_163319 0545_165272 0546_30724 0730_32196 0910_96866 0427_12847 0425_23173 0424_25451 0475_114926 04
28_44669 0421_14377 0422_27895 0428_79517 0454_26686 0477_76526 0481_51805 0539_22388 0545_86479 0546_23459 0450_30062 0546_31676 0437_820 0740_6902 0740_90
53 0436_75434 0427_5396 0425_65534 0433_113207 0479_42501 0450_41529 0456_63985 0457_503 0458_20159 0470_30141
0001    0481_206403 0732_17231 0730_5902 0425_21756 0437_32744 0450_30493 0457_1339 0475_21591 0475_43849 0475_48123 0481_129436

Importe cerca de dez gigabytes de dados para o Redis. Se apenas um loop simples for usado, o código de amostra é o seguinte:

r = redis.Redis(host='localhost', port=6379, decode_responses=True, db=1)

with open('data/news.txt', 'r', encoding='utf-8') as f:
    data = f.read().strip().split('\n')

for line in data:
    line = line.split('\t')
    indice = line[1].split(' ')
    for index in indice:
        r.sadd(line[0], index)

Leva muito tempo (cerca de algumas horas? Executar em segundo plano, o tempo específico não é muito claro).

Agora precisamos de uma maneira adequada para importar rapidamente os dados necessários para o Redis .

2. Teste de método de importação (3)

import redis
from timeUtil import TimeDuration

'''
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> info keyspace
# Keyspace
127.0.0.1:6379>
'''

t = TimeDuration()

r = redis.Redis(host='localhost', port=6379, decode_responses=True, db=1)


with open('data/news.txt', 'r', encoding='utf-8') as f:
    data = f.read().strip().split('\n')

t.start()
for line in data[:1000]:
    line = line.split('\t')
    indice = line[1].split(' ')
    for index in indice:
        r.sadd(line[0], index)

t.stop()
# 0:00:01.974389


r = redis.Redis(host='localhost', port=6379, decode_responses=True, db=2)

t.start()
with r.pipeline(transaction=False) as p:
    for line in data[:1000]:
        line = line.split('\t')
        indice = line[1].split(' ')
        for index in indice:
            p.sadd(line[0], index)
    p.execute()
t.stop()
# 0:00:00.285798


r = redis.Redis(host='localhost', port=6379, decode_responses=True, db=3)

t.start()
for line in data[:1000]:
    line = line.split('\t')
    indice = line[1].split(' ')
    r.sadd(line[0], *indice)

t.stop()
# 0:00:00.166977

# 127.0.0.1:6379> info keyspace
# # Keyspace
# db1:keys=1000,expires=0,avg_ttl=0
# db2:keys=1000,expires=0,avg_ttl=0
# db3:keys=1000,expires=0,avg_ttl=0
# timeUtil.py
# 时间统计封装类
# 参考:https://blog.csdn.net/a_flying_bird/article/details/46431061

import datetime
import time


class TimeDuration(object):
    
    def __init__(self):
        pass

    def start(self):
        self.sTime = datetime.datetime.now()
        self.eTime = None

    def stop(self):
        if self.sTime is None:
            print("ERROR: start() must be called before stop().")
            return
        self.eTime = datetime.datetime.now()
        delta = self.eTime - self.sTime
        print(delta)

0: 00: 01.974389
0: 00: 00.285798
0: 00: 00.166977

Pode-se verificar que o terceiro método é o que leva menos tempo. Mas, em uso real, o terceiro método relatará o seguinte erro:

redis.exceptions.ResponseError: MISCONF Redis está configurado para salvar instantâneos RDB, mas atualmente não é capaz de persistir no disco. Os comandos que podem modificar o conjunto de dados são desabilitados, porque esta instância está configurada para relatar erros durante as gravações se o instantâneo RDB falhar (opção stop-write-on-bgsave-error). Verifique os logs do Redis para obter detalhes sobre o erro RDB.

A análise se deve ao fato de que no armazenamento Redis, a lista de compactação ziplist é usada para armazenar dados de pequeno volume primeiro, economizando memória. Mas quando os dados de valor continuam aumentando no triste processo, ele mudará para usar hashtable, portanto, ocorre um erro. (Em dúvida)

3. Análise de princípio

Se o cliente deseja obter ou definir várias chaves, ele pode enviar vários comandos get e set. Mas sempre que um comando é transmitido do cliente para o servidor redis, ele consome tempo da rede. O envio n vezes consumirá o tempo da rede n vezes (o primeiro método de importação).

Tempo de espera do cliente = n vezes de tempo de rede + n vezes de tempo de comando (incluindo tempo de fila de comando e tempo de execução)

Usando mget ou mset (* indice, o terceiro método de importação), o comando foi enviado apenas uma vez.

Tempo de espera do cliente = 1 tempo de rede + tempo de comando n (incluindo tempo de fila de comando e tempo de execução)

Usando o pipeline, você pode empacotar e enviar vários comandos ao servidor para processamento em lote de uma vez (o segundo método de importação).

Tempo de espera do cliente = 1 tempo de rede + tempo de comando n (incluindo tempo de fila de comando e tempo de execução)

Link de referência: https://blog.csdn.net/weixin_39975366/article/details/113963503

Pergunta: Por que o segundo método e o terceiro método demoram um tempo diferente?

Acho que você gosta

Origin blog.csdn.net/MaoziYa/article/details/114269129
Recomendado
Clasificación