자바/개인 정리

리프레시 토큰 조회 성능 테스트(k6, Postgres, Redis)

limdae94 2025. 3. 29.

리프레시 토큰을 RDBMS (관계형 데이터베이스) 가 아니라 Redis (인메모리 데이터베이스) 에 보관하는 이유는 주로 성능, 확장성, 편의성 측면에서 Redis가 훨씬 더 이점이 많다는 내용을 쉽게 찾아볼 수 있습니다. 그러나 정말로 리프레시 토큰을 Redis에 보관하는 것이 RDBMS 안에 보관하는 것보다 모든 측면에서 우수한 지는 실제로 경험한 바가 없기 때문에 의문이었습니다. 이 의문을 해결하기 위해 Postgres와 Redis를 사용하여 리프레시 토큰을 생성하고 조회하는 테스트 시나리오를 구성하고 k6으로 성능 테스트를 수행합니다. 앞서 시작하기 전에 사용하게 될 도구와 코드를 소개하고 진행합니다.

Redis 사용 이유

  1. 성능 측면 (속도)
    • Redis는 메모리 기반 저장소이기 때문에 디스크를 사용하는 RDBMS보다 훨씬 빠릅니다.
    • 리프레시 토큰은 인증 과정에서 매우 빈번하게 접근되므로, 높은 처리 속도가 필요한데 Redis는 이를 효과적으로 처리합니다.
  2. 확장성 측면 (Scale-Out)
    • Redis는 클러스터링을 통해 여러 서버로 쉽게 확장할 수 있어 수평 확장(Scale-Out)이 용이합니다.
    • RDBMS는 데이터베이스 락이나 트랜잭션 제약 때문에 확장성이 좋지 않은 경우가 많습니다.
  3. ⭐TTL (Time-To-Live) 기능의 편리함⭐
    • 리프레시 토큰은 일정 기간 이후 만료되어야 하는데, Redis는 각 키별로 TTL을 설정할 수 있습니다.
    • Redis의 EXPIRE 명령어를 사용하면 토큰의 유효기간을 쉽게 설정할 수 있어 만료 처리가 매우 간편합니다.
  4. 효율적인 데이터 삭제
    • RDBMS에서는 만료된 토큰을 정기적으로 삭제하기 위해 배치 작업(CRON 또는 배치 스케줄러)을 사용해야 하는 경우가 많습니다.
    • Redis는 TTL이 만료되면 자동으로 데이터를 삭제하므로 불필요한 데이터를 자동으로 제거합니다.
  5. 블랙리스트 처리의 용이함 (JWT 블랙리스트)
    • Redis에 블랙리스트를 추가로 저장하여, 만료되기 전에 로그아웃된 토큰이나 강제로 폐기된 토큰을 쉽게 관리할 수 있습니다.
    • 예를 들어 BlackList:<토큰> 형태로 저장하고, 만료 시간이 지나면 자동으로 제거되는 구조로 구현 가능합니다.
  6. ⭐단순한 데이터 구조 관리⭐
    • 리프레시 토큰은 보통 단순한 Key-Value 구조로 저장할 수 있어, Redis와 같은 Key-Value 저장소에 적합합니다.
    • Key: 사용자 ID - Value: 리프레시 토큰 같은 형태로 쉽게 저장하고 조회할 수 있습니다.
  7. 유연한 스케일링과 장애 대응
    • Redis는 복제 및 클러스터링 기능을 통해 분산 환경에서도 높은 가용성을 제공합니다.
    • 서버 장애 시에도 빠르게 다른 서버로 데이터를 이전하거나 복구할 수 있습니다.

RDBMS는 디스크 기반 저장소라서 I/O 성능이 낮고, 정기적인 만료 데이터 삭제가 필요하며 확장성이 제한됩니다. 반면에 Redis는 인메모리 기반 저장소빠른 속도, TTL 지원, 손쉬운 확장성, 자동 삭제 기능 등을 제공하여 리프레시 토큰 관리에 최적화되어 있습니다. 정리하자면 Redis를 리프레시 토큰 저장소로 사용하는 가장 큰 이유는 다음과 같습니다.

  • 리프레시 토큰을 Redis로 관리하면 Redis의 빠른 속도와 유연한 데이터 만료 관리 기능을 최대한 활용이 가능
  • 특히 JWT 블랙리스트 관리와 자동 만료 처리에서 엄청난 강점

테스트 환경

build.gradle 핵심 요소

  • spring Boot 3.4.3
  • azul Java 17
  • jjwt:0.12.6

하드웨어 핵심 요소

  • Windows 11 Home 24H2
  • SAMSUNG DDR5 32GB PC5-4800B
  • Intel(R) Core(TM) Ultra 7 155H 3.80 GHz

애플리케이션 구성 환경

  • Docker Compose로 Postgres, Redis, k6 실행합니다. 모든 환경은 기본 상태입니다.
  • 각 애플리케이션을 동일한 설정과 네트워크 환경을 갖도록 구성합니다.
services:
  postgres:
    image: postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: faster
    ports:
      - "5433:5432"
    networks:
      - my_network

  redis:
    image: redis
    command: redis-server
    ports:
      - "6379:6379"
    networks:
      - my_network

  k6:
    image: grafana/k6
    volumes:
      - ./k6:/scripts
    networks:
      - my_network

networks:
  my_network:

회원 생성 코드

CREATE OR REPLACE PROCEDURE create_users_procedure()
    LANGUAGE plpgsql
AS $$
DECLARE
    i INT := 1;
    roles TEXT[] := ARRAY['ROLE_MASTER', 'ROLE_HUB', 'ROLE_COMPANY', 'ROLE_DELIVERY'];
BEGIN
    WHILE i <= 100000 LOOP
            INSERT INTO p_user (
                created_at, updated_at, name, username, slack_id, password, role
            ) VALUES (
                         NOW(), NOW(),
                         'User' || i,
                         'username' || i,
                         'slack_id' || i,
                         'hashed_password_' || i,
                         roles[(i % 4) + 1]  -- 1, 2, 3, 4 반복
                     );
            i := i + 1;
        END LOOP;
END;
$$;

--CALL create_users_procedure();
  • 회원 생성은 프로시저로 생성했습니다.
    권한은 ENUM 데이터에 의해 생성된 정책에 따라서 해당하는 4 개 중 반드시 1개이어야만 합니다.

