对于Redis脚本使用过的同学都知道,这个主要是为了防止竞态条件而用的。因为脚本是顺序执行的。(不用担心效率问题)比如我在工作用,用来设置考试最高分。

如果还没有用过的话,先去看Redis脚本的介绍,发送脚本,缓存脚本,发送sha1执行脚本,以及基本的lua脚本的语法。

1. Redis脚本的使用场景

在一些缓存的设置中,经常会出现竞态条件,由于并发导致数据有误。比如大家熟知的++操作。我们自己通过Redis实现++的话,很容易在并发下出现误差。所以Redis提供了incr函数。我在设置最高分的时候,获取分数70,A得分80,然后设置80。然后在同时,B获取分数70,但是B得分78,78>70,然后设置78。这样的话,B的修改就把A的修改替换了。这也是数据库隔离级别中的第一类更新丢失。

为了防止这个问题,Redis提供了顺序执行命令的方法,就是使用脚本。

2. 脚本类RedisScript

RedisTemplate对脚本提供了很高的支持,执行方法同之前的类似,都是通过connection回调。但是这里要注意的是:脚本不支持事务,所以脚本之前不能进行connection.multi()开启事务,也不能用@Transactional注解让spring来开启事务,这些都会抛出异常,大家可以自行测试下。

看到这里,我们新建一个最高分的脚本类,MaxscoreScript,继承自接口RedisScript,需要实现三个方法:

public interface RedisScript<T> {

    /**
* @return The SHA1 of the script, used for executing Redis evalsha command
*/
String getSha1(); /**
* @return The script result type. Should be one of Long, Boolean, List, or deserialized value type. Can be null if
* the script returns a throw-away status (i.e "OK")
*/
Class<T> getResultType(); /**
* @return The script contents
*/
String getScriptAsString(); }

getSha1(),是获取脚本摘要。看过脚本的应该知道,Redis支持缓存脚本,第一次发送脚本以后,后面都直接发送该脚本的sha1摘要信息来直接执行,大概是为了节省传输成本吧。毕竟摘要只需要32个字符。

getResultType(),是获取返回类型的class,需要和定义的泛型T一致。其实之前在Hibernate的Basedao里面,都用过直接通过实例来获取泛型的class。虽然不明白这里为什么加,但是实现这个方法,绝对是没有错。

getScriptAsString(),毫无疑问,这个是返回脚本内容。至于怎么写脚本,这里不做说明。

3. 使用RedisTemplate执行脚本

    public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {
return scriptExecutor.execute(script, keys, args);
} public <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer,
List<K> keys, Object... args) {
return scriptExecutor.execute(script, argsSerializer, resultSerializer, keys, args);
}

相比之下,第二个是自己传入序列化器,第一个是用默认的key和value序列化器。

强烈建议使用第二个。然后统一传入(StringSerializer),里面的参数全部转为String。我在使用的时候,lua进行大小比较,发现比较的有误。后来调试才发现,是JdkSerializer中,Redis把他当一串\xu..这样的字符串,字符串比较显然不准确。但是如果是"11","12"这样的字符串,就可以通过lua转为num类型,再进行比较。

当然,也许有别的业务场景,不需要这么做,也有可能。自行分析斟酌。

我们看到执行脚本的源码

    protected <T> T eval(RedisConnection connection, RedisScript<T> script, ReturnType returnType, int numKeys,
byte[][] keysAndArgs, RedisSerializer<T> resultSerializer) { Object result;
try {
result = connection.evalSha(script.getSha1(), returnType, numKeys, keysAndArgs);
} catch (Exception e) { if (!exceptionContainsNoScriptError(e)) {
throw e instanceof RuntimeException ? (RuntimeException) e : new RedisSystemException(e.getMessage(), e);
} result = connection.eval(scriptBytes(script), returnType, numKeys, keysAndArgs);
} if (script.getResultType() == null) {
return null;
} return deserializeResult(resultSerializer, result);
}

这一段代码是最终调用的,之前还有一些序列化参数的操作没有贴出来。

默认直接提交sha1摘要,得到异常,如果异常属于exceptionContainsNoScriptError ,再发送执行脚本,获取返回结果后通过用户定义的结果序列化器进行反序列化。

4. 总结

这一篇讲了RedisTemplate如何使用脚本。可能对于不懂脚本在connection 或者在redis控制台下使用的同学来说,还是不能理解。但是如果懂得控制台发送脚本,那么通过RedisTemplate的使用,会让很多问题迎刃而解。

