.Net Core使用分布式缓存Redis:Lua脚本

一、前言

  运行环境window,redis版本3.2.1。此处暂不对Lua进行详细讲解,只从Redis的方面讲解。

二、Redis的Lua脚本

  在Redis的2.6版本推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行,在Lua脚本中也可以调用大部分的Redis命令。使用脚本有以下三个好处:

  (1) 减少网络开销:有些时候需要多次请求Redis获取处理数据,而使用脚本功能就可以只使用一次请求完成相同操作,减少了网络往返时延。

  (2) 原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。也就是说在编写脚本的过程中无须担心会出现竞态条件,也就是无须使用事务。事务可以完成的所有功能,都可以用脚本来完成。

  (3) 复用:客户端发送的脚本会永久存储在Redis中,这就意味着其他客户端(可以是其他语言开发的项目)可以复用这一脚本而不需要使用代码完成同样的逻辑。

三、Redis调用Lua

  1、EVAL命令

  编写完脚本后最重要的就是在程序中执行脚本。Redis提供了EVAL命令可以使开发者像调用其他Redis内置命令一样调用脚本。EVAL的命令格式如下:

127.0.0.1:6379> eval script numkeys key [key ...] arg [arg ...]  

  script:脚本内容。numkeys:key参数的数量。key和arg:这两个参数向脚本传递数据,它们的值可以在脚本中分别使用KEYS[index]和ARGV[index]两个表类型的全局变量访问,numkeys为key的数量和其索引的最大值,argv的索引为key和argv数量总和减去numkeys,它们的索引都是从1开始,超出则返回nil。如下:

C:\Users\Xu>redis-cli
127.0.0.1:6379> eval ‘return ARGV[3]‘ 2 key1 key2 value1 value2 value3
"value3"
127.0.0.1:6379> eval ‘return KEYS[2]‘ 2 key1 key2 value1 value2 value3
"key2"
127.0.0.1:6379> eval ‘return KEYS[3]‘ 2 key1 key2 value1 value2 value3
(nil)

  其中要读写的键名应该为key参数,其他数据都作为arg参数。

  除了上面直接写lua脚本,还可以读取lua脚本文件来执行脚本,命令如下:

C:\Users\Xu>redis-cli --eval lua_file_path key1 key2 , arg1 arg2 arg3

  注意不需要numkeys,逗号前后必须有空格,否则会被认为一个连起来的字符串。

//lua文件内容
return ARGV[2]
//执行命令
C:\Users\Xu>redis-cli.exe --eval e:\redis\a.lua key1 , value1 value2
"value2"
C:\Users\Xu>redis-cli.exe --eval e:\redis\a.lua key1 , value1 value2,value3
"value2,value3"

  2、EVALSHA命令

  考虑到在脚本比较长的时候,如果每次调用脚本都需要将整个脚本传给Redis会占用较多的带宽。所以,Redis提供了EVALSHA命令允许开发者通过脚本内容的SHA1摘要来执行脚本,该命令的用法和EVAL一样,不过就是将脚本内容的script替换为它的SHA1摘要。

  Redis在执行EVAL命令时会计算脚本的SHA1摘要并记录在脚本缓存中,如果执行EVALSHA命令时没有从脚本缓存中找到相应的摘要,则返回错误。

127.0.0.1:6379> evalsha c349a436bd639369c62c971941fc5f7a80626ee6 1 key1 value1
(integer) 666
127.0.0.1:6379> evalsha c349a436bd639369c62c971941fc5f7a80626ee61 1 key1 value1
(error) NOSCRIPT No matching script. Please use EVAL.

  在程序中使用EVALSHA的流程如下:

  (1) 先计算脚本SHA1摘要,并使用EVALSHA执行。

  (2) 获得返回值,如果返回错误则使用EVAL重新执行脚本。

  3、SCRIPT LOAD命令

  如果只是想将脚本加入到脚本缓存中而不执行则则可以用SCRIPT LOAD命令,返回值时脚本的SHA1摘要。

127.0.0.1:6379> script load ‘return 666‘
"c349a436bd639369c62c971941fc5f7a80626ee6"

  4、SCRIPT EXISTS命令

  SCRIPT EXISTS命令可以同时查找一个或者多个脚本的SHA1摘要是否已经本缓存,1为存在0为不存在。

127.0.0.1:6379> script exists c349a436bd639369c62c971941fc5f7a80626ee6 123ls436bd639369c62c971941fc5f7a80626ee6
1) (integer) 1
2) (integer) 0

  5、SCRIPT FLUSH命令

  Redis将脚本的SHA1摘要加入到脚本缓存后会永久保存,不会删除,但是可以用SCRIPT FLUSH删除所有脚本缓存。

127.0.0.1:6379> script flush
OK
(1.51s)

  6、SCRIPT KILL 和 SHUTDOWN NOSAVE

  由于Redis的脚本是原子性的,脚本执行期间不会执行其他命令。为了防止某个脚本执行时间过长导致Redis无法提供服务(比如死循环),Redis提供了lua-time-limit参数限制脚本最长运行时间,默认是5秒。再脚本执行期间,执行其他命令会返回“BUSY”错误,如下:

