国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Redis之Lua腳本講解

這篇具有很好參考價值的文章主要介紹了Redis之Lua腳本講解。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1 Lua

1.1 簡介

當涉及Lua編程時,以下是對前述12個關(guān)鍵概念的詳細說明,附帶Lua代碼示例以幫助更深入了解這門編程語言

1.1.1 注釋

注釋在Lua中用于添加說明和注解。單行注釋以--開始,多行注釋則使用--[[ ... ]]。

-- 這是一條單行注釋

--[[ 
    這是一個多行注釋
    可以跨越多行
]]

1.1.2 變量

變量在Lua中無需顯式聲明類型。使用local關(guān)鍵字創(chuàng)建局部變量,全局變量直接聲明。

local age = 30
name = "John" -- 全局變量

1.1.3 數(shù)據(jù)類型

基本數(shù)據(jù)類型包括整數(shù)、浮點數(shù)、字符串、布爾值和nil
其中表是一種非常靈活的數(shù)據(jù)結(jié)構(gòu),使用花括號 {} 或者 table 構(gòu)造函數(shù)。

local num = 42
local str = "Hello, Lua!"
local flag = true
local empty = nil
local person = { name = "John", age = 30 }

表是Lua的核心數(shù)據(jù)結(jié)構(gòu),使用花括號 {} 或者 table 構(gòu)造函數(shù)。
表可以包含鍵值對,鍵和值可以是任何數(shù)據(jù)類型。

local person = { name = "John", age = 30, hobbies = {"Reading", "Gaming"} }
print("姓名:" .. person.name)
print("年齡:" .. person.age)

1.1.4 控制結(jié)構(gòu)

條件語句:使用if、else和elseif來實現(xiàn)條件分支。

if age < 18 then
    print("未成年")
elseif age >= 18 and age < 65 then
    print("成年")
else
    print("老年")
end

循環(huán)結(jié)構(gòu):Lua支持for循環(huán)、while循環(huán)和repeat…until循環(huán)。

for i = 1, 5 do
    print(i)
end

local count = 0
while count < 3 do
    print("循環(huán)次數(shù): " .. count)
    count = count + 1
end

repeat
    print("至少執(zhí)行一次")
until count > 5

1.1.5 函數(shù)

函數(shù)在Lua中使用function關(guān)鍵字定義,可以接受參數(shù)并返回值。

function add(a, b)
    return a + b
end

local result = add(5, 3)
print("5 + 3 = " .. result)

1.1.6 模塊

Lua支持模塊化編程,允許將相關(guān)功能封裝在獨立的模塊中,并通過require關(guān)鍵字加載它們

1.1.7 字符串操作

Lua提供了許多字符串處理函數(shù),例如string.sub用于截取子串,string.find用于查找字符串中的子串等。

local text = "Lua programming"
local sub = string.sub(text, 1, 3)
print(sub) -- 輸出 "Lua"

1.1.8 錯誤處理

錯誤處理通常使用pcall函數(shù)來包裹可能引發(fā)異常的代碼塊,以捕獲并處理錯誤。這通常與assert一起使用。

local success, result = pcall(function()
    error("出錯了!")
end)

if success then
    print("執(zhí)行成功")
else
    print("錯誤信息: " .. result)
end

1.1.9 標準庫

Lua標準庫包含豐富的功能,如文件操作、網(wǎng)絡(luò)編程、正則表達式、時間處理等??梢酝ㄟ^內(nèi)置的模塊來使用這些功能,如io、socket等。

總之,Lua是一種靈活的編程語言,其簡潔性和強大的表格數(shù)據(jù)結(jié)構(gòu)使其在各種應(yīng)用中具有廣泛的用途。這些示例代碼應(yīng)該有助于更好地理解Lua的基本概念和語法。

1.2 Redis和Lua腳本結(jié)合優(yōu)點