리프레시 토큰 저장 코드

  @GetMapping("/store-refresh-tokens")
  public ResponseEntity<String> storeRefreshTokens(
      @RequestParam(value = "type", required = true) String type) {

    long startTime = System.currentTimeMillis();

    LongStream.rangeClosed(1, 100000).forEach(userId ->
        CompletableFuture.runAsync(() -> storeTokenForUser(userId, type))
    );

    long endTime = System.currentTimeMillis();
    long duration = endTime - startTime;

    logger.info("Refresh Tokens 저장 완료. 저장 방식: {} | 소요 시간: {} ms", type, duration);
    return ResponseEntity.ok("Refresh Tokens 저장 완료. 저장 방식: " + type + " | 소요 시간: " + duration + " ms");
  }
  1. 시간 기록
    PostMan에서 직관적으로 요청
    이 시작된 시간을 기록해서 저장 작업에 걸린 시간을 측정하기 위한 용도로 작성했습니다.
    참고로, k6으로 성능 테스트를 수행하는 시점에서는 System 객체로 전체 작업 시간을 측정할 필요가 없습니다.
  2. LongStream.rangeClosed(1, 1000)
    startInclusive 부터 endInclusive 까지
    숫자를 생성하는 스트림입니다.
    rangeClosed()는 endInclusive 숫자까지 포함하고, range()는 포함하지 않습니다.
  3. CompletableFuture.runAsync(() -> storeTokenForUser(userId, type))
    CompletableFuture는
    비동기 연산 수행을 처리하는 클래스입니다. 해당 클래스의 runAsync() 메서드는 main 스레드가 아니라 새로운 스레드에서 비동기적으로 실행합니다.
    따라서 storeTokenForUser(userId, type) 메서드를 비동기적으로 호출하게 됩니다. 즉, 토큰을 비동기적으로 생성하여 빠른 속도로 작업을 수행합니다.
  private void storeTokenForUser(long userId, String type) {
    String refreshToken = jwtProvider.createRefreshToken(userId);

    if ("redis".equalsIgnoreCase(type)) {
      authService.storeRefreshToken(userId, refreshToken, true);
    } else if ("postgres".equalsIgnoreCase(type)) {
      authService.storeRefreshToken(userId, refreshToken, false);
    }
  }
  1. JWT 토큰 생성
    userId를 기반으로 리프레시 토큰을 생성하고, 생성된 토큰 저장을 redis인 경우와 postgres인 경우를 구분하기 위해 type을 지정합니다.
    내부적으로 더 어떻게 구현했는지 살펴보고 싶으면 아래를 클릭해주세요.
  • storeRefreshToken 코드 살펴보기
  • public void storeRefreshToken(long userId, String refreshToken, boolean isRedis) { if (isRedis) { redisService.saveRefreshToken(userId, refreshToken); } else { refreshTokenRepository.save(new RefreshTokenEntity(userId, refreshToken)); } }
  • RedisService 코드 살펴보기
  • @RequiredArgsConstructor @Service public class RedisService { private final StringRedisTemplate redisTemplate; public void saveRefreshToken(long userId, String refreshToken) { String key = "refresh_token:" + userId; redisTemplate.opsForValue().set(key, refreshToken, Duration.ofDays(7)); } public String getRefreshToken(long userId) { String key = "refresh_token:" + userId; return redisTemplate.opsForValue().get(key); } }

리프레시 토큰 조회 코드

  @GetMapping("/retrieve-refresh-tokens")
  public ResponseEntity<String> retrieveRefreshTokens(@RequestParam("type") String type) {
    long startTime = System.currentTimeMillis();

    LongStream.rangeClosed(1, 100000).forEach(userId ->
        CompletableFuture.runAsync(() -> retrieveTokenForUser(userId, type))
    );

    long endTime = System.currentTimeMillis();
    long duration = endTime - startTime;

    logger.info("Refresh Tokens 조회 완료. 조회 방식: {} | 소요 시간: {} ms", type, duration);
    return ResponseEntity.ok("Refresh Tokens 조회 완료. 조회 방식: " + type + " | 소요 시간: " + duration + " ms");
  }
  1. 시간 기록
    PostMan에서 직관적으로 요청
    이 시작된 시간을 기록해서 저장 작업에 걸린 시간을 측정하기 위한 용도로 작성했습니다.
    참고로, k6으로 성능 테스트를 수행하는 시점에서는 System 객체로 전체 작업 시간을 측정할 필요가 없습니다.
  2. LongStream.rangeClosed(1, 1000)
    startInclusive 부터 endInclusive 까지
    숫자를 생성하는 스트림입니다.
    rangeClosed()는 endInclusive 숫자까지 포함하고, range()는 포함하지 않습니다.
  3. CompletableFuture.runAsync(() -> storeTokenForUser(userId, type))
    CompletableFuture는
    비동기 연산 수행을 처리하는 클래스입니다. 해당 클래스의 runAsync() 메서드는 main 스레드가 아니라 새로운 스레드에서 비동기적으로 실행합니다.
    따라서 storeTokenForUser(userId, type) 메서드를 비동기적으로 호출하게 됩니다. 즉, 토큰을 비동기적으로 생성하여 빠른 속도로 작업을 수행합니다.
  private void retrieveTokenForUser(long userId, String type) {
    if ("redis".equalsIgnoreCase(type)) {
      authService.getRefreshTokenFromRedis(userId);
    } else if ("postgres".equalsIgnoreCase(type)) {
      authService.getRefreshTokenFromPostgres(userId);
    }
  • getRefreshTokenFromRedis, getRefreshTokenFromPostgres 코드 보기
        public String getRefreshToken(long userId) {
          String key = "refresh_token:" + userId;
          return redisTemplate.opsForValue().get(key);
        }
  • // Redis에서 리프레시 토큰 조회 public String getRefreshTokenFromRedis(long userId) { return redisService.getRefreshToken(userId); } // PostgreSQL에서 리프레시 토큰 조회 public String getRefreshTokenFromPostgres(long userId) { return refreshTokenRepository.findByUserId(userId) .map(RefreshTokenEntity::getRefreshToken) .orElse(null); }

1000건 저장 속도 측정: Postman

Postgres와 Redis 모두 이미 생성된 상태가 아니라 초기화된 상태에서 리프레시 토큰 1000건을 저장했습니다.

  • Refresh Tokens 저장 완료. 저장 방식: postgres | 소요 시간: 5 ms
  • Refresh Tokens 저장 완료. 저장 방식: redis | 소요 시간: 1 ms

1000건 조회 속도 측정: Postman(10번 수행)

  • Refresh Tokens 조회 완료. 조회 방식: postgres | 소요 시간: 1 ms (평균)
  • Refresh Tokens 조회 완료. 조회 방식: redis | 소요 시간: 4 ms (평균)

수행 결과

Postman으로 실제 저장과 조회 속도를 측정하면 명확한 결과를 얻기 어렵지만, 빠르고 대략적인 근사값을 구하는 데 매우 유용합니다.
1000개 데이터를 기준으로 대략 Postgres의 조회 속도가 3배 더 빠르다는 것을 확인할 수 있습니다.

k6로 실행하기

  • Postgres: user_id 인덱스 X, 환경 설정 X
  • Redis: appendonly yes
  • 회원, 리프레시 토큰 10만개
  • 동시에 접속하는 사용자 수 50 이상은 컴퓨터가 못버틴다…!

1차 테스트 스크립트

Postgres k6 스크립트

import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
    vus: 10,
    duration: '30s',
};

