반응형

루어는 달을 뜻하는 포르투칼어 이며 1993 브라질에서 처음 개발 되었으며, 강력하고 가벼우면서 다른 언어에 이식하기 좋은 스크립트 언어이다.

강력한 문자열 함수와 수학 함수를 제공해 주기 때문에 쉽게 익힐 있다.

그래픽 시뮬레이션을 위한 스크립트 언어로 개발 되었기 때문에 스크립트 언어보다 빠른 성능을 제공한다.

자바처럼 가비지 컬렉션을 제공하기 때문에 사용되지 않는 변수를 제거하기 위해 별도의 처리가 필요 없다.

그러나 즉시 가비지 컬렉션 대상으로 만들려면 변수에 nil 할당 하면 된다.

 

루아 스크립트는

1. 대소 문자를 구분한다.

2. 다른 스크립트 언어와 같이 변수 선언하지 않는다. 입력된 값에 의해서 변수형이 자동으로 지정된다. (TYPE 이용하여 변수 확인 가능)

3. 변수 명은 반드시 영어 또는 _(언더 스코어) 시작해야 한다.

4. 예약어를 변수로 사용할 없다.

예약어

and / break / do / else / elseif / end / false / for / function / if / in / local / nil / not / or / repeat / return / then / true / until / while

5. 전역 변수와 지역 변수르 구분 한다.

전역 변수는 프로그램 어느 위치에서나 사용이 가능하나, 지역변수는 변수가 선언 블럭 에서만 참조 된다.

특별한 지정을 하지 않으면 모두 전역 변수 인식한다. 함수도 전역 함수로 지정 .

local 키워드를 써서 지역변수 지역함수임을 선언한다.

6. 선언되지 않고 사용되는 변수는 전역 변수 판단한다.

7. 배열의 인덱스는 1부터 시작 .

 

 

레디스에서 루아를 사용하기 위해서는 가지 제약 사항이 따른다.

1. 지역 변수 사용해야 한다. (전역변수를 사용하면 된다.)

루아 스크립트에서 선언한 전역 변수나 전역 함수가 레디스 자체의 함수들과 충돌이 발생 있어서 권장 하지 않음.

2. 루아의 배열인 테이블을 사용할 첨자는 숫자로만 사용해야 한다.

 

레디스 + 루아

레디스에서 루아 스크립트를 지원하기 시작한 것은 레디스 2.6버전 부터이며, 루아 5.1 버전을 지원하기 시작했다.

eval 명령어를 이용하여 수행하고자 하는 스크립트를 레디스로 전송한다. (일회성)

루아 스크립트를 script load 명령을 이용하여 레디스 서버에 등록 시킨 사용할 있다.

레디스에서 루아 스크립트를 실행 할때 스크립트에 대한 인수를 입력 있다.

루아 스크립트에서 레디스의 명령을 사용할 있다.

레디스 서버에서 실행 되는 루아 스크립트는 원자적으로 처리 된다.(사용자가 작성한 스크립트가 실행되는 동안은 다른 레디스 명령이 실행 되지 못한다.)



1. Return 되는 Key count

 

redis.call 앞에 # 붙여 주면 된다. length 구할 # 사용한다.

 

127.0.0.1:6379> eval "return redis.call('keys','jstar:Info:*')" 0

1) "jstar:Info:Age"

2) "jstar:Info:JoinDay"

3) "jstar:Info:LastLoginDay"

4) "jstar:Info:Sex"

5) "jstar:Info:SiteID"

6) "jstar:Info:TotalLoginCount"

127.0.0.1:6379> eval "return #redis.call('keys','jstar:Info:*')" 0

(integer) 6

 

2. 리스트 값을 가져와서 값으로 비교 하기

 

127.0.0.1:6379> smembers Campaign:1:UserFilter:FilterCount

1) "3"

 

3 값을 가져와서 비교를 하려고 하는데 다음과 같은 에러가 발생

 

127.0.0.1:6379> eval "local ans local filtercount = redis.call('smembers',KEYS[1]) if filtercount >0 then ans = filtercount else ans = 0 end return ans" 1 Campaign:1:UserFilter:FilterCount

(error) ERR Error running script (call to f_fa7d4e6c4da479ae5c2e37141ec731dc8fb2c301): @user_script:1: user_script:1: attempt to compare number with table

 

 

값을 가져오게 unpack 사용하고, 해당 값이 string으로 나오니, 숫자로 변환을 주어야 한다.

 

