使用Azure Redis的StackExchange.Redis速度非常慢或引发超时错误

时间:2014-08-21 00:33:56

标签: c# asp.net caching azure redis

我将现有的Azure In-Role缓存使用全部移至Redis,并决定将Azure Redis预览与StackExchange.Redis库(https://github.com/StackExchange/StackExchange.Redis)一起使用。我为它编写了所有代码没有太多问题,但是在运行时它绝对非常慢并且不断抛出超时错误(我的超时时间设置为15秒)。

以下是我如何设置Redis连接并将其用于简单操作的相关代码:

    private static ConnectionMultiplexer _cacheService;
    private static IDatabase _database;
    private static object _lock = new object();

    private void Initialize()
    {
        if (_cacheService == null)
        {
            lock (_lock)
            {
                if (_cacheService == null)
                {
                    var options = new ConfigurationOptions();
                    options.EndPoints.Add("{my url}", 6380);
                    options.Ssl = true;
                    options.Password = "my password";
                    // needed for FLUSHDB command
                    options.AllowAdmin = true;

                    // necessary?
                    options.KeepAlive = 30;
                    options.ConnectTimeout = 15000;
                    options.SyncTimeout = 15000;

                    int database = 0;

                    _cacheService = ConnectionMultiplexer.Connect(options);
                    _database = _cacheService.GetDatabase(database);
                }
            }
        }

    }

    public void Set(string key, object data, TimeSpan? expiry = null)
    {
        if (_database != null)
        {
            _database.Set(key, data, expiry: expiry);
        }
    }

    public object Get(string key)
    {
        if (_database != null)
        {
            return _database.Get(key);
        }
        return null;
    }

执行非常简单的命令(如获取和设置)通常会超时或需要5-10秒才能完成。似乎它有点否定了将它用作缓存的全部目的,如果它比实际从我的数据库中获取真实数据慢得多:)

我做了什么明显错误的事吗?

编辑:以下是我从服务器中提取的一些统计信息(使用Redis桌面管理器),以防任何事情发生。

Server
redis_version:2.8.12
redis_mode:standalone
os:Windows  
arch_bits:64
multiplexing_api:winsock_IOCP
gcc_version:0.0.0
process_id:2876

tcp_port:6379
uptime_in_seconds:109909
uptime_in_days:1
hz:10
lru_clock:16072421
config_file:C:\Resources\directory\xxxx.Kernel.localStore\1\redis_2092_port6379.conf

Clients
connected_clients:5
client_longest_output_list:0
client_biggest_input_buf:0
client_total_writes_outstanding:0
client_total_sent_bytes_outstanding:0
blocked_clients:0

Memory
used_memory:4256488
used_memory_human:4.06M
used_memory_rss:67108864
used_memory_rss_human:64.00M
used_memory_peak:5469760
used_memory_peak_human:5.22M
used_memory_lua:33792
mem_fragmentation_ratio:15.77
mem_allocator:dlmalloc-2.8

Persistence
loading:0
rdb_changes_since_last_save:72465
rdb_bgsave_in_progress:0
rdb_last_save_time:1408471440
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

Stats
total_connections_received:25266
total_commands_processed:123389
instantaneous_ops_per_sec:10
bytes_received_per_sec:275
bytes_sent_per_sec:65
bytes_received_per_sec_human:

编辑2 :以下是我用于Get / Set的扩展方法 - 它们是非常简单的方法,只需将对象转换为JSON并调用StringSet即可。

    public static object Get(this IDatabase cache, string key)
    {
        return DeserializeJson<object>(cache.StringGet(key));
    }

    public static void Set(this IDatabase cache, string key, object value, TimeSpan? expiry = null)
    {
        cache.StringSet(key, SerializeJson(value), expiry: expiry);
    }

