Redis study notes - not just accessing data

Recently, a lot of redis has been used in the project, and I feel that it has been limited to the level of get/set data before. In fact, as a powerful NoSql database product, if you make good use of it, it will bring many unexpected effects. (Because I am engaged in java, so let's add something from the perspective of jedis. PS: not necessarily all, just personal understanding, don't like it if you don't like it)

 

1、关于JedisPool.returnSource(Jedis jeids)

 

This method releases a redis connection from the redis pool, similar to how the thread pool manages the recycling of threads. See the demo code below:

 

public static void main(String[] args) {
		Jedis jedis = JedisFactory.get();//This method is used to get the redis instance, which is omitted here
		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);//This method is used to recycle the redis instance, which is omitted here
		System.out.println("After the redis pool recycles jedis1:");
		System.out.println(jedis.get("key1") + ", " + jedis2.get("key2"));
	}

 After opening the redis server, the print results are as follows:

 

good job, holy crap

After the redis pool recycles jedis1:

good job, holy crap

 

The above demo code illustrates two points:

1) The data stored by different redis instances in the redis pool can be shared - both jedis and jeids2 are obtained through the JedisPool.getResource() method, that is, they are different instances obtained from the pool, but the data of the jeids set can be used by jeids2 get to

2) After calling the pool's JedisPool.returnResource (Jedis jedis), the data of the jedis set will not be deleted.

So, a common exception is drawn:

 java.util.NoSuchElementException: Timeout waiting for idle object

This exception indicates that no free redis instance was obtained, causing the timeout.

The cliché is that in the try{ ... } catch(Exception e){ ... } finally{ ... } code structure, JedisPool.returnResource(Jedis jedis) must be placed in finally so that whether or not it happens Exceptions, jedis can be returned to the pool well, so that it can be used by other threads next time. The test of the above demo shows that after returning to the pool, there is no need to worry that the data will be deleted! This is also where I have been confused before!

 

2. Redis key management - what should I do when there are more and more keys?

Redis can store a lot of data, and it is stored in the form of kv. It is also often used in high-concurrency environments, and the data will definitely increase. At this time, the problem comes - how to avoid more and more keys, but in fact, most of the time the data is only temporarily cached, and we do not need to keep it in memory for a long time. At this time, managing keys becomes very important. See the demo code below:

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来测试了解。

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326928057&siteId=291194637