본문 바로가기
RediS

레디스 데이터 구조1 (문자열 데이터, 비트연산)

by 타마마임팩트_쫀 2018. 3. 16.

레디스가 지원하는 데이터형은 5가지다.

레디스는 거대한 키-값 저장소, 거대한 map 데이터 구조 저장소다.

맵 데이터 구조는 키 하나에 데이터형 하나가 저장되는 단순한 구조로 장점은 쉽고 직관적이나 단점은 데이터 가공이 제한적이다.




문자열 데이터

키 하나에 문자열 하나를 저장가능.

저장 가능한 문자열의 크기는 최대 512MB.

문자열 데이터를 저장할 때는 인코딩된 문자열과 몇 가지 부가 정보가 포함됨 구조체로 변환하여 저장하며 이를 레디스 객체형이라 부른다.


문자열 데이터의 입력과 조회


다중 입력과 다중 조회

127.0.0.1:6379> mset key1 value1 key2 value2 key3 value3

OK

127.0.0.1:6379> mget key54 key1 key2 key3 

1) (nil)

2) "value1"

3) "value2"

4) "value3"

mset 명령으로 3개의 키와 데이터를 한번에 입력.

인자가 존재하지 않는 key54를 조회하여 nil을 반환.


mget으로 데이터를 조회한 뒤 값이 없으면 mget으로 저장하는 방법으로 msetnx 명령어를 제공.

msetnx 명령은 인자로 N개의 키와 값의 쌍을 요청 했을때,

이미 존재하는 값이 하나라도 있다면 입력된 모든 키와 값은 저장하지 않는다.

127.0.0.1:6379> mset nx:key1 value1 nx:key3 value3

OK

127.0.0.1:6379> setnx nx:key3 'new value'

(integer) 0

127.0.0.1:6379> mget nx:key1 nx:key3

1) "value1"

2) "value3"

127.0.0.1:6379> msetnx nx:key2 'new value2' nx:key3 'new value3'

(integer) 0

127.0.0.1:6379> mget nx:key1 nx:key2 nx:key3

1) "value1"

2) (nil)

3) "value3"

nx:key3 키가 있어 setnx명령으로 nx:key3 키의 값이 저장되지 않음.

nx:key2가 존재하지 않지만 nx:key3 키가 존재하여 전체 키가 저장되지 않음.


setset명령은 입력된 값을 저장하고 이전 저장값을 돌려준다.

만약 족재하지 않는 키에 대하여 getset 명령이 수행되면 값을 저장하고 결과로 nil을 돌려준다.

127.0.0.1:6379> set mykey1 'old value'

OK

127.0.0.1:6379> getset mykey1 'new value'

"old value"

127.0.0.1:6379> get mykey1

"new value"

127.0.0.1:6379> getset newkey 'sample'

(nil)

127.0.0.1:6379> get newkey

"sample"

getset 명령의 결과로 이전에 된장된 'old value' 값을 돌려주고 입력값인 'new value'를 저장한다.

아직 존재하지 않는 키인 newkey 를 입력하면 이전에 저장된 값이 없어 nil을 돌려준다.


숫자의 증가와 감소

incr, decr은 값이 1씩 증감되는데, by 접미사가 붙은 incrby, decrby 명령은 입력된 인자의 값을 증감시킨다.

(키에 저장된 값이 숫자일 때만 수행된다.)

127.0.0.1:6379> incr test:key1

(integer) 1

127.0.0.1:6379> incrby test:key1 5

(integer) 6

127.0.0.1:6379> incrby test:key1 -1

(integer) 5

127.0.0.1:6379> decr test:key1

(integer) 4

127.0.0.1:6379> decrby test:key1 +3

(error) ERR value is not an integer or out of range

127.0.0.1:6379> decrby test:key1 3

(integer) 1

127.0.0.1:6379> incrby test:key1 +5

(error) ERR value is not an integer or out of range

127.0.0.1:6379> incrby test:key1 5

(integer) 6

127.0.0.1:6379> decrby test:key1 -3

(integer) 9

127.0.0.1:6379> decrby test:key1

(error) ERR wrong number of arguments for 'decrby' command

처음 incr 명령이 키 값이 없어도 자동으로 생성하고 값을 증가 시킨다.

