Redis是个好东西,经过上两个星期的研究和实践,目前正在项目里大规模的替换掉原来的本地内存cache。但是替换过程中却发现,Redis这东西高端,大气上档次,似乎不是我想象里的使用方法。

在没有深入Redis之前,在我的概念里,缓存,就是key-value。而使用方式肯定不需要改动多少代码,一 切都是Get/Set。但是实际用的时候却发现,我错了,不是所有的场景都是简单的Get/Set。也不是所有的数据都适合key-Value,于是有了 这个问题,Redis到底该如何使用?我问自己,也向园子里的朋友们求助,希望帮忙解答。当然,这篇文章抛砖引玉,先谈谈我这两周的感悟。

使用场景

在我的项目里,有一个提供给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)来代替。

这段话换而言之就是慎用Keys搜索的意思,那么有什么更好的解决方案呢?由于这篇文章我拖得很久了,这里留个问题在末尾,期待有大牛能够帮忙解答,感激不尽。当然还有下一篇内容,我会讲讲我目前的处理方法。

下篇文章里,我会根据Redis作者的博客(资料1)里的做法以及我最后查到的资料,做一个新的方案,请大家到时指教。

参考资料

  1. Redis作者博客,这是其中一篇讲如何基于Redis实现AutoComplete的文章:http://oldblog.antirez.com/post/autocomplete-with-redis.html
  2. Redis 第三方管理工具 For Windows:http://redisdesktop.com/
  3. Redis .NET链接工具的Top20:http://nugetmusthaves.com/Tag/Redis
  4. Redis命令中文文档:http://redisdoc.com/

Redis到底该如何利用?【转自:http://www.cnblogs.com/capqueen/p/HowToUseRedis.html】的更多相关文章

  1. Redis到底该如何利用?

    Redis是个好东西,经过上两个星期的研究和实践,目前正在项目里大规模的替换掉原来的本地内存cache.但是替换过程中却发现,Redis这东西高端,大气上档次,似乎不是我想象里的使用方法. 在没有深入 ...

  2. Redis到底该如何利用(三)?

    上两篇受益匪浅,秉着趁热打铁,不挖到最深不罢休的精神,我决定追加这篇.上一篇里最后我有提到实现分级缓存管理应该是个可行的方案,因此今天特别实践了一下.不过缓存分级之后也发现了一些问题,例如下图: 当a ...

  3. Redis到底该如何利用(二)?

    上一篇文章里我简述了使用Keys作为Redis搜索的方式,确实感受到了社区的力量,写文章好处多.首先谢谢各位前辈的指导,我知道了拿Redis作为搜索是个错误的方向.本来这篇文章我觉得确实没必要发了,但 ...

  4. redis弱密码漏洞利用

    背景: redis无认证,或者弱密码,可以成功连接到redis服务器 反弹shell拿到的权限取决于redis的启动账号 操作: 1. Centos7安装redis客户端 #yum install r ...

  5. Redis07——Redis到底能用在什么地方(下)

    在前一篇文章中,我们已经介绍过Redis的一些实际应用.如KV缓存.分布式锁.消息队列,由于篇幅原因,并未介绍完全.接下来将继续为各位带来Redis的更多应用. bitmat(位图) 实现 位图的基本 ...

  6. Redis配置及攻击利用

    Redis配置及攻击利用 Redis及其安全配置 Redis介绍 redis默认会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样 ...

  7. Redis入门和Java利用jedis操作redis

    Redis入门和Java利用jedis操作redis Redis介绍 Redis 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库. Redis 与其他 key - val ...

  8. 利用Word发布文章到cnblogs博客

    利用Word发布文章到cnblogs博客 用博客园cnblogs:http://www.cnblogs.com/博客名称/services/metablogapi.aspx,word老是提醒" ...

  9. Redis 到底是单线程还是多线程?我要吊打面试官!

    最近在Java技术栈公众号发布的一篇文章,其中有一道题: Redis是多线程还是单线程?(回答单线程的请回吧,为什么请回,请往下看) 好些粉丝在后台问我:为什么请回,Redis不是单线程吗? 大家注意 ...

随机推荐

  1. 学习资料 50个常用的sql语句

    Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,Tname) 教师表 问题 ...

  2. JS实现星级评分

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  3. file not found while xcode archive

    今天碰到一个问题,就是我像在我的ios app中添加支付宝功能,按照文档我添加好了测试也成功,但是在archive的时候就是出问题,很奇怪.最后网上查了文档,最后在stackoverflow上面找到了 ...

  4. 【MongoBD】MongoBD持久化

    参考:http://f.dataguru.cn/thread-139560-1-1.html 参考:http://blog.mongodb.org/post/33700094220/how-mongo ...

  5. SQL笔记 [长期更新] (-2013.7)

    --IF EXISTS(SELECT * FROM dbo.SysObjects WHERE ID = object_id(N'[TABLEA]') ) DROP TABLE tableA--CREA ...

  6. poj2031 Building a Space Station

    这题目,用G++ WA,用C++ AC. 题目要求,现给出n个球,然后要使每两个球直接或者间接连通,可以在任意两球之间做管道(在表面),最后的要求是,如果使得都连通的话,管道最小长度是多少. 思路简单 ...

  7. php mkdir函数

    if(!is_dir($targetPath)){mkdir($targetPath, 0700); } is_dir 判断目录是否存在 mkdir 不能创建多级目录

  8. Tornado小记 -- 模板中的Handler

    在Tornado的前端页面模板中,Tornado提供了一些对象别名来快速访问对象,具体定义可以参考Tornado官方文档! 这里我想将的是Handler这个对象,Handler指向的处理当前这个页面的 ...

  9. Visual Studio 2010 更新NuGet Package Manager出错解决办法

    在Visual Studio 2010的扩展管理器中发现NuGet Package Manger有最新版本更新提示,选择更新安装提示以下错误信息: 2013/4/25 1:11:48 - Micros ...

  10. 第一节 MongoDB介绍及下载与安装

    引言 MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.他支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类 ...