【问题标题】:How can retrieve values from a RedisResult if the result is a MultiBulk如果结果是 MultiBulk,如何从 RedisResult 中检索值
【发布时间】:2019-11-15 05:39:06
【问题描述】:

我可以通过执行以下命令来检索 Redis 数据库中的所有键:

RedisResult allRedisKeys = redisDatabase.Execute("KEYS", new List<object>
{
   "*"
})

但我不能使用变量 allRedisKeys,因为它的 _value 字段是私有的。如何从 RedisResult 中提取值?在我的例子中,_value 拥有一个 RedisResult[],而 allRedisKeys 的 RedisType 为 RedisType.MultiBulk。

【问题讨论】:

    标签: c# redis stackexchange.redis


    【解决方案1】:

    首先,根据the documentation,不要在生产环境中使用KEYS 命令。但是,您可以在每个服务器上使用 .Keys 方法,但这需要单独查询每个服务器(而且它是 O(N),所以也不是最好的)。

    如需更快(但更复杂)的解决方案,请查看 Marc Gravell 的 this answer。基本上,您将所有密钥存储在一个 SET 中,因此您有 O(1) 响应时间。

    这是我对他建议的实施:

    /// <summary>
    /// Class that keeps track of cache keys and the expiration of said keys. 
    /// Uses a LIST to store the keys because it's faster than using SCAN on the entire Redis database.
    /// Expirations are stored as ticks.
    /// </summary>
    public static class KeyAndExpirationTracker
    {
        private const string ALL_KEYS = "ALL_KEYS";
        private const string EXPIRATION_SUFFIX = "|EXPIRATION";
    
        public static void StoreKeyAndExpiration(IDatabase db, string key, TimeSpan? timeToLive, ITransaction transaction)
        {
            string expiryKey = $"{key}{EXPIRATION_SUFFIX}";
            // although Redis will automatically replace an existing key, 
            // we still have to remove this in case this we went from having an expiration to not expiring
            db.KeyDelete(expiryKey);
    
            // add the item key to the list of keys
            transaction.SetAddAsync(key);
            // add the separate cache item for expiration (if necessary)
            if (timeToLive.HasValue)
            {
                DateTime expiry;
                try
                {
                    expiry = DateTime.UtcNow.Add(timeToLive.Value);
                    transaction.StringSetAsync(expiryKey, expiry.Ticks.ToString());
                }
                catch (ArgumentOutOfRangeException)
                {
                    // no expiration then
                }
            }
        }
    
        public static void StoreKeyAndExpiration(IDatabase db, string key, DateTime? expiration, ITransaction transaction)
        {
            string expiryKey = $"{key}{EXPIRATION_SUFFIX}";
            // although Redis will automatically replace an existing key, we still have to remove this in case this we went from having an expiration to not expiring
            db.KeyDelete(expiryKey);
    
            // add the item key to the list of keys for this client type
            transaction.SetAddAsync(cacheKey, key);
            // add the separate cache item for expiration (if necessary)
            if (expiration.HasValue)
            {
                transaction.StringSetAsync(expiryKey, expiration.Value.Ticks.ToString());
            }
        }
    
        public static IEnumerable<string> GetKeys(IDatabase db)
        {
            RedisValue[] keys = db.SetMembers(ALL_KEYS));
            if (keys.Length == 0)
                return new string[0];
    
            List<string> expiredKeys = new List<string>(keys.Count());
            List<string> ret = new List<string>(keys.Count());
    
            // get expiration values using SORT with GET: https://redis.io/commands/sort
            RedisValue[] results = db.Sort(
                ALL_KEYS, 
                sortType: SortType.Alphabetic, 
                by: "notarealkey", // a (hopefully) non-existent key which causes the list to actually not be sorted
                get: new RedisValue[] { $"*{EXPIRATION_SUFFIX}", "#" } // # = "get the list item itself" which means the key in our case
            ));
    
            // SORT results will be in the same order as the GET parameters: 
            // result[0] = expiration (= null if no matching expiration item found)
            // result[1] = key
            // (repeat)
            for (int i = 0; i < results.Length; i += 2)
            {
                string key = results[i + 1].Replace(EXPIRATION_SUFFIX, string.Empty);
    
                RedisValue expiryRedis = results[i];
                long ticks;
                if (long.TryParse(expiryRedis, out ticks))
                {
                    DateTime expiry = new DateTime(ticks, DateTimeKind.Utc);
                    if (expiry <= DateTime.UtcNow)
                        expiredKeys.Add(key); // expired: add to list for removal
                    else
                        ret.Add(key);
                }
                else
                    ret.Add(key);
            }
    
            // remove any expired keys from list
            if (expiredKeys.Count > 0)
            {
                IBatch batch = db.CreateBatch();
                batch.SetRemoveAsync(cacheKey, expiredKeys.ConvertAll(x => (RedisValue)x).ToArray());
                foreach (string expiredKey in expiredKeys)
                {
                    batch.KeyDeleteAsync(expiredKey);
                }
                batch.Execute();
            }
    
            return ret;
       }
    
        public static void RemoveKeyAndExpiration(IDatabase db, string key, ITransaction transaction)
        {
            // remove the key from the list and its corresponding expiration value
            string expiryKey = $"{key}{EXPIRATION_SUFFIX}";
            transaction.SetRemoveAsync(ALL_KEYS, key);
            transaction.KeyDeleteAsync(expiryKey);
        }
    
    }
    

    注意:StoreKeyAndExpirationRemoveKeyAndExpiration 方法都采用ITransaction,因为我在执行这些操作时会同时添加或删除缓存中的项目,所以我想将它们全部打包到一个事务中。事务不会在此处执行,以防调用方法要向其添加更多操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多