设为首页 收藏本站
查看: 767|回复: 0

[经验分享] redis学习笔记——不仅仅是存取数据

[复制链接]

尚未签到

发表于 2016-12-20 11:20:52 | 显示全部楼层 |阅读模式
最近项目中用到比较多redis,感觉之前对它一直局限于get/set数据的层面。其实作为一个强大的NoSql数据库产品,如果好好利用它,会带来很多意想不到的效果。(因为我搞java,所以就从jedis的角度来补充一点东西吧。PS:不一定全,只是个人理解,不喜勿喷)
 
1、关于JedisPool.returnSource(Jedis jeids)
 
这个方法是从redis的池中释放一个redis连接的,类似线程池对线程的回收管理的。看下面的demo代码:
 

public static void main(String[] args) {
Jedis jedis = JedisFactory.get();//这个方法是用来得到redis实例的,这里省略
Jedis jedis2 = JedisFactory.get();
jedis.set("key1", "good job");
jedis.set("key2", "holy crap");
System.out.println(jedis.get("key1") + ", " + jedis2.get("key2"));
JedisFactory.close(jedis);//这个方法是用来回收redis实例的,这里省略
System.out.println("redis池回收jedis1之后:");
System.out.println(jedis.get("key1") + ", " + jedis2.get("key2"));
}
 在打开redis服务器后,打印结果如下:
 
good job, holy crap
redis池回收jedis1之后:
good job, holy crap
 
上述demo代码说明了两点:
1)redis池中不同redis实例存储的数据可以共享——jedis和jeids2都是通过JedisPool.getResource()方法得到的,即它们都是从池中得到的不同实例,但是jeids set的数据可以被jeids2 get到
2)调用池的JedisPool.returnResource(Jedis jedis)之后,并不会把jedis set的数据删掉。
于是,引出我们常见的一个异常:
 java.util.NoSuchElementException: Timeout waiting for idle object
这个异常表明没有得到空闲的redis实例,从而引起超时。
老生常谈的问题是,在try{ ... } catch(Exception e){ ... } finally{ ... }代码结构中,必须把JedisPool.returnResource(Jedis jedis)放在finally中,这样无论是否发生异常,jedis都能很好的被归还到池中,以便下次被其他线程使用。而经过上述demo的测试表明——return给池后,并不需要担心数据会被删除!这也是我之前一直困惑的地方!
 
2、redis的key的管理——当key越来越多了咋办?
redis是可以存放很多数据,并且都是以k-v形式存放的。它也常常被使用在高并发的环境下,数据肯定也会越来越多。这时候问题来了——如何避免key越来越多,而其实大多时候数据只是暂时缓存一下而已,并不需要我们长久保存在内存中。这时候管理key就显得很重要了。看下面的demo代码:

public static void main(String[] args) {
Jedis jedis = JedisFactory.get();
Jedis jedis2 = JedisFactory.get();
jedis.set("test1", "well done");
jedis.set("test2", "pain ends");
System.out.println(jedis.get("test1") + " " + jedis2.get("test2"));
jedis2.del("test1");
jedis2.expire("test2", 12);
System.out.println(jedis.get("test1") + " " + jedis2.get("test2"));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("10s后:" + jedis.get("test1") + " " + jedis2.get("test2"));
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("再过3s后:" + jedis.get("test1") + " " + jedis2.get("test2"));
}
 代码跑完后,打印结果为:
well done pain ends
null pain ends
10s后:null pain ends
 
再过3s后:null null
 
打印结果表明:对key的删除操作和设置key的过期时间效果等同——都会在某段时间后删除数据,包括对应的key。在项目中,删除数据的时间点往往不好控制,所以最好是设置key的过期时间,该key会在指定过期时间后自动被内存删除。从而不会一直占用redis的内存。
当然设置redis中某个key的过期时间,必须在保存该数据的操作之后再进行,否则也会无效~
 
3、redis库的“分区”
redis默认会有16个"库"——我们可以把它理解成16个内存分区。而我们默认是使用第一个库即:select(0),下面的demo:

Jedis jedis = JedisFactory.get();//这个方法是用来得到redis实例的,这里省略
Jedis jedis2 = JedisFactory.get();
System.out.println(jedis == jedis2);
jedis.set("key1", "good job");
//jedis.set("key2", "holy crap");
jedis2.select(1);
System.out.println(jedis.get("key1") + ", " + jedis2.get("key1"));
 跑出来的结果为:
false
good job, null
 
