"semplicemente" è un termine molto relativo e non ha davvero senso senza più contesto, in particolare:quanto sono grandi questi carichi utili?
tuttavia, per chiarire alcuni punti per aiutarti a indagare:
- non è necessario bloccare un
IDatabase
a meno che non sia puramente per i tuoi scopi; SE.Redis si occupa internamente di thread safety ed è concepito per essere utilizzato da thread concorrenti - al momento, la tempistica di questo includerà tutto il codice di serializzazione (
JsonConvert.SerializeObject
); questo si sommerà, soprattutto se i tuoi oggetti sono grandi; per ottenere una misura decente, ti consiglio vivamente di cronometrare la serializzazione e i tempi di redis separatamente - il
batch.Execute()
utilizza un'API della pipeline e non attende le risposte tra le chiamate, quindi:il tempo che vedi non l'effetto cumulativo della latenza; ciò lascia solo la CPU locale (per la serializzazione), la larghezza di banda della rete e la CPU del server; gli strumenti della libreria client non possono influire su nessuna di queste cose - c'è un
StringSet
sovraccarico che accetta unaKeyValuePair<RedisKey, RedisValue>[]
; tu potresti scegli di usarlo invece di un batch, ma l'unica differenza qui è che è il varadicMSET
anziché piùSET
; in ogni caso, bloccherai la connessione per altri chiamanti per la durata (poiché lo scopo del batch è rendere i comandi contigui) - non in realtà è necessario utilizzare
CreateBatch
qui, soprattutto dal momento che stai bloccando il database (ma suggerisco comunque che non sia necessario farlo); lo scopo diCreateBatch
consiste nel creare una sequenza di comandi sequenziali , ma non vedo che tu abbia bisogno di questo qui; potresti semplicemente usare_database.StringSetAsync
per ogni comando a sua volta, che sarebbe anche avere il vantaggio di eseguire la serializzazione parallelamente a il comando precedente inviato - ti consentirebbe di sovrapporre la serializzazione (rilegato alla CPU) e redis ops (rilegato all'IO) senza alcun lavoro tranne che per eliminare ilCreateBatch
chiamata; questo significherà anche che non monopolizzi la connessione di altri chiamanti
Così; il primo la cosa che farei sarebbe rimuovere un po' di codice:
private static StackExchange.Redis.IDatabase _database;
static JsonSerializerSettings _redisJsonSettings = new JsonSerializerSettings {
ContractResolver = new SerializeAllContractResolver(),
ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
public void SetAll<T>(Dictionary<string, T> data, int cacheTime)
{
TimeSpan expiration = new TimeSpan(0, cacheTime, 0);
var list = new List<Task<bool>>();
foreach (var item in data)
{
string serializedObject = JsonConvert.SerializeObject(
item.Value, Formatting.Indented, _redisJsonSettings);
list.Add(_database.StringSetAsync(item.Key, serializedObject, expiration));
}
Task.WhenAll(list.ToArray());
}
La seconda cosa che farei sarebbe programmare la serializzazione separatamente per il lavoro di redis.
La terza cosa che farei sarebbe vedere se riesco a serializzare su un MemoryStream
invece, idealmente uno che posso riutilizzare - per evitare la string
assegnazione e codifica UTF-8:
using(var ms = new MemoryStream())
{
foreach (var item in data)
{
ms.Position = 0;
ms.SetLength(0); // erase existing data
JsonConvert.SerializeObject(ms,
item.Value, Formatting.Indented, _redisJsonSettings);
list.Add(_database.StringSetAsync(item.Key, ms.ToArray(), expiration));
}
}