gpt4 book ai didi

c# - 与基准测试相比,StackExchange redis 客户端非常慢

转载 作者:IT王子 更新时间:2023-10-29 05:55:31 26 4
gpt4 key购买 nike

我正在使用 Stackexchange Redis 客户端实现一个 Redis 缓存层,而现在的性能几乎无法使用。

我有一个本地环境,其中 Web 应用程序和 Redis 服务器在同一台机器上运行。我针对我的 Redis 服务器运行了 Redis 基准测试,结果实际上非常好(我只是在我的文章中包括设置和获取操作):

C:\Program Files\Redis>redis-benchmark -n 100000
====== PING_INLINE ======
100000 requests completed in 0.88 seconds
50 parallel clients
3 bytes payload
keep alive: 1

====== SET ======
100000 requests completed in 0.89 seconds
50 parallel clients
3 bytes payload
keep alive: 1

99.70% <= 1 milliseconds
99.90% <= 2 milliseconds
100.00% <= 3 milliseconds
111982.08 requests per second

====== GET ======
100000 requests completed in 0.81 seconds
50 parallel clients
3 bytes payload
keep alive: 1

99.87% <= 1 milliseconds
99.98% <= 2 milliseconds
100.00% <= 2 milliseconds
124069.48 requests per second

因此,根据基准,我每秒查看超过 100,000 组和 100,000 次获取。我编写了一个单元测试来执行 300,000 次设置/获取:

private string redisCacheConn = "localhost:6379,allowAdmin=true,abortConnect=false,ssl=false";


[Fact]
public void PerfTestWriteShortString()
{
CacheManager cm = new CacheManager(redisCacheConn);

string svalue = "t";
string skey = "testtesttest";
for (int i = 0; i < 300000; i++)
{
cm.SaveCache(skey + i, svalue);
string valRead = cm.ObtainItemFromCacheString(skey + i);
}

}

这使用以下类通过 Stackexchange 客户端执行 Redis 操作:

using StackExchange.Redis;    

namespace Caching
{
public class CacheManager:ICacheManager, ICacheManagerReports
{
private static string cs;
private static ConfigurationOptions options;
private int pageSize = 5000;
public ICacheSerializer serializer { get; set; }

public CacheManager(string connectionString)
{
serializer = new SerializeJSON();
cs = connectionString;
options = ConfigurationOptions.Parse(connectionString);
options.SyncTimeout = 60000;
}

private static readonly Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(options));
private static ConnectionMultiplexer Connection => lazyConnection.Value;
private static IDatabase cache => Connection.GetDatabase();

public string ObtainItemFromCacheString(string cacheId)
{
return cache.StringGet(cacheId);
}

public void SaveCache<T>(string cacheId, T cacheEntry, TimeSpan? expiry = null)
{
if (IsValueType<T>())
{
cache.StringSet(cacheId, cacheEntry.ToString(), expiry);
}
else
{
cache.StringSet(cacheId, serializer.SerializeObject(cacheEntry), expiry);
}
}

public bool IsValueType<T>()
{
return typeof(T).IsValueType || typeof(T) == typeof(string);
}

}
}

我的 JSON 序列化程序只使用 Newtonsoft.JSON:

using System.Collections.Generic;
using Newtonsoft.Json;

namespace Caching
{
public class SerializeJSON:ICacheSerializer
{
public string SerializeObject<T>(T cacheEntry)
{
return JsonConvert.SerializeObject(cacheEntry, Formatting.None,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
}

public T DeserializeObject<T>(string data)
{
return JsonConvert.DeserializeObject<T>(data, new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});

}


}
}

我的测试时间约为 21 秒(300,000 组和 300,000 次获取)。这给了我每秒大约 28,500 次操作(至少比我预期使用基准测试慢 3 倍)。我正在转换为使用 Redis 的应用程序非常繁琐,某些繁重的请求可能对 Redis 进行大约 200,000 次总操作。显然,我没有预料到我在使用系统运行时缓存时会遇到同样的情况,但此更改后的延迟很明显。我的实现是否有问题,有人知道为什么我的基准数据比我的 Stackechange 测试数据快得多吗?

谢谢,保罗

最佳答案

我从下面的代码中得到的结果:

