ServiceStack.Redis 中关系操作的局限与bug

redis是文档型的,nosql中难处理的是关系。

比如人可以发博客,博客可以有分类。按照传统sql中,用户表和分类表都是主表,博客表是从表,有用户的外键和分类的外键

如果使用文档型的思考方式。

为用户A(User id=1)存储他的博客,在redis中是list或set

为分类A(Cate id=1)存储分类下的博客,在redis中是list或set

则当用户A向分类A中添加一条新博客时,需要同时向两个list(或set)中增加数据,而且理论上应该是事务的,修改的时候也需要同时修改两个。

这样的好处是读操作是完全优化的,直接从一个key中读出来的东西,马上就可以用

坏处是写操作太复杂,稍不注意可能就漏掉什么东西,更新博客需要更新非常多个list中的元素。

 

ServiceStack的redis客户端专门为这种情况提供了几个方法。

先来看实体类

public class User
{
    public int Id { get; set; }

    public string Name { get; set; }

}

public class Blog
{
    public int Id { get; set; }

    public string Title { get; set; }
}

public class Cate
{
    public int Id { get; set; }

    public string Name { get; set; }
}

很简单的3个类,用于表示用户,分类,博客3种概念

使用强类型的client保存3个实例

var clientsManager = new PooledRedisClientManager();
using (IRedisClient redis = clientsManager.GetClient())
{
    redis.FlushAll();

    var u = new User { Id = 1, Name = "A" };

    var c = new Cate { Id = 1, Name = "A" };

    var blog = new Blog { Id = 1, Title = "blog" };

    redis.As<User>().Store(u);
    redis.As<Cate>().Store(c);
    redis.As<Blog>().Store(blog);

}

可以通过客户端软件查看,3个实体都保存成功,但是并没有体现关系

redis.As<User>().StoreRelatedEntities(u.Id, blog);
redis.As<Cate>().StoreRelatedEntities(c.Id, blog);

之后调用保存关系的语句,as的是主表,第一个是主表主键,第二个是从对象

redis中,新建了2个key,ref:Cate/Blog:1和ref:User/Blog:1

他们的值是一个set

set中的具体内容并不是对象本身,而是对象在urn中的key

 

 

var blogs = redis.As<User>().GetRelatedEntities<Blog>(u.Id);

可以通过相关语句来获取从表内容

直接取到了blog的实体

 

但是在删除的时候有一个bug

他的方法指定的第二个参数是childId,所以我们传进去id,但是删除不掉

 

redis.As<User>().DeleteRelatedEntity<Blog>(u.Id, blog);

不使用id,而使用对象,也依然删除不掉

查看源码发现,当他运行从set中删除东西的时候,找key是对的,但是要被删掉的元素生成的不对

添加的时候,他拿UrnKey<T>(x)生成了实体保存的key,而删除的时候没有

删除的时候,直接是序列化的,则1,序列化后就是1,而我们的set中,并没有1这个值,所以是没有删掉任何东西的。

client的UrnKey是个internal的方法,再次被恶心了

redis.As<User>().DeleteRelatedEntity<Blog>(u.Id, (redis as RedisNativeClient).NamespacePrefix + IdUtils.CreateUrn(blog));

我们只能使用这么复杂的方式,等于把他内部的代码都拿到外面来处理了,当然你可以clone他的源码去改或者写扩展方法。

 

 

根据关系的key,我们大概可以分析出

ref:主表/从表:主表主键值

但这样的方式有一定的局限性,就是对同一个主从类型,他们之间只能表达一种关系。

比如人与博客,如果我需要表达 人写的博客,人推荐的博客 这两种关系(都是人与博客的),则无法实现

比如User 1,他写了Blog 1,推荐了Blog 2。但是他们都会被加入到ref:User/Blog:1中,无法区分是他写的还是他推荐的。

所以我们需要为两种类型之间的关系去给一个名字,来区分到底是那种关系

 

在RedisTypedClient<T>中有一个GetChildReferenceSetKey方法,是来生成这个key的,private方法,再次被恶心