Redis(十):使用RedisTemplate执行Redis脚本的更多相关文章

  1. RedisTemplate执行Redis脚本

    对于Redis脚本使用过的同学都知道,这个主要是为了防止竞态条件而用的.因为脚本是顺序执行的.(不用担心效率问题)比如我在工作用,用来设置考试最高分. 如果还没有用过的话,先去看Redis脚本的介绍, ...

  2. 深入理解Spring Redis的使用 (四)、RedisTemplate执行Redis脚本

    对于Redis脚本使用过的同学都知道,这个主要是为了防止竞态条件而用的.因为脚本是顺序执行的.(不用担心效率问题)比如我在工作用,用来设置考试最高分. 如果还没有用过的话,先去看Redis脚本的介绍, ...

  3. Redis(九):使用RedisTemplate访问Redis数据结构API大全

    RedisTemplate介绍 spring封装了RedisTemplate对象来进行对redis的各种操作,它支持所有的 redis 原生的api. RedisTemplate在spring代码中的 ...

  4. SpringBoot通过RedisTemplate执行Lua脚本

    如果你对Redis和Lua的关系不太清楚,请先阅读:Redis进阶之使用Lua脚本开发 1.RedisScript 首先你得引入spring-boot-starter-data-redis依赖,其次把 ...

  5. SpringBoot + Redis 执行lua脚本

    1.背景 有时候,我们需要一次性操作多个 Redis 命令,但是 这样的多个操作不具备原子性,而且 Redis 的事务也不够强大,不支持事务的回滚,还无法实现命令之间的逻辑关系计算.所以,一般在开发中 ...

  6. redis原子性读写操作之LUA脚本和watch机制

    最近在开发电商平台的子系统--储值卡系统,系统核心业务涉及到金额消费以及库存控制,因此为了解决建立在内存上高并发情况下的事务控制,使用了spring封装的RedisTemplate执行lua脚本进行原 ...

  7. PHP中使用redis执行lua脚本示例

    摸索了一下在PHP中如何使用redis执行lua脚本,写了一个脚本如下,供以后参考 <?php $redis = new Redis(); #实例化redis类 $redis->conne ...

  8. Redis执行Lua脚本示例

    Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行.使用脚本的好处如下: 1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在red ...

  9. Redis脚本插件之————执行Lua脚本示例

    Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行.使用脚本的好处如下: 1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在red ...

随机推荐

  1. 洛谷 P1270 “访问”美术馆

    P1270 “访问”美术馆 题目描述 经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动.艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室.Peer ...

  2. repeater做成gridview【更新删除编辑等】

    原文发布时间为:2009-06-14 -- 来源于本人的百度文章 [由搬家工具导入] 不多说,不会说。。看我做的范例。。。 http://download.csdn.net/source/138556 ...

  3. 禁止复制(copy),禁用鼠标右键!

    <SCRIPT> //加入页面保护 function rf() {return false; } document.oncontextmenu = rf function keydown( ...

  4. sooket数据成传输压缩

    样压缩不以文件为基础的数据 Q: 回答了两个使用Java进行数据压缩的问题. 第一个问题是: 我怎样才能压缩那些不在文件中的数据. 第二个问题是: 我以极大的热情阅读了Todd Sundsted的&q ...

  5. android的动态代码

    1,Android代码设置Shape,corners,Gradient  (http://blog.csdn.net/houshunwei/article/details/17392409) int ...

  6. 【原创】设置EXCEL2010打开多个独立窗口

            最近发现一个奇怪的问题,发现office中的word和ppt在我使用笔记本分屏幕(双屏)的时候都可以将2份文档分别在2个窗口打开,但是在使用excel的时候却发现不行,最后研究发现原因 ...

  7. 慎用lodash的cloneDeep函数

    lodash的cloneDeep函数能够很方便的拷贝对象,但是一旦拷贝一些很复杂的对象就有可能报错.比如用cloneDeep克隆一个vue实例,就有可能包key.charAt is not a Fun ...

  8. Codeforces 509E(思维)

                                                                                                         ...

  9. OceanBase 2.1 的ORACLE兼容性能力探秘

    概述 OceanBase是一款通用的分布式关系型数据库,目前内部业务使用比较多有两个版本:1.4和2.1.OceanBase每个版本变化总能带给人很多惊喜,其中2.1版本实现了ORACLE很多特性的兼 ...

  10. NanoPC-T3 64位裸机编程 —— 启动和运行状态切换

    参考: https://github.com/metro94/s5p6818_spl https://github.com/trebisky/Fire3/tree/master/Boot_NSIH h ...