• Redis基础(转)


    ServiceStack.Redis实践
       Redis的C#客户端我选择的是ServiceStack.Redis,相比Booksleeve redis-sharp等方案,它提供了一整套从 Redis数据结构都强类型对象转换的机制;看一个例子来了解一下ServiceStack.Redis是如何组织数据的,我们使用的实体类定义如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class User
       {
           public User()
           {
               this.BlogIds = new List<long>();
           }
     
           public long Id { get; set; }
           public string Name { get; set; }
           public List<long> BlogIds { get; set; }
       }</long></long>

    使用下面的代码片段,我们存入两条数据到Redis:
    1
    2
    3
    4
    5
    6
    7
    using (var redisUsers = redisClient.GetTypedClient<user>())
            {
                var ayende = new User { Id = redisUsers.GetNextSequence(), Name = "Oren Eini" };
                var mythz = new User { Id = redisUsers.GetNextSequence(), Name = "Demis Bellot" };
                redisUsers.Store(ayende);
                redisUsers.Store(mythz);
              }</user>

    我们看下Redis中的结果:
    1
    2
    3
    4
    5
    redis 127.0.0.1:6379[1]> keys *
    1) "seq:User"
    2) "ids:User"
    3) "urn:user:1"
    4) "urn:user:2"

    我们逐一检查一下数据类型:
     seq:User    
    string  
    维护当前类型User的ID自增序列,用做对象唯一ID
     ids:User
    set       
    同一类型User所有对象ID的列表
     urn:user:1
    string 
    user对象
    seq:User 维护的是类型User的ID序列 redisUsers.GetNextSequence()
    1
    2
    3
    4
    5
    6
    7
    8
    public long GetNextSequence(int incrBy)
      {
                 return IncrementValueBy(SequenceKey, incrBy);
      }
     public long IncrementValue(string key)
        {
                  return client.Incr(key);
         }

    这里的SequenceKey就是 "seq:User",然后我们通过存一个对象到Redis看另外两个key是什么作用:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public T Store(T entity)
           {
               var urnKey = entity.CreateUrn();
               this.SetEntry(urnKey, entity);
     
               return entity;
           }
           //entity.CreateUrn();的结果是"urn:user:1"
           public void SetEntry(string key, T value)
           {
               if (key == null)
                   throw new ArgumentNullException("key");
     
               client.Set(key, SerializeValue(value));
               client.RegisterTypeId(value);
           }
     
           internal void RegisterTypeId<t>(T value)
           {
               var typeIdsSetKey = GetTypeIdsSetKey<t>();
               var id = value.GetId().ToString();
     
               if (this.Pipeline != null)
               {
                   var registeredTypeIdsWithinPipeline = GetRegisteredTypeIdsWithinPipeline(typeIdsSetKey);
                   registeredTypeIdsWithinPipeline.Add(id);
               }
               else
               {
                   this.AddItemToSet(typeIdsSetKey, id);
               }
           }</t></t>
    这里的typedIdsSetKey 就是"ids:User" 

    ids:User相当于一个索引,包含了所有同为类型User的ID;由于维护了这样一个分组信息,所以很容易实现GetAll()这样的功能;

    在redis-cli中查询一下 get urn:user:1 返回值是 JSON格式:
    "{"Id":1,"Name":"Oren Eini","BlogIds":[1]}"
     
    ServiceStack.Redis 自己实现了一套序列化功能, Fastest JSON Serializer for .NET released  支持 POCO(Plain Old CLR Object)序列化.
     
       实际应用中,由于我们使用的数据是来自关系型数据库,本身包含关联关系,所以并不需要这样的对象组织方式;我们只需要把关系型数据中一对多的关系在Redis中表达出来即可;这里我扩展修改了RedisClient的实现,由于RedisClient本身已经通过 partial方式 分割成若干个文件,所以很容易把变动的代码集中在同一个代码文件中.具体业务对象存储,主帖和回帖会有字段级修改,所以设计成为Hash结构,其它几个子对象读写都是以对象为单位,设计成为POCO方式持久化;
    使用管道Pipeline遇到的问题

      使用管道可以将客户端到Redis的往返次数减少,不过在使用ServiceStack.Redis的时候,遇到这样一个问题,比如要把一个List全部存储,代码不可以写成下面这样:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    %%第一种写法
               logs.ForEach(n =>
                    {
                        pipeline.QueueCommand(r =>
                        {
                            ((RedisClient)r).Store<oplog>(n, n.GetObjectID(), n.GetUrnKey());
                            ((RedisClient)r).Expire(n.GetUrnKey(), dataLifeTime);
                        });
                    });</oplog>

    而是要写成这样:
    1
    2
    3
    4
    5
    6
    7
    8
    %%第二种写法
     logs.ForEach(n =>
                    {
       
                        pipeline.QueueCommand(r => ((RedisClient)r).Store<log>(n, n.ID, n.GetUrnKey()));
                        pipeline.QueueCommand(r => ((RedisClient)r).Expire(n.GetUrnKey(), dataLifeTime));
     
                    });</log>

    什么原因呢?RedisQueueCompletableOperation的AddCurrentQueuedOperation方法会在 执行CurrentQueuedOperation = null;如果按照第一种写法会丢失回调函数,这就造成有返回值在没有及时提取,后续的操作获取返回值时首先取到的是积压的结果信息,就出现了异常,而第 二种写法就避免了这个问题.
    1
    2
    3
    4
    5
    protected virtual void AddCurrentQueuedOperation()
          {
              this.QueuedCommands.Add(CurrentQueuedOperation);
              CurrentQueuedOperation = null;
          }
  • 相关阅读:
    iOS开发之静态库(二)—— .a
    iOS开发之静态库(一)—— 基本概念
    Linux中ctrl-c, ctrl-z, ctrl-d 区别
    JNI技术基础(1)——从零开始编写JNI代码
    开篇纪念
    java面试题
    jvm系列二之GC收集器
    jvm系列一
    ConcurrentHashMap源码剖析(1.8版本)
    博客系统对比
  • 原文地址:https://www.cnblogs.com/tyhj-zxp/p/4825631.html
Copyright © 2020-2023  润新知