Lua腳本在Redis中的使用有許多優(yōu)勢,使其成為執(zhí)行復(fù)雜操作的理想選擇。以下是一些主要原因:

  • 性能:
    Lua腳本在Redis中執(zhí)行,避免了多次的客戶端與服務(wù)器之間的通信。這可以減少網(wǎng)絡(luò)開銷,提高性能,特別是在需要執(zhí)行多個Redis命令以完成一個操作時。
    原子性:Redis保證Lua腳本的原子性執(zhí)行,無需擔(dān)心競態(tài)條件或并發(fā)問題。
  • 事務(wù):
    Lua腳本可以與Redis事務(wù)一起使用,確保一系列命令的原子性執(zhí)行。這允許將多個操作視為一個單一的事務(wù),要么全部成功,要么全部失敗。
  • 復(fù)雜操作:
    Lua腳本提供了一種在Redis中執(zhí)行復(fù)雜操作的方法,允許在一個腳本中組合多個Redis命令。這對于處理復(fù)雜的業(yè)務(wù)邏輯非常有用,例如計算和更新分布式計數(shù)器、實現(xiàn)自定義數(shù)據(jù)結(jié)構(gòu)等。
  • 原子鎖:
    使用Lua腳本,你可以實現(xiàn)復(fù)雜的原子鎖,而不僅僅是使用RedisSETNX(set if not exists)命令。這對于分布式鎖的實現(xiàn)非常重要。
  • 減少網(wǎng)絡(luò)開銷:
    對于大批量的數(shù)據(jù)處理,Lua腳本可以減少客戶端和服務(wù)器之間的往返次數(shù),從而顯著減少網(wǎng)絡(luò)開銷。
  • 減少服務(wù)器負載:
    通過將復(fù)雜的計算移至服務(wù)器端,可以減輕客戶端的負擔(dān),降低服務(wù)器的負載。
  • 原生支持:
    Redis天生支持Lua腳本,因此不需要額外的插件或擴展。
  • 可讀性和維護性:
    Lua腳本是一種常見的腳本語言,易于編寫和維護。將復(fù)雜邏輯封裝在腳本中有助于提高代碼的可讀性。

總之,Lua腳本在Redis中的優(yōu)勢在于它可以原子性地執(zhí)行復(fù)雜操作、減少網(wǎng)絡(luò)通信、提高性能、減輕服務(wù)器負載,以及提高代碼的可讀性。這使得它成為執(zhí)行一系列復(fù)雜操作的理想選擇,尤其是在分布式系統(tǒng)中需要高性能和可伸縮性的場景下。通過Lua腳本,Redis不僅成為一個鍵值存儲,還能執(zhí)行復(fù)雜的數(shù)據(jù)操作。

1.3 Lua腳本應(yīng)用和調(diào)試

Lua腳本在Redis中有廣泛的應(yīng)用場景,以下是一些示例場景,展示了Lua腳本的實際用途

1.3.1 緩存更新

場景:在緩存中存儲某些數(shù)據(jù),但需要定期或基于條件更新這些數(shù)據(jù),同時確保在更新期間不會發(fā)生并發(fā)問題。
示例:使用Lua腳本,你可以原子性地檢查數(shù)據(jù)的新鮮度,如果需要更新,可以在一個原子性操作中重新計算數(shù)據(jù)并更新緩存。

local cacheKey = KEYS[1] -- 獲取緩存鍵
local data = redis.call('GET', cacheKey) -- 嘗試從緩存獲取數(shù)據(jù)
if not data then
    -- 數(shù)據(jù)不在緩存中,重新計算并設(shè)置
    data = calculateData()
    redis.call('SET', cacheKey, data)
end
return data

1.3.2 原子操作

場景:需要執(zhí)行多個Redis命令作為一個原子操作,確保它們在多線程或多進程環(huán)境下不會被中斷。
示例:使用Lua腳本,可以將多個命令組合成一個原子操作,如實現(xiàn)分布式鎖、計數(shù)器、排行榜等。