Connecting to server...
Connected
PING (sync per op)
1709ms for 1000000 ops on 50 threads took 1.709594 seconds
585137 ops/s
SET (sync per op)
759ms for 500000 ops on 50 threads took 0.7592914 seconds
658761 ops/s
GET (sync per op)
780ms for 500000 ops on 50 threads took 0.7806102 seconds
641025 ops/s
PING (pipelined per thread)
3751ms for 1000000 ops on 50 threads took 3.7510956 seconds
266595 ops/s
SET (pipelined per thread)
1781ms for 500000 ops on 50 threads took 1.7819831 seconds
280741 ops/s
GET (pipelined per thread)
1977ms for 500000 ops on 50 threads took 1.9772623 seconds
252908 ops/s

===

服务器配置:确保禁用持久性等

您应该在基准测试中做的第一件事是:基准测试一件事。目前,您包含了大量的序列化开销,这无助于获得清晰的画面。理想情况下,对于同类基准测试,您应该使用 3 字节的固定负载,因为:

3 bytes payload

接下来,您需要了解并行性:

50 parallel clients

不清楚您的测试是否是并行的,但如果不是,我们绝对期望看到更少的原始吞吐量。方便的是,SE.Redis 被设计成易于并行化:你可以启动多个线程与同一个连接(这实际上也有避免数据包碎片的优点,因为你最终可以每个数据包多条消息,其中单线程同步方法保证每个数据包最多使用一条消息。

最后,我们需要了解列出的基准测试在做什么。它在做什么:

(send, receive) x n

或者它在做什么

send x n, receive separately until all n are received

?两种选择都是可能的。您的同步 API 使用是第一个,但第二个测试同样定义明确,据我所知:这就是它正在测量的内容。有两种方法可以模拟第二个设置:

  • 发送前 (n-1) strip 有“即发即弃”标志的消息,因此您实际上只等待最后一条
  • 对所有消息使用 *Async API,并且仅对最后一个 Task 使用 Wait()await

这是我在上面使用的基准测试,它显示了“每个操作同步”(通过同步 API)和“每个线程的管道”(使用 *Async API 并等待每个线程的最后一个任务),都使用 50 个线程:

using StackExchange.Redis;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

static class P
{
static void Main()
{
Console.WriteLine("Connecting to server...");
using (var muxer = ConnectionMultiplexer.Connect("127.0.0.1"))
{
Console.WriteLine("Connected");
var db = muxer.GetDatabase();

RedisKey key = "some key";
byte[] payload = new byte[3];
new Random(12345).NextBytes(payload);
RedisValue value = payload;
DoWork("PING (sync per op)", db, 1000000, 50, x => { x.Ping(); return null; });
DoWork("SET (sync per op)", db, 500000, 50, x => { x.StringSet(key, value); return null; });
DoWork("GET (sync per op)", db, 500000, 50, x => { x.StringGet(key); return null; });

DoWork("PING (pipelined per thread)", db, 1000000, 50, x => x.PingAsync());
DoWork("SET (pipelined per thread)", db, 500000, 50, x => x.StringSetAsync(key, value));
DoWork("GET (pipelined per thread)", db, 500000, 50, x => x.StringGetAsync(key));
}
}
static void DoWork(string action, IDatabase db, int count, int threads, Func<IDatabase, Task> op)
{
object startup = new object(), shutdown = new object();
int activeThreads = 0, outstandingOps = count;
Stopwatch sw = default(Stopwatch);
var threadStart = new ThreadStart(() =>
{
lock(startup)
{
if(++activeThreads == threads)
{
sw = Stopwatch.StartNew();
Monitor.PulseAll(startup);
}
else
{
Monitor.Wait(startup);
}
}
Task final = null;
while (Interlocked.Decrement(ref outstandingOps) >= 0)
{
final = op(db);
}
if (final != null) final.Wait();
lock(shutdown)
{
if (--activeThreads == 0)
{
sw.Stop();
Monitor.PulseAll(shutdown);
}
}
});
lock (shutdown)
{
for (int i = 0; i < threads; i++)
{
new Thread(threadStart).Start();
}
Monitor.Wait(shutdown);
Console.WriteLine($@"{action}
{sw.ElapsedMilliseconds}ms for {count} ops on {threads} threads took {sw.Elapsed.TotalSeconds} seconds
{(count * 1000) / sw.ElapsedMilliseconds} ops/s");
}
}
}

关于c# - 与基准测试相比,StackExchange redis 客户端非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35707867/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com