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

use*_*100 29 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;
    }
Run Code Online (Sandbox Code Playgroud)

执行非常简单的命令(如获取和设置)通常会超时或需要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:
Run Code Online (Sandbox Code Playgroud)

编辑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);
    }
Run Code Online (Sandbox Code Playgroud)

编辑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
Run Code Online (Sandbox Code Playgroud)

Mik*_*der 22

以下是Azure Redis缓存文档中推荐的模式:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

一些要点:

  • 它使用Lazy <T>来处理线程安全的初始化
  • 它设置"abortConnect = false",这意味着如果初始连接尝试失败,ConnectionMultiplexer将在后台静默重试,而不是抛出异常.
  • 它并没有检查IsConnected财产,因为如果连接中断ConnectionMultiplexer会在后台自动重试.

  • @DirkBoer,根据文档(https://github.com/StackExchange/StackExchange.Redis/blob/master/Docs/Basics.md#using-a-redis-database),"从GetDatabase返回的对象是一个便宜的传递-thru对象,不需要存储." 我认为数据库对象的预期用途是为每个操作获取一个新实例. (2认同)

chr*_*989 5

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

我意识到缓存服务器位于与网站不同的位置.我更新了缓存服务器以与网站位于同一位置,现在一切都按预期工作.

同一页面现在在4-6秒内加载.

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


Sha*_*ula 3

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

注意:还要检查您的 Redis 缓存实例正在运行的 Azure 区域以及您的 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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 在Session_Start中这样做似乎有点奇怪。您不需要为每个连接到站点的新用户(会话)执行此操作。 (4认同)
  • 此方法的另一个问题是,在 ConnectionMultiplexer 实例被 CLR 垃圾收集器清理之前,连接将被泄漏。如果缓存负载过重,则可能会达到 10k 连接限制。也没有好的方法在旧的 ConnectionMultiplexer 上调用 Dispose(),因为另一个线程可能仍在尝试使用它。我们发现的最佳方法是忽略 IsConnected 属性并让 ConnectionMultiplexer 自行重试连接。 (3认同)
  • 静态,根据 CLR 中的定义*始终*相同。它是在整个应用程序域中共享的实例——它的实例化时间/地点/方式并不重要。我在 Azure 设置的 Redis 实例与我的应用程序位于完全相同的区域,并且在不更改任何代码的情况下,我已经迁移到 AWS,我没有遇到该问题。我能告诉你的就这么多了。您可以声称问题不是 Azure,但这只会让您听起来像是在 Microsoft 工作:) (2认同)