local key = KEYS[1] -- 獲取鍵名
local value = ARGV[1] -- 獲取參數(shù)值
local current = redis.call('GET', key) -- 獲取當前值
if not current or tonumber(current) < tonumber(value) then
    -- 如果當前值不存在或新值更大,設(shè)置新值
    redis.call('SET', key, value)
end

1.3.3 數(shù)據(jù)處理

場景:需要對Redis中的數(shù)據(jù)進行復(fù)雜的處理,如統(tǒng)計、篩選、聚合等。
示例:使用Lua腳本,可以在Redis中執(zhí)行復(fù)雜的數(shù)據(jù)處理,而不必將數(shù)據(jù)傳輸?shù)娇蛻舳诉M行處理,減少網(wǎng)絡(luò)開銷。

local keyPattern = ARGV[1] -- 獲取鍵名的匹配模式
local keys = redis.call('KEYS', keyPattern) -- 獲取匹配的鍵
local result = {}
for i, key in ipairs(keys) do
    local data = redis.call('GET', key) -- 獲取每個鍵對應(yīng)的數(shù)據(jù)
    -- 處理數(shù)據(jù)并添加到結(jié)果中
    table.insert(result, processData(data))
end
return result

1.3.4 分布式鎖

場景:實現(xiàn)分布式系統(tǒng)中的鎖機制,確保只有一個客戶端可以執(zhí)行關(guān)鍵操作。
示例:使用Lua腳本,你可以原子性地嘗試獲取鎖,避免競態(tài)條件,然后在完成后釋放鎖。

local lockKey = KEYS[1] --獲取鎖的鍵名
local lockValue = ARGV[1] -- 獲取鎖的值
local lockTimeout = ARGV[2] -- 獲取鎖的超時時間
if redis.call('SET', lockKey, lockValue, 'NX', 'PX', lockTimeout) then
    -- 鎖獲取成功,執(zhí)行關(guān)鍵操作
    -- ...
    redis.call('DEL', lockKey) -- 釋放鎖
    return true
else
    return false -- 無法獲取鎖

這些場景只是Lua腳本在Redis中的應(yīng)用之一。Lua腳本允許你在Redis中執(zhí)行更復(fù)雜的操作,而無需進行多次的網(wǎng)絡(luò)通信,從而提高性能和可伸縮性,同時確保數(shù)據(jù)的一致性和原子性。這使得Lua成為Redis的強大工具,用于處理各種分布式系統(tǒng)需求。

1.3.5 Redis中調(diào)試Lua

RedisLua 腳本中,KEYSARGV 是兩個特殊的全局變量,用于獲取傳遞給腳本的鍵和參數(shù)。

  • KEYS變量:
    KEYS 是一個數(shù)組,包含了傳遞給腳本的所有鍵。可以使用 KEYS 變量來訪問這些鍵,并執(zhí)行相應(yīng)的操作,如獲取值、修改值等。
    例如:local value = redis.call("GET", KEYS[1])
    在例中使用 KEYS[1] 來獲取傳遞給腳本的第一個鍵,并使用 redis.call 函數(shù)來獲取該鍵的值。
  • ARGV 變量:
    ARGV 是一個數(shù)組,包含了傳遞給腳本的所有參數(shù)??梢允褂?ARGV 變量來訪問這些參數(shù),并執(zhí)行相應(yīng)的操作,如解析參數(shù)、計算參數(shù)等。

redis中驗證 lua腳本的兩種方式:

  • 登錄redis后執(zhí)行eval命令:EVAL script numkeys key [key ...] arg [arg ...]
    例如:EVAL "local key = KEYS[1]\nlocal value = ARGV[1]\nredis.call('SET', key, value)" 1 mykey myvalue
    • script:是要執(zhí)行的Lua腳本
    • numkeys:是腳本中用到的鍵的數(shù)量
    • key [key ...]:是腳本中用到的鍵的名稱
    • arg [arg ...]:是腳本中用到的參數(shù)
  • 不登錄執(zhí)行 --eval命令,如果lua腳本較長,可以使用redis-cli --eval的方式,新建lua.lua文件,在文件中輸入:return KEYS[1]..ARGV[1]
    linux中執(zhí)行:redis-cli --eval 文件路徑 keys , argvs
    key和參數(shù)間需要使用逗號(,)隔開,并且逗號前后需要占用空格