127.0.0.1:6379> eval "local ans local value = unpack(redis.call('smembers' ,KEYS[1])) if tonumber(value) > 0 then  ans = value else ans = 0 end  return ans" 1  Campaign:1:UserFilter:FilterCount

"3"

 

 

 

3. 목록을 출력한 후에 해당 list 키들의 값을 가져오기

 

127.0.0.1:6379> keys jstar:Info:*

1) "jstar:Info:Age"

2) "jstar:Info:JoinDay"

3) "jstar:Info:TotalLoginCount"

4) "jstar:Info:SiteID"

5) "jstar:Info:LastLoginDay"

6) "jstar:Info:Sex"

127.0.0.1:6379> EVAL "local members = redis.call('keys','jstar:Info:*') local results = {}  for index,key in ipairs(members) do results[index] = key end return results" 0

1) "jstar:Info:Age"

2) "jstar:Info:JoinDay"

3) "jstar:Info:LastLoginDay"

4) "jstar:Info:Sex"

5) "jstar:Info:SiteID"

6) "jstar:Info:TotalLoginCount"

127.0.0.1:6379> smembers jstar:Info:Age

1) "40"

127.0.0.1:6379> smembers jstar:Info:JoinDay

1) "2017.01.01"

127.0.0.1:6379> smembers jstar:Info:TotalLoginCount

1) "54"

127.0.0.1:6379> smembers jstar:Info:SiteID

1) "job"

2) "academy"

3) "campstudy"

127.0.0.1:6379> smembers jstar:Info:LastLoginDay

1) "2017.12.22"

127.0.0.1:6379> smembers jstar:Info:Sex

1) "M"

127.0.0.1:6379> EVAL "local members = redis.call('keys','jstar:Info:*') local results = {}  for index,key in ipairs(members) do results[index] = redis.call('smembers',key) end return results" 0

1) 1) "40"

2) 1) "2017.01.01"

3) 1) "2017.12.22"

4) 1) "M"

5) 1) "academy"

   2) "campstudy"

   3) "job"

6) 1) "54"

127.0.0.1:6379> smembers jstar:Info:Age

1) "40"

127.0.0.1:6379> smembers jstar:Info:JoinDay

1) "2017.01.01"

127.0.0.1:6379> smembers jstar:Info:TotalLoginCount

1) "54"

127.0.0.1:6379> smembers jstar:Info:SiteID

1) "job"

2) "academy"

3) "campstudy"

127.0.0.1:6379> smembers jstar:Info:LastLoginDay

1) "2017.12.22"

127.0.0.1:6379> smembers jstar:Info:Sex

1) "M"

127.0.0.1:6379> EVAL "local members = redis.call('keys','jstar:Info:*') local results = {} for index,key in ipairs(members) do results[index] = unpack(redis.call('smembers', key ))  .. ' / ' .. key end return results" 0

1) "40 / jstar:Info:Age"

2) "2017.01.01 / jstar:Info:JoinDay"

3) "2017.12.22 / jstar:Info:LastLoginDay"

4) "M / jstar:Info:Sex"

5) "academy / jstar:Info:SiteID"

6) "54 / jstar:Info:TotalLoginCount"

 

단점이 있음. SiteID 값이 여러개 인데 하나 밖에 출력을 시킴.

좀더 찾아 봐야 .

 

4. 더하기

 

127.0.0.1:6379[2]> keys 'Site:ID:201801*:Try'

1) "Site:ID:20180103:Try"

2) "Site:ID:20180102:Try"

3) "Site:ID:20180101:Try"

127.0.0.1:6379[2]> get Site:ID:20180103:Try

"1000"

127.0.0.1:6379[2]> get Site:ID:20180102:Try

"20"

127.0.0.1:6379[2]> get Site:ID:20180101:Try

"100"

127.0.0.1:6379[2]>

127.0.0.1:6379[2]> EVAL "local members = redis.call('keys',ARGV[1]) local total = 0 for index,key in ipairs(members) do total = total + redis.call('get',key) end return total" 0 'Site:ID:201801*:Try'

(integer) 1120 

 

5. Set 들어있는 값의 Like 검색

 

127.0.0.1:6379[2]> smembers Test:Info:Site

1) "www.naver.com"

2) "www.daum.net"

3) "www.korea.com"

4) "www.google.com"

127.0.0.1:6379[2]> sscan Test:Info:Site 0 match '*com*'

