해시 데이터

해시 데이터는 문자열 필드와 값으로 이루어진 맵(map)구조로 되어 있다.

해시 데이터는 232-1 개의 필드와 값을 저장한다.


그룹 데이터 저장

127.0.0.1:6379> hmset user1:info name gil-dong email david.kil@test.com age 21

OK

127.0.0.1:6379> hsetnx user1:info email foo@bar.com

(integer) 0

127.0.0.1:6379> hmset user1:info lastlogin 20150319 device android

OK

127.0.0.1:6379> hmget user1:info name email

1) "gil-dong"

2) "david.kil@test.com"

127.0.0.1:6379> hlen user1:info

(integer) 5

127.0.0.1:6379> hdel user1:info device

(integer) 1

127.0.0.1:6379> hlen user1:info

(integer) 4

hmset으로 유저1의 이름, 이메일, 나이를 저장.

hsetnx눈 주어진 키의 필드가 존재하는지 검사하고 저장한다. 이메일이 이미 등록되어 있어 0(실패) 반환.

hmset으로 유저1의 마지막 로그인 일자와 디바이스 정보를 저장.

hmget으로 유저1의 이름과 이메일 확인.

hlen은 해시 데이터의 저장된 필드 수를 조회.

hdel로 유저1의 디바이스 정보를 삭제.


해시에 저장된 데이터의 필드값이 숫자일 때 Hincrby, hincrbyfloat 명령으로 증감처리를 할 수 있다. 

문자열 데이터와는 다르게 해시 명령은 증가 명령만 지원한다. 

숫자의 감소를 처리하기 위해서는 인자에 음수를 입력하면 된다.

hincrbyfloat는 소수점 17자리로 고정.

127.0.0.1:6379> hmset kris:wizard life 1000 mana 1300

OK

127.0.0.1:6379> hincrby kris:wizard life -21

(integer) 979

127.0.0.1:6379> hincrby kris:wizard mana -30

(integer) 1270

127.0.0.1:6379> hincrby kris:wizard mana 25

(integer) 1295

127.0.0.1:6379> hincrbyfloat kris:wizard critical 12.5

"12.5"


해시 데이터에 저장된 모든 키의 목록과 값을 조회할 때는 hgetall을 사용하면 된다. 키의 목록이나 값의 목록만 조회하는 명령은 hkeys, hvals다.

127.0.0.1:6379> hgetall kris:wizard

1) "life"

2) "979"

3) "mana"

4) "1295"

5) "critical"

6) "12.5"

127.0.0.1:6379> hkeys kris:wizard

1) "life"

2) "mana"

3) "critical"

127.0.0.1:6379> hvals kris:wizard

1) "979"

2) "1295"

3) "12.5"


셋 데이터

셋 데이터는 중복을 혀용하지 않는 집합 형태의 자료구조이며, 정렬되어 있지 않다.

셋 데이터는 232-1 개의 값을 저장할 수 있다.

값이 저장될 때 중복에 대한 확인이 일어나므로 NX접미사의 명령이 필요하지 않다.

셋 데이터는 집합 연산(합집합, 교집합, 차집합)이 가능하다.

셋 데이터의 집합 연산은 명령의 수행 비용이 매우 비싸다. (사용하지 마라.)

127.0.0.1:6379> sadd user:1:follows 2

(integer) 1

127.0.0.1:6379> sadd user:2:followers 1

(integer) 1

127.0.0.1:6379> sadd user:3:follows 1

(integer) 1

127.0.0.1:6379> sadd user:1:followers 3

(integer) 1

127.0.0.1:6379> sadd user:1:follows 3

(integer) 1

127.0.0.1:6379> sadd user:3:followers 1

(integer) 1

127.0.0.1:6379> sadd user:2:follows 3

(integer) 1

127.0.0.1:6379> sadd user:3:follower 2

(integer) 1

127.0.0.1:6379> sinter user:1:follows user:1:followers

1) "3"

서로 팔로잉 하고 있는 사용자를 구하는 예이다.

sinter 명령으로 유저1의 팔로우 정보와 팔로워의 교집합(서로 팔로잉 하는 사용자)을 구한다.


셋 테이터의 다양한 조회 명령

127.0.0.1:6379> sadd user:1:friends Scott Sean Simon Hal Hank

(integer) 5

127.0.0.1:6379> sadd user:1:friends Uriah Jack Jake James Jason

(integer) 5

127.0.0.1:6379> sadd user:1:friends Justin Van Vernon Victor Vincent

(integer) 5

127.0.0.1:6379> scard user:1:friends

(integer) 15

127.0.0.1:6379> srem user:1:friends Justin

(integer) 1

127.0.0.1:6379> spop user:1:friends

"Sean"

127.0.0.1:6379> scard user:1:friends

(integer) 13

127.0.0.1:6379> sismember user:1:friends Scott

(integer) 1

127.0.0.1:6379> smove user:1:friends test:set scott

(integer) 0

127.0.0.1:6379> smembers user:1:friends

 1) "Simon"

 2) "Van"

 3) "Vernon"

 4) "Hank"

 5) "James"

 6) "Victor"

 7) "Uriah"

 8) "Hal"

 9) "Jason"

10) "Vincent"

11) "Jack"

12) "Jake"

13) "Scott"

127.0.0.1:6379> smove user:1:friends test:set Scott

(integer) 1

127.0.0.1:6379> smembers user:1:friends

 1) "Simon"

 2) "Van"

 3) "Vernon"

 4) "Hank"

 5) "James"

 6) "Victor"

 7) "Uriah"

 8) "Hal"

 9) "Jason"

10) "Vincent"

11) "Jack"

12) "Jake"

scard 명령은 'cardinality'의 약자로 주어진 키에 저장된 원소 개수를 조회한다.

srem로 유저1의 친구 중 Justin을 제거한다.

spop은 친구 중 임의의 값을 제거 한다. Sean이 삭제 됨.(반환 값이 삭제된 대상)

sismember로 친구 중 Scott이 있는지 확인.

smove로 유저1의 친구 중 scott을 다른 셋 데이터를 생성하고 이동 시키려 했으나 대소문자를 잘못 입력하여 실패.

다시 시도 하여 Scott을 이동 시켰다.


'RediS' 카테고리의 다른 글

레디스 데이터 구조1 (문자열 데이터, 비트연산)  (0) 2018.03.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

+ Recent posts