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. python时间相关

    1.格式化时间time.strftime,返回字符串 import time time.strftime('%Y-%m-%d %H:%M:%S') 2.时间差 timedelta from datet ...

  2. http协议状态码对照表

    1**:请求收到,继续处理 2**:操作成功收到,分析.接受 3**:完成此请求必须进一步处理 4**:请求包含一个错误语法或不能完成 5**:服务器执行一个完全有效请求失败 100——客户必须继续发 ...

  3. hql 关联查询

    HQL 带的连接语句只能是实体与 该实体的属性 进行连接 其意义就是为了优化(通过延迟加载查询关联的属性)需要进行配置 from A left join A.B where (b.flag is nu ...

  4. js设计模式-建造者模式

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Redis+php-resque实现消息队列

      服务器硬件配置 Dell PowerEdge R310英特尔单路机架式服务器 Intel Xeon Processor X3430 2.4GHz, 8MB Cache 8GB内存(2 x 4GB) ...

  6. hbase删除region块的脚本

    删除hbase表region块脚本 文件hua.txt格式: CHAT_INFO,1318153079100530000314050,1318173760068.991ca04ff164c3f7987 ...

  7. 由后序遍历结果构造二叉查找树 && 二叉查找树链表化

    二叉查找树通俗说就是左孩子比父亲小,右孩子比父亲大.构造这么一个树,树嘛,递归即可. 例如一棵树后序遍历是这样(下图的树):2 9 8 16 15 10 25 38 45 42 30 20.最后的20 ...

  8. view上添加点手势 button无法响应点击事件

    在view 上添加手势 有的时候  会把Button的 点击事件盖掉,这个 时候 我们用UITapGestureRecognizer的代理方法 //手势的代理方法 - (BOOL)gestureRec ...

  9. 使用HTML5构建下一代的Web Form

    HTML语言作为如今编程最为广泛的语言,具有易用.快捷.多浏览平台兼容等特点,但是随着时代的进步,HTML的标准却停滞不前,这一次还在不断开发中的[color=#444444 !important]H ...

  10. mysql快速上手1

    mysql简介 1.什么是数据库 ? 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,它产生于距今六十多年前,随着信息技术和市场的发展,特别是二十世纪九十年代以后,数据管理不再仅 ...