1) "0"

2) 1) "www.naver.com"

   2) "www.korea.com"

   3) "www.google.com"

 

위와 같이 컴맨드 라인에서는 결과가 나오는데, Like 검색의 결과로 값의 개수를 구하고 싶은 경우.

번째 커서를 나타내는 0 필요 없고, 두번째 값의 카운트를 세면 된다.

 

스크립트

 

$cat sscan.lua

 

local key, pat = KEYS[1], ARGV[1]

local cursor, res = 0, 0

repeat

local rep = redis.call('SSCAN', key, cursor, 'MATCH', pat)

cursor = rep[1]

res = res + #rep[2]

until cursor == '0'

return res

 

$ redis-cli --eval sscan.lua Test:Info:Site , "*niver*"

(integer) 0

$ redis-cli --eval sscan.lua Test:Info:Site , "*naver*"

(integer) 1

$ redis-cli --eval sscan.lua Test:Info:Site , "*com*"

(integer) 3

 

127.0.0.1:6379[2]> eval "local key, pat = KEYS[1], ARGV[1]  local cursor, res = 0, 0 repeat   local rep = redis.call('SSCAN', key, cursor, 'MATCH', pat)   cursor = rep[1]   res = res + #rep[2]  until cursor == '0'  return res" 1 Test:Info:Site  "*com*"

(integer) 3

127.0.0.1:6379[2]> eval "local key, pat = KEYS[1], ARGV[1]  local cursor, res = 0, 0 repeat   local rep = redis.call('SSCAN', key, cursor, 'MATCH', pat)   cursor = rep[1]   res = res + #rep[2]  until cursor == '0'  return res" 1 Test:Info:Site  "*niver*"

(integer) 0

127.0.0.1:6379[2]> eval "local key, pat = KEYS[1], ARGV[1]  local cursor, res = 0, 0 repeat   local rep = redis.call('SSCAN', key, cursor, 'MATCH', pat)   cursor = rep[1]   res = res + #rep[2]  until cursor == '0'  return res" 1 Test:Info:Site  "*naver*"

(integer) 1

 


6. Hash Table안에 있는 key 더하기

Hash table 키를 관리하고 있는데, 월별 Key값을 더해야 하는 경우가 발생 .

해당 날짜에 키가 있는지 없는지 체크 하는 대신 1 부터 31일까지의 값을 가져오게 .

해당 날짜의 key 없을 경우 nil 발생 되므로, nil check 로직 추가.

 

# 테스트 데이터 입력

127.0.0.1:6379[3]> hmset SiteID:TotalCnt 20180101 10 20180102 2 20180103 5 20180120 10 20180131 30 20180205 20 20180210 5

OK

 

# 입력된 데이터 확인

127.0.0.1:6379[3]> hgetall SiteID:TotalCnt

 1) "20180101"

 2) "10"

 3) "20180102"

 4) "2"

 5) "20180103"

 6) "5"

 7) "20180120"

 8) "10"

 9) "20180131"

10) "30"

11) "20180205"

12) "20"

13) "20180210"

14) "5"

 

# 더하고자 하는 201801 Key값만 확인

127.0.0.1:6379[3]> hmget SiteID:TotalCnt 20180101 20180102 20180103 20180120 20180131

1) "10"

2) "2"

3) "5"

4) "10"

5) "30"

 

sumkey.lua :

 

local key, mon = KEYS[1], ARGV[1]

local subkey

local sumkey, keyval= 0

 

for i = 1,31 do

    if i < 10 then subkey = mon .. '0' .. tostring(i)

    else subkey = mon .. tostring(i)

    end

 

    keyval = tonumber(redis.call('hget',key,subkey))

 

    if keyval ~= nil then

        sumkey = sumkey + keyval

    end

end

 

return sumkey

 

ubuntu@ip-172-31-26-85:~$ redis-cli --eval sumkey.lua  SiteID:TotalCnt , '201801'

(integer) 57

 

 

127.0.0.1:6379[3]> eval "local key, mon = KEYS[1], ARGV[1] local subkey local sumkey, keyval= 0 for i = 1,31 do if i < 10 then subkey = mon .. '0' .. tostring(i) else subkey = mon .. tostring(i) end keyval = tonumber(redis.call('hget',key,subkey)) if keyval ~= nil then sumkey = sumkey + keyval end end return sumkey " 1  SiteID:TotalCnt '201801'

(integer) 57 

 


