redis学习(三)--- 常用技术

本文深入探讨Redis的事务机制,包括MULTI-EXEC模型、原子性和一致性保证,以及如何在Spring框架中实现Redis事务。此外,还介绍了watch命令监控事务、流水线技术提升性能、发布订阅模式、超时命令和内存回收策略,最后提到了Lua脚本增强Redis的数据处理能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:     

    和大部分的NoSQL不同,Redis是存在事务的。例如应用于一些高并发的网站当中,使用Redis读/写数据要比数据库快得多。那存在事务就有可能出现并发下的数据一致问题。Redis事务是使用MULTI-EXEC的命令组合,使用它可以保证如下两个重要保证:

  • Redis会将一个事务中的所有命令序列化,然后按顺序执行。Redis不可能在一个Redis事务的执行过程中插入执行另一个客户端发出的请求。这样便能保证Redis将这些命令作为一个单独的隔离操作执行。
  • 在一个Redis事务中,Redis要么执行其中的所有命令,要么什么都不执行。因此,Redis事务能够保证原子性。

一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。
  • 命令入队。
  • 执行事务。

一、Redis的基础事务

       开启事务是 multi 命令,执行事务是 exec 命令。

      以下是一个事务的例子, 它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中( 批量操作在发送 EXEC 命令前被放入队列缓存), 最后由 EXEC 命令触发事务一并执行事务中的所有命令

redis 127.0.0.1:6379> MULTI
OK

redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED

redis 127.0.0.1:6379> GET book-name
QUEUED

redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED

redis 127.0.0.1:6379> SMEMBERS tag
QUEUED

redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
   2) "C++"
   3) "Programming"

     如果要回滚事务,就可以用discard命令,取消事务,放弃执行事务块内的所有命令。

下面看看在Spring中使用redis事务命令,需要借助SessionCallback接口。

public class TestTransaction {
	@SuppressWarnings("rawtypes")
	public static void main(String[] args) {
		ApplicationContext cxt = 
				new ClassPathXmlApplicationContext("applicationContext.xml");
		RedisTemplate redisTemplate = cxt.getBean(RedisTemplate.class);
		
		SessionCallback callBack = (RedisOperations ops)->{
			//开启事务
			ops.multi();
			//进入缓存队列,设置键为key1,执行事务是把值为value1
			ops.boundValueOps("key1").set("value1");
			//尝试下获取key1对应的value值,如果为null,则说明在事务还没有被执行
			System.out.println(ops.boundValueOps("key1").get());
			//执行事务,并保存在list中
			List list = ops.exec();
			//把value返回				
			return ops.boundValueOps("key1").get();		
		};
		
		//执行redis命令,打印返回结果
		System.out.println(redisTemplate.execute(callBack));
		
	}
}

结果是:

null
value1

二、使用watch命令监控事务(watch key1)

在Redis中使用watch命令决定事务是执行还是回滚。一般流程如下:

  •  在 multi 命令之前使用watch命令监控某些键值对,开始事务后,这些各类对事务操作的命令,这个时候命令进入队列。
  •  当exec命令执行事务时,它首先会去比对被 watch 命令监控的键值对,如果没有发送变化,正常执行,提交事务;如果发生变化(有可以客户端二对修改了这个键值对),则回滚事务。
  • 最后,无论有没有发生回滚,redis都会去取消执行事务前的watch命令

原理:Redis是参考多线程中使用的CAS去执行的。

三、流水线(pipelined)

         在上面我们谈到事务中Redis提供了队列,批量执行命令,以提高系统的性能。但是multi...exec的使用是有系统开销的,因为它会去检测对应的锁和序列化命令。为了消除这个开销,redis提供了流水线技术。

        举例在spring中执行流水线,测试10万条的读/写的2个操作:花了2256毫秒,不同机器结果不一样