export default function () {
    const url = 'http://host.docker.internal:10001/internal/users/retrieve-refresh-tokens?type=postgres';
    const res = http.get(url);

    check(res, { 'status was 200': (r) => r.status === 200 });
    sleep(1);
}

Redis k6 스크립트

import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
    vus: 10,
    duration: '30s',
};

export default function () {
    const url = 'http://host.docker.internal:10001/internal/users/retrieve-refresh-tokens?type=redis';
    const res = http.get(url);

    check(res, { 'status was 200': (r) => r.status === 200 });
    sleep(1);
}

1차 테스트 수행(10VUs)

  • Postgres에서 반복되는 조회에 대해 캐싱이 남아있을 수 있습니다. 지금 테스트에서는 데이터를 추가한 이후에 최초 조회를 분석합니다.

Redis vs PostgreSQL 성능 비교 (10 VUs)

측정 지표 Redis PostgreSQL 차이 (PostgreSQL 기준)
평균 응답 시간 (avg) 45.92ms 40.78ms ✅ PostgreSQL이 약 11% 더 빠름
최소 응답 시간 (min) 13.27ms 11.22ms ✅ PostgreSQL이 더 빠름
중앙값 (median) 24.42ms 22.47ms ✅ PostgreSQL이 더 빠름
최대 응답 시간 (max) 361.16ms 240.05ms ✅ PostgreSQL이 더 빠름
P90 응답 시간 (p(90)) 100.77ms 81.23ms ✅ PostgreSQL이 더 빠름
P95 응답 시간 (p(95)) 177.39ms 112.58ms ✅ PostgreSQL이 더 빠름
총 요청 수 (requests) 290 292 거의 동일
요청 속도 (req/s) 9.51 요청/초 9.42 요청/초 거의 동일

전체적인 결과는 성능 비교 결과는 Postgres가 조금 더 빠른 성능을 보입니다.

  • P95 응답 시간에서 177.39ms의 결과를 살펴보면, Redis의 일부 요청이 느리게 응답하는 경우가 발생할 수 있음을 의미합니다.
  • 요청 속도는 거의 동일한 처리량을 보이고 있습니다.
  • Postgres는 user_id 인덱스를 생성 및 환경 설정을 하지 않았으며, Redis 또한 별도로 설정하지 않은 상태에서 진행한 테스트입니다.

Redis 설정 값 확인 및 변경 (최적화 확인)

Redis의 설정은 400 개가 넘습니다. Redis 성능을 최적화를 위해서는 기본 설정으로 되어 있는 특정 몇 가지 설정을 직접 수정해야 합니다.

  1. Redis 설정 값 확인하기 (CONFIG GET *)