7. 6번에 이어서 오늘 부터 한달 사이의 Key값을 더하기

현재 날짜로 부터 30 전의 날짜 값은 계산해서 넣어 줘야 .

tostring()대신 ("%02d"):format() 사용할 경우 파일로 수행 시키는 것은 상관 없지만, 컴멘드 라인에서 수행 " 앞에 \ 넣어 주어야 .

eval " " 수행하기 때문에 중간에 " 문자로 인식하게 하기 위해서 \" 바꿔 주어야 .

 

#입력된 테스트 데이터

 

127.0.0.1:6379[3]> hgetall SiteID:TotalCnt

 1) "20180101"

 2) "10"

 3) "20180102"

 4) "2"

 5) "20180103"

 6) "5"

 7) "20180120"

 8) "10"

 9) "20180131"

10) "30"

11) "20180205"

12) "20"

13) "20180210"

14) "5"

15) "20180231"

16) "23"

17) "20181230"

18) "1230"

19) "20190130"

20) "130"

21) "20180301"

22) "310"

 

sumrangekey.lua :

 

local key = KEYS[1]

local beforeyear, beforemon, beforeday = tonumber(ARGV[1]), tonumber(ARGV[2]), tonumber(ARGV[3])

local afteryear, aftermon, afterday = tonumber(ARGV[4]), tonumber(ARGV[5]), tonumber(ARGV[6])

 

local subkey

local sumkey, keyval= 0

 

if beforemon ~= aftermon then

 

    for i = beforeday, 31 do

        subkey = tostring(beforeyear) .. ("%02d"):format(beforemon) .. ("%02d"):format(i)

        keyval = tonumber(redis.call('hget',key,subkey))

        if keyval ~= nil then

            sumkey = sumkey + keyval

        end

    end

 

    for i = 1 , afterday do

        subkey = tostring(afteryear) .. ("%02d"):format(aftermon) .. ("%02d"):format(i)

        keyval = tonumber(redis.call('hget',key,subkey))

        if keyval ~= nil then

            sumkey = sumkey + keyval

        end

    end

 

else

 

    for i = beforeday, afterday do

        subkey = tostring(beforeyear) .. ("%02d"):format(beforemon) .. ("%02d"):format(i)

        keyval = tonumber(redis.call('hget',key,subkey))

        if keyval ~= nil then

            sumkey = sumkey + keyval

        end

    end

 

end

 

return sumkey

 

 

ubuntu$ redis-cli -n 3 --eval sumrangekey.lua  SiteID:TotalCnt , '2018' '12' '1' '2019' '1' '31'

(integer) 1360

ubuntu$ redis-cli -n 3 --eval sumrangekey.lua  SiteID:TotalCnt , '2018' '2' '1' '2018' '3' '1'

(integer) 358

ubuntu$ redis-cli -n 2 --eval sumrangekey.lua  SiteID:TotalCnt , '2018' '1' '1' '2018' '2' '10'

(integer) 82

 

 

 

 

127.0.0.1:6379[3]> eval "local key = KEYS[1] local beforeyear, beforemon, beforeday = tonumber(ARGV[1]), tonumber(ARGV[2]), tonumber(ARGV[3]) local afteryear, aftermon, afterday = tonumber(ARGV[4]), tonumber(ARGV[5]), tonumber(ARGV[6]) local subkey local sumkey, keyval= 0 if beforemon ~= aftermon then     for i = beforeday, 31 do         subkey = tostring(beforeyear) .. (\"%02d\"):format(beforemon) .. (\"%02d\"):format(i)         keyval = tonumber(redis.call('hget',key,subkey))         if keyval ~= nil then             sumkey = sumkey + keyval         end     end     for i = 1 , afterday do         subkey = tostring(afteryear) .. (\"%02d\"):format(aftermon) .. (\"%02d\"):format(i)         keyval = tonumber(redis.call('hget',key,subkey))         if keyval ~= nil then             sumkey = sumkey + keyval         end     end else     for i = beforeday, afterday do         subkey = tostring(beforeyear) .. (\"%02d\"):format(beforemon) .. (\"%02d\"):format(i)         keyval = tonumber(redis.call('hget',key,subkey))         if keyval ~= nil then             sumkey = sumkey + keyval         end     end end return sumkey" 1 SiteID:TotalCnt '2018' '1' '1' '2018' '2' '10'

(integer) 82

 

 

 

반응형

+ Recent posts