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 = , Name = "aaren", Age=},
new User{Id = , Name = "issy", Age=},
new User{Id = , Name = "janina", Age=},
new User{Id = , Name = "karena", Age=}
}; 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", );
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到底该如何利用?的更多相关文章

  1. Redis到底该如何利用?【转自:http://www.cnblogs.com/capqueen/p/HowToUseRedis.html】

    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. Redis 到底是单线程还是多线程?我要吊打面试官!

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

  9. redis 未授权漏洞利用直接登录服务器

    在没有查到杀手之前我是先把带宽&端口用iptables 做了限制这样能保证我能远程操作服务器才能查找原因 2 在各种netstat –ntlp  的查看下没有任何异常 在top 下查到了有异常 ...

随机推荐

  1. iOS多线程的详情使用示例--简进祥

    大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的,一个复杂的多步操作只能 ...

  2. c#.NET微信自定义菜单

    private void customMenu() { //获取access_token string access_token = GetAccessToken(); StringBuilder s ...

  3. base64和图片的转换

    /// <summary> /// base64转图片 /// </summary> /// <param name="strBase64">& ...

  4. 【原创】自己动手写工具----签到器[Beta 1.0]

    一.写在前面 最近公司没有什么项目,想通过项目练练手的机会也没有,只能自己学习了,因此空下来的时间也挺多的,就打开网页看看吧,哎,一打开就让签到(像什么百度知道啊.百度云盘啊之类的),我签到的目的是获 ...

  5. oracle---jdbc--laobai

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import ...

  6. oracle 11g express 快速入门

    创建表空间CREATE TABLESPACE testdb LOGGING DATAFILE 'F:\oracle\app\oracle\oradata\XE\testdb.dbf' SIZE 100 ...

  7. ajax方法简单实现

    //option {url,medthod,type,data,fSuccess,fError} function ajax(option) { var xhr = window.XMLHttpRqu ...

  8. MRDS学习四——自动型机器车

    由自己的所在开始,探索自己周围的简单机器车,假设车子的行走路径如下: 我们要把L型路径写成一个Activity,然后由外部输入这个L的大小,最后这个Activity要能够在完成行走路径时吐出更大的L大 ...

  9. SQL 的坑1 除法“”不可用“”

    今天工作中遇见 一问题,有5各部分,现要求5个部分各自的比例,SQL语句没有问题,后来还试了"加","减","乘","Round& ...

  10. [Qt]用QItemDelegate的来修改QStandardItem字体颜色

    1.重写ItemDelegate的Item方法 这里我使用的QListView来显示Log日志,将写好的代理在初始化中就可以直接赋值上. m_LogModel = new QStandardItemM ...