CONFIG GET *
  • Redis 설정 전체 살펴보기
  • # redis-cli 127.0.0.1:6379> CONFIG GET * 1) "aof-timestamp-enabled" 2) "no" 3) "replica-ignore-maxmemory" 4) "yes" 5) "zset-max-listpack-value" 6) "64" 7) "rdb-save-incremental-fsync" 8) "yes" 9) "hide-user-data-from-log" 10) "no" 11) "cluster-slave-validity-factor" 12) "10" 13) "shutdown-on-sigint" 14) "default" 15) "aof_rewrite_cpulist" 16) "" 17) "stream-node-max-entries" 18) "100" 19) "cluster-announce-ip" 20) "" 21) "loglevel" 22) "notice" 23) "slave-announce-ip" 24) "" 25) "stream-node-max-bytes" 26) "4096" 27) "cluster-allow-replica-migration" 28) "yes" 29) "zset-max-ziplist-entries" 30) "128" 31) "syslog-ident" 32) "redis" 33) "active-defrag-cycle-max" 34) "25" 35) "disable-thp" 36) "yes" 37) "no-appendfsync-on-rewrite" 38) "no" 39) "tls-replication" 40) "no" 41) "crash-log-enabled" 42) "yes" 43) "slave-announce-port" 44) "0" 45) "activerehashing" 46) "yes" 47) "cluster-slave-no-failover" 48) "no" 49) "save" 50) "3600 1 300 100 60 10000" 51) "activedefrag" 52) "no" 53) "replica-announce-ip" 54) "" 55) "jemalloc-bg-thread" 56) "yes" 57) "lazyfree-lazy-user-flush" 58) "no" 59) "cluster-announce-tls-port" 60) "0" 61) "protected-mode" 62) "no" 63) "lfu-decay-time" 64) "1" 65) "appenddirname" 66) "appendonlydir" 67) "ignore-warnings" 68) "" 69) "replicaof" 70) "" 71) "repl-diskless-load" 72) "disabled" 73) "proc-title-template" 74) "{title} {listen-addr} {server-mode}" 75) "zset-max-listpack-entries" 76) "128" 77) "unixsocketperm" 78) "0" 79) "dir" 80) "/data" 81) "repl-diskless-sync" 82) "yes" 83) "bgsave_cpulist" 84) "" 85) "min-replicas-to-write" 86) "0" 87) "maxmemory-clients" 88) "0" 89) "tls-client-cert-file" 90) "" 91) "tls-cluster" 92) "no" 93) "syslog-facility" 94) "local0" 95) "slowlog-log-slower-than" 96) "10000" 97) "active-expire-effort" 98) "1" 99) "latency-monitor-threshold" 100) "0" 101) "supervised" 102) "no" 103) "slowlog-max-len" 104) "128" 105) "list-max-ziplist-size" 106) "-2" 107) "tls-cert-file" 108) "" 109) "oom-score-adj-values" 110) "0 200 800" 111) "aclfile" 112) "" 113) "tls-port" 114) "0" 115) "enable-protected-configs" 116) "no" 117) "cluster-allow-reads-when-down" 118) "no" 119) "server_cpulist" 120) "" 121) "tls-protocols" 122) "" 123) "max-new-connections-per-cycle" 124) "10" 125) "tls-session-caching" 126) "yes" 127) "appendonly" 128) "no" 129) "databases" 130) "16" 131) "slaveof" 132) "" 133) "hll-sparse-max-bytes" 134) "3000" 135) "auto-aof-rewrite-percentage" 136) "100" 137) "tcp-keepalive" 138) "300" 139) "repl-backlog-ttl" 140) "3600" 141) "list-max-listpack-size" 142) "-2" 143) "maxclients" 144) "10000" 145) "lazyfree-lazy-eviction" 146) "no" 147) "replica-announce-port" 148) "0" 149) "min-slaves-max-lag" 150) "10" 151) "tls-ciphers" 152) "" 153) "slave-ignore-maxmemory" 154) "yes" 155) "aof-rewrite-incremental-fsync" 156) "yes" 157) "lazyfree-lazy-expire" 158) "no" 159) "lazyfree-lazy-server-del" 160) "no" 161) "repl-timeout" 162) "60" 163) "repl-disable-tcp-nodelay" 164) "no" 165) "bio-cpulist" 166) "" 167) "masterauth" 168) "" 169) "rdbcompression" 170) "yes" 171) "tls-client-key-file-pass" 172) "" 173) "hash-max-listpack-entries" 174) "512" 175) "maxmemory" 176) "0" 177) "syslog-enabled" 178) "no" 179) "maxmemory-policy" 180) "noeviction" 181) "cluster-port" 182) "0" 183) "cluster-config-file" 184) "nodes.conf" 185) "set-max-listpack-entries" 186) "128" 187) "io-threads" 188) "1" 189) "shutdown-timeout" 190) "10" 191) "crash-memcheck-enabled" 192) "yes" 193) "stop-writes-on-bgsave-error" 194) "yes" 195) "masteruser" 196) "" 197) "slave-serve-stale-data" 198) "yes" 199) "bind-source-addr" 200) "" 201) "client-query-buffer-limit" 202) "1073741824" 203) "lfu-log-factor" 204) "10" 205) "cluster-announce-hostname" 206) "" 207) "bind" 208) "* -::*" 209) "min-slaves-to-write" 210) "0" 211) "maxmemory-samples" 212) "5" 213) "tls-ca-cert-file" 214) "" 215) "active-defrag-cycle-min" 216) "1" 217) "slave-lazy-flush" 218) "no" 219) "tls-prefer-server-ciphers" 220) "no" 221) "repl-ping-slave-period" 222) "10" 223) "slave-priority" 224) "100" 225) "server-cpulist" 226) "" 227) "aof-use-rdb-preamble" 228) "yes" 229) "tls-dh-params-file" 230) "" 231) "shutdown-on-sigterm" 232) "default" 233) "acllog-max-len" 234) "128" 235) "tls-ca-cert-dir" 236) "" 237) "active-defrag-ignore-bytes" 238) "104857600" 239) "latency-tracking-info-percentiles" 240) "50 99 99.9" 241) "repl-diskless-sync-delay" 242) "5" 243) "lua-time-limit" 244) "5000" 245) "active-defrag-max-scan-fields" 246) "1000" 247) "acl-pubsub-default" 248) "resetchannels" 249) "timeout" 250) "0" 251) "cluster-announce-human-nodename" 252) "" 253) "bio_cpulist" 254) "" 255) "always-show-logo" 256) "no" 257) "cluster-migration-barrier" 258) "1" 259) "repl-backlog-size" 260) "1048576" 261) "tls-key-file" 262) "" 263) "bgsave-cpulist" 264) "" 265) "tls-key-file-pass" 266) "" 267) "cluster-preferred-endpoint-type" 268) "ip" 269) "cluster-require-full-coverage" 270) "yes" 271) "max-new-tls-connections-per-cycle" 272) "1" 273) "enable-module-command" 274) "no" 275) "replica-serve-stale-data" 276) "yes" 277) "locale-collate" 278) "" 279) "min-replicas-max-lag" 280) "10" 281) "rdbchecksum" 282) "yes" 283) "lazyfree-lazy-user-del" 284) "no" 285) "logfile" 286) "" 287) "notify-keyspace-events" 288) "" 289) "unixsocket" 290) "" 291) "slave-read-only" 292) "yes" 293) "dynamic-hz" 294) "yes" 295) "tls-ciphersuites" 296) "" 297) "active-defrag-threshold-lower" 298) "10" 299) "replica-lazy-flush" 300) "no" 301) "maxmemory-eviction-tenacity" 302) "10" 303) "cluster-node-timeout" 304) "15000" 305) "io-threads-do-reads" 306) "no" 307) "rdb-del-sync-files" 308) "no" 309) "replica-ignore-disk-write-errors" 310) "no" 311) "socket-mark-id" 312) "0" 313) "cluster-enabled" 314) "no" 315) "aof-rewrite-cpulist" 316) "" 317) "replica-read-only" 318) "yes" 319) "cluster-announce-bus-port" 320) "0" 321) "cluster-announce-port" 322) "0" 323) "repl-diskless-sync-max-replicas" 324) "0" 325) "latency-tracking" 326) "yes" 327) "tls-session-cache-size" 328) "20480" 329) "zset-max-ziplist-value" 330) "64" 331) "hz" 332) "10" 333) "appendfsync" 334) "everysec" 335) "repl-ping-replica-period" 336) "10" 337) "replica-priority" 338) "100" 339) "set-max-intset-entries" 340) "512" 341) "tls-auth-clients" 342) "yes" 343) "active-defrag-threshold-upper" 344) "100" 345) "enable-debug-command" 346) "no" 347) "proto-max-bulk-len" 348) "536870912" 349) "appendfilename" 350) "appendonly.aof" 351) "hash-max-listpack-value" 352) "64" 353) "tracking-table-max-keys" 354) "1000000" 355) "cluster-allow-pubsubshard-when-down" 356) "yes" 357) "pidfile" 358) "" 359) "sanitize-dump-payload" 360) "no" 361) "replica-announced" 362) "yes" 363) "tls-session-cache-timeout" 364) "300" 365) "dbfilename" 366) "dump.rdb" 367) "requirepass" 368) "" 369) "auto-aof-rewrite-min-size" 370) "67108864" 371) "cluster-replica-validity-factor" 372) "10" 373) "hash-max-ziplist-entries" 374) "512" 375) "client-output-buffer-limit" 376) "normal 0 0 0 slave 268435456 67108864 60 pubsub 33554432 8388608 60" 377) "daemonize" 378) "no" 379) "cluster-replica-no-failover" 380) "no" 381) "oom-score-adj" 382) "no" 383) "set-max-listpack-value" 384) "64" 385) "hash-max-ziplist-value" 386) "64" 387) "tcp-backlog" 388) "511" 389) "aof-load-truncated" 390) "yes" 391) "list-compress-depth" 392) "0" 393) "tls-client-key-file" 394) "" 395) "propagation-error-behavior" 396) "ignore" 397) "port" 398) "6379" 399) "set-proc-title" 400) "yes" 401) "cluster-link-sendbuf-limit" 402) "0" 403) "busy-reply-threshold" 404) "5000" 127.0.0.1:6379>

