Redis uses lua script

Posted by jiayanhuang on Tue, 01 Feb 2022 15:16:48 +0100

1. redis uses lua script syntax

Redis Eval command - execute Lua script

redis 127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

among
Script: the parameter is a Lua 5.1 script program. A script need not (and should not) be defined as a Lua function.
numkeys: used to specify the number of key name parameters.
key [key...]: calculated from the third parameter of EVAL, it indicates the Redis KEYS (KEYS) used in the script. These key name parameters can be accessed in Lua through the global variable KEYS array with 1 as the base address (KEYS[1], KEYS[2], and so on).
arg [arg...]: additional parameters are accessed through the global variable ARGV array in Lua, and the access form is similar to the KEYS variable (ARGV[1], ARGV[2], and so on).

You can directly execute the written lua script through redis cli -- Eval:

redis-cli --eval /test.lua 0

2,Lua

lua is a lightweight and compact script language, which is written in standard C language and open in the form of source code. Its design purpose is to be embedded in the application, so as to provide flexible expansion and customization functions for the application.

download

print('hello world') 

-- notes

a=1
b="abc"
c={}
d=print
c={"a","b","c"}

print(type(a))
print(type(b))
print(type(c))
print(type(d))

-- multiline comment 
[[
-------- Output ------
number
string
table
function
]]

a="single 'quoted' string and double \"quoted\" string inside"
b='single \'quoted\' string and double "quoted" string inside'
c= [[ multiple line
with 'single'
and "double" quoted strings inside.]]

print(a)
print(b)
print(c)

[[
-------- Output ------

single 'quoted' string and double "quoted" string inside
single 'quoted' string and double "quoted" string inside
 multiple line
with 'single'
and "double" quoted strings inside.
]]

a,b,c,d,e = 1, 2, "three", "four", 5

a,b,c,d,e = 1, 1.123, 1E9, -123, .0008
print("a="..a, "b="..b, "c="..c, "d="..d, "e="..e)


[[
-------- Output ------

a=1     b=1.123 c=1000000000    d=-123  e=0.0008
]]

address={} -- empty address
address.Street="Wyman Street"
address.StreetNumber=360
address.AptNumber="2a"
address.City="Watertown"
address.State="Vermont"
address.Country="USA"
print(address.StreetNumber, address["AptNumber"])

-- end end
a=1
if a==1 then
    print ("a is one")
end

c=3
if c==1 then
    print("c is 1")
elseif c==2 then
    print("c is 2")
else
    print("c isn't 1 or 2, c is "..tostring(c))
end

a=1
b=(a==1) and "one" or "not one"
print(b)

-- b = ((a==1) ? "one" : "not one")

-- loop
a=1
while a~=5 do -- Lua uses ~= to mean not equal
    a=a+1
    io.write(a.." ")
end

a=0
repeat
    a=a+1
    print(a)
until a==5

for a=1,6,3 do io.write(a) end

-- 14
[[ 
for (int i = 1; i < 6; i += 3) {
	printf(i);
}
]]

for key,value in pairs({1,2,3,4}) do print(key, value) end

[[
-------- Output ------

1       1
2       2
3       3
4       4
]]

a={1,2,3,4,"five","elephant", "mouse"}

for i,v in pairs(a) do print(i,v) end

[[
-------- Output ------

1       1
2       2
3       3
4       4
5       five
6       elephant
7       mouse
]]

-- break
a=0
while true do
    a=a+1
    if a==10 then
        break
    end
end

-- function
function myFirstLuaFunctionWithMultipleReturnValues(a,b,c)
    return a,b,c,"My first lua function with multiple return values", 1, true
end

a,b,c,d,e,f = myFirstLuaFunctionWithMultipleReturnValues(1,2,"three")
print(a,b,c,d,e,f)

[[
-------- Output ------

1       2       three   My first lua function with multiple return values       1       true

]]

-- local local variable
function myfunc()
    local b=" local variable"
    a="global variable"
    print(a,b)
end

function printf(fmt, ...)
    io.write(string.format(fmt, ...))