(A)127.0.0.1:6379> eval ‘while true do end‘ 0
(B)127.0.0.1:6379> get foo
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

  此时Redis只会接受并执行两个命令:SCRIPT KILL 和 SHUTDOWN NOSVAE。

  通过SCRIPT KILL 可以终止当前脚本的运行,脚本停止并返回错误:

(B)127.0.0.1:6379> script kill
OK
(B)127.0.0.1:6379> get foo
(nil)

(A)127.0.0.1:6379> eval ‘while true do end‘ 0
(error) ERR Error running script (call to f_694a5fe1ddb97a4c6a1bf299d9537c7d3d0f84e7): @user_script:1: Script killed by user with SCRIPT KILL...
(175.99s)

  如果当前执行的脚本对Redis的数据进行了修改,则SCRIPT KILL不会终止脚本的运行,因为这样违背了原子性。那么需要通过SHUTDOWN NOSAVE来强制终止Redis将原先脚本的修改操作返回,不进行持久化操作,这意味着所有发送在上一次的快照后的数据库修改都会丢失。

四、Redis获取脚本中的返回值

  很多情况下,都需要脚本通过return返回值,如果没有执行return则默认返回nil。因为我们可以像调用其他Redis内置命令一样调用我们自己写的脚本,所以同样Redis会自动将脚本返回值的Lua数据类型转化成Redis的返回值类型。具体的转换规则如下:

  (1) Lua的数字类型,Redis为整数类型。

127.0.0.1:6379> eval ‘return 1.1‘ 0
(integer) 1

  (2) Lua的字符串类型,Redis也是字符串类型

  (3) Lua的表类型(数组形式),Redis会返回多行字符串

127.0.0.1:6379> eval ‘return {0,1}‘ 0
1) (integer) 0
2) (integer) 1

  (4) Lua表类型(只有一个ok字段存储状态信息),Redis为成功状态回复

127.0.0.1:6379> eval ‘return {ok="this is ok"}‘ 0
this is ok

  (5)Lua表类型(只有一个err字段存储状态信息),Redis为错误状态回复

127.0.0.1:6379> eval ‘return {err="so bad"}‘ 0
(error) so bad

  (6)Lua的bool类型中true为Redis的1,false为nil

127.0.0.1:6379> eval ‘return true‘ 0
(integer) 1
127.0.0.1:6379> eval ‘return false‘ 0
(nil)

五、沙盒与随机数

  Redis脚本禁止使用Lua标准库中与文件或系统调用相关的函数,在脚本中只允许对Redis的数据进行处理。并且Redis还通过禁用脚本的全局变量的方式保证每个脚本都是相对隔离的,不会互相干扰。

  使用沙盒不仅是为了保证服务器的安全性,而且还确保了脚本的执行结果只有和脚本本身和执行时传递的参数有关,不依赖外界条件(如系统时间、系统中某个文件的内容、其他脚本执行结果登)。这是因为在执行复制和AOF持久话操作时记录的脚本的内容而不是脚本调用的命令,所以必须保证在脚本内容和参数一样的前提下脚本的执行结果必须一样。

  对于随机数,Redis替换了math.random和math。randomseed函数使得每次执行脚本时生成的随机数列都相同,如果希望获得不同的随机数序列,最简单的方法时由程序生成随机数并通过参数传递给脚本,或者采用更灵活的方法,即在程序中生成随机数传给脚本作为随机数种子。

六、在net core中使用脚本

  很简单,直接上代码,这里举例最基本的,还有很多的重写方法大家可以自己试试。最简单的使用eval。

var script = " return KEYS[1];";
var keys = new RedisKey[]{ "key1","key2"};
var values = new RedisValue[] { "value1", "value2" };
return await redisConnection.GetDatabase().ScriptEvaluateAsync(script, keys, values);

  缓存脚本,并使用。

var bytes = await redisConnection.GetServer(Config.Get("ConnectionStrings:Redis:ConnectionString")).ScriptLoadAsync("return 1");
var result = await redisConnection.GetDatabase().ScriptEvaluateAsync(bytes, null, null);

  脚本是否已缓存。

bool exist = await redisConnection.GetServer(Config.Get("ConnectionStrings:Redis:ConnectionString")).ScriptExistsAsync("return 1");

  删除所有脚本缓存,这个操作需要连接的ConfigurationOptions配置中AllowAdmin = true,没有会报错哦。

redisConnection.GetServer(Config.Get("ConnectionStrings:Redis:ConnectionString")).ScriptFlush();

  还有LuaScript和LoadedLuaScript两个类可以对脚本进行更多复杂的脚本,LuaScript将@myVar形式的脚本中的变量重写为redis所需的合适的ARGV[someIndex]。如果传递的参数是RedisKey类型,它将作为KEYS集合的一部分自动发送。如下。

var lua = LuaScript.Prepare("return @key");
var result = redisConnection.GetDatabase().ScriptEvaluate(lua,new {key= (RedisKey)"key1",value = "value1" });

  