Redis 설정 분석 결과 (중요 설정 값 확인)

설정 이름 현재 값(기본 설정) 권장 값 (개선 필요 시)
appendonly no 정상 설정 (영속화 비활성화)
maxmemory 0 (무제한) 메모리 제한을 설정하면 좋을 수 있음 (예: 1gb)
maxmemory-policy noeviction 변경 권장: allkeys-lru (LRU 방식)
hz 10 변경 권장: 100 (이벤트 루프 속도 증가)
tcp-backlog 511 기본값으로 충분 (문제 없음)
latency-tracking yes 정상 설정
lazyfree-lazy-eviction no 변경 권장: yes (비동기 삭제)
lazyfree-lazy-user-del no 변경 권장: yes (비동기 삭제)
1. 메모리 관리 정책 설정 (maxmemory-policy)    
- 현재: noeviction (메모리가 부족할 경우, 요청을 거부)    
- 권장: allkeys-lru (가장 오랫동안 사용되지 않은 키를 제거)    
```
CONFIG SET maxmemory-policy allkeys-lru
```

  1. 이벤트 루프 속도 증가 (hz)
    • 현재: 10
    • 권장: 100 (Redis의 이벤트 처리 속도를 증가)
    • CONFIG SET hz 100

  1. 비동기 삭제 활성화 (성능 최적화)
    • Redis는 기본적으로 데이터 삭제 작업을 동기적으로 처리함
    • 비동기 방식으로 변경하면, 대량의 데이터 삭제 시 지연을 줄일 수 있음
    • CONFIG SET lazyfree-lazy-eviction yes CONFIG SET lazyfree-lazy-user-del yes

  1. 메모리 제한 설정 (maxmemory)
    • 현재: 0 (제한 없음)
    • 테스트 환경이라면 설정하지 않아도 되지만, 성능을 조금 더 최적화하려면 메모리를 제한할 수 있음
    • CONFIG SET maxmemory 1gb

Redis 데이터 구조 변경 (String → Hash)

  • 기존 Redis의 String 타입을 Hash 타입으로 변경하면 더 효율적인 조회가 가능
public void saveRefreshToken(long userId, String refreshToken) {
    String key = "refresh_token:" + userId;
    redisTemplate.opsForHash().put(key, "token", refreshToken);
}

  • Redis 환경 설정 그림으로 살펴보기

 

2차 테스트 수행(10 VUs)

  • Postgres: user_id 인덱스 X, 환경 설정 X
  • Redis: appendonly no, maxmemory-policy allkeys-lru, hz 100, lazyfree-lazy-eviction yes, lazyfree-lazy-user-del yes
     execution: local
        script: /scripts/redis.js
        output: -

     scenarios: (100.00%) 1 scenario, 10 max VUs, 1m0s max duration (incl. graceful stop):
              * default: 10 looping VUs for 30s (gracefulStop: 30s)

  █ TOTAL RESULTS

    checks_total.......................: 293     9.462528/s
    checks_succeeded...................: 100.00% 293 out of 293
    checks_failed......................: 0.00%   0 out of 293

    ✓ status was 200

    HTTP
    http_req_duration.......................................................: avg=35.57ms min=16.91ms med=24.86ms max=206.44ms p(90)=72.15ms p(95)=98.58ms
      { expected_response:true }............................................: avg=35.57ms min=16.91ms med=24.86ms max=206.44ms p(90)=72.15ms p(95)=98.58ms
    http_req_failed.........................................................: 0.00%  0 out of 293
    http_reqs...............................................................: 293    9.462528/s

    EXECUTION
    iteration_duration......................................................: avg=1.03s   min=1.01s   med=1.02s   max=1.2s     p(90)=1.07s   p(95)=1.1s
    iterations..............................................................: 293    9.462528/s
    vus.....................................................................: 10     min=10       max=10
    vus_max.................................................................: 10     min=10       max=10

    NETWORK
    data_received...........................................................: 131 kB 4.2 kB/s
    data_sent...............................................................: 42 kB  1.4 kB/s

running (0m31.0s), 00/10 VUs, 293 complete and 0 interrupted iterations
default ✓ [======================================] 10 VUs  30s

Redis 성능 테스트 결과 (최적화 후, 10VUs)

측정 지표 이전 설정 (최적화 전) 최적화 후 설정 개선 여부 (향상률)
평균 응답 시간 (avg) 38.72ms 35.57ms 개선됨 (약 8% 향상)
최소 응답 시간 (min) 9ms 16.91ms ❌ 오히려 느려짐
중앙값 (median) 21.77ms 24.86ms ❌ 오히려 느려짐
최대 응답 시간 (max) 208.97ms 206.44ms 약간 개선됨
P90 응답 시간 (p(90)) 74.55ms 72.15ms 약간 개선됨
P95 응답 시간 (p(95)) 155.41ms 98.58ms 크게 개선됨 (약 37% 향상)
총 요청 수 (requests) 292 293 거의 동일
요청 속도 (req/s) 9.40 9.46 거의 동일

 

  • 평균 응답 시간이 38.72ms → 35.57ms 로 줄어들면서 8%의 성능 향상이 확인됩니다.
  • 응답 시간인 P95 값이 155.41ms → 98.58ms 로 크게 줄어들면서 37%의 성능 향상이 확인됩니다. → 비동기 삭제 설정 (lazyfree-lazy-eviction & lazyfree-lazy-user-del) 이 영향을 준 것으로 보입니다.
  • 최소 응답 시간과 중앙값이 오히려 느려짐 - min: 9ms → 16.91ms (느려짐)
  • median: 21.77ms → 24.86ms (느려짐) 이유: 설정 변경으로 인해 Redis가 일부 요청을 더 오래 대기시키면서 효율성을 높인 것으로 예상됩니다.
  • 최대 응답 시간은 약간 개선됨 - 208.97ms → 206.44ms 로 아주 조금 줄어듦. 하지만 여전히 Redis의 최대 응답 시간이 PostgreSQL과 비슷하거나 더 느릴 때도 있습니다.


