【问题标题】:Improve performance of Redis to reduce timeout exceptions提高 Redis 的性能以减少超时异常
【发布时间】:2020-02-10 04:21:21
【问题描述】:

我在 Centos 服务器上有一个 Redis 数据库,3 个 Windows 服务器连接到它,每秒大约 1,000 次读取/写入,所有这些都在同一个本地 LAN 上,因此 ping 时间不到一毫秒。 问题是至少 5% 的读取操作会超时,而我在“syncTimeout=15”的读取操作中读取最大 3KB 数据,这远远超过网络延迟。

我在 Windows 10 上的 bash 上安装了 Redis,并模拟了问题。我也停止了写作操作。但是,0.5% 的超时问题仍然存在,而没有网络延迟。 我还在我的 LAN 中使用了 Centos 服务器来模拟问题,在这种情况下,我需要 100 毫秒的“syncTimeout”来确保超时量小于 1%。 我考虑使用一些字典来缓存来自 Redis 的数据,因此不需要逐项请求,我可以利用管道。但是我遇到了 StackRedis.L1,它是作为 Redis 的 L1 缓存开发的,它对更新 L1 缓存没有信心。

这是我模拟问题的代码:

    var connectionMulti = ConnectionMultiplexer.Connect(
            "127.0.0.1:6379,127.0.0.1:6380,allowAdmin=true,syncTimeout=15");

    // 100,000 keys
    var testKeys = File.ReadAllLines("D:\\RedisTestKeys.txt");

    for (var i = 0; i < 3; i++)
    {
        var safeI = i;
        Task.Factory.StartNew(() =>
        {
            var serverName = $"server {safeI + 1}";
            var stringDatabase = connectionMulti.GetDatabase(12);
            PerformanceTest($"{serverName} -> String: ",
                key => stringDatabase.StringGet(key), testKeys);
        });
    }

PerformanceTest 方法是:

private static void PerformanceTest(string testName, Func<string, RedisValue> valueExtractor,
        IList<string> keys)
    {
        Task.Factory.StartNew(() =>
        {
            Console.WriteLine($"Starting {testName} ...");
            var timeouts = 0;
            var errors = 0;
            long totalElapsedMilliseconds = 0;

            var stopwatch = new Stopwatch();
            foreach (var key in keys)
            {
                var redisValue = new RedisValue();
                stopwatch.Restart();
                try
                {
                    redisValue = valueExtractor(key);

                }
                catch (Exception e)
                {
                    if (e is TimeoutException)
                        timeouts++;
                    else
                        errors++;
                }
                finally
                {
                    stopwatch.Stop();
                    totalElapsedMilliseconds += stopwatch.ElapsedMilliseconds;
                    lock (FileLocker)
                    {
                        File.AppendAllLines("D:\\TestResult.csv",
                            new[]
                            {
                                $"{stopwatch.ElapsedMilliseconds.ToString()},{redisValue.Length()},{key}"
                            });
                    }
                }
            }

            Console.WriteLine(
                $"{testName} {totalElapsedMilliseconds * 1.0 / keys.Count} (errors: {errors}), (timeouts: {timeouts})");
        });
    }

我希望所有读取操作都将在 15 毫秒内成功完成。 实现这一点,考虑 Redis 缓存的 L1 缓存是一个好的解决方案吗? (它非常快,以纳秒为单位,但我该怎么做才能同步) 或者 Redis 可以通过集群或其他方式来增强? (虽然我在 PC 上的 bash 上对其进行了测试,但没有收到预期的结果)

【问题讨论】:

标签: c# performance redis


【解决方案1】:

或者 Redis 可以通过集群或其他方式来增强?

Redis 可以以不同的方式进行集群:

  • “常规”redis可以复制到辅助只读节点,在同一台机器或不同机器上;然后,您可以向某些副本发送“读取”流量
  • 存在redis“集群”,它允许您将键空间拆分(分片)到多个主节点,向每个节点发送适当的请求
  • redis“集群”也可以使用分片节点的只读副本

这是否适当或有用取决于上下文,需要本地知识和测试。

实现这一点,考虑将 L1 缓存用于 Redis 缓存是一个好的解决方案吗?

是的,这是一个很好的解决方案。您不提出的请求比您提出的请求要快得多(并且对影响的影响要小得多)。有一些工具可以帮助缓存失效,包括使用 pub/sub API 进行失效。 Redis vNext 还在研究针对这种 L1 场景的其他知识 API专门

【讨论】:

    猜你喜欢
    • 2022-10-04
    • 2019-10-01
    • 1970-01-01
    • 2011-05-18
    • 1970-01-01
    • 2019-11-02
    • 1970-01-01
    • 2021-09-17
    • 1970-01-01
    相关资源
    最近更新 更多