当前位置: 首页 > wzjs >正文

遵义在线遵义晚报高级seo招聘

遵义在线遵义晚报,高级seo招聘,seo网站优化怎么做,网站模板颜色在分布式系统与高并发架构的战场中,开发者们始终在与两个永恒的命题博弈:数据一致性与系统性能。当我们试图用Redis构建高速缓存、实现分布式锁或设计秒杀系统时,往往会陷入这样的困境——如何在保证原子性的同时,避免网络往返带来…

在分布式系统与高并发架构的战场中,开发者们始终在与两个永恒的命题博弈:数据一致性系统性能。当我们试图用Redis构建高速缓存、实现分布式锁或设计秒杀系统时,往往会陷入这样的困境——如何在保证原子性的同时,避免网络往返带来的性能损耗?如何让复杂的多命令操作像单一指令般高效执行?

这正是Redis Lua脚本闪耀的舞台。作为Redis的"核武器级"特性,Lua脚本不仅实现了原子性、隔离性的操作保障,更能将复杂的业务逻辑压缩成服务端的高性能执行单元。

为什么要选择Lua脚本?

在Redis中使用Lua脚本能带来以下核心优势:

原子性保证:Lua脚本在Redis中以单线程方式原子执行,避免多命令操作时的竞态条件。例如库存扣减、分布式锁等场景必须依赖这种特性。

减少网络开销:将多个Redis命令合并为一个脚本执行,减少客户端与服务端之间的网络往返次数(RTT)。对于高频操作性能提升显著。

逻辑复用与版本控制:脚本上传后可通过SHA摘要重复调用,结合SCRIPT LOAD/EVALSHA实现服务端逻辑复用,避免重复传输代码。

服务端计算能力:利用Redis服务端的计算资源处理数据,减少客户端计算压力。例如实现复杂统计、数据过滤等操作。

事务增强版:相比MULTI事务,Lua脚本提供更灵活的逻辑控制(支持if/else、循环等),且执行期间不会被其他命令打断。

Lua脚本命令的使用

有关事务的命令可以通过help @scripting命令来查看。有关命令的使用可以通过help 命令来查看,例如help eval

EVAL

eval:执行脚本。

语法:

EVAL script numkeys key [key ...] arg [arg ...]

参数说明:

  • script:Lua脚本代码
  • numkeys:后面key数组的数量
  • key:脚本代码中所需要操作的redis中的key数组
  • arg:脚本代码中所需要用到的变量参数数组

使用:

127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 k1 k2 v1 v2
1) "k1"
2) "k2"
3) "v1"
4) "v2"

SCRIPT系列命令

语法:

# 设置执行脚本为调试模式
SCRIPT DEBUG YES|SYNC|NO# 验证脚本是否存在
SCRIPT EXISTS sha1 [sha1 ...]# 清空脚本缓存
SCRIPT FLUSH [ASYNC|SYNC]# 终止运行中的脚本
SCRIPT KILL -# 缓存脚本,并返回SHA摘要
SCRIPT LOAD script

使用:

127.0.0.1:6379> script load "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"
"a42059b356c875f0717db19a51f6aaca9ae659ea"127.0.0.1:6379> script exists "a42059b356c875f0717db19a51f6aaca9ae659ea"
1) (integer) 1127.0.0.1:6379> script flush
OK127.0.0.1:6379> script exists "a42059b356c875f0717db19a51f6aaca9ae659ea"
1) (integer) 0

EVALSHA

evalsha:通过脚本的SHA1摘要执行已缓存的脚本。

语法:

EVALSHA sha1 numkeys key [key ...] arg [arg ...]

使用:

127.0.0.1:6379> evalsha a42059b356c875f0717db19a51f6aaca9ae659ea 2 k1 k2 v1 v2
(error) NOSCRIPT No matching script. Please use EVAL.127.0.0.1:6379> script load "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"
"a42059b356c875f0717db19a51f6aaca9ae659ea"127.0.0.1:6379> evalsha a42059b356c875f0717db19a51f6aaca9ae659ea 2 k1 k2 v1 v2
1) "k1"
2) "k2"
3) "v1"
4) "v2"

EVAL命令要求你在每次执行脚本的时候都发送一次脚本主体(script body)。Redis有一个内部的缓存机制,因此它不会每次都重新编译脚本,不过在很多场合,付出无谓的带宽来传送脚本主体并不是最佳选择。

为了减少带宽的消耗,Redis实现了EVALSHA命令,它的作用和EVAL一样,都用于对脚本求值,但它接受的第一个参数不是脚本,而是脚本的SHA1校验和(sum)。

