Oi
precisamos implementar a inserção de várias chaves da maneira mais rápida possível
Eu li o seguinte problema:
https://github.com/StackExchange/StackExchange.Redis/issues/432
mas isso não me ajudou
qual é a prática recomendada para inserir N
Espero que haja uma maneira melhor do que chamar StringSet () N vezes
obrigado
A maneira mais simples de fazer isso provavelmente seria fazer um loop pegando "algum número" (100? 250? 500? - depende do tamanho das chaves e dos valores, da largura de banda da sua rede, etc.) e emitir um número de MSET
operações, que você pode fazer por meio de StringSet(KeyValuePair<RedisKey, RedisValue>[])
. Talvez, por exemplo:
c#
const int BatchSize = 100; // play with
var batch = new List<KeyValuePair<RedisKey, RedisValue>>(BatchSize);
foreach(var pair in yourSourceData)
{
batch.Add(new KeyValuePair<RedisKey, RedisValue>(pair.Key, pair.Value));
if (batch.Count == BatchSize) {
db.StringSet(batch.ToArray());
batch.Clear();
}
}
if (batch.Count != 0) // final batch
db.StringSet(batch.ToArray());
Você pode preferir usar await StringSetAsync
e / ou CommandFlags.FireAndForget
, dependendo de suas necessidades. Se você estava usando a API assíncrona, também poderia adiar cada await
até um pouco antes do próximo lote, de modo que continue criando o próximo lote enquanto o primeiro é processado, mas isso fica mais complexo.
Na extremidade extrema: você poderia escrever um tubo de rolagem de comprimento fixo de aguardáveis e emitir cada item individualmente, mas ... eu suspeito que isso teria mais sobrecarga.
Qualquer uso?
Oi
Obrigado pela resposta detalhada
Parece uma boa solução, vou testá-la para ver se isso ajuda
**EDITAR:
a única coisa que é um problema é o fato de que a sobrecarga de função que leva um array não permite que você defina um tempo de expiração por algum motivo
bool StringSet(KeyValuePair<RedisKey, RedisValue>[] values, When when = When.Always, CommandFlags flags = CommandFlags.None);
O "algum motivo" é porque: isso não é suportado pelo próprio redis - veja MSET
- falta qualquer tipo de validade (o parâmetro when
oscila entre MSET
e MSETNX
).
Se você precisa de tempos de espera, bem como, duas opções de saltar à mente:
SETEX
(discutido anteriormente)EVAL
/ EVALSHA
( ScriptEvaluate
em SE.Redis)Aqui está um exemplo completo da segunda opção que assume que um único vencimento pode ser usado para todas as inserções:
`` `c #
usando StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Diagnostics;
usando System.Linq;
classe estática P
{
static void Main ()
{
// AVISO: exemplo limpa o banco de dados como parte da execução
const int DB = 0;
var muxer = ConnectionMultiplexer.Connect(new ConfigurationOptions {
EndPoints = { "127.0.0.1" },
AllowAdmin = true, // enables FLUSHDB for our example
});
var db = muxer.GetDatabase(DB);
var server = muxer.GetServer(muxer.GetEndPoints().Single());
Console.WriteLine($"FLUSHING DATABASE {DB}...");
server.FlushDatabase(DB);
Console.WriteLine($"size before: {server.DatabaseSize(DB)}");
const int BatchSize = 100, ExpirySeconds = 120;
List<RedisKey> keys = new List<RedisKey>(BatchSize);
List<RedisValue> values = new List<RedisValue>(BatchSize + 1);
// note that ARGV[1] is the expiry, so all the values are
// off-by-one compared to their keys
const string lua = @"
expiração local = tonumber (ARGV [1])
contagem local = 0
para i, chave em ipairs (KEYS) faça
redis.call ('SETEX', chave, expiração, ARGV [i + 1])
contagem = contagem + 1
fim
contagem de retorno
";
values.Add (ExpirySeconds);
foreach (par var em InventData (1024))
{
keys.Add (pair.key);
valores.Adicionar (par.valor);
if(keys.Count == BatchSize)
{
// execute
Console.WriteLine($"sending batch of {keys.Count}...");
var count = (int)db.ScriptEvaluate(lua, keys.ToArray(), values.ToArray());
Debug.Assert(count == keys.Count); // check expectation
// reset for next batch
keys.Clear();
values.Clear();
values.Add(ExpirySeconds);
}
}
if (keys.Count != 0)
{
// execute final batch
Console.WriteLine($"sending batch of {keys.Count}...");
var count = (int)db.ScriptEvaluate(lua, keys.ToArray(), values.ToArray());
Debug.Assert(count == keys.Count); // check expectation
}
Console.WriteLine($"size after: {server.DatabaseSize(DB)}");
}
static IEnumerable<(string key, string value)>
InventData(int count)
{
var rand = new Random();
unsafe string Invent(int len)
{
string alphabet = "0123456789 abcdefghijklmnopqrstuvwxyz";
string s = new string('\0', len);
fixed(char* ptr = s)
{
for (int i = 0; i < len; i++)
ptr[i] = alphabet[rand.Next(alphabet.Length)];
}
return s;
}
for(int i = 0; i < count; i++)
{
yield return (Invent(20), Invent(50));
}
}
}
`` `
muito obrigado
ambas as soluções parecem válidas para mim. vamos tentar
Comentários muito úteis
O "algum motivo" é porque: isso não é suportado pelo próprio redis - veja
MSET
- falta qualquer tipo de validade (o parâmetrowhen
oscila entreMSET
eMSETNX
).Se você precisa de tempos de espera, bem como, duas opções de saltar à mente:
SETEX
(discutido anteriormente)EVAL
/EVALSHA
(ScriptEvaluate
em SE.Redis)Aqui está um exemplo completo da segunda opção que assume que um único vencimento pode ser usado para todas as inserções:
`` `c #
usando StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Diagnostics;
usando System.Linq;
classe estática P
{
static void Main ()
{
// AVISO: exemplo limpa o banco de dados como parte da execução
expiração local = tonumber (ARGV [1])
contagem local = 0
para i, chave em ipairs (KEYS) faça
redis.call ('SETEX', chave, expiração, ARGV [i + 1])
contagem = contagem + 1
fim
contagem de retorno
";
values.Add (ExpirySeconds);
foreach (par var em InventData (1024))
{
keys.Add (pair.key);
valores.Adicionar (par.valor);
}
`` `