[Redis] 기타 명령어 및 사용 패턴

JINJI·2024년 3월 16일
1

SETRANGE, GETRANGE

Syntax

SETRANGE key offset value
  • SETRANGE 명령어는 지정된 오프셋부터 시작하여 키에 저장된 문자열의 일부를 전체 값 길이에 대해 덮어쓴다.
  • 오프셋이 키에 있는 문자열의 현재 길이보다 큰 경우, 오프셋을 맞추기 위해 문자열에 zero-bytes가 추가된다.
  • 초기값이 설정되어 있지 않은 오프셋에 대해서는 빈문자열로 간주한다.
GETRANGE key start end
  • GETRANGE 명령어는 키에 저장된 문자열 값의 부분 문자열을 가져온다.
  • 시작 오프셋과 끝 오프셋으로 지정된 범위를 포함한다.
  • 음수 오프셋을 사용하여 문자열 끝에서부터 오프셋을 제공할 수 있다.
  • 요청이 문자열 길이를 벗어나는 경우 결과 범위를 문자열의 실제 길이로 제한한다.

Example

redis> SET key1 "Hello World"
"OK"
redis> SETRANGE key1 6 "Redis"
(integer) 11
redis> GET key1
"Hello Redis"
redis> SETRANGE key2 6 "Redis" # 초기값이 설정되어 있지 않은 오프셋에 대해서는 빈문자열로 간주한다
(integer) 11
redis> GET key2
"Redis"
redis>
redis> SET mykey "This is a string"
"OK"
redis> GETRANGE mykey 0 3
"This"
redis> GETRANGE mykey -3 -1
"ing"
redis> GETRANGE mykey 0 -1
"This is a string"
redis> GETRANGE mykey 10 100
"string"

INCR, INCRBY, INCRBYFLOAT, DECR, DECRBY

Syntax

INCR key
  • INCR 명령어는 키에 저장된 숫자를 1씩 증가시킨다. 키가 존재하지 않으면 연산을 수행하기 전에 0으로 설정된다. 키가 잘못된 유형의 값을 포함하거나 정수로 표현할 수 없는 문자열 값을 포함하고 있으면 오류가 발생한다.
  • 참고: Redis에는 정수형이 없기 때문에 이는 문자열 연산이다. 키에 저장된 문자열은 연산을 실행하기 위해 10진법으로 해석되는 64비트 부호 있는 정수로 해석된다.
INCRBY key increment
  • INCRBY 명령은 키에 저장된 숫자를 지정된 increment만큼 증가시킨다.
  • 만약 키가 존재하지 않는다면, 이 명령을 실행하기 전에 0으로 설정된다.
  • 키가 잘못된 유형의 값을 포함하거나 정수로 표현할 수 없는 문자열을 포함하고 있다면 오류가 발생한다.
INCRBYFLOAT key increment
  • INCRBYFLOAT 명령어는 키에 저장된 부동 소수점 숫자를 지정된 increment만큼 증가시킨다.
  • 음수 증분값을 사용하면 키에 저장된 값이 감소한다.
  • 키가 존재하지 않는다면, 작업을 실행하기 전에 0으로 설정된다.
  • 키가 잘못된 유형의 값을 포함하거나, 키 내용 또는 지정된 증분이 double precision 부동 소수점 숫자로 분석될 수 없는 경우 오류가 발생한다.
  • 증가된 새 값이 키의 새 값으로 저장되고 문자열로 반환된다.
DECR key
  • DECR 명령어는 키에 저장된 숫자를 1 감소시킨다.
  • 키가 존재하지 않는 경우, 작업을 수행하기 전에 키가 0으로 설정된다.
  • 키가 잘못된 유형의 값을 포함하거나 정수로 표현할 수 없는 문자열을 포함하고 있다면 오류가 반환된다.
  • 이 작업은 64비트 부호 있는 정수로 제한된다.
DECRBY key decrement
  • DECRBY 명령어는 키에 저장된 숫자를 감소분만큼 감소시킨다.
  • 키가 존재하지 않는 경우, 작업을 수행하기 전에 키가 0으로 설정된다.
  • 키가 잘못된 유형의 값을 포함하거나 정수로 표현할 수 없는 문자열을 포함하고 있다면 오류가 발생한다.
  • 이 작업은 64비트 부호 있는 정수로 제한된다.

Example

redis> SET mykey "10"
"OK"
redis> INCR mykey
(integer) 11
redis> GET mykey
"11"
redis> SET mykey "10"
"OK"
redis> INCRBY mykey 5
(integer) 15
redis> SET mykey 10.50
"OK"
redis> INCRBYFLOAT mykey 0.1
"10.6"
redis> INCRBYFLOAT mykey -5
"5.6"
redis> SET mykey 5.0e3
"OK"
redis> INCRBYFLOAT mykey 2.0e2
"5200"
redis> SET mykey "10"
"OK"
redis> DECR mykey
(integer) 9
redis> SET mykey "234293482390480948029348230948"
"OK"
redis> DECR mykey
(error) value is not an integer or out of range
redis> SET mykey "10"
"OK"
redis> DECRBY mykey 3
(integer) 7

