`
Everyday都不同
  • 浏览: 725309 次
  • 性别: Icon_minigender_1
  • 来自: 宇宙
社区版块
存档分类
最新评论

redis学习笔记——不仅仅是存取数据

阅读更多

最近项目中用到比较多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的东西,这是因为jedis2.select(1);它选择了第二个库,自然获取不到第一个库里set的数据了。如果要获取到对应库设置的值,必须先select(int index)再获取。——因为我们平常都是默认使用第一个库,所以不需要该操作;

但为了对key按不同功能来分区,达到解耦和易于管理的目的,最好选择不同的库空间。需要get不同的数据时,先要注意它之前是在哪个库set的。

——是否get set只与所在库有关?而与redis实例无关?——只要在同一个库,不同redis实例set的数据,另一个redis实例便可得到。

 

4、用redis统计过去某段时间的总数据、平均数据等

这个我之前一直是“事后统计”的想法,即把该段时间的东西先存到redis,再去遍历集合,然后再做统计。这样循环效率很低,尤其是在高并发数据的情形下。所以我们可以边按不同统计指标来边入redis。比如使用jedis.incr(key)累加,或jedis.incrBy(key, num)按num累加,统计时就可以直接拿key对应的value,因为它不断进行了incr操作了,就已经是我们要的总数据了,而非事后拿到所有的数据累加;lpush(key, data)可以缓存出一个集合(如果需要去重则用sadd(setKey, data)!需要排序用zadd,之后再用zrange(升序)或zrevrange(降序)取排好序的元素),便于做统计时使用边入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-从大到小)。

 

——对于redis的强大可以从很多简单的demo来测试了解。

4
1
分享到:
评论

相关推荐

    Redis学习笔记整理

    二、 redis学习笔记之数据类型 3 三、 redis学习笔记之排序 11 四、 redis学习笔记之事务 16 五、 redis学习笔记之pipeline 20 六、 redis学习笔记之发布订阅 23 七、 redis学习笔记之持久化 28 八、 redis学习笔记...

    redis学习笔记

    redis学习笔记redis 是一个开源的 key-value 数据库。它又经常被认为是一个数据结构服务器。 因为它的 value 不仅包括基本的 string 类型还有 list,set ,sorted set 和 hash 类型。当 然这些类型的元素也都是 string...

    Redis学习笔记.pdf

    Redis作为一款高性能的键值存储数据库,支持多种数据类型,包括String、List、Set、ZSet和Hash,并且提供了数据持久化的机制,包括快照(Snapshot)和AOF(Append Only File)两种方式。在Java操作方面,可以利用...

    Redis全套学习笔记 (带章节目录) 完整版pdf

    本文是一篇关于Redis全套学习笔记的文章,主要介绍了Redis的基础知识、数据结构、持久化、集群、高可用、性能优化等方面的内容。通过本文的学习,读者可以全面掌握Redis的使用和应用,提高自己的技术水平和实践能力...

    超详细的redis学习笔记

    ### 超详细的Redis学习笔记知识点汇总 #### 1. Redis 的启动与停止 ##### 1.1 直接启动 Redis 服务 - **默认端口启动**:使用 `$ redis-server` 命令,默认监听端口为 `6379`。 - **指定端口启动**:使用 `$ ...

    redis学习笔记.zip

    这个“redis学习笔记.zip”压缩包很可能是包含了关于Redis的学习资料,可能包括概念解释、操作教程、实践案例等内容,适合初学者和有一定基础的学习者参考。 Redis的学习可以分为以下几个主要部分: 1. **基础知识...

    redis学习笔记.docx

    Redis学习笔记 Redis是基于键值对存储的NoSQL数据库,可以用来存储和检索数据。下面是Redis的基础知识点: 基础命令 * set key value:保存一个数据,重复set相同的key只会保存最新的value * get key:获取一个...

    redis学习笔记Redis.md

    ### Redis 学习笔记知识点概览 #### 一、Redis 概述与应用场景 ##### 1.1 NoSQL 数据库简介 - **定义**: NoSQL(Not Only SQL)泛指非关系型数据库,它们通常不使用传统的表格关系来存储数据。 - **特性**: NoSQL ...

    Redis学习笔记

    Redis学习笔记

    redis学习笔记.pdf

    Redis学习笔记 Redis是一个开源的基于键值对(Key-Value)NoSQL数据库,使用ANSI C语言编写、支持网络、基于内存但支持持久化。性能优秀,并提供多种语言的API。Redis可以被称为KV数据库,键值对数据库,内部存储...

    Redis全套学习笔记

    Redis 是一款高性能的键值数据库,它以非关系型、内存...总之,Redis 以其高性能、易用性和丰富的数据结构,成为了现代Web开发中不可或缺的工具,开发者应深入理解其原理和使用方法,以便在实际项目中发挥其最大潜力。

    redis的学习笔记 redis.pdf

    尽管单线程模型可能听起来效率不高,但事实上,由于Redis主要操作的是内存中的数据,以及它无需频繁的线程上下文切换,这使得Redis在性能上表现优秀。对于CPU密集型任务来说,可能多线程更有效率,但Redis主要的瓶颈...

    Redis全套学习笔记-带章节目录-114页.pdf

    Redis全套学习笔记 Redis是一种基于内存的NoSQL数据库,具有高性能、可扩展性和灵活性等特点。以下是Redis的详细知识点: 安装和启动 * 安装Redis可以通过下载软件包或使用yum、apt-get等安装工具进行安装。 * ...

    Redis学习笔记-包括周阳和狂神说

    在本“Redis学习笔记-包括周阳和狂神说”中,我们将深入探讨Redis的核心概念、功能特性以及实际应用。 1. Redis基本概念 - 键值对:Redis的核心数据结构,键是唯一的标识,值可以是多种类型,如字符串、哈希、列表...

    Redis全套学习笔记 完整版pdf.rar

    Redis全套学习笔记 完整版pdf.rar set:添加键值对 get:获取值 apend:追价值 strlen:获取值的长度 setnx:key不存在时,设置key的值 incr:原子递增1 decr:原子递减1 incrby/decrby:递增或者递减指定的数字 ...

    Redis学习笔记.rar

    二、 redis学习笔记之数据类型 3 三、 redis学习笔记之排序 11 四、 redis学习笔记之事务 16 五、 redis学习笔记之pipeline 20 六、 redis学习笔记之发布订阅 23 七、 redis学习笔记之持久化 28 八、 redis...

    7.Redis学习笔记.pdf

    Redis不仅支持基本的键值对存储,还支持多种数据结构,包括字符串(strings)、散列(hash)、列表(list)、集合(set)、有序集合(sorted set)以及范围查询、位图(bitmaps)、超日志(hyperloglogs)和地理空间索引...

Global site tag (gtag.js) - Google Analytics