1.4 Lua腳本在Spring Boot中的實現(xiàn)

Spring Boot中實現(xiàn)Lua腳本的執(zhí)行主要涉及Spring Data RedisLettuce(或Jedis)客戶端的使用。以下是編寫、加載和執(zhí)行Lua腳本的步驟和示例:

1.4.1 pom.xml和配置

首先,在Spring Boot項目的pom.xml中,添加Spring Data RedisLettuce(或Jedis)的依賴。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>io.lettuce.core</groupId>
    <artifactId>lettuce-core</artifactId> <!-- 或使用Jedis -->
</dependency>

配置Redis連接:
application.propertiesapplication.yml中配置Redis連接屬性,包括主機、端口、密碼等。

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=yourPassword

1.4.2 創(chuàng)建Lua腳本

創(chuàng)建一個Lua腳本,以執(zhí)行你需要的操作。將腳本保存在Spring Boot項目的合適位置。
例如,假設(shè)你有一個Lua腳本文件myscript.lua,它實現(xiàn)了一個簡單的計算:

local a = tonumber(ARGV[1])
local b = tonumber(ARGV[2])
return a + b

編寫Java代碼:
Spring Boot應(yīng)用中,編寫Java代碼以加載和執(zhí)行Lua腳本。使用Spring Data Redis提供的StringRedisTemplateLettuceConnectionFactory。

提供兩種不同的示例來執(zhí)行Lua腳本,一種是直接運行Lua腳本字符串,另一種是運行腳本文件。以下是這兩種示例:

1.4.2.1 運行Lua腳本字符串
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

@Service
public class LuaScriptService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public Integer executeLuaScriptFromString() {
        String luaScript = "local a = tonumber(ARGV[1])\nlocal b = tonumber(ARGV[2])\nreturn a + b";
        RedisScript<Integer> script = new DefaultRedisScript<>(luaScript, Integer.class);
        String[] keys = new String[0]; // 通常情況下,沒有KEYS部分
        Object[] args = new Object[]{10, 20}; // 傳遞給Lua腳本的參數(shù)
        Integer result = stringRedisTemplate.execute(script, keys, args);
        return result;
    }
}
1.4.2.2 運行Lua腳本文件

首先,將Lua腳本保存到文件,例如myscript.lua。
然后,創(chuàng)建一個Java類來加載和運行該腳本文件:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

@Service
public class LuaScriptService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ResourceLoader resourceLoader;

    public Integer executeLuaScriptFromFile() {
        Resource resource = resourceLoader.getResource("classpath:myscript.lua");
        String luaScript;
        try {
            luaScript = new String(resource.getInputStream().readAllBytes());
        } catch (Exception e) {
            throw new RuntimeException("Unable to read Lua script file.");
        }
        
        RedisScript<Integer> script = new DefaultRedisScript<>(luaScript, Integer.class);
        String[] keys = new String[0]; // 通常情況下,沒有KEYS部分
        Object[] args = new Object[]{10, 20}; // 傳遞給Lua腳本的參數(shù)
        Integer result = stringRedisTemplate.execute(script, keys, args);
        return result;
    }
}

通過這兩種示例,可以選擇要執(zhí)行Lua腳本的方式,是直接在Java代碼中定義腳本字符串,還是從文件中讀取腳本。

1.4.3 使用Lua腳本限流

1.4.3.1 自定義注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RedisLimitAnnotation {
 
    /**
     * key
     */
    String key() default "";
    /**
     * Key的前綴
     */
    String prefix() default "";
    /**
     * 一定時間內(nèi)最多訪問次數(shù)
     */
    int count();
    /**
     * 給定的時間范圍 單位(秒)
     */
    int period(); 
 
}
1.4.3.2 自定義redis配置類
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
 