end

printf("Hello %s from %s on %s\n",
       os.getenv"USER" or "there", _VERSION, os.date())

-- Math functions:
-- math.abs, math.acos, math.asin, math.atan, math.atan2,
-- math.ceil, math.cos, math.cosh, math.deg, math.exp, math.floor,
-- math.fmod, math.frexp, math.huge, math.ldexp, math.log, math.log10,
-- math.max, math.min, math.modf, math.pi, math.pow, math.rad,
-- math.random, math.randomseed, math.sin, math.sinh, math.sqrt,
-- math.tan, math.tanh

-- String functions:
-- string.byte, string.char, string.dump, string.find, string.format,
-- string.gfind, string.gsub, string.len, string.lower, string.match,
-- string.rep, string.reverse, string.sub, string.upper

-- Table functions:
-- table.concat, table.insert, table.maxn, table.remove, table.sort

-- IO functions:
-- io.close , io.flush, io.input, io.lines, io.open, io.output, io.popen,
-- io.read, io.stderr, io.stdin, io.stdout, io.tmpfile, io.type, io.write,
-- file:close, file:flush, file:lines ,file:read,
-- file:seek, file:setvbuf, file:write

print(io.open("file doesn't exist", "r"))

-- OS functions:
-- os.clock, os.date, os.difftime, os.execute, os.exit, os.getenv,
-- os.remove, os.rename, os.setlocale, os.time, os.tmpname

-- require Import package
require( "iuplua" )
ml = iup.multiline
    {
    expand="YES",
    value="Quit this multiline edit app to continue Tutorial!",
    border="YES"
    }
dlg = iup.dialog{ml; title="IupMultiline", size="QUARTERxQUARTER",}
dlg:show()
print("Exit GUI app to continue!")
iup.MainLoop()

Lua runs a garbage collector to collect all dead objects (that is, objects that can no longer be accessed in Lua) to complete automatic memory management. All memory used in Lua, such as string, table, user data, function, thread, internal structure, etc., are subject to automatic management.

Lua implements an incremental tag scan collector. It uses these two numbers to control the garbage collection cycle: the garbage collector intermittent rate and the garbage collector step rate. Both numbers are in percentages (for example, a value of 100 internally represents 1).

3. redis uses Lua

Return the result through return and redis Call to execute the redis command:

eval "return redis.call('keys','*')" 0

The above command returns all keys, which is similar to executing keys directly*

The following command deletes all key values in dict * format

eval "local redisKeys = redis.call('keys',KEYS[1]..'*');for i,k in pairs(redisKeys) do redis.call('del',k);end;return redisKeys;" 1 dict

Expand as follows

local redisKeys = redis.call('keys',KEYS[1]..'*');
for i,k in pairs(redisKeys) do 
	redis.call('del',k);
end;
return redisKeys;

The following command deletes all key values

eval "local sum = 0;for i,k in pairs(redis.call('keys','*')) do redis.call('del', k);sum=sum+1;end; return 'clear '..sum..' key'" 0

like this:

Batch production key value, set expiration time, parameters: 2. Number of keys, key prefix, value of key, expiration time of key (optional)

 eval "for i=1,KEYS[1],1 do local k=KEYS[2]..i; redis.call('set',k,ARGV[1]);if ARGV[2] then redis.call('expire',k,ARGV[2]) end;end;return redis.call('keys',KEYS[2]..'*');" 2 10 test 0 20

Delete all key s with value 0. Parameters: 0, value X

 eval "local ks = {};for i,k in pairs(redis.call('keys','*')) do local v = redis.call('get',k);if v==ARGV[1] then redis.call('del',k);table.insert(ks,k); end;end;return ks;" 0 0

Delete all key s that never expire

 eval "local ks = {};for i,k in pairs(redis.call('keys','*')) do local ttl = redis.call('ttl',k);if ttl==-1 then redis.call('del',k);table.insert(ks,k); end;end;return ks;" 0