public class TestPipeline {
	public static void main(String[] args) {
		ApplicationContext cxt = 
				new ClassPathXmlApplicationContext("applicationContext.xml");
		RedisTemplate redisTemplate = cxt.getBean(RedisTemplate.class);
		
		SessionCallback callBack = (RedisOperations ops)->{
			for(int i=0;i<100000;i++) {
				ops.boundValueOps("key_"+i).set("value_"+i);
				ops.boundValueOps("key_"+i).get();		
			}
				
			return null;		
		};
		//开始时间
		long start = System.currentTimeMillis();
		//执行流水线命令
		redisTemplate.executePipelined(callBack);
		long end  = System.currentTimeMillis();
		System.out.println(end-start);
		
	}
}

四、发布订阅

    Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。观察者模式就是这个模型的典型应用

    Redis 客户端可以订阅任意数量的频道。

   常用命令:

1PSUBSCRIBE pattern [pattern ...] 
订阅一个或多个符合给定模式的频道。
2PUBSUB subcommand [argument [argument ...]] 
查看订阅与发布系统状态。
3PUBLISH channel message 
将信息发送到指定的频道。
4PUNSUBSCRIBE [pattern [pattern ...]] 
退订所有给定模式的频道。
5SUBSCRIBE channel [channel ...] 
订阅给定的一个或多个频道的信息。
6UNSUBSCRIBE [channel [channel ...]] 
指退订给定的频道。

五、超时命令

          redis是基于内存的,内存资源又十分宝贵,也存在着对内存垃圾的回收与管理。我们可以给键值对设置超时命令,因为在大部分情况下,我们都想回收那些超时的键值对。

         expire key seconds : 设置超时时间戳,以秒为单位。

         pexpire key:设置键值的超时时间,以毫秒为单位

 

注意:一个键超时了,Redis并不会自动回收这个key的存储空间,只会对其进行标记。

redis提供了两种方式来回收这些键值对:

  • 定时回收:在某个时间戳触发一段代码,回收这次超时的键值对。缺点:造成停顿
  • 惰性回收:当一个超时的键再次被get命令访问时,触发将其从内存清空。可以理解为system.gc()缺点:造成停顿

顺便说说redis内存回收策略

在redis配置文件中,当redis的内存达到规定的最大值时,允许配置6种策略中的一种进行淘汰键值,并且将一些键值对进行回收

  • volatile-lru:采用最近使用最少的淘汰策略,Redis将回收那些超时的(仅仅是超时的)键值对,也就是它只淘汰那些超时的键值对。

  • allkeys-lru:采用最近最少使用的淘汰策略,Redis将对所有(不仅仅是超时的)的键值对采用最近最少使用的淘汰策略。

  • volatile-random:采用随机淘汰策略删除超时(仅仅是超时的)的键值对。

  • allkeys-random:采用随机淘汰策略删除所有的(不仅仅是超时的)键值对,这个策略不常用。

  • volatile-ttl:采用删除存活时间最短的键值对策略。

  • noeviction:不淘汰任何键值对,当内存满时,如果进行读操作,例如get命令,它将正常工作,而做写操作,它将返回错误,也就是说,当Redis采用这个策略内存达到最大的时候,它就只能读不能写了。

备注:Redis默认采用noeviction策略。LRU算法或者TTL算法都是不精确的算法,而是一个近似算法。

       Redis不会通过对全部的键值对进行比较来确定最精确的时间值,因为这太消耗时间,导致回收垃圾占用的时间太多造成服务器卡顿。在配置文件中,有一个参数maxmemory-samples,它的默认值是3。

六、使用Lua语言

     Redis 2.6 版本通过内嵌支持 Lua 环境。弥补了redis计算能力的不足。

    在Redis中,执行Lua语言是原子性,也就是说Redis执行Lua的时候是不会被中断的,具备原子性,这个特性有助于Redis对并发数据一致性的支持。

    Redis支持两种方法运行脚本,一种是直接输入一些Lua语言的程序代码,另一种是将Lua语言编写成文件。在实际应用中,一些简单的脚本可以采取第一种方式,对于有一定逻辑的一般采用第二种。而对于采用简单脚本的,Redis支持缓存脚本,只是它会使用SHA-1算法对脚本进行签名,然后把SHA-1标识返回,只要通过这个标识运行就可以了。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值