【问题标题】:How to convert Redis Value[] into Deserialize object c#?如何将 Redis Value[] 转换为反序列化对象 c#?
【发布时间】:2021-01-26 19:56:01
【问题描述】:

我正在处理 webjob 中的列表排序。 当我使用 c# List 时它工作正常。但为了提高性能,我将数据以列表的形式保存到 Redis 缓存中。

我的最终目标是只取出最近 5 分钟的数据。

工作 c# 代码 -

public class MyObject
    {
        public uint InstrumentID { get; set; }
        public decimal Close { get; set; }
        public decimal High { get; set; }
        public decimal Low { get; set; }
        public decimal Open { get; set; }
        public DateTime TimeStamp { get; set; }
        public uint Volume { get; set; }

        public DateTime Created { get; } = DateTime.Now;
        public DateTime Ttl { get; } = DateTime.Now.AddMinutes(5);
        public DateTime? Persisted { get; set; }

        public bool IsDead => DateTime.Now > Ttl;
        public bool IsPersisted => Persisted.HasValue;
        public bool TimeToPersist => IsPersisted == false && DateTime.Now > Created.AddMinutes(5);

        public DateTime GetStartOfPeriodByMins(int numMinutes)
        {
            int oldMinutes = TimeStamp.Minute;
            int newMinutes = (oldMinutes / numMinutes) * numMinutes;

            DateTime startOfPeriod = new DateTime(TimeStamp.Year, TimeStamp.Month, TimeStamp.Day, TimeStamp.Hour, newMinutes, 0);

            return startOfPeriod;
        }
    }

var inputList = new SortedSet<MyObject>(new MyObjectComparer());

            inputList.Add(new MyObject() { TimeStamp = DateTime.Now, Open = 9, High = 12, Low = 8, Close = 11, InstrumentID = 2526 });
            Thread.Sleep(10000);
            inputList.Add(new MyObject() { TimeStamp = DateTime.Now, Open = 9, High = 12, Low = 8, Close = 11, InstrumentID = 2526 });
            Thread.Sleep(10000);
            inputList.Add(new MyObject() { TimeStamp = DateTime.Now, Open = 9, High = 12, Low = 8, Close = 11, InstrumentID = 2526 });
            Thread.Sleep(50000);
            inputList.Add(new MyObject() { TimeStamp = DateTime.Now, Open = 9, High = 12, Low = 8, Close = 11, InstrumentID = 2526 });

            var resultSet = inputList
                .GroupBy(i => i.GetStartOfPeriodByMins(5))
                .Select(gr =>
               new
               {
                   StartOfPeriod = gr.Key,
                   Min = gr.Min(item => item.Open),
                   Max = gr.Max(item => item.Open),
                   Open = gr.OrderBy(item => item.TimeStamp).First().Open,
                   Close = gr.OrderBy(item => item.TimeStamp).Last().Open
               });

现在我不断地将相同的记录插入到 redis 缓存中。在尝试获取最后 5 分钟的数据时,我想使用相同的 GetStartOfPeriodByMins 概念,但它需要 MyObject 类的列表,并且 redis 返回 RedisValue[]。

Redis 代码 - 使用 StackExchange.Redis 包

var cache = RedisConnectorHelper.Connection.GetDatabase();

            //int i = 0;
            for (int i = 0; i < 10000; i++)
            {
                var tickDataHis = new MyObject()
                {
                    InstrumentID = 2526,
                    Close = 14 + i,
                    High = 16 + i,
                    Low = 11 + i,
                    Open = 12 + i,
                    TimeStamp = DateTime.Now,
                    Volume = 11111
                };

                // insert into redis
                cache.ListRightPush("tickData", JsonConvert.SerializeObject(tickDataHis));

                Thread.Sleep(3000);
            }

            var inputList = cache.ListRange("tickData");

或者有没有其他方法可以从 redis 获取最新的 5 分钟数据 缓存?

【问题讨论】:

  • 我有一段时间没有使用 StackExchange.Redis,但据我所知。它没有内置解串器。因此,您可以为您的代码逐个实现,也可以只使用 JSON 反序列化器。
  • 我尝试使用 json 反序列化器,但如果您发现任何代码 sn-p,则很难从 Redis Value [] 转换数据,请分享谢谢
  • 我过去使用过(我认为)这个 nuget 包。 nuget.org/packages/StackExchange.Redis.Extensions.Newtonsoft

标签: c# c#-4.0 redis stackexchange.redis


【解决方案1】:

过去我使用 Redis 存储时间序列数据。为了优化数据检索,我使用了一个排序集(在这种情况下有多个,但概念相同),其中分数是记录数据时的 unix 时间戳,我使用 @ 序列化我的数据987654325@图书馆。

代码是这样的:

var myData = new MyObject() { SomeProperty = "my text" };
long dataTimestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
string serializedData = Newtonsoft.Json.JsonConvert.Convert(myData);
redisDb.SortedSetAdd("mySortedSet", dataTimestamp, serializedData);

这样做,如果你想只检索最近5分钟的数据,可以直接使用SortedSetRangeByScore方法过滤从Redis加载的数据,并传递“now - 5 minutes " 作为起始分数,这样你就可以只反序列化你需要的东西(这当然比反序列化整个列表要便宜):

var minutesToTake = 5;
long startingTime = DateTimeOffset.Now.AddMinutes(-minutesToTake).ToUnixTimeMilliseconds();
RedisValue[] redisData = redisDb.SortedSetRangeByScore("mySortedSet", startingTime);

之后,您可以借助 linq 轻松反序列化您的数据(考虑 RedisValue 实现运算符到字符串的转换):

MyObject[] myData = redisData.Select(d => JsonConvert.DeserializeObject<MyObject>(d)).ToArray();

编辑:我没有使用@Hasan Emrah Süngü 建议的包。也许效率更高,我只是在解释我当时做了什么。

编辑 2redisDb 是我的 StackExchange.Redis.IDatabase 实例。

编辑 3:有用的参考链接:

【讨论】:

  • 我唯一的问题是,如果我的 redis 缓存不断更新新数据并且我试图在每 5 分钟后获取最新的 5 分钟数据,可以吗?
  • @Neo 它可以工作,因为它会不断获取最新数据,并且只有那些数据会被反序列化,但我建议处理一些保留策略。 Redis 使您可以在键上设置 TTL(请参阅此处 redis.io/topics/…),但不幸的是,不能在列表和集合的单个项目上。 [在下一条评论中继续]
  • @Neo 一旦你为你的数据决定了一个 TTL,你有两种可能:你可以创建一个计时器,它会删除比你想要的持久性更旧的数据(使用方法SortedSetRemoveRangeByScore,通过分数从 0 到“现在 - TTL”),或者您可以创建更多集合(按分钟/小时划分,因为这对您更好)在键上设置所需的 TTL,但在这种情况下,您必须更改加载逻辑,因为您需要考虑多个集合才能检索所需的所有数据。我在答案中放了一些有用的参考链接。
猜你喜欢
  • 2021-10-06
  • 1970-01-01
  • 2018-10-11
  • 1970-01-01
  • 1970-01-01
  • 2020-07-19
  • 2011-12-31
  • 1970-01-01
  • 2017-06-10
相关资源
最近更新 更多