import java.io.Serializable;
 
@Configuration
public class RedisConfiguration {
 
     @Bean
    public DefaultRedisScript<Long> redisluaScript() {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("limit.lua")));
        redisScript.setResultType(Long.class);
        return redisScript;
    }
 
    @Bean("redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance ,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        //設(shè)置value的序列化方式為JSOn
//        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        //設(shè)置key的序列化方式為String
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
 
        return redisTemplate;
    }
 
}
1.4.3.3 自定義限流AOP類
import cn.annotation.RedisLimitAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

@Slf4j
@Configuration
public class LimitRestAspect {
 
 
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
 
    @Autowired
    private DefaultRedisScript<Long> redisluaScript;
 
 
    @Pointcut(value = "@annotation(com.congge.config.limit.RedisLimitAnnotation)")
    public void rateLimit() {
 
    }
 
    @Around("rateLimit()")
    public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        RedisLimitAnnotation rateLimit = method.getAnnotation(RedisLimitAnnotation.class);
        if (rateLimit != null) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String ipAddress = getIpAddr(request);
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append(ipAddress).append("-")
                    .append(targetClass.getName()).append("- ")
                    .append(method.getName()).append("-")
                    .append(rateLimit.key());
            List<String> keys = Collections.singletonList(stringBuffer.toString());
            //調(diào)用lua腳本,獲取返回結(jié)果,這里即為請求的次數(shù)
            Long number = redisTemplate.execute(
                    redisluaScript,
                    // 此處傳參只要能轉(zhuǎn)為Object就行(因為數(shù)字不能直接強轉(zhuǎn)為String,所以不能用String序列化)
					//new GenericToStringSerializer<>(Object.class),
					// 結(jié)果的類型需要根據(jù)腳本定義,此處是數(shù)字--定義的是Long類型
                	//new GenericToStringSerializer<>(Long.class)
                    keys,
                    rateLimit.count(),
                    rateLimit.period()
            );
            if (number != null && number.intValue() != 0 && number.intValue() <= rateLimit.count()) {
                logger.info("限流時間段內(nèi)訪問了第:{} 次", number.toString());
                return joinPoint.proceed();
            }
        } else {
            return joinPoint.proceed();
        }
        throw new RuntimeException("訪問頻率過快,被限流了");
    }
 
    /**
     * 獲取請求的IP方法
     * @param request
     * @return
     */
    private static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
            }
            // 對于通過多個代理的情況,第一個IP為客戶端真實IP,多個IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) {
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }
        return ipAddress;
    } 
}

該類要做的事情和上面的兩種限流措施類似,不過在這里核心的限流是通過讀取lua腳步,通過參數(shù)傳遞給lua腳步實現(xiàn)的。

1.4.3.4 自定義lua腳本

在工程的 resources 目錄下,添加如下的lua腳本

local key = "rate.limit:" .. KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")
 
if current + 1 > limit then
  return 0
else
   -- 沒有超閾值,將當前訪問數(shù)量+1,并設(shè)置2秒過期(可根據(jù)自己的業(yè)務(wù)情況調(diào)整)
   redis.call("INCRBY", key,"1")
   redis.call("expire", key,"2")
   return current + 1
end
1.4.3.5 添加測試接口
@RestController
public class RedisController {

    @GetMapping("/redis/limit")
    @RedisLimitAnnotation(key = "queryFromRedis",period = 1, count = 1)
    public String queryFromRedis(){
        return "success";
    } 
}

為了模擬效果,這里將QPS設(shè)置為1 ,啟動工程后(提前啟動redis服務(wù)),調(diào)用一下接口,正常的效果如下,如果快速刷接口,超過每秒1次的請求時報錯

1.5 使用Lua提高SpringBoot性能