编辑3 :以下是一些示例错误消息:

    A first chance exception of type 'System.TimeoutException' occurred in StackExchange.Redis.dll
    Timeout performing GET MyCachedList, inst: 11, queue: 1, qu=1, qs=0, qc=0, wr=0/1, in=0/0

    A first chance exception of type 'System.TimeoutException' occurred in StackExchange.Redis.dll
    Timeout performing GET MyCachedList, inst: 1, queue: 97, qu=0, qs=97, qc=0, wr=0/0, in=3568/0

5 个答案:

答案 0 :(得分:22)

以下是推荐的模式,来自Azure Redis Cache documentation

private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() => {
    return ConnectionMultiplexer.Connect("mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");
});

public static ConnectionMultiplexer Connection {
    get {
        return lazyConnection.Value;
    }
}

一些要点:

  • 它使用Lazy&lt; T&gt;。处理线程安全初始化
  • 设置“abortConnect = false”,这意味着如果初始连接尝试失败,ConnectionMultiplexer将在后台静默重试,而不是抛出异常。
  • 检查IsConnected属性,因为如果连接断开,ConnectionMultiplexer将在后台自动重试。

答案 1 :(得分:5)

我遇到了类似的问题。 Redis缓存异常缓慢,但肯定是缓存。在某些情况下,加载页面需要20-40秒。

我意识到缓存服务器位于与网站不同的位置。 我更新了缓存服务器,使其与网站位于同一位置,现在一切正常。

同一页现在加载4-6秒。

祝所有遇到这些问题的人好运。

答案 2 :(得分:4)

问题是如何创建和使用连接对象。我们最初遇到了确切的问题,并通过在所有Web请求中使用的单个连接对象进行了修复。我们检查它是空的还是在会话开始时连接,以便优雅地重新创建对象。解决了这个问题。

  

注意:还要检查Azure的哪个区域是您的Redis缓存实例   运行以及Web服务器所在的区域。最好维护   在同一区域

在Global.ascx.cs文件中

public static ConnectionMultiplexer RedisConnection;
public static IDatabase RedisCacheDb;

protected void Session_Start(object sender, EventArgs e)
    {
        if (ConfigurationManager.ConnectionStrings["RedisCache"] != null)
        {
            if (RedisConnection == null || !RedisConnection.IsConnected)
            {
                RedisConnection = ConnectionMultiplexer.Connect(ConfigurationManager.ConnectionStrings["RedisCache"].ConnectionString);
            }
            RedisCacheDb = RedisConnection.GetDatabase();
        }
    }

答案 3 :(得分:3)

在我们的案例中,问题是使用SSL连接时。您显示您的桌面管理器正在非SSL端口上运行,但您的代码正在使用SSL。

我们的Azure redis没有SSL的快速基准测试,使用LRANGE命令(也使用.net和StackExchange.Redis)检索大约80k值基本上是instand。使用SSL时,同一查询需要27秒。

WebApp:标准S2

Redis:标准1 GB

编辑:检查SLOWLOG,Redis本身似乎实际上命中了慢速日志,它需要大约14ms的时间才能获取行,但这与启用SSL的实际传输相差甚远。我们最终得到了一个优质的Redis,以便在Redis和Web Apps之间实现某种安全性。

答案 4 :(得分:2)

它适用于我的情况。不要忘记增加SyncTimeout。默认值为1秒。

private static Lazy<ConnectionMultiplexer> ConnectionMultiplexerItem = new Lazy<ConnectionMultiplexer>(() =>
{
    var redisConfig = ConfigurationOptions.Parse("mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");

    redisConfig.SyncTimeout = 3000;

    return ConnectionMultiplexer.Connect(redisConfig);
});

检查Azure中的Azure Redis缓存和客户端是否位于同一区域。例如,当您的缓存位于美国东部但客户端位于美国西部且请求未在同步超时时间内完成时,您可能会遇到超时,或者当您从本地开发机器x调试时可能会超时。 强烈建议将缓存和客户端放在同一个Azure区域中。如果您有进行跨区域调用的方案,则需要将synctimeout设置为更高的值。

了解更多: https://azure.microsoft.com/en-us/blog/investigating-timeout-exceptions-in-stackexchange-redis-for-azure-redis-cache/