incrby 명령은 인자의 값만큼 증가시키는데 음수가 입력되면 값의 감소가 이루어 진다.

인자로 양수 기호(+) 사용시 에러 발생. (4.0.6 버젼 기준) 이전 2버젼대에서는 정상 동작 함.

decrby 명열의 인자로 음수 입력시 음수의 뺄셈 처리가 되어 값이 증가 한다.


증가 감소 최대치

127.0.0.1:6379> set test:key2 9223372036854775806

OK

127.0.0.1:6379> incr test:key2

(integer) 9223372036854775807

127.0.0.1:6379> incr test:key2

(error) ERR increment or decrement would overflow

127.0.0.1:6379> set test:key3 -9223372036854775807

OK

127.0.0.1:6379> decr test:key3

(integer) -9223372036854775808

127.0.0.1:6379> decr test:key3

(error) ERR increment or decrement would overflow

증감 처리가 64비트 부호화 정수 signed interger로 표현할 수 있는 범위에서만 가능하다.


비트연산

비트 연산을 통해 저장되는 문자열 데이터를 비트 단위로 처리 가능.

127.0.0.1:6379> setbit bit:key1 257 1

(integer) 0

127.0.0.1:6379> getbit bit:key1 257

(integer) 1

127.0.0.1:6379> setbit bit:key1 257 1

(integer) 1

127.0.0.1:6379> getbit bit:key1 257

(integer) 1

127.0.0.1:6379> getbit bit:key1 256

(integer) 0

127.0.0.1:6379> strlen bit:key1

(integer) 33

setbit 명령은 bit:key1 키의 257번째 비트에 1을 저장. 

setbit의 응답값은 이전에 저장된 값이다.

getbit 명령으로 bit:key1의 257번째 비트값을 조회하면 1을 반환.

strlen 명령으로 저장된 데이터의 길이 측정. 값은 '저장을 요청한 비트 위치/8+1' 이다


비트의 저장 순서 이해

127.0.0.1:6379> set bit:key2 m

OK

127.0.0.1:6379> getbit bit:key2 0

(integer) 0

127.0.0.1:6379> getbit bit:key2 1

(integer) 1

127.0.0.1:6379> getbit bit:key2 2

(integer) 1

127.0.0.1:6379> getbit bit:key2 3

(integer) 0

127.0.0.1:6379> getbit bit:key2 4

(integer) 1

127.0.0.1:6379> getbit bit:key2 5

(integer) 1

127.0.0.1:6379> getbit bit:key2 6

(integer) 0

127.0.0.1:6379> getbit bit:key2 7

(integer) 1

127.0.0.1:6379> append bit:key2 y

(integer) 2

127.0.0.1:6379> getbit bit:key2 0

(integer) 0

127.0.0.1:6379> getbit bit:key2 1

(integer) 1

127.0.0.1:6379> getbit bit:key2 2

(integer) 1

127.0.0.1:6379> getbit bit:key2 3

(integer) 0

127.0.0.1:6379> getbit bit:key2 4

(integer) 1

127.0.0.1:6379> getbit bit:key2 5

(integer) 1

127.0.0.1:6379> getbit bit:key2 6

(integer) 0

127.0.0.1:6379> getbit bit:key2 7

(integer) 1

127.0.0.1:6379> 

127.0.0.1:6379> get bit:key2

"my"

bit:key2 키에 문자열 값으로 'm'을 저장했다. 'm'은 아스키값이 6D이고, 이 값을 이진수로 변환하면 '01101101'이 된다.

getbit 명령으로 0번째부터 7번째 비트 값을 순서대로 출력한다.

append 명령으로 bit:key2 키에 'y' 문자를 더하여 값을 'my'로 바꾸고 난 뒤에도 0번째 부터 7번째 비트 값은 유지되고, 두 번째 바이트에 'y' 값이 추가 된다.

setbit 명령을 사용할 때는 문자열 데이터의 최대 크기인 512MB 제한만 고려 하면 된다.


비트연산의 사용 예) 사용자가 오늘 로그인하였는지 확인하는 기능

