Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 与基准测试相比,StackExchange redis客户端速度非常慢_C#_Redis_Stackexchange.redis - Fatal编程技术网

C# 与基准测试相比,StackExchange redis客户端速度非常慢

C# 与基准测试相比,StackExchange redis客户端速度非常慢,c#,redis,stackexchange.redis,C#,Redis,Stackexchange.redis,我正在使用StackExchangeRedis客户端实现一个Redis缓存层,目前的性能几乎无法使用 我有一个本地环境,其中web应用程序和redis服务器在同一台机器上运行。我在我的Redis服务器上运行了Redis基准测试,结果真的很好(我只是在我的编写中包含了set和get操作): C:\ProgramFiles\Redis>Redis基准-n 100000 =========PING_INLINE====== 在0.88秒内完成100000个请求 50个并行客户端 3字节有效负载 保持活

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

我有一个本地环境,其中web应用程序和redis服务器在同一台机器上运行。我在我的Redis服务器上运行了Redis基准测试,结果真的很好(我只是在我的编写中包含了set和get操作):

C:\ProgramFiles\Redis>Redis基准-n 100000
=========PING_INLINE======
在0.88秒内完成100000个请求
50个并行客户端
3字节有效负载
保持活力:1
=======套======
在0.89秒内完成100000个请求
50个并行客户端
3字节有效负载
保持活力:1

99.70%您以同步方式获取数据(50个客户端并行,但每个客户端的请求是同步而不是异步进行的)

一种选择是使用async/await方法(StackExchange.Redis支持该方法)


如果您需要一次获取多个键(例如,假设您每天保存访问者计数器键,则构建网站访问者的每日图表),那么您应该尝试使用异步方式从redis获取数据,这将为您提供更好的性能

下面代码中的我的结果:

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字节有效负载

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

50个并行客户端

目前还不清楚您的测试是否是并行的,但如果不是,我们完全可以预期原始吞吐量会降低。为了方便起见,SE.Redis被设计为易于并行化:您只需启动多个线程与同一个连接进行通信(这实际上还具有避免数据包碎片化的优点,因为每个数据包可以有多条消息,而单线程同步方法保证每个数据包最多使用一条消息)

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

(send, receive) x n
或者它在做什么

send x n, receive separately until all n are received
??这两种选择都是可能的。您的syncapi使用是第一个测试,但第二个测试同样定义良好,据我所知:这就是它所衡量的。有两种方法可以模拟第二种设置:

  • 发送第一(n-1)条带有“开火并忘记”标志的消息,这样您实际上只等待最后一条消息
  • 对所有消息使用
    *Async
    API,并且仅
    Wait()
    Wait
    最后一个
    任务
下面是我在上面使用的一个基准测试,它显示了“每操作同步”(通过同步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");
        }
    }
}
使用StackExchange.Redis;
使用制度;
使用系统诊断;
使用系统线程;
使用System.Threading.Tasks;
静态P类
{
静态void Main()
{
Console.WriteLine(“连接到服务器…”);
使用(var muxer=ConnectionMultiplexer.Connect(“127.0.0.1”))
{
控制台。写入线(“连接”);
var db=muxer.GetDatabase();
RedisKey=“some key”;
字节[]有效载荷=新字节[3];
新随机(12345)。下一个字节(有效载荷);
再贴现价值=有效载荷;
DoWork(“PING(每操作同步)”,db,1000000,50,x=>{x.PING();返回null;});
DoWork(“SET(sync per op)”,db,500000,50,x=>{x.StringSet(key,value);返回null;});
DoWork(“GET(每操作同步)”,db,500000,50,x=>{x.StringGet(key);返回null;});
DoWork(“PING(每线程流水线)”,db,1000000,50,x=>x.PingAsync();
DoWork(“SET(每线程流水线)”,db,500000,50,x=>x.StringSetAsync(key,value));
DoWork(“GET(每线程流水线)”,db,500000,50,x=>x.StringGetAsync(key));
}
}
静态void DoWork(字符串操作、IDatabase数据库、int计数、int线程、Func op)
{
对象启动=新对象(),关闭=新对象();
int activeThreads=0,outstandingOps=count;
秒表sw=默认值(秒表);
var threadStart=新的threadStart(()=>
{
锁定(启动)
{
如果(++activeThreads==线程)
{
sw=秒表。开始新();
监视器。脉冲密封(启动);
}
其他的
{
监视器。等待(启动);
}
}
任务final=null;
while(联锁减量(参考未完成操作)>=0)
{
最终=op(db);
}
如果(final!=null)final.Wait();
锁定(关闭)
{
如果(--activeThreads==0)
{
sw.Stop();
监视器。脉冲密封(关闭);
}
}
});
锁定(关闭)
{
对于(int i=0;i
旧版本的StackExchange redis客户端存在性能问题。 升级至最新版本。请在此处阅读更多信息:

在本文中:

这是回购协议中的问题:

我想知道为什么流水线产生的吞吐量比sync-proc低
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
(send, receive) x n
send x n, receive separately until all n are received
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");
        }
    }
}