在我的项目里,有一个提供给Autocomplete的功能,数据量大概在几万。这篇文章里我用姓名检索的例子来说明,列表请戳来自Redis作者的Demo。
在这样的列表里全是用户名,例如我们的系统里有一个用户对象:
public Class User { public string Id{get; set;} public string Name {get; set;} .... public string UserHead {get; set;} }
系统里需要一个用户的下拉列表,由于数据量大不能一次显示完,于是就加上了一个AutoComplete功能。缓存可以在本地内存中直接存储,不必使用像Redis这样的集中式缓存,这样缓存结构会更加简单
var users = new List<User>{...};//读到一个用户列表MemoryCache.Set("capqueen:users", users);//放入内存//读取var users = MemoryCache.Get<List<User>>("capqueen:users");
因为都是在内存里,所以直接存List就可以了,搜索的时候也可以直接的如下:
var findUsers = users.Where(user => user.Name.StartWith("A")).ToList();例如输入的字符是 “A“
相当简单,完全不用考虑如何存储,存储的数据结构。然而,一旦转向使用Redis这样的集中式缓存服务,我们需要重新考虑如何存储。
方案一:类似内存式的缓存实现。
本文里使用的Redis链接库是StactkExchange.Redis,出自StackOverFlow的开源产品。
var db = redis.GetDataBase();//获取0数据库var usersJson = JsonConvert.SerializeObject(users)//序列化db.StringSet("capqueen:users", usersJson);//存储var usersString = db.StringGet("capqueen:users"); var userList = JsonConvert.DeserializeObject<List<User>>(users);//反序列化
上面的方式逻辑上是没有问题的,编译也可以通过。但是仔细想一想,Redis作为独立的缓存服务和appSever是分开来的,这样的读取方式对redis服务器的IO是个负担,甚至这样的读取比本地内存缓存慢了太多了。
那如何解决呢?试想key-value的精髓是在于Key,那么对于List来说应该要把item分开来存储。
方案二:Keys模糊匹配。
在翻阅Redis的命令文档(见参考资料4)后,他惊奇地发现了Keys命令,这让他立刻修改了自己的方案。首先我们需要把要搜索的关键词建立为key,这里我把key定义为 "capqueen:user:{id}:{name}",其中{}内的是要用item对应属性替换的。代码如下:
var redis = ConnectionMultiplexer.Connect("localhost");var db = redis.GetDatabase(); var users = new List<User> { new User{Id = 6, Name = "aaren", Age=10}, new User{Id = 7, Name = "issy", Age=11}, new User{Id = 8, Name = "janina", Age=13}, new User{Id = 9, Name = "karena", Age=14} }; users.ForEach(item => { var key = string.Format("capqueen:user:{0}:{1}", item.Id, item.Name); var value = JsonConvert.SerializeObject(item); db.StringSet(key, value); });
所有的user都以单独的Key-Value方式存储,那么如何利用Keys搜索呢?我们来看下Redis的Keys命令:
KEYS pattern 查找所有符合给定模式 pattern 的 key 。 KEYS * 匹配数据库中所有 key 。 KEYS h?llo 匹配 hello , hallo 和 hxllo 等。 KEYS h*llo 匹配 hllo 和 heeeeello 等。 KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。 特殊符号用 \ 隔开
也就是说Keys能够进行简单的模糊匹配,那么我们这里的搜索就可以换成如下的方式:
var redis = ConnectionMultiplexer.Connect("192.168.10.178");var db = redis.GetDatabase();var server = redis.GetServer("192.168.10.178", 6379);var keys = server.Keys(pattern: "capqueen:user:*:a*");var values = db.StringGet(keys.ToArray());//反序列化var jsonValues = new StringBuilder("["); values.ToList().ForEach(item => jsonValues.Append(item).Append(",")); jsonValues.Append("]");var userList = JsonConvert.DeserializeObject<List<User>>(jsonValues.ToString());
注意以上的代码里,因为每个value是一个json,为了增加转化时的效率,我先处理成json arry再进行反序列化。
这种方案,确实是解决了我目前的问题,然而我注意到了Redis文档里的一段话:
KEYS 的速度非常快,但在一个大的数据库中使用它仍然可能造成性能问题,如果你需要从一个数据集中查找特定的 key
,你最好还是用 Redis 的集合结构(set)来代替。
以上是Redis该怎么使用的详细内容。更多信息请关注PHP中文网其他相关文章!