정리하자면 Redis의 성능이 일부 개선되었지만, 기대했던 만큼의 성능 향상은 보이지 않습니다. 그리고 PostgreSQL과 비교할 때, Redis가 아직도 평균 응답 시간에서 느리게 작동하고 있습니다. 데이터 구조 변경 및 부하 테스트 강화를 통해 성능을 더 개선할 필요가 있는 상태로, 아직은 HashMap 으로 변환하지 않고 테스트를 수행했기 때문에 성능 향상의 여지가 아직 남아 있습니다.

현재 테스트에서는 동시 접속자 수가 10명입니다. 정확한 테스트를 위해 동시 접속자 수를 더 늘려서 테스트를 진행하게 된다면 결과가 달라질 수 있으므로 동시 접속자 수를 30명으로 조정합니다.


3차 테스트 수행(30 UVs)

  • Postgres: user_id 인덱스 X, 환경 설정 X
  • Redis: appendonly no, maxmemory-policy allkeys-lru, hz 100, lazyfree-lazy-eviction yes, lazyfree-lazy-user-del yes
     execution: local
        script: /scripts/redis.js
        output: -

     scenarios: (100.00%) 1 scenario, 30 max VUs, 1m0s max duration (incl. graceful stop):
              * default: 30 looping VUs for 30s (gracefulStop: 30s)

  █ TOTAL RESULTS

    checks_total.......................: 827     27.196897/s
    checks_succeeded...................: 100.00% 827 out of 827
    checks_failed......................: 0.00%   0 out of 827

    ✓ status was 200

    HTTP
    http_req_duration.......................................................: avg=89.88ms min=9.56ms med=26.93ms max=2.07s p(90)=139.62ms p(95)=181.2ms
      { expected_response:true }............................................: avg=89.88ms min=9.56ms med=26.93ms max=2.07s p(90)=139.62ms p(95)=181.2ms
    http_req_failed.........................................................: 0.00%  0 out of 827
    http_reqs...............................................................: 827    27.196897/s

    EXECUTION
    iteration_duration......................................................: avg=1.09s   min=1.01s  med=1.02s   max=3.07s p(90)=1.15s    p(95)=1.2s
    iterations..............................................................: 827    27.196897/s
    vus.....................................................................: 30     min=30       max=30
    vus_max.................................................................: 30     min=30       max=30

    NETWORK
    data_received...........................................................: 369 kB 12 kB/s
    data_sent...............................................................: 119 kB 3.9 kB/s

running (0m30.4s), 00/30 VUs, 827 complete and 0 interrupted iterations
default ✓ [======================================] 30 VUs  30s
     execution: local
        script: /scripts/postgres.js
        output: -

     scenarios: (100.00%) 1 scenario, 30 max VUs, 1m0s max duration (incl. graceful stop):
              * default: 30 looping VUs for 30s (gracefulStop: 30s)

  █ TOTAL RESULTS

    checks_total.......................: 50      0.833169/s
    checks_succeeded...................: 100.00% 50 out of 50
    checks_failed......................: 0.00%   0 out of 50

    ✓ status was 200

    HTTP
    http_req_duration.......................................................: avg=4.01s min=21.76ms med=45.82ms max=33.61s p(90)=9.36s  p(95)=29.79s
      { expected_response:true }............................................: avg=4.01s min=21.76ms med=45.82ms max=33.61s p(90)=9.36s  p(95)=29.79s
    http_req_failed.........................................................: 0.00% 0 out of 50
    http_reqs...............................................................: 50    0.833169/s

    EXECUTION
    iteration_duration......................................................: avg=5.02s min=1.04s   med=1.07s   max=34.61s p(90)=10.36s p(95)=30.78s
    iterations..............................................................: 50    0.833169/s
    vus.....................................................................: 26    min=26      max=30
    vus_max.................................................................: 30    min=30      max=30

    NETWORK
    data_received...........................................................: 23 kB 375 B/s
    data_sent...............................................................: 11 kB 186 B/s

running (1m00.0s), 00/30 VUs, 50 complete and 26 interrupted iterations
default ✓ [======================================] 30 VUs  30s

Redis vs PostgreSQL 성능 비교1 (30 VUs 테스트)

측정 지표 Redis PostgreSQL 차이 (PostgreSQL 기준)
평균 응답 시간 (avg) 89.88ms 4.01s (4010ms) ❌ PostgreSQL이 44배 느림
최소 응답 시간 (min) 9.56ms 21.76ms ❌ PostgreSQL이 약 2배 느림
중앙값 (median) 26.93ms 45.82ms ❌ PostgreSQL이 느림
최대 응답 시간 (max) 2.07s 33.61s ❌ PostgreSQL이 훨씬 느림
P90 응답 시간 (p(90)) 139.62ms 9.36s ❌ PostgreSQL이 67배 느림
P95 응답 시간 (p(95)) 181.2ms 29.79s ❌ PostgreSQL이 훨씬 느림
총 요청 수 (requests) 827 50 ❌ PostgreSQL이 16배 적음
요청 속도 (req/s) 27.20 요청/초 0.83 요청/초 ❌ PostgreSQL이 훨씬 느림
성공률 (checks_succeeded) 100% 100% 동일

Redis vs PostgreSQL 성능 비교2 (30 VUs 테스트)

     execution: local
        script: /scripts/redis.js
        output: -

     scenarios: (100.00%) 1 scenario, 30 max VUs, 1m0s max duration (incl. graceful stop):
              * default: 30 looping VUs for 30s (gracefulStop: 30s)

  █ TOTAL RESULTS

    checks_total.......................: 805     26.086744/s
    checks_succeeded...................: 100.00% 805 out of 805
    checks_failed......................: 0.00%   0 out of 805

    ✓ status was 200

    HTTP
    http_req_duration.......................................................: avg=144.49ms min=7.14ms med=41.37ms max=2s    p(90)=140.93ms p(95)=937.66ms
      { expected_response:true }............................................: avg=144.49ms min=7.14ms med=41.37ms max=2s    p(90)=140.93ms p(95)=937.66ms
    http_req_failed.........................................................: 0.00%  0 out of 805
    http_reqs...............................................................: 805    26.086744/s

    EXECUTION
    iteration_duration......................................................: avg=1.14s    min=1s     med=1.04s   max=2.98s p(90)=1.14s    p(95)=1.91s
    iterations..............................................................: 805    26.086744/s
    vus.....................................................................: 30     min=30       max=30
    vus_max.................................................................: 30     min=30       max=30

    NETWORK
    data_received...........................................................: 359 kB 12 kB/s
    data_sent...............................................................: 116 kB 3.8 kB/s

