Redis 자료 구조 - Hash

    개요

    마치 RDBMS에서의 테이블과 같은 형태를 저장하는데 유용한 구조이다. RDBMS의 테이블은 PK외 Secondary Index로도 조회를 하지만, PK 기준으로 데이터를 액세스 해야 한다는 점이 다르다. 또한, 스키마가 RDBMS의 경우는 고정형이지만, Redis의 Hash는 비고정형이며 이를 schemaless라고 한다. 따라서, RDBMS의 테이블 alter는 굉장히 무거운 작업이지만, Redis의 Hash는 그렇지 않다. 따라서, User 데이터를 저장한다 하더라도, RDBMS 테이블과 달리 데이터마다 설정된 필드들의 수가 다를 수도 있다.

     

    명령어

    Redis가 지원하는 Hash 관련 기능은 아래와 같다. RDBMS 테이블의 값을 설정하는 방식과 비교하면서 상상해 보자. 어떤 온라인 쇼핑몰의 고객이라고 가정해보자.

    • userId: john
    • membership: silver
    • point: 3200
    • sex: male
    • age: 30

    위와 같은 데이터가 있다고 가정하자. Java에서의 객체를 생각한다면 대략 다음과 같을 것이다.

    User user = new User();
    user.setId("john");
    user.setMembership("silver"); // enum 대신 스트링을 직접 쓴다고 치자.
    user.setPoint(3200);
    user.setSex("male"); // enum 대신 스트링을 직접 쓴다고 치자.
    user.setAge(30);

    단, Key에 해당하는 값을 user:john로 지정한다는 점이, 일반 객체를 생성하는 과정과 차이가 난다.

     

    HSET, HGET

    127.0.0.1:6379> help hset
    
      HSET key field value [field value ...]
      summary: Set the string value of a hash field
      since: 2.0.0
      group: hash
    
    127.0.0.1:6379> help hget
    
      HGET key field
      summary: Get the value of a hash field
      since: 2.0.0
      group: hash

    HSET으로 하나 또는 여러 개의 값을 설정할 수 있다. 예전에는 한 번에 하나의 필드-값을 설정할 수 있었다면, 이제는 여러 개의 필드를 동시에 설정할 수 있기 때문에 사실상 HMSET과 동일하다 볼 수 있다.

    // 한 개의 값 설정하기
    hset user:john membership silver
    (integer) 1
    
    // 여러 개의 값 설정하기
    127.0.0.1:6379> hset user:john membership gold point 4000
    (integer) 1

    HSET으로 썼던 값을, 다시 실행하면 기존 값을 덮어 쓰게 된다. 즉, 앞에서 silver로 썼지만, 뒤의 명령어로 인해 gold로 변경된다.
    HGET으로 설정된 값을 조회해 보자.

    // membership 값을 조회한다. 
    127.0.0.1:6379> hget user:john membership
    "gold"
    
    // point 값을 조회한다.
    127.0.0.1:6379> hget user:john point
    "4000"
    
    // 존재하지 않는 값을 조회하면?
    127.0.0.1:6379> hget user:john address
    (nil)

    HSET은 여러 개의 값을 설정할 수 있는 반면, HGET은 여러 개의 값을 한 번에 조회할 수 없다.

    // 한 번에 여러 값을 조회할 수 없는 HGET
    127.0.0.1:6379> hget user:john membership point
    (error) ERR wrong number of arguments for 'hget' command

    전체 필드와 그 값들을 조회하려면, HGETALL 명령어를 참고하자.

     

    HGETALL

    127.0.0.1:6379> help hgetall
    
      HGETALL key
      summary: Get all the fields and values in a hash
      since: 2.0.0
      group: hash

    Key에 매핑되는 모든 필드와 그 값들을 조회한다.

    127.0.0.1:6379> hgetall user:john
    1) "membership"
    2) "gold"
    3) "point"
    4) "4000"

    user:john에 대한 필드 key value 매핑을 모두 조회한다. 1, 2, 3, 4와 같이 조회되었는데,

    • membership -> gold
    • point -> 4000
      으로 해석하면 된다. 일렬로 나열되어 알아보기 불분명 하다면, HKEYS와 HVALS 명령어로 나눠서 확인할 수 있다.

     

    HKEYS

    127.0.0.1:6379> help hkeys
    
      HKEYS key
      summary: Get all the fields in a hash
      since: 2.0.0
      group: hash

    해시 키에 매핑되는 모든 필드 리스트를 조회한다.

    127.0.0.1:6379> hkeys user:john
    1) "membership"
    2) "point"

    HGETALL로 봤을 때는 섞여 있던 데이터가 필드 데이터만 추출해 주었다.

     

    HVALS

    127.0.0.1:6379> help hvals
    
      HVALS key
      summary: Get all the values in a hash
      since: 2.0.0
      group: hash

    마찬가지로, 이번에는 해당 키에 존재하는 (필드의) 값들만 추출해 보자.

    127.0.0.1:6379> hvals user:john
    1) "gold"
    2) "4000"

     

    HMSET, HMGET

    127.0.0.1:6379> help hmset
    
      HMSET key field value [field value ...]
      summary: Set multiple hash fields to multiple values
      since: 2.0.0
      group: hash
    
    127.0.0.1:6379> help hmget
    
      HMGET key field [field ...]
      summary: Get the values of all the given hash fields
      since: 2.0.0
      group: hash

    이제 여러 개의 필드를 set하고, 여러 개의 필드를 get하는 명령어를 살펴보자. 원래는 HMSET과 HSET의 차별성은 한 번에 여러 개를 설정할 수 있냐 없냐의 차이가 있었다면, Redis 최신 버전에서는 이 차이가 없다. 오히려, Redis 4.0.0 이후로는 HMSET 대신 HSET을 쓰라고 권고한다. 앞서 사용한 유저 말고, 다른 유저를 한번 생성/ 조회해 보자.

    // 여러 값 설정
    127.0.0.1:6379> hmset user:jane membership gold point 6000 sex female age 25
    OK
    
    // 여러 필드 조회
    127.0.0.1:6379> hmget user:jane membership point age
    1) "gold"
    2) "6000"
    3) "25"

     

    HEXSITS

    127.0.0.1:6379> help hexists
    
      HEXISTS key field
      summary: Determine if a hash field exists
      since: 2.0.0
      group: hash

    특정 필드가 존재하는지 확인하는 기능이다. 값이 존재하면 1을, 존재하지 않으면 0을 리턴한다.

    // 쿠폰이 있나?
    127.0.0.1:6379> hexists user:john coupon
    (integer) 0

     

    HSETNX

    127.0.0.1:6379> help hsetnx
    
      HSETNX key field value
      summary: Set the value of a hash field, only if the field does not exist
      since: 2.0.0
      group: hash

    어떤 유저에게 이제 정식 멤버십을 부여하고 싶다. 멤버십이 없을 경우에 이제 분발하시라고 bronze 등급을 부여하려고 하는데, 만약 그 유저가 이미 멤버십이 있다면, 덮어쓰면 곤란하지 않은가? 즉, bronze를 주려고 하는데, 이미 silver나 gold 사용자라고 한다면 덮어쓰면 낭패다.

    // 이미 gold인 john에게 bronze를 주려고 시도함
    127.0.0.1:6379> hsetnx user:john membership bronze
    (integer) 0
    
    // john의 정보가 보존되었다
    127.0.0.1:6379> hget user:john membership
    "gold"

    HSETNX는 기존에 존재하지 않는 필드일 경우(not exist)에만 값을 방어적으로 설정하도록 하는 기능이다.

     

    HDEL

    127.0.0.1:6379> help hdel
    
      HDEL key field [field ...]
      summary: Delete one or more hash fields
      since: 2.0.0
      group: hash

    기존에 존재하는 필드를 삭제할 수 있다.

    // john에게 생일 쿠폰이 지급되었다
    127.0.0.1:6379> hset user:john coupon birthday
    (integer) 1
    
    // 쿠폰 보유 확인
    127.0.0.1:6379> hget user:john coupon
    "birthday"
    
    // 쿠폰 사용으로 삭제
    127.0.0.1:6379> hdel user:john coupon
    (integer) 1
    
    // 이제 쿠폰이 없는 것을 확인
    127.0.0.1:6379> hget user:john coupon
    (nil)
    127.0.0.1:6379> hexists user:john coupon
    (integer) 0
    127.0.0.1:6379>

     

    HINCRBY

    127.0.0.1:6379> help hincrby
    
      HINCRBY key field increment
      summary: Increment the integer value of a hash field by the given number
      since: 2.0.0
      group: hash

    활발하게 구매를 한 john의 마일리지가 올라간다.

    // 10점 올라간다
    // 업데이트 된 후의 값이 리턴된다.
    127.0.0.1:6379> hincrby user:john point 10
    (integer) 4010
    
    // 다시 확인해 본다
    127.0.0.1:6379> hget user:john point
    "4010"

    뒤의 숫자에 음수를 주면, 감소도 시킬 수 있다.

    127.0.0.1:6379> hincrby user:john point -10
    (integer) 4000

     

    HINCRBYFLOAT

    127.0.0.1:6379> help hincrbyfloat
    
      HINCRBYFLOAT key field increment
      summary: Increment the float value of a hash field by the given amount
      since: 2.6.0
      group: hash

    HINCR와 동일한 기능인데, 소숫점 기반으로 연산을 한다는 점이 다르다.

     

    HLEN

    127.0.0.1:6379> help hlen
    
      HLEN key
      summary: Get the number of fields in a hash
      since: 2.0.0
      group: hash

    해당 키에 정의된 필드+값의 수를 확인한다.

    127.0.0.1:6379> hlen user:john
    (integer) 2
    
    127.0.0.1:6379> hlen user:jane
    (integer) 4

     

    HSTRLEN

    127.0.0.1:6379> help hstrlen
    
      HSTRLEN key field
      summary: Get the length of the value of a hash field
      since: 3.2.0
      group: hash

    필드에 매핑된 값(문자열)의 길이를 리턴한다.

    127.0.0.1:6379> hstrlen user:john membership
    (integer) 4
    127.0.0.1:6379> hstrlen user:john point
    (integer) 4
    
    // 설정하지 않은 필드-값 -> 0
    127.0.0.1:6379> hstrlen user:john age
    (integer) 0
    127.0.0.1:6379> hstrlen user:john sex
    (integer) 0
    127.0.0.1:6379> hget user:jane age
    "25"
    127.0.0.1:6379> hstrlen user:jane age
    (integer) 2
    127.0.0.1:6379> hstrlen user:jane sex
    (integer) 6

     

    HSCAN

    127.0.0.1:6379> help hscan
    
      HSCAN key cursor [MATCH pattern] [COUNT count]
      summary: Incrementally iterate hash fields and associated values
      since: 2.8.0
      group: hash

    다른 SCAN류의 형태와 동일하고, 같은 목적을 갖고 있다.

    127.0.0.1:6379> hscan user:jane 0 count 2
    1) "0"
    2) 1) "membership"
       2) "gold"
       3) "point"
       4) "6000"
       5) "sex"
       6) "female"
       7) "age"
       8) "25"

    필드가 많을 경우, 분할해서 조회하는 역할을 수행한다. cursor는 현재 어느 페이지에 있느냐를 나타내는 값이며, 임무를 완수하면 0이 리턴된다. 0이 아닌 값으로 cursor가 리턴되면, 다음 페이지는 그 값을 cursor로 지정하면, 이어서 조회할 수 있다. 희망하는 count를 준다고 해서 딱 그 값에 맞춰서 조회해 주지는 않는다. match 조건을 부여하고 필터링할 수 있다.

     

    댓글

    Designed by JB FACTORY