標籤:
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提交
ServiceStack.Redis 中關係操作的局限與bug