使用Lua腳本可以顯著提高Spring Boot應(yīng)用程序的性能,尤其是在與Redis交互方面。以下是如何使用Lua腳本來實現(xiàn)性能優(yōu)化的幾種方法:

1.5.1 減少網(wǎng)絡(luò)開銷

Redis是內(nèi)存數(shù)據(jù)庫,數(shù)據(jù)存儲在內(nèi)存中,而網(wǎng)絡(luò)通信通常是Redis操作的性能瓶頸之一。通過使用Lua腳本,你可以將多個操作組合成一個原子操作,從而減少了多次的網(wǎng)絡(luò)往返次數(shù)。這對于需要執(zhí)行多個Redis命令以完成一個操作的情況非常有用。

1.5.2 原子操作

Lua腳本的執(zhí)行是原子的,這意味著在Lua腳本執(zhí)行期間,沒有其他客戶端可以插入其他操作。這使得Lua腳本在實現(xiàn)諸如分布式鎖、計數(shù)器、排行榜等需要原子操作的情況下非常有用。
例如,考慮一個計數(shù)器的場景,多個客戶端需要原子性地增加計數(shù)。使用Lua腳本,你可以實現(xiàn)原子遞增:

local key = KEYS[1]
local increment = ARGV[1]
return redis.call('INCRBY', key, increment)

1.5.3 復(fù)雜操作

Lua腳本允許你在Redis服務(wù)器端執(zhí)行復(fù)雜的數(shù)據(jù)處理。這減少了將數(shù)據(jù)傳輸?shù)娇蛻舳诉M行處理的開銷,并允許你在Redis中執(zhí)行更復(fù)雜的邏輯,從而提高性能。
例如,可以使用Lua腳本來處理存儲在多個鍵中的數(shù)據(jù)并返回聚合結(jié)果:

local total = 0
for _, key in ipairs(KEYS) do
    local value = redis.call('GET', key)
    total = total + tonumber(value)
end
return total

1.5.4 事務(wù)

Lua腳本一起使用事務(wù)可以確保一系列Redis命令的原子性執(zhí)行。這對于需要一組操作要么全部成功,要么全部失敗的情況非常重要。
例如,可以使用Lua腳本在事務(wù)中執(zhí)行一系列更新操作,如果其中一個操作失敗,整個事務(wù)將回滾:

local key1 = KEYS[1]
local key2 = KEYS[2]
local value = ARGV[1]

redis.call('SET', key1, value)
redis.call('INCRBY', key2, value)

-- 如果這里的任何一步失敗,整個事務(wù)將回滾

總之,使用Lua腳本可以大大提高Spring Boot應(yīng)用程序與Redis之間的性能。它減少了網(wǎng)絡(luò)開銷,允許執(zhí)行原子操作,執(zhí)行復(fù)雜操作并實現(xiàn)事務(wù),這些都有助于提高應(yīng)用程序的性能和可伸縮性。因此,Lua腳本是在與Redis交互時實現(xiàn)性能優(yōu)化的有力工具。

1.6 錯誤處理和安全性

處理Lua腳本中的錯誤和確保安全性在與Redis交互時非常重要。以下是如何處理這些問題的一些建議:

1.6.1 錯誤處理

  • 錯誤返回值:Lua腳本在執(zhí)行期間可能會遇到錯誤,例如腳本本身存在語法錯誤,或者在腳本中的某些操作失敗。Redis執(zhí)行Lua腳本后,會返回腳本的執(zhí)行結(jié)果。可以檢查這個結(jié)果以查看是否有錯誤,通常返回值是一個特定的錯誤標識。例如,如果腳本執(zhí)行成功,返回值通常是OK,否則會有相應(yīng)的錯誤信息。
  • 異常處理: 在Spring Boot應(yīng)用程序中,可以使用異常處理來捕獲Redis執(zhí)行腳本時可能拋出的異常。Spring Data Redis提供了一些異常類,如RedisScriptExecutionException,用于處理腳本執(zhí)行期間的錯誤。可以使用try-catch塊來捕獲這些異常并采取相應(yīng)的措施,例如記錄錯誤信息或執(zhí)行備用操作。