오늘 로그인했는지 확인해야 할 회원수가 천만 명이고 5분 동안 십만 명이 로그인 할때 관계형 데이터베이스로 이기능을 구현하면 많은 비용이 들어간다. 이 때 레디스의 비트연산이 빛을 발한게 된다. 문자열의 각 비트를 on/off 스위치로 생각해보자. 사용자의 아이디를 숫자로 바꾸어 비트 연산의 오프셋값과 대응하자. 사용자가 로그인을 하면 사용자의 아이디에 해당하는 오프셋의 스위치를 on으로 바꾼다. 스위치를 처리(setbit 명령)는 시스템마다 약간씩 다르지만 평균적으로 1초에 수만 번 이상 처리 가능하다.

시나리오 1. 사용자가 로그인 요청을 한다.

            2. 오늘 날짜의 로그인 키를 만든다. ex) login:20180316

            3. 사용자 번호에 해당하는 오프셋에 로그인 상태를 표시하기 위해서 비트를 1로 바꾼다.

            4. 오늘 로그인한 사용자의 수를 확인한다.

127.0.0.1:6379> info memory

# Memory

used_memory:81369056

used_memory_human:77.60M

used_memory_rss:94932992

used_memory_rss_human:90.54M

used_memory_peak:83989440

used_memory_peak_human:80.10M

used_memory_peak_perc:96.88%

used_memory_overhead:49009662

used_memory_startup:765544

used_memory_dataset:32359394

used_memory_dataset_perc:40.15%

total_system_memory:33626218496

total_system_memory_human:31.32G

used_memory_lua:37888

used_memory_lua_human:37.00K

maxmemory:0

maxmemory_human:0B

maxmemory_policy:noeviction

mem_fragmentation_ratio:1.17

mem_allocator:jemalloc-4.0.3

active_defrag_running:0

lazyfree_pending_objects:0

127.0.0.1:6379> setbit login:20180316 9851254 1

(integer) 0

127.0.0.1:6379> getbit login:20180315 92456

(integer) 0

127.0.0.1:6379> info memory

# Memory

used_memory:82679832

used_memory_human:78.85M

used_memory_rss:97030144

used_memory_rss_human:92.54M

used_memory_peak:83989440

used_memory_peak_human:80.10M

used_memory_peak_perc:98.44%

used_memory_overhead:49009702

used_memory_startup:765544

used_memory_dataset:33670130

used_memory_dataset_perc:41.10%

total_system_memory:33626218496

total_system_memory_human:31.32G

used_memory_lua:37888

used_memory_lua_human:37.00K

maxmemory:0

maxmemory_human:0B

maxmemory_policy:noeviction

mem_fragmentation_ratio:1.17

mem_allocator:jemalloc-4.0.3

active_defrag_running:0

lazyfree_pending_objects:0

127.0.0.1:6379> setbit login:20180316 125154 1

(integer) 0

127.0.0.1:6379> setbit login:20180316 51784 1

(integer) 0

127.0.0.1:6379> setbit login:20180316 897884 1

(integer) 0

127.0.0.1:6379> setbit login:20180316 19684 1

(integer) 0

127.0.0.1:6379> bitcount login:20180316

(integer) 5

127.0.0.1:6379> bitcount login:20180316 100000 1000000

(integer) 1

127.0.0.1:6379> bitcount login:20180316 12500 125000

(integer) 2

데이터를 저장하기 이전의 메모리 사용량을 표시.

2018년 3월 16일에 사용자번호 9851254인 사용자가 로그인 했음을 표시.

2018년 3월 15일에 사용자번호 92456인 사용자가 로그인 했는지 확인.

약 천만 명의 사용자에 대한 로그인 정보를 저장하는 데 단지 1.2MB의 메모리가 사용 됐다. 단순 계산하면 36MB의 저장소만으로 한 달 동안의 사용자의 로그인 정보를 저장할 수 있다.

bitcount로 중복을 제거한 로그인 사용자 수를 조회 한다.

bitcount 명령으로 100000번째 비트부터 1000000번째 비트까지 세는 명령은 'bitcount login:20180316 12500 125000' 이다

bitcount 명령의 인자는 바이트의 범위를 나타낸다.

레디스는 저장된 값의 비트 범위를 조회하는 명령이 없다. bitcount 명령 사용시 주의하자.

'RediS' 카테고리의 다른 글

레디스 데이터 구조 2 (해시 데이터, 셋 데이터)  (0) 2018.03.19