running (0m30.9s), 00/30 VUs, 805 complete and 0 interrupted iterations
default ✓ [======================================] 30 VUs  30s
PS C:\github\faster\user>
     execution: local
        script: /scripts/postgres.js
        output: -

     scenarios: (100.00%) 1 scenario, 30 max VUs, 1m0s max duration (incl. graceful stop):
              * default: 30 looping VUs for 30s (gracefulStop: 30s)

  █ TOTAL RESULTS

    checks_total.......................: 50      0.833169/s
    checks_succeeded...................: 100.00% 50 out of 50
    checks_failed......................: 0.00%   0 out of 50

    ✓ status was 200

    HTTP
    http_req_duration.......................................................: avg=4.01s min=21.76ms med=45.82ms max=33.61s p(90)=9.36s  p(95)=29.79s
      { expected_response:true }............................................: avg=4.01s min=21.76ms med=45.82ms max=33.61s p(90)=9.36s  p(95)=29.79s
    http_req_failed.........................................................: 0.00% 0 out of 50
    http_reqs...............................................................: 50    0.833169/s

    EXECUTION
    iteration_duration......................................................: avg=5.02s min=1.04s   med=1.07s   max=34.61s p(90)=10.36s p(95)=30.78s
    iterations..............................................................: 50    0.833169/s
    vus.....................................................................: 26    min=26      max=30
    vus_max.................................................................: 30    min=30      max=30

    NETWORK
    data_received...........................................................: 23 kB 375 B/s
    data_sent...............................................................: 11 kB 186 B/s

running (1m00.0s), 00/30 VUs, 50 complete and 26 interrupted iterations
default ✓ [======================================] 30 VUs  30s

Redis vs PostgreSQL 성능 비교 (30 VUs 테스트)

중앙값 (median) 41.37ms 4.16s (4160ms) ❌ PostgreSQL이 100배 느림
최대 응답 시간 (max) 2s 36.92s ❌ PostgreSQL이 훨씬 느림
P90 응답 시간 (p(90)) 140.93ms 29.31s (29310ms) ❌ PostgreSQL이 훨씬 느림
P95 응답 시간 (p(95)) 937.66ms 33.36s (33360ms) ❌ PostgreSQL이 훨씬 느림
총 요청 수 (requests) 805 32 ❌ PostgreSQL이 25배 적음
요청 속도 (req/s) 26.09 요청/초 0.53 요청/초 ❌ PostgreSQL이 훨씬 느림
성공률 (checks_succeeded) 100% 100% 동일
  • PostgreSQL 성능이 대폭 감소했습니다. (Redis 대비 65배 느림)
  • 평균 응답 시간 (avg) 에서 PostgreSQL이 9.44s 로 매우 느립니다.
  • 특히, 최대 응답 시간 (max) 이 36.92s 로 심각하게 느립니다.
  • P90 및 P95 값도 각각 29.31s, 33.36s 로 거의 모든 요청이 지연됩니다.
  • 요청 처리는 Postgres가 약 25배 적습니다.
  • Redis는 모든 지표에서 PostgreSQL을 크게 우세합니다.

 

4차 테스트 수행(30 UVs)

  • Postgres: user_id 인덱스 생성, 환경 설정 X
  • Redis: appendonly no, maxmemory-policy allkeys-lru, hz 100, lazyfree-lazy-eviction yes, lazyfree-lazy-user-del yes

PostgreSQL 성능 테스트 결과 (30 VUs, 인덱스 추가 후)

측정 지표 인덱스 추가 전 인덱스 추가 후 (현재 결과) 개선 여부 (향상률)
평균 응답 시간 (avg) 4.01s (4010ms) 94.27ms 95.5% 개선됨!
최소 응답 시간 (min) 21.76ms 11.17ms ✅ 개선됨
중앙값 (median) 45.82ms 23.56ms ✅ 개선됨
최대 응답 시간 (max) 33.61s 1.5s 95.5% 개선됨!
P90 응답 시간 (p(90)) 9.36s 115.59ms 98.7% 개선됨!
P95 응답 시간 (p(95)) 29.79s 483.95ms 98.4% 개선됨!
총 요청 수 (requests) 50 840 16배 증가!
요청 속도 (req/s) 0.83 요청/초 27.37 요청/초 32배 증가!
  • user_id 인덱스 추가로 인해 평균 응답 시간이 95.5% 개선됐습니다.
  • 요청 수가 단 50건 에서 840건 으로 크게 증가합니다.
  • PostgreSQL이 이제 Redis와 비교할 수 있을 만큼의 성능을 보여주고 있습니다.

 

 

Redis와의 성능 비교 (30 VUs)

측정 지표 Redis PostgreSQL (인덱스 추가 후)
평균 응답 시간 (avg) 89.88ms 94.27ms
최소 응답 시간 (min) 9.56ms 11.17ms
중앙값 (median) 26.93ms 23.56ms
최대 응답 시간 (max) 2.07s 1.5s
P90 응답 시간 (p(90)) 139.62ms 115.59ms
P95 응답 시간 (p(95)) 181.2ms 483.95ms
요청 속도 (req/s) 27.20 요청/초 27.37 요청/초
  • 평균 응답 시간: 두 DB가 거의 비슷합니다. (Redis: 89.88ms, PostgreSQL: 94.27ms)
  • 최대 응답 시간: PostgreSQL (1.5s) 이 Redis (2.07s) 보다 더 빠릅니다.
  • 중앙값: PostgreSQL (23.56ms) 이 Redis (26.93ms) 보다 더 빠릅니다.
  • P90 값: PostgreSQL (115.59ms) 이 Redis (139.62ms) 보다 더 빠릅니다.
  • 요청 처리 속도: 거의 동일합니다. (Redis: 27.20 요청/초, PostgreSQL: 27.37 요청/초).
  • PostgreSQL이 Redis와 비슷한 성능을 보이게 되었습니다.