1.6.2 安全性

  • 參數(shù)驗證: 在執(zhí)行Lua腳本之前,始終驗證傳遞給腳本的參數(shù)。確保參數(shù)是合法的,并且不包含惡意代碼。避免將不受信任的用戶輸入直接傳遞給Lua腳本,因為它可能包含惡意的Lua代碼。
  • 限制權(quán)限: 在Redis服務(wù)器上配置適當?shù)臋?quán)限,以限制對Lua腳本的執(zhí)行。確保只有授權(quán)的用戶能夠執(zhí)行腳本,并且不允許執(zhí)行具有破壞性或不安全操作的腳本。
  • 白名單: 如果你允許動態(tài)加載Lua腳本,確保只有受信任的腳本可以執(zhí)行??梢詣?chuàng)建一個白名單,只允許執(zhí)行白名單中的腳本,防止執(zhí)行未經(jīng)審核的腳本。
  • 沙盒模式: 一些Redis客戶端庫支持將Lua腳本運行在沙盒模式下,以限制其訪問和執(zhí)行權(quán)限。在沙盒模式下,腳本無法執(zhí)行危險操作,如文件訪問。
  • 監(jiān)控日志: 記錄Redis執(zhí)行Lua腳本的相關(guān)信息,包括誰執(zhí)行了腳本以及執(zhí)行的腳本內(nèi)容。這有助于跟蹤執(zhí)行情況并發(fā)現(xiàn)潛在的安全問題。

總之,處理Lua腳本中的錯誤和確保安全性是非常重要的。通過適當?shù)腻e誤處理和安全措施,可以確保Lua腳本在與Redis交互時不會引入潛在的問題,并提高應(yīng)用程序的穩(wěn)定性和安全性文章來源地址http://www.zghlxwxcb.cn/news/detail-714961.html