说明jedis2没有get到jedis1 set的东西,这是因为[size=1em]jedis2.select(1);它选择了第二个库,自然获取不到第一个库里set的数据了。如果要获取到对应库设置的值,必须先select(int index)再获取。——因为我们平常都是默认使用第一个库,所以不需要该操作;
[size=1em]但为了对key按不同功能来分区,达到解耦和易于管理的目的,最好选择不同的库空间。需要get不同的数据时,先要注意它之前是在哪个库set的。
[size=1em]——是否get set只与所在库有关?而与redis实例无关?——只要在同一个库,不同redis实例set的数据,另一个redis实例便可得到。
 
[size=1em]4、用redis统计过去某段时间的总数据、平均数据等
[size=1em]这个我之前一直是“事后统计”的想法,即把该段时间的东西先存到redis,再去遍历集合,然后再做统计。这样循环效率很低,尤其是在高并发数据的情形下。所以我们可以边按不同统计指标来[size=1em]边入redis。[size=1em]比如使用jedis.incr(key)累加,或jedis.incrBy(key, num)按num累加,统计时就可以直接拿key对应的value,因为它不断进行了incr操作了,就已经是我们要的总数据了,而非事后拿到所有的数据累加;lpush(key, data)可以缓存出一个集合(如果需要去重则用sadd(setKey, data)!需要排序用zadd,之后再用zrange(升序)或zrevrange(降序)取排好序的元素),便于做统计时使用[size=1em]边入jeids.sort(key)来排序,得出最大、最小之类的指标,而非通过排序算法来不断比较。
 
5、关于hmset hgetAll
这个是value为map类型的,要慎用!因为一旦map里面某个Entry的value为null的话,会报jedisdataexception异常的,提示set的value不能为null(即:一旦map里面有一对k-v的value为null,即使整个map不为null,也会报错!)
所以一般尽量避免使用hmset,不仅容易出错,而且你取的时候,先得到map,再从map里用对应的key去取value,效率极低。
 
6、incr  incrBy
incr(String key)用于向给定的key对应的值(数值上必须表现为整型)自增1,incrBy(String key, long value)用于向给定的key对应的值增加value值。他们常常用于做累加统计。“测试下面的代码:

System.out.println(jedis.get("test2") + "," + jedis.keys("*"));
jedis.incr("test2");
System.out.println(jedis.get("test2") + "," + jedis.keys("*"));
jedis.incr("test2");
System.out.println(jedis.get("test2") + "," + jedis.keys("*"));
jedis.del("test2");
System.out.println(jedis.get("test2") + "," + jedis.keys("*"));
 打印结果:
  null,[]
  1,[test2]
  2,[test2]
  null,[]
 
而测试:

System.out.println(jedis.get("test") + "," + jedis.keys("*"));
jedis.incrBy("test", 12);
System.out.println(jedis.get("test") + "," + jedis.keys("*"));
jedis.incrBy("test", 3);
System.out.println(jedis.get("test") + "," + jedis.keys("*"));
jedis.del("test");
System.out.println(jedis.get("test") + "," + jedis.keys("*"));
 打印结果为:
  null,[]
  12,[test]
  15,[test]
  null,[]
  这说明incr  incrBy这2个命令可以在“即使redis中不存在该Key”的情况下,创建该key。并赋给“第一次”的值——incr命令为1,incrBy命令为第一次执行该命令的第二个参数。
 
7、sort

jedis.sadd("test", "1");
jedis.sadd("test", "2");
jedis.sadd("test", "2");
jedis.sadd("test", "1");
jedis.sadd("test", "16");
jedis.sadd("test", "7");
jedis.sadd("test", "3");
jedis.sadd("test", "22");
System.out.println(jedis.smembers("test"));
System.out.println(jedis.sort("test"));
jedis.del("test");
 打印结果为:
  [3, 2, 1, 7, 22, 16]
  [1, 2, 3, 7, 16, 22]
  这说明:sadd具有去重添加的功能,得到的是一个set集合; sort则具有默认的按从小到的排序的功能(关于排序也可以使用sortedset的数据结构,存的时候用zadd方法:zadd(String key, double score, String value),score为排序的权重。取的时候用zrange-从小到大或zrevrange-从大到小)。
 
[size=1em]——对于redis的强大可以从很多简单的demo来测试了解。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-316945-1-1.html 上篇帖子: 【备份】redis源码分析-如何rehash 下篇帖子: Redis中5种数据结构的使用场景介绍
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表