5차 테스트 수행(30 UVs)

  • Postgres: user_id 인덱스 생성, hikari maximum-size:30, connection-timeout: 30000, idle-timeout: 600000, max-lifetime: 1800000
  • Redis: appendonly no, maxmemory-policy allkeys-lru, hz 100, lazyfree-lazy-eviction yes, lazyfree-lazy-user-del yes
     execution: local
        script: /scripts/postgres.js
        output: -

     scenarios: (100.00%) 1 scenario, 30 max VUs, 1m0s max duration (incl. graceful stop):
              * default: 30 looping VUs for 30s (gracefulStop: 30s)

  █ TOTAL RESULTS

    checks_total.......................: 850     27.031877/s
    checks_succeeded...................: 100.00% 850 out of 850
    checks_failed......................: 0.00%   0 out of 850

    ✓ status was 200

    HTTP
    http_req_duration.......................................................: avg=107.62ms min=11.24ms med=31.3ms max=1.53s p(90)=139.74ms p(95)=460.1ms
      { expected_response:true }............................................: avg=107.62ms min=11.24ms med=31.3ms max=1.53s p(90)=139.74ms p(95)=460.1ms
    http_req_failed.........................................................: 0.00%  0 out of 850
    http_reqs...............................................................: 850    27.031877/s

    EXECUTION
    iteration_duration......................................................: avg=1.1s     min=1.01s   med=1.03s  max=2.54s p(90)=1.14s    p(95)=1.46s
    iterations..............................................................: 850    27.031877/s
    vus.....................................................................: 30     min=30       max=30
    vus_max.................................................................: 30     min=30       max=30

    NETWORK
    data_received...........................................................: 382 kB 12 kB/s
    data_sent...............................................................: 125 kB 4.0 kB/s

running (0m31.4s), 00/30 VUs, 850 complete and 0 interrupted iterations
default ✓ [======================================] 30 VUs  30s
PS C:\github\faster\user>

PostgreSQL 성능 테스트 결과 (30 VUs, HikariCP 설정 적용 후)

측정 지표 인덱스 추가 후 (이전) HikariCP 설정 적용 후 (현재) 개선 여부 (향상률)
평균 응답 시간 (avg) 94.27ms 107.62ms ❌ 약간 느려짐 (14% 감소)
최소 응답 시간 (min) 11.17ms 11.24ms 거의 동일
중앙값 (median) 23.56ms 31.3ms ❌ 약간 느려짐
최대 응답 시간 (max) 1.5s 1.53s 거의 동일
P90 응답 시간 (p(90)) 115.59ms 139.74ms ❌ 느려짐
P95 응답 시간 (p(95)) 483.95ms 460.1ms ✅ 약간 개선됨 (5% 향상)
총 요청 수 (requests) 840 850 거의 동일
요청 속도 (req/s) 27.37 요청/초 27.03 요청/초 거의 동일
  • 평균 응답 시간 증가 (94.27ms → 107.62ms)로 약간의 성능 저하가 발생했습니다.
  • PostgreSQL의 Connection Pool (HikariCP) 설정으로 인해 연결 대기 시간이 발생했을 가능성이 있습니다.
  • 하지만 전반적으로 요청 수 (850) 와 요청 속도 (27.03 요청/초) 는 거의 동일합니다.
  • 최소 응답 시간: 11.17ms → 11.24ms (거의 동일)
  • 최대 응답 시간: 1.5s → 1.53s (거의 동일) Connection Pool 설정이 응답에 큰 영향을 주지 않았다는 의미일 수 있습니다.
  • P95 응답 시간은 약간 개선되면서 (483.95ms → 460.1ms) P95 값이 약간 개선된 것으로 보면 일부 요청에서 성능이 더 안정적으로 처리됩니다.
  • maximum-pool-size 가 30 으로 설정되었는데, 이는 테스트 환경의 VU (30) 와 동일합니다.
  • 즉, 모든 요청이 동시에 들어와도 HikariCP Pool 이 커넥션을 충분히 제공할 수 있습니다.
  • 하지만 동시에 30개 이상의 요청이 들어오면 대기 시간이 발생할 수 있습니다.

6차 테스트 수행(30 UVs)

  • Postgres: user_id 인덱스 생성, hikari maximum-size:30, connection-timeout: 30000, idle-timeout: 600000, max-lifetime: 1800000
  • Redis: opsForHash() 수정, appendonly no, maxmemory-policy allkeys-lru, hz 100, lazyfree-lazy-eviction yes, lazyfree-lazy-user-del yes

Redis 성능 비교 (opsForValue() vs. opsForHash() - 30 VUs 테스트)

측정 지표 opsForHash() (Hash 사용) opsForValue() (String 사용) 차이 (opsForHash 기준)
평균 응답 시간 (avg) 139.75ms 9.72s (9720ms) ✅ Hash가 69배 더 빠름
최소 응답 시간 (min) 9.38ms 1.43s (1430ms) ✅ Hash가 더 빠름
중앙값 (median) 48.2ms 8.51s (8510ms) ✅ Hash가 177배 더 빠름
최대 응답 시간 (max) 1.31s (1310ms) 24.81s (24810ms) ✅ Hash가 19배 더 빠름
P90 응답 시간 (p(90)) 478.75ms 23.93s (23930ms) ✅ Hash가 더 빠름
P95 응답 시간 (p(95)) 1.08s (1080ms) 24.81s (24810ms) ✅ Hash가 더 빠름
총 요청 수 (requests) 792 14 ✅ Hash가 56배 더 많음
요청 속도 (req/s) 26.18 요청/초 0.23 요청/초 ✅ Hash가 113배 더 빠름
  • 평균 응답 시간이 139.75ms (Hash) → 9.72s (String) 로 약 69배 더 빠릅니다.
  • 요청 처리량(requests)이 792 → 14 로 약 56배 더 많습니다.
  • 요청 속도(req/s) 또한 26.18 요청/초 → 0.23 요청/초113배 차이입니다.
  • 모든 지표에서 Hash 타입이 월등히 우수합니다.
  • opsForValue() (String) 의 최대 응답 시간: 24.81s
  • opsForHash() (Hash) 의 최대 응답 시간: 1.31s → 19배 더 빠른 처리 시간입니다.

 

Redis의 데이터 구조 변경 효과

  • Hash 타입 사용 시 Redis의 효율적인 메모리 관리와 빠른 데이터 조회 효과를 보여줍니다.
  • 단일 String 저장 방식은 요청량이 많아지면 병목 현상이 발생합니다.

제 컴퓨터에서는 불가능하기 때문에…. 컴퓨터 성능이 좋다면 아래 목표를 이어나가는 방법도 떠올랐습니다.

  1. 요청 수(requests)를 1000 이상으로 증가시키는 목표를 설정해야 합니다.
  2. 현재 803 요청 에서 1000 요청 이상으로 늘려야 합니다.
  3. max 응답 시간이 1s 이하 로 감소하는 것을 목표로 설정합니다.

댓글