„jen“ je velmi relativní pojem a ve skutečnosti nedává smysl bez dalšího kontextu, zejména:jak velké jsou tyto užitečné zatížení?
abychom však objasnili několik bodů, které vám pomohou při vyšetřování:
- není potřeba zamykat
IDatabase
pokud to není čistě pro vaše vlastní účely; SE.Redis se interně zabývá bezpečností vláken a je určen pro použití konkurenčními vlákny - v tuto chvíli bude vaše načasování zahrnovat veškerý serializační kód (
JsonConvert.SerializeObject
); to se přidá, zejména pokud jsou vaše předměty velké; abyste získali slušnou míru, důrazně doporučuji načasovat serializaci a časy redis samostatně batch.Execute()
metoda používá pipeline API a nečeká na odpovědi mezi voláními, takže:čas, který vidíte, není kumulativní účinek latence; to ponechává pouze místní CPU (pro serializaci), šířku pásma sítě a CPU serveru; nástroje klientské knihovny nemohou žádnou z těchto věcí ovlivnit- existuje
StringSet
přetížení, které přijímáKeyValuePair<RedisKey, RedisValue>[]
; mohli zvolte použít toto místo dávky, ale jediný rozdíl je v tom, že je to varadicMSET
spíše než víceSET
; v každém případě po dobu trvání blokujete spojení pro ostatní volající (protože účelem dávky je zajistit souvislé příkazy) - ve skutečnosti ne musíte použít
CreateBatch
zde, zejména protože zamykáte databázi (ale přesto vám doporučuji, abyste to nemuseli dělat); účelCreateBatch
je vytvořit sekvenci příkazů sekvenčně , ale nevidím, že to tady potřebujete; stačí použít_database.StringSetAsync
pro každý příkaz v pořadí, což by také mají výhodu, že byste spouštěli serializaci paralelně s odesílání předchozího příkazu - umožnilo by vám to překrýt serializaci (vázáno na CPU) a redis ops (vázáno na vstup) bez jakékoli práce kromě odstraněníCreateBatch
volání; to bude také znamenat, že nebudete monopolizovat připojení od ostatních volajících
Tak; první co bych udělal, by bylo odstranit nějaký kód:
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());
}
Druhá věc, kterou bych udělal, by bylo načasovat serializaci odděleně od práce redis.
Třetí věcí, kterou bych udělal, by bylo zjistit, zda mohu serializovat do MemoryStream
místo toho ideálně takovou, kterou mohu znovu použít – abych se vyhnul string
alokace a kódování 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));
}
}