Pattern: Counter

  1. 페이지 뷰가 발생할 때마다 INCREXPIRE를 함께 사용하여 지정된 시간 내에 구분된 최근 N개의 페이지 뷰만 세는 카운터를 생성할 수 있다.
  2. 클라이언트는 현재 카운터 값을 원자적으로 가져와서 카운터를 0으로 재설정하는 GETSET을 사용할 수 있다.
  3. 사용자의 작업에 따라 값이 커지거나 작아질 수 있는 값을 처리하기 위해 DECR 또는 INCRBY와 같은 다른 원자적 증가/감소 명령을 사용할 수 있다. 예를 들어, 온라인 게임에서 여러 사용자의 점수를 생각해볼 수 있다.

Pattern: Rate limiter

레이트 리미터는 특정 시간 동안 요청의 수를 제한하는 데 사용된다. 이를 통해 서버를 과도하게 사용하는 경우를 방지하고 서비스의 안정성을 유지할 수 있다.

INCR을 사용하여 두 가지 방법으로 구현할 수 있다. 예를 들어 IP 주소당 초당 최대 10개의 요청으로 API 호출 수를 제한하는 것으로 가정한다.

Pattern: Rate limiter 1

  • Redis의 트랜잭션 기능 사용
  • 각 클라이언트의 IP 주소와 현재 시간을 조합하여 고유한 키 생성
  • 이 키를 사용하여 Redis의 INCR 명령을 호출하여 요청을 카운트한다.
  • 동시에 해당 키의 만료 시간을 설정하여 특정 시간 동안 유지될 수 있도록 한다.
  • INCR 명령의 결과를 사용하여 요청 수를 확인하고, 지정된 제한을 초과하는지 확인한다.
  • 요청 수가 제한을 초과하지 않으면 API 호출을 수행한다.
import (
	"context"
	"errors"
	"time"

	"github.com/go-redis/redis/v9"
)

func LimitAPICall(ip string, client *redis.Client) error {
	ts := time.Now().Unix()
	keyname := ip + ":" + string(ts)

	// Redis의 TxPipeline을 사용하여 트랜잭션 시작
	pipe := client.TxPipeline()

	// INCR keyname 및 만료를 10초로 설정
	incrResult := pipe.Incr(context.Background(), keyname)
	pipe.Expire(context.Background(), keyname, 10*time.Second)

	// 트랜잭션 실행
	if _, err := pipe.Exec(context.Background()); err != nil {
		return err
	}

	// INCR 작업 결과 가져오기
	current, err := incrResult.Result()
	if err != nil {
		return err
	}

	// 요청 수가 제한을 초과하는지 확인
	if current > 10 {
		return errors.New("too many requests per second")
	}

	// 제한이 초과되지 않으면 API 호출 수행
	PerformAPICall()

	return nil
}

Pattern: Rate limiter 2

  • Redis의 리스트 데이터 구조를 사용하여 구현한다.
  • 각 클라이언트의 IP 주소를 리스트의 요소로 저장한다.
  • 클라이언트의 요청이 있는지 확인하고, 요청 수가 제한을 초과하는지 확인한다.
  • 요청 수가 제한을 초과하지 않으면 IP 주소를 리스트에 추가하고, 만료 시간을 설정하여 특정 시간 동안 유지될 수 있도록 한다.
  • 유청 수가 제한을 초과하지 않으면 API 호출을 수행한다.
import (
	"context"
	"errors"

	"github.com/go-redis/redis/v9"
)

func LimitAPICall(ip string, client *redis.Client) error {
	// 현재 IP 주소의 요청 수 조회
	current := client.LLen(context.Background(), ip).Val()

	// 요청 수가 10 초과하는지 확인
	if current > 10 {
		return errors.New("too many requests per second")
	}

	// 키가 존재하지 않는 경우 IP 주소를 리스트에 추가하고 1초 후 만료
	if client.Exists(context.Background(), ip).Val() == 0 {
		_, err := client.TxPipelined(context.Background(), func(pipe redis.Pipeliner) error {
			pipe.RPush(context.Background(), ip, ip)
			pipe.Expire(context.Background(), ip, 1)
			return nil
		})
		if err != nil {
			return err
		}
	} else {
		// 키가 이미 존재하는 경우 IP 주소를 리스트에 추가
		client.RPushX(context.Background(), ip, ip)
	}

	// API 호출 수행
	PERFORM_API_CALL()

	return nil
}

결론

Redis의 SETRANGE 및 GETRANGE는 문자열 조작에 유용하며, INCR 및 DECR은 카운팅 및 증감 작업시 유용하다. 이러한 기능들은 데이터 조작의 효율성을 높이고, 원자성을 보장하여 안정적인 애플리케이션을 구축하는 데 기여한다.

0개의 댓글