標簽服務是一個較為通用的基礎業(yè)務服務,比如博客系統(tǒng)對文章加標簽、社交網(wǎng)絡中為好友添加印象、收藏的歌曲貼標簽方便整理等等。
其主要提供兩類接口:
標簽實體的管理 / 查詢:負責標簽實體的 CRUD
標簽關聯(lián)的管理 / 查詢:將外部業(yè)務實體與標簽建立 / 刪除關聯(lián),根據(jù)外部業(yè)務實體 id 查詢標簽集
RDB 實現(xiàn)
基于關系型數(shù)據(jù)庫的實現(xiàn)是最容易的,并且上大多數(shù)應用也是這樣做的。
建立 tag
表,其中包含了 tag 的基礎屬性,例如 name
、description
等
建立 tag_rel
關聯(lián)表,其中主要包含了 object_id
、tag_id
管理服務(創(chuàng)建 / 更新 / 刪除)的實現(xiàn)非常容易;根據(jù) object_id
查詢其對應的標簽集也很容易實現(xiàn):
SELECT tag_rel.tag_id, tag_rel.object_id, tag.nameFROM tag_relLEFT JOIN tag ON tag_rel.tag_id = tag.tag_idWHERE tag_rel.object_id = '2db775c1d2174a8c67fc39b86c3fc168'
問題
RDB 實現(xiàn)標簽服務時最主要會面臨的是性能問題,隨著 tag_rel
數(shù)據(jù)量的不斷增長,上面查詢的性能會不斷下降。
優(yōu)化
數(shù)據(jù)庫
給列 tag_rel.object_id
加普通索引
升級數(shù)據(jù)庫服務器硬件配置
使用數(shù)據(jù)庫產(chǎn)品的特性,比如表分區(qū) / 內存表
在應用層面,可以緩存查詢結果,如果實時性要求不高,緩存機制是比較容易實現(xiàn)的,但大多數(shù)業(yè)務場景可能并不適用。
分表
前面提到的數(shù)據(jù)庫產(chǎn)品的表分區(qū)特性(Oracle)我們也可以在應用層代碼通過分表來實現(xiàn)。
建立多張標簽關聯(lián)表 tag_rel_0
、tag_rel_1
、...、tag_rel_31
將查詢條件 object_id
通過某種散列算法(比如 UUID 字符串的話可以通過 FNV Hash 后取模 32)得到 [0, 31] 的結果
SQL 落到具體的某張具體的分表上 FROM tag_rel_16
大部分應用應該通過分表就能解決性能問題,還解決不了的話可以通過這個思路進行分庫、分實例。
NoSQL 實現(xiàn)
基于前文的背景可知,標簽服務的定位是一個獨立的服務,并不維護業(yè)務主體(object),所以將標簽嵌套到業(yè)務主體中的設計在這里并不適合。
排除了這一條,我們還是只能用 RDB 的思想來進行設計:
數(shù)據(jù)結構沿用 RDB 設計,即同樣分為兩個“表”:tag
、tag_rel
將 SQL 中的 join 分成兩步進行:1. 根據(jù) object_id
查詢出 tag_id
集合;2. 根據(jù) tag_id
集合查詢出 tag
集合
Redis
Redis 介紹和“為什么選用 Redis”略過。下面我們介紹一下如何使用 Redis 實現(xiàn)上述思路。
數(shù)據(jù)類型
我們可以使用 Redis Hash
類型來保存標簽,使用 Redis Set
來保存標簽關聯(lián)(這也是 Redis 官方文檔的示例)。
給新聞(id: 1000)添加 4 個標簽(通過 id 關聯(lián)):
sadd news:1000:tags 1 2 5 77(integer) 4
添加反向關聯(lián):
sadd tag:1:news 1000(integer) 1> sadd tag:2:news 1000(integer) 1> sadd tag:5:news 1000(integer) 1> sadd tag:77:news 1000(integer) 1
查詢該新聞的標簽 id 集合:
smembers news:1000:tags1. 52. 13. 774. 2
然后根據(jù)返回的標簽 id 集合查詢標簽。雖然分了兩步進行查詢,但 Redis 的高性能將為我們帶來很低的耗時。
結論
數(shù)據(jù)庫分表能夠解決標簽服務的性能問題實現(xiàn)簡單,在現(xiàn)有實現(xiàn)的基礎上改造很小
依然是基于關系型數(shù)據(jù)庫,不用調整技術架構,開發(fā)模式、運維等保持不變
使用 Redis 同樣可以實現(xiàn)標簽服務使用 Hash 保存標簽、Set 保存關聯(lián)關系
分兩步進行查詢,Redis 自身的實現(xiàn)解決性能問題
原文地址:http://88250.b3log.org/articles/2015/12/01/1448958541321.html