這種方式是我實踐過後,結合上篇的前輩給的觀點覺得唯一比較可行且符合redis特性的方式,不過最終效率上還是比不過內存。
詳細的實作思維清看Redis作者博客(參考資料1),這裡的例子還是基於UserName,英文,並且只針對詞組做了長度為3的分詞,其他場景請自行擴展。
首先基於AutoComplete的字母搜索,那麼我們需要對所有的Name做一個分詞,即:
abc => (a, ab, abc)
#當輸入a時,我們會直接取得集合a中的內容;當輸入ab時,我們會直接取得集合ab中的內容。那我們開始轉換,首先我們需要對User表的姓名進行分詞:
var redis = ConnectionMultiplexer.Connect("localhost");var db = redis.GetDatabase();for (var i = 1; i < 4; i++) { var data = dbCon.Lookup<string, int>(string.Format(@"select words, id from ( select Row_number() over (partition by words order by name) as rn,id,words from ( select id, SUBSTRING(name, 1, {0}) as words, name from User ) as t ) t2 where rn <= {1} and words != '' and words is not null", i, 20)); data.ForEach((key, item) => { db.SetAdd("capqueen:Cache:user:" + key.ToLower(), item.Select<int, RedisValue>(j => j).ToArray()); }); }
第一步:採用SQL,分組排序篩選出每個分詞的前20條數據,這裡使用的是OrmLite的語法。
第二部:存入RedisSet,注意這裡其實只是做了一個索引,並不保存具體的User內容
接著搜尋的時候我們可以實現如下:
public List<User> SearchWords(string keywords) { var redis = ConnectionMultiplexer.Connect("localhost"); var db = redis.GetDatabase(); var result = db.SetMembers("capqueen:Cache:user:" + keywords.ToLower()); var users = new List<User>(); if (result.Any()) { //转换成ids var ids = result.ToList().Select<RedisValue, RedisKey>(i => i.ToString()); //按照keys获取value ,事先已经存好了Usersvar values = db.StringGet(ids.ToArray()); //构造List Json以加速解析var portsJson = new StringBuilder("["); values.ToList().ForEach(item =>{ if (!string.IsNullOrWhiteSpace(item)) { portsJson.Append(item).Append(","); } }); portsJson.Append("]"); users = JsonConvert.DeserializeObject<List<User>>(portsJson.ToString()); } }
經過實際的測試,這樣的寫法比前面的Keys確實好了不少,但是性能還是差強人意的。
這個方法是我在查閱了Redis的文件之後,發現的,但是也就是試驗一下,估計也不能用做生產環境大規模查詢。
根據資料結構的不同,Scan分為了SCAN、HSCAN、SSCAN和ZSCAN。更詳細的資訊請參閱文件。我們在這裡採用了ZSCAN:
ZSCAN key cursor [MATCH pattern] [COUNT count]
這裡cursor是搜尋的迭代的一個遊標,具體還沒弄清楚,pattern就是符合規則count就是記錄條數
由於我使用的是StackExchange.Redis,它提供的zscan方法是:
IEnumerable SortedSetScan(RedisKey key, RedisValue pattern = null, int pageSize = 10, long cursor = 0, int pageOffset = 0, CommandFlags flags = CommandFlags.None);
public void CreateTerminalCache(List<User> users) { if (users == null) return; var db = ConnectionMultiplexer.GetDatabase(); var sourceData = new List<KeyValuePair<RedisKey, RedisValue>>(); //构造集合数据var list = users.Select(item =>{ var value = JsonConvert.SerializeObject(item); //构造原始数据sourceData.Add(new KeyValuePair<RedisKey, RedisValue>("capqueen:users:" + item.Id, value)); //构造数据 return new SortedSetEntry(item.Name, item.Id); }); //添加进有序集合,采用name - id db.SortedSetAdd("capqueen:users:index", list.ToArray()); //添加港口数据key-value db.StringSet(sourceData.ToArray(), When.Always, CommandFlags.None); }
然後搜尋的時候如下:
public List<User> GetUserByWord(string words) { var db = ConnectionMultiplexer.GetDatabase(); //搜索var result = db.SortedSetScan("capqueen:users:index", words + "*", 10, 1, 30, CommandFlags.None).Take(30).ToList(); var users = new List<User>(); if (result.Any()) { //转换成ids var ids = result.ToList().Select<SortedSetEntry, RedisKey>(i => i.ToString()); //按照keys获取valuevar values = db.StringGet(ids.ToArray()); //构造List Json以加速解析var portsJson = new StringBuilder("["); values.ToList().ForEach(item =>{ if (!string.IsNullOrWhiteSpace(item)) { portsJson.Append(item).Append(","); } }); portsJson.Append("]"); users = JsonConvert.DeserializeObject<List<User>>(portsJson.ToString()); } return users; }
以上是Redis分詞索引法怎麼用的詳細內容。更多資訊請關注PHP中文網其他相關文章!