到了這里,關(guān)于Redis之Lua腳本講解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • Springboot+Redis執(zhí)行l(wèi)ua腳本

    ????????隨著Redis數(shù)據(jù)庫的廣泛應(yīng)用,它在服務(wù)器端應(yīng)用程序中的作用越來越重要。Redis具有快速讀寫、數(shù)據(jù)持久化、發(fā)布訂閱、事務(wù)處理等諸多特性,而這些特性使得它在處理高并發(fā)、實時數(shù)據(jù)操作等方面表現(xiàn)出色。然而,單純使用Redis還不足以滿足一些復(fù)雜業(yè)務(wù)邏輯的需

    2024年02月09日
    瀏覽(23)
  • Redis命令行使用Lua腳本

    Lua腳本在Redis中的使用非常有用,它允許你在Redis服務(wù)器上執(zhí)行自定義腳本,可以用于復(fù)雜的數(shù)據(jù)處理、原子性操作和執(zhí)行多個Redis命令。以下是Lua腳本在Redis中的基本使用詳細講解: 運行Lua腳本: 在Redis中,你可以使用 EVAL 或 EVALSHA 命令來運行Lua腳本。 EVAL 用于執(zhí)行未經(jīng)緩存

    2024年02月07日
    瀏覽(15)
  • 【Redis】Redis 的學(xué)習(xí)教程(十二)之在 Redis使用 lua 腳本

    lua 菜鳥教程:https://www.runoob.com/lua/lua-tutorial.html 在 Redis 使用 lua 腳本的好處: 減少網(wǎng)絡(luò)開銷 。可以將多個請求通過腳本的形式一次發(fā)送,減少網(wǎng)絡(luò)時延及開銷 原子性操作 。Redis會將整個腳本作為一個整體執(zhí)行,中間不會被其他請求插入。因此在腳本運行過程中無需擔(dān)心會出

    2024年02月07日
    瀏覽(23)
  • lua腳本獲取table類型-Java使用lua腳本操作redis獲取zset元素的集合

    lua腳本獲取table類型-Java使用lua腳本操作redis獲取zset元素的集合 7.0點贊功能-定時持久化到數(shù)據(jù)庫-lua腳本的編寫_嗶哩嗶哩_bilibili https://www.bilibili.com/video/BV1bu411j75u 這個腳本主要是放到Springboot工程里的, 這里如果是向放到字段控制臺執(zhí)行,那就要加入 eval 以及其他參數(shù):

    2024年02月13日
    瀏覽(17)
  • redis事務(wù)對比Lua腳本區(qū)別是什么

    redis事務(wù)對比Lua腳本區(qū)別是什么

    redis官方對于lua腳本的解釋:Redis使用同一個Lua解釋器來執(zhí)行所有命令,同時,Redis保證以一種原子性的方式來執(zhí)行腳本:當lua腳本在執(zhí)行的時候,不會有其他腳本和命令同時執(zhí)行,這種語義類似于 MULTI/EXEC。從別的客戶端的視角來看,一個lua腳本要么不可見,要么已經(jīng)執(zhí)行完

    2024年02月12日
    瀏覽(19)
  • 【征服redis9】快速征服lua腳本

    lua腳本,這個名字總讓人想歪,不過老外發(fā)明名字,我們只能跟著叫了。這個腳本語言在redis里和Nginx里都有用,所以我們就來看一下。 目錄 1 lua的介紹與說明 2 lua的基本語句體驗 3.Lua的數(shù)據(jù)結(jié)構(gòu)和高級特性 Lua是一種小巧而強大的腳本語言,最初由巴西里約熱內(nèi)盧天主教大學(xué)

    2024年01月20日
    瀏覽(24)
  • lua腳本實現(xiàn)Redis令牌桶限流

    令牌桶限流是一種常見的流量控制算法,用于控制系統(tǒng)的請求處理速率,防止系統(tǒng)過載。在令牌桶限流算法中,可以將請求看作是令牌,而令牌桶則表示系統(tǒng)的處理能力。系統(tǒng)在處理請求時,首先需要從令牌桶中獲取令牌,如果令牌桶中沒有足夠的令牌,就需要等待一定時間

    2024年02月14日
    瀏覽(18)
  • Redis Lua腳本執(zhí)行原理和語法示例

    Redis Lua腳本執(zhí)行原理和語法示例

    在學(xué)習(xí)本文之前,我是強烈建議大家了解一下我的其他兩篇博客 《Redis 從入門到精通【進階篇】一文學(xué)會Lua腳本》 《Redis 從入門到精通【進階篇】之Lua腳本詳解》 Redis通過嵌入Lua解釋器,實現(xiàn)了對Lua腳本的執(zhí)行。在執(zhí)行過程中,Redis保證了腳本的原子性和阻塞性,同時通過腳

    2024年02月12日
    瀏覽(21)
  • Spring Boot - 結(jié)合 Redis 使用 Lua腳本

    在Spring Boot中整合Redis并使用Lua腳本: 添加Spring Boot和Redis的依賴: 首先,在Spring Boot項目的 pom.xml 文件中添加Spring Boot和Spring Data Redis的依賴: 配置Redis連接: 在 application.properties 或 application.yml 中配置Redis的連接信息,以及 redis 配置: RedisConfig.java

    2024年02月08日
    瀏覽(20)
  • Java生態(tài)/Redis中如何使用Lua腳本

    Java生態(tài)/Redis中如何使用Lua腳本

    Mac上安裝LUA很簡單,直接使用 brew 相關(guān)命令; 使用 lua -v 命令可以看到lua已經(jīng)安裝完畢。 創(chuàng)建一個test.lua文件,內(nèi)容為: 執(zhí)行命令: 輸出為: Lua 提供了交互式編程和腳本式編程: 交互式編程:直接在命令行中輸入語法,可以立即執(zhí)行并查看到執(zhí)行效果。 腳本是編程:編寫

    2024年01月20日
    瀏覽(15)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包