原文地址:https://www.cnblogs.com/xwc1996/p/12188963.html

时间: 2024-10-15 00:19:44

.Net Core使用分布式缓存Redis:Lua脚本的相关文章

分布式缓存Redis Centos下单节点安装

摘要: Redis官网:http://redis.io 独立缓存服务器:IP:xxx.xxx.xxx.xxx 安装环境:CentOS 6.6 Redis 版本:redis-3.0 (因为 Redis3.0 在集群和性能提升方面的特性,rc 版为正式版的候选版,请在安装时去官网选用最新版) 用户:root 安装目录:/usr/local/redis 声明: 网上关于分布式缓存Redis的学习资料很多,大家可以去网上找些资料去学习.另外 JEESZ框架面向企业的大型互联网分布式企业架构,分布式缓存是

分布式缓存Redis

分布式缓存Redis参考文档http://doc.redisfans.com#######################################################Memcache与Redis对比 Memcached Redis不支持持久化 持久化纯set get性能好 不如memcached开发都会用 开发都知道简单易用 易用支持类型单一 支持多种数据类型依赖magent 集群没有验证 有基础验证session 购物车(登录前方cookis,等于后写入)tomcat-se

企业项目开发--分布式缓存Redis

第九章 企业项目开发--分布式缓存Redis(1) 注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis 1.1.为什么用分布式缓存(或者说本地缓存存在的问题)? 见<第八章 企业项目开发--分布式缓存memcached> 1.2.有了memcached,为什么还要用redis? 见<第一章 常用的缓存技术> 2.代码实现 2.1.ssmm0 pom.xml 只在dev环境下添加了以下代码

第十章 企业项目开发--分布式缓存Redis(2)

注意:本章代码是在上一章的基础上进行添加修改,上一章链接<第九章 企业项目开发--分布式缓存Redis(1)> 上一章说了ShardedJedisPool的创建过程,以及redis五种数据结构的第一种String类型的常用缓存操作方法.下面说余下的四种: list(有序列表) set(无序集合) sorted set(有序集合) hash 1.ssmm0-cache 1.1.RedisListUtil(有序列表工具类) 1 package com.xxx.cache.redis; 2 3 im

.NET分布式缓存Redis从入门到实战

一.课程介绍 今天阿笨给大家带来一堂NOSQL的课程,本期的主角是Redis.希望大家学完本次分享课程后对redis有一个基本的了解和认识,并且熟悉和掌握 Redis在.NET中的使用.本次分享课程包含以下知识点: 1.StackExchange.Redis (简称:SE)驱动在C#中Redis几种数据结构学习和使用. 2.ServiceStack.Redis ( 简称: SS) 驱动在C#中Redis几种数据结构学习和使用. 3.Redis主从集群服务器搭建和学习. 4.在ASP.NET中Re

分布式缓存Redis应用场景解析

Redis的应用场景非常广泛.虽然Redis是一个key-value的内存数据库,但在实际场景中,Redis经常被作为缓存来使用,如面对数据高并发的读写.海量数据的读写等.举个例子,A网站首页一天有100万人访问,其中有一个"积分商城"的板块,要直接从数据库查询,那么一天就要多消耗100万次数据库请求.如果将这些数据储存到Redis(内存)中,要用的时候,直接从内存调取,不仅可以大大节省系统直接读取磁盘来获得数据的IO开销,提高服务器的资源利用率,还能极大地提升速度.随着日益增长的用户

Redis进阶应用:Redis+Lua脚本实现复合操作

一.引言 Redis是高性能的key-value数据库,在很大程度克服了memcached这类key/value存储的不足,在部分场景下,是对关系数据库的良好补充.得益于超高性能和丰富的数据结构,Redis已成为当前架构设计中的首选key-value存储系统. 虽然Redis官网上提供了200多个命令,但做程序设计时还是避免不了为了实现一小步业务逻辑而多次调用Redis的情况. 以compare and set场景为例.如果使用Redis原生命令,需要从Redis中获取这个key,然后提取其中的

Redis Lua 脚本使用

Lua语言提供了如下几种数据类型:booleans(布尔).numbers(数值).strings(字符串).tables(表格). 下面是一些 Lua 的示例,里面注释部分会讲解相关的作用: -- -- -- 拿客 -- 网站:www.coderknock.com -- QQ群:213732117 -- 三产 创建于 2017年06月15日 12:04:54. -- 描述: -- -- local strings website = "coderknock.com" print(we

5、分布式缓存Redis之bitmap、setbit

基本语法: 1)SETBIT redis 127.0.0.1:6379> setbit KEY_NAME OFFSET VALUE //该命令用于对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit).时间复杂度O(1) 1   在redis中,存储的字符串都是以二进制的形式存在的.比如:设置一个key-value,键的名字叫“andy” ,值为字符’a’,‘a’ 的ASCII码是97.转换为二进制是:01100001.offset的学名叫做“偏移” ,二进制中的每一位就是offse