如果服务器还记得给定的SHA1校验和所指定的脚本,那么执行这个脚本,如果服务器不记得给定的SHA1校验和所指定的脚本,那么它返回一个特殊的错误,提醒用户使用EVAL代替EVALSHA。

Lua中执行redis命令

在Lua中,可以通过内置的函数redis.call()和redis.pcall()来执行redis命令。

redis.call()和redis.pcall()两个函数的参数可以是任意的Redis命令:

127.0.0.1:6379> eval "return redis.call('set','foo','bar')" 0
OK

需要注意的是,上面这段脚本的确实现了将键foo的值设为bar的目的,但是,它违反了EVAL命令的语义,因为脚本里使用的所有键都应该由KEYS数组来传递,就像这样:

127.0.0.1:6379> eval "return redis.call('set',KEYS[1],'bar')" 1 foo
OK

要求使用正确的形式来传递键(key)是有原因的,因为不仅仅是EVAL这个命令,所有的Redis命令,在执行之前都会被分析,借此来确定命令会对哪些键进行操作。

因此,对于EVAL命令来说,必须使用正确的形式来传递键,才能确保分析工作正确地执行。除此之外,使用正确的形式来传递键还有很多其他好处,它的一个特别重要的用途就是确保Redis集群可以将你的请求发送到正确的集群节点。

redis.call()与redis.pcall()很类似,他们唯一的区别是当redis命令执行结果返回错误时,redis.call()将返回给调用者一个错误,而redis.pcall()会将捕获的错误以Lua表的形式返回。

下面的例子演示了redis.call()与redis.pcall()的区别:

127.0.0.1:6379> eval "return redis.call('set1',KEYS[1],'bar')" 1 foo
(error) ERR Error running script (call to f_d968406ee98123006fa91fd2ee764d4f7f859dd7): @user_script:1: @user_script: 1: Unknown Redis command called from Lua script127.0.0.1:6379> eval "return redis.pcall('set1',KEYS[1],'bar')" 1 foo
(error) @user_script: 1: Unknown Redis command called from Lua script127.0.0.1:6379> eval "return type(redis.call('set1',KEYS[1],'bar'))" 1 foo
(error) ERR Error running script (call to f_c62b83c8313fd8f2557865e37d2bb5133f1789af): @user_script:1: @user_script: 1: Unknown Redis command called from Lua script127.0.0.1:6379> eval "return type(redis.pcall('set1',KEYS[1],'bar'))" 1 foo
"table"

Lua数据类型和Redis数据类型之间转换

当Lua通过call()或pcall()函数执行Redis命令的时候,命令的返回值会被转换成Lua数据结构。

同样地,当Lua脚本在Redis内置的解释器里运行时,Lua脚本的返回值也会被转换成Redis协议(protocol),然后由EVAL将值返回给客户端。

数据类型之间的转换遵循这样一个设计原则:如果将一个Redis值转换成Lua值,之后再将转换所得的Lua值转换回Redis值,那么这个转换所得的Redis 值应该和最初时的Redis值一样。

换句话说,Lua类型和Redis类型之间存在着一一对应的转换关系。

LuaRedis示例
number(整型)integer3 → 3
number(浮点型)bulk string3.3 -> “3.3”
stringbulk string“value” → “value”
table (数组形式)multi-bulk{1,2,3} → [“1”,“2”,“3”]
table(键值对形式)multi-bulk{name=“Bob”} → (empty array),无法转换需使用json库序列化为字符串
table(只带一个ok的键值对)status{ok=‘success’} -> success
table(只带一个err的键值对)error{err=‘My Error’} -> (error) My Error
booleanintegertrue → 1, false → (nil)
nilnilnil -> 无返回

Lua中整数和浮点数之间没有什么区别。因此,我们始终将Lua的数字转换成整数的回复,这样将舍去小数部分。如果你想从Lua返回一个浮点数,你应该将它作为一个字符串,比如ZSCORE命令。

以下是几个类型转换的例子:

127.0.0.1:6379> eval "return 10" 0
(integer) 10127.0.0.1:6379> eval "return {1,2,{3,'Hello World!'}}" 0
1) (integer) 1
2) (integer) 2
3) 1) (integer) 32) "Hello World!"127.0.0.1:6379> eval "return redis.call('get','foo')" 0
"bar"

最后一个例子展示如果是Lua直接命令调用它是如何可以从redis.call()或redis.pcall()接收到准确的返回值。

下面的例子我们可以看到浮点数和nil将怎么样处理:

127.0.0.1:6379> eval "return {1,2,3.3333,'foo',nil,'bar'}" 0
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) "foo"