当然,可以通过对NamespacePrefix设置一个不同的值来区分,但是感觉上怪怪的,因为这个在我看来是不同的应用程序,为防止key重复而设置的

有兴趣的朋友可以写几个扩展方法,反正源码基本都能看到

 

再再再次被恶心到的是,竟然github没有开放issues提交

时间: 2024-08-03 05:58:42

ServiceStack.Redis 中关系操作的局限与bug的相关文章

hibernate中关系操作(inverse)和级联操作(cascade)详解

以用户.角色.用户文件为例讲解inverse(关系操作)和(cascade)操作 inverse 取值 true(不维护关系)或false(维护关系  默认为false) 该属性主要操作的是外键 cascade 取值null(默认值).save-update .all .delete 用户.角色是多对多的关系 用户的映射文件表示: <hibernate-mapping> <class name="com.xing.elec.domain.ElecUser" table

Service-stack.redis 使用PooledRedisClientManager 速度慢的原因之一

现在越来越多的开发者使用service-stack.redis 来进行redis的访问,但是获取redisclient的方式有多种方式,其中有一种从缓冲池获取client的方式很是得到大家的认可. 1 List<string> listWrite = new List<string>() { "[email protected]:6380" }; 2 List<string> readHosts = new List<string>()

用C#封装的ServiceStack.redis操作类

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ServiceStack.Redis; namespace TestRedis { class RedisHelper:IDisposable { /*[email protected] All Rights Reserved * Author:Mars

ServiceStack.Redis 使用过程中碰到的两个问题

Redis是一个非常NB的内存级的数据库,我们可以把很多"热数据"(即读写非常多的数据)放入其中来操作,这样就减少了和关系型数据库(如SqlServer/My Sql等)之间的交互,程序的响应速度也大大提升. C#利用ServiceStack.Redis来操作Redis,它是Redis官方推荐的C#客户端,性能非常优越,使用也很方便,但是我最近在使用这个工具的时候碰到两个问题: 1.每小时只能访问Redis 6000次 2.用  GetById  方法获取不到对象. 第一个问题一开始本

Redis 在.Net中的使用 ServiceStack.Redis / StackExchange.Redis

NuGet 直接搜索安装 ServiceStack.Redis 代码如下: using ServiceStack.Redis; using System; namespace redisDemo { class Program { static void Main(string[] args) { RedisClient redisClient = new RedisClient("114.67.234.9", 6379);//redis服务IP和端口 Console.WriteLin

redis的hash操作在集中式session中的应用

在集群部署时,为了高可用性的目的,往往把session进行共享,共享分为两种:session复制和集中式管理. redis在session集中式管理中可以起到比较大的作用. 制约session集中式共享的两大因素: 1. session必须有ha机制,集群中部分服务器发生故障时,保证session不丢失. 2. session的生命周期管理. 3. session的大小未知,整体的序列化和反序列化成本比较高. redis的解决方式 1. redis具有持久化功能,且sentiment具有ha功效

【redis,1】java操作redis: 将string、list、map、自定义的对象保存到redis中

一.操作string .list .map 对象 1.引入jar: jedis-2.1.0.jar 2.代码 /** * @param args */ public static void main(String[] args) { //连接redis服务 Jedis jedis = new Jedis("192.168.88.15",6379); //密码验证-如果你没有设置redis密码可不验证即可使用相关命令 //        jedis.auth("abcdefg&

.NET中使用Redis之ServiceStack.Redis学习(一)安装与简单的运行

1.下载ServiceStack.Redis PM> Install-Package ServiceStack.Redis 2.vs中创建一个控制台程序 class Program { //构建Redis连接 static RedisClient redisClient = new RedisClient("127.0.0.1", 6379); static void Main(string[] args) { Console.WriteLine(string.Join(&quo

ServiceStack.Redis 数据操作

简单的字符串类型数据写. Poco在redis中会被序列化成Json字符串. 1 using (var redis = new RedisClient(connString)) 2 { 3 if (redis.Db != 7) 4 ((RedisClient)redis).ChangeDb(7); 5 6 var client = redis.As<Poco>(); 7 var list = new List<Poco>(); 8 9 foreach(var key in keys