Get the list of all key s with 0 value and test prefix. Parameters: 2, x, y

 eval "local ks = {};for i,k in pairs(redis.call('keys',KEYS[1]..'*')) do local v = redis.call('get',k);if v==ARGV[1] then table.insert(ks,k); end;end;return ks;" 1 test 0

redis distributed lock is implemented and locked. If there is no lock, set local to 233, and set the expiration time to 60. If 1 is returned, the lock is successful, and 0 is returned, the lock fails. This operation is an atomic operation and can be replaced by the equivalent command set lock 233 nx px 60:

eval "if redis.call('get',KEYS[1]) then return 0;else redis.call('set',KEYS[1],ARGV[1]);redis.call('expire',KEYS[1],ARGV[2]);return  1;end;" 1 lock 233 60

Expand as follows

if redis.call('get',KEYS[1]) 
	then return 0;
else 
	redis.call('set',KEYS[1],ARGV[1]);
	redis.call('expire',KEYS[1],ARGV[2]);
	return  1;
end

redis distributed lock implementation, which releases the lock. If there is no lock, it does not need to be released. If there is a lock and the value is consistent with the passed in value, the lock is deleted and the release is successful. In other cases, the release fails. Success: 1, failure: 0.

eval "local v = redis.call('get',KEYS[1]);if v then if v~=ARGV[1] then return 0;end;redis.call('del',KEYS[1]);end;return 1;" 1 lock 233

Expand as follows

local v = redis.call('get',KEYS[1]);
if v then 
	-- If it is different from the value passed in, a return of 0 indicates failure
	if v~=ARGV[1] then 
		return 0;
	end;
	-- delete key
	redis.call('del',KEYS[1]);
end;
return 1;

1. A program lock_a. Set value 233, lock for 600 seconds, return 0 success

2. B program tries to give lock_a locks, returns 0 and fails

3. B program attempts to release A's lock (which is of course not allowed), and B does not know lock_ The value of A, failed to release the lock, and returned 0

4. A program releases the lock, returns 1, and the release is successful

5. B program tries to give lock again_ A lock up, lock up successfully

4. redisTemplate method encapsulation for executing script

@Component
public class RedisUtil {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * Execute lua script
     * @author hengyumo
     * @since 2021-06-05
     *
     * @param luaScript  lua script
     * @param returnType Returned structure type
     * @param keys       KEYS
     * @param argv       ARGV
     * @param <T>        generic paradigm
     *           
     * @return Results of execution
     */
    public <T> T executeLuaScript(String luaScript, Class<T> returnType, String[] keys, String... argv) {
        return redisTemplate.execute(RedisScript.of(luaScript, returnType),
                new StringRedisSerializer(),
                new GenericToStringSerializer<>(returnType),
                Arrays.asList(keys),
                (Object[])argv);
    }
}

It is easy to use. The following two scripts used above are used as examples:

    @Resource
    private RedisUtil redisUtil;

    @Test
    @SuppressWarnings("unchecked")
    public void testExecuteLuaScript() {
        String script = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}";
        List<Object> list = (List<Object>)redisUtil.executeLuaScript(script,
                List.class, new String[] {"a", "b"}, "a", "b");
        list.forEach(x -> System.out.println(x.toString()));

        script = "for i=1,KEYS[1],1 do local k=KEYS[2]..i; redis.call('set',k,ARGV[1]);" +
                "if ARGV[2] then redis.call('expire',k,ARGV[2]) end;end;" +
                "return redis.call('keys',KEYS[2]..'*');";
        list = (List<Object>)redisUtil.executeLuaScript(script,
                List.class, new String[] {"10", "test"}, "0", "60");
        list.forEach(x -> System.out.println(x.toString()));

    }


Output the result. The returned result is list < list >:

[a]
[b]
[a]
[b]
[test1]
[test10]
[test2]
[test3]
[test4]
[test5]
[test6]
[test7]
[test8]
[test9]

View redis:

END

Writing is not easy, your little one 👍 Will make me more motivated.

Before the end, I'm willing to love again—— ink

Topics: Java Redis lua