正如你看到的3.333被转换成了3,并且nil后面的字符串bar没有被返回回来。

可以使用tostring()函数将数字转字符串:

127.0.0.1:6379> eval "return tostring(3.3333)" 0
"3.3333"

有两个辅助函数从Lua返回Redis的类型:

  • redis.error_reply(error_string):returns an error reply. This function simply returns the single field table with the err field set to the specified string for you.
  • redis.status_reply(status_string):returns a status reply. This function simply returns the single field table with the ok field set to the specified string for you.

使用redis.error_reply()函数与直接返回一个table效果一样:

127.0.0.1:6379> eval "return {err='My Error'}" 0
(error) My Error127.0.0.1:6379> eval "return redis.error_reply('My Error')" 0
(error) My Error

可用库

Redis Lua解释器可用加载以下Lua库:

  • base lib.
  • table lib.
  • string lib.
  • math lib.
  • debug lib.
  • struct lib.
  • cjson lib.
  • cmsgpack lib.
  • bitop lib.
  • redis.sha1hex function.

每一个Redis实例都拥有以上的所有类库,以确保您使用脚本的环境都是一样的。

struct,CJSON和cmsgpack都是外部库,所有其他库都是标准Lua库。

CJSON库为Lua提供极快的JSON处理:

127.0.0.1:6379> eval 'return cjson.encode({["foo"]= "bar"})' 0
"{\"foo\":\"bar\"}"127.0.0.1:6379> eval 'return cjson.decode(ARGV[1])["foo"]' 0 "{\"foo\":\"bar\"}"
"bar"127.0.0.1:6379> eval "local table = {} table['foo']='bar' table['hello']='world' return cjson.encode(table)" 0
"{\"hello\":\"world\",\"foo\":\"bar\"}"

在java中的使用

lua脚本:

-- 库存扣减脚本
local key = KEYS[1]
local quantity = tonumber(ARGV[1])local stock = tonumber(redis.call('GET', key))
if not stock thenreturn -1
endif stock >= quantity thenreturn redis.call('DECRBY', key, quantity)
elsereturn 0
end

在java中使用lua脚本:

package com.morris.redis.demo.lua;import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.Collections;/*** RedisTemplate中lua脚本的使用*/
@Service
public class RedisTemplateLuaDemo {@Resourceprivate RedisTemplate redisTemplate;public void testLua() {DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("scripts/deduct_stock.lua")));redisScript.setResultType(Long.class);Object object = redisTemplate.execute(redisScript,Collections.singletonList("stock:10086"),Integer.toString(1));System.out.println(object);}}

RedisTemplate底层源码已经实现了evalsha+eval,无需手动加载脚本,源码如下:

org.springframework.data.redis.core.script.DefaultScriptExecutor#eval

	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 (!ScriptUtils.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);}
http://www.dtcms.com/wzjs/62188.html

相关文章:

  • 自建木屋教程夫唯seo教程
  • 长春网站优化页面海淀区seo搜索引擎优化企业
  • 网站开发asp.net线上培训机构有哪些
  • 做团购的家居网站有哪些企业网站的主要类型有
  • 用什么软件做网站最简单公司网站域名续费一年多少钱
  • 盐城建设厅网站设计备案网站建设公司seo关键词
  • 网站建设关键要素软文营销的特点有哪些
  • 网站网页和网址的关系百度网址导航
  • 温州文成县高端网站设计免费二级域名注册网站有哪些
  • 新建免费网站网络营销策划书格式
  • 哪个网站有天天做股市直播的沈阳seo排名优化教程
  • 租房子网站怎么做企业网站推广的方法有
  • 定制型网站开发怎么制作网站平台
  • 龙华网站建设的软件百度推广的方式有哪些
  • 青岛专业网站排名推广百度云搜索引擎
  • 连锁店管理网站开发网站网页设计
  • 做网站需要的素材资料百度怎么注册公司网站
  • 哪些网站可以做商家seo赚钱项目
  • 做p2p网站seo策略主要包括
  • 做百度推广需要网站吗培训班管理系统 免费
  • 网站建设客户需求表友点企业网站管理系统
  • 网站建设目标责任百度seo策略
  • 凡科网页登录西安关键词排名优化
  • 湛江网站制作搜索引擎技术包括哪些
  • 驻马店网站网站建设推广关键词排名查询
  • 上海营销型网站制作搜索引擎优化师工资
  • 网站维护的重要性百度公司有哪些部门
  • 微网站如何做微信支付宝支付宝支付接口搜索引擎营销是什么
  • 网站搭建中单页面百度统计代码安装位置
  • 佛山优化网站公司百度地图疫情实时动态