Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DeadLock & Redis Queue

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →

DeadLock & Redis Queue

DeadLock과 Redis 큐를 이용한 문제해결 방안을 알아봅니다.
(23' Google I/O Extended Songdo)

Avatar for Rosie Yang

Rosie Yang

April 10, 2024

More Decks by Rosie Yang

Other Decks in Technology

Transcript

  1. Contents 01 02 03 04 05 06 07 08 Intro

    DeadLock DeadLock Issue Redis Redis Queue Redis Distribution Lock Performance Test QnA
  2. Section 03 2023-02-06 11:57:33.526 WARN 3028 --- [o-8080-exec-431] o.h.engine.jdbc.spi.SqlExceptionHelper :

    SQL Error: 1205, SQLState: 40001 2023-02-06 11:57:33.526 ERROR 3028 --- [o-8080-exec-431] o.h.engine.jdbc.spi.SqlExceptionHelper : Transaction (Process ID 61) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction. 2023-02-06 11:57:33.527 ERROR 3028 --- [o-8080-exec-431] h.hsas.service.Exception Controller : ErrorResponse(location=[CannotAcquireLockException]
  3. Section 03 DeadLock Issue 개선방안 • 구현 로직 • synchronized

    • 테이블 설계 • 데이터베이스 인덱스 • 크기 • 방향 • Lock • 격리수준 • @Retryable • 쿼리 튜닝 • read/write 서버 분리 • 캐시 서버 • 대기열 • 분산락 Basic Transaction DB Server
  4. Section 03 Basic • 구현 로직 • 테이블 설계 •

    데이터베이스 인덱스 • synchronized 회원 / 관리자 예약 신청 예약 후기 예약 등록 기타 서비스 예약 프로그램
  5. Section 03 Basic • 구현 로직 • 테이블 설계 •

    데이터베이스 인덱스 • synchronized 카테고리 상태 날짜 생성자 Non-Clustered Index
  6. Section 03 Basic • 구현 로직 • 테이블 설계 •

    데이터베이스 인덱스 • synchronized public synchronized void method(){ sharedValue++; System.out.println("Shared value inside synchronized method: " + sharedValue); }
  7. Section 03 DeadLock Issue 개선방안 • 구현 로직 • synchronized

    • 테이블 설계 • 데이터베이스 인덱스 • 크기 • 방향 • Lock • 격리수준 • @Retryable • 쿼리 튜닝 • read/write 서버 분리 • 캐시 서버 • 대기열 • 분산락 Basic Transaction DB Server
  8. Section 03 Transaction • 크기 • 방향 • Lock •

    격리수준 • @Retryable • 쿼리 튜닝 @Transactional public void processTransaction() { try { Test test = testRepository.findById(6135184); if(testRepository.checkCount(test)>0) test.setStatus(Status.go); if(testRepository.checkDate(test)!=null) test.setPossible(0); testRepository.save(test); } catch (Exception e) { log(e.getMessage); } }
  9. Section 03 Transaction • 크기 • 방향 • Lock •

    격리수준 • @Retryable • 쿼리 튜닝 @Transactional public void processTransaction() { try { Test test = testRepository.findById(6135184); if(testRepository.checkCount(test)>0) test.setStatus(Status.go); if(testRepository.checkDate(test)!=null) test.setPossible(0); testRepository.save(test); } catch (Exception e) { log(e.getMessage); } }
  10. Section 03 Transaction • 크기 • 방향 • Lock •

    격리수준 • @Retryable • 쿼리 튜닝 @Transactional public void processTransaction() { try { Test test = testRepository.findById(6135184); if(testRepository.checkCount(test)>0) test.setStatus(Status.go); if(testRepository.checkDate(test)!=null) test.setPossible(0); testRepository.save(test); } catch (Exception e) { log(e.getMessage); } }
  11. Section 03 Transaction • 크기 • 방향 • Lock •

    격리수준 • @Retryable • 쿼리 튜닝 @Transactional public void processTransaction() { try { Test test = testRepository.findById(6135184); if(testRepository.checkDate(test)!=null) test.setPossible(0); if(testRepository.checkCount(test)>0) test.setStatus(Status.go); testRepository.save(test); } catch (Exception e) { log(e.getMessage); } }
  12. Section 03 Transaction • 크기 • 방향 • Lock •

    격리수준 • @Retryable • 쿼리 튜닝 transaction transaction transaction
  13. Section 03 Transaction • 크기 • 방향 • Lock •

    격리수준 • @Retryable • 쿼리 튜닝 @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("select u from User u where u.id = :id") Optional<User> findUserForUpdate(@Param("id") Long id); @Transactional(isolation = Isolation.READ_COMMITTED) public void updateUserWithIsolation(Long userId, String newEmail) { User user = userRepository.findUserForUpdate(userId).orElse(null); if (user != null) { user.setEmail(newEmail); userRepository.save(user); } }
  14. Section 03 Transaction • 크기 • 방향 • Lock •

    격리수준 • @Retryable • 쿼리 튜닝 @Transactional(retry = @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 100))) public void myTransactionalMethod() { try { Thread.sleep(3000); System.out.println("Complete!"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println("Stop!"); } }
  15. Section 03 Transaction • 크기 • 방향 • Lock •

    격리수준 • @Retryable • 쿼리 튜닝 SELECT o.order_id, o.order_date, c.customer_name, p.product_name, od.quantity, od.unit_price FROM orders o JOIN customers c ON o.customer_id = c.customer_id JOIN order_details od ON o.order_id = od.order_id JOIN products p ON od.product_id = p.product_id WHERE o.order_date BETWEEN '2022-01-01' AND '2022-12-31' AND c.country = 'USA' AND p.category = 'Electronics' ORDER BY o.order_date DESC;
  16. Section 03 DeadLock Issue 개선방안 • 구현 로직 • synchronized

    • 테이블 설계 • 데이터베이스 인덱스 • 크기 • 방향 • Lock • 격리수준 • @Retryable • 쿼리 튜닝 • read/write 서버 분리 • 캐시 서버 • 대기열 • 분산락 Basic Transaction DB Server
  17. Section 03 DB Server • read/write 서버 분리 • 캐시

    서버 • 대기열 • 분산락 read / write read replication
  18. Section 03 DB Server • read/write 서버 분리 • 캐시

    서버 • 대기열 • 분산락 🔐
  19. Redis Section 04 고성능의 오픈 소스 키-값 저장소로서, 메모리 기반

    데이터 저장과 다양한 데이터 구조 및 연산을 제공하는 데이터베이스 시스템
  20. Section 04 1.x 2009 Salvatore Sanfilippo 오픈소스 공개 2015 클러스터링

    강화 Stream 도입 GEO 데이터 타입 지원 모듈 시스템 도입 2010 가상 메모리 사용 새로운 데이터타입 추가 (Set, Sorted Set, BitMap, HyperLogLog 등) 클러스터터링 기본 구조 7.x 2.x 3.x 4.x 5.x 6.x 2017 RDB 스냅샷 지속성 업데이트 메모리 관리, 프로토콜 성능 향상 CLI 도입 RESP3 프로토콜 도입 2022 Client Side Caching Redis Functions ACLv2 명령 검사 및 Sharded Pub/Sub 2019 Bitfield 타입 추가 멀티 스레드 기능 추가 클러스터링 관련 기능과 안정성 개선 모듈 시스템 관련 기능과 안정성 개선 2020 Memory Persistence 개선 RDB 스냅샷과 AOF 로그 혼합 방식의 지속성 구현 레디스 랩(Redis Raft) 프로젝트 공개로 분산 로깅을 위한 기능 제공 ACL
  21. Key-value Sorted set NoSQL Stream 메모리 파편화 Failover Cluster In-Memory

    Data Collections 모니터링 Sentinel Replication RSS Jemalloc Redis
  22. Key-value Sorted set NoSQL Stream AOF RDB 메모리 파편화 Failover

    Cluster In-Memory Data Collections Persistence 모니터링 Sentinel Replication RSS Jemalloc Redis
  23. Key-value Sorted set NoSQL Stream AOF RDB 메모리 파편화 Failover

    Cluster In-Memory Data Collections Persistence 모니터링 Sentinel Replication TTL Cache 분산 시스템 RSS Jemalloc Redis
  24. Key-value Sorted set NoSQL Stream AOF RDB 메모리 파편화 Failover

    Cluster In-Memory Data Collections Persistence 모니터링 Sentinel Replication TTL Cache 분산 시스템 RSS Jemalloc Redis 분산 락 Redis Queue 메시지 큐 / 작업 큐 ACL Lua Scripting
  25. Section 05 Sorted Sets Tester 1 04:43 Tester 2 07:53

    Tester 3 11:10 Tester 4 15:45 Tester 5 19:51 Tester 1 651 Tester 2 Tester 3 Tester 4 Tester 5 680 765 789 912 value score 0 1 2 3 4 rank
  26. Section 05 Zip List zlbytes zltail zllen Entry 1 Entry

    2 Entry 3 zlend Entry 127 · · · 4byte 2byte 2byte 1byte 128개 미만 & 64byte 미만 Tester 680
  27. Section 05 Skip List typedef struct zskiplistNode { sds ele;

    double score; struct zskiplistNode *backward; struct zskiplistLevel { struct zskiplistNode *forward; unsigned long span; } level[]; } zskiplistNode; typedef struct zskiplist { struct zskiplistNode *header, *tail; unsigned long length; int level; } zskiplist; typedef struct zset { dict *dict; zskiplist *zsl; } zset;
  28. Section 05 Skip List *header *tail length(5) level(4) ele score

    *backward *forward span(1) *forward span(1) *forward span(2) *forward span(4) ele score *backward *forward span(1) *forward span(1) ele score *backward *forward span(1) *forward span(2) *forward span(2) ele score *backward *forward span(1) ele score *backward *forward span *forward span *forward span *forward span zskiplist zskiplistNode zskiplistNode zskiplistNode zskiplistNode zskiplistNode 0 1 2 3
  29. Section 05 Skip List *header *tail length(5) level(4) ele score

    *backward *forward span(1) *forward span(1) *forward span(2) *forward span(4) ele score *backward *forward span(1) *forward span(1) ele score *backward *forward span(1) *forward span(2) *forward span(2) ele score *backward *forward span(1) ele score *backward *forward span *forward span *forward span *forward span zskiplist zskiplistNode zskiplistNode zskiplistNode zskiplistNode zskiplistNode 0 1 2 3
  30. Section 05 Skip List *header *tail length(5) level(4) ele score

    *backward *forward span(1) *forward span(1) *forward span(2) *forward span(4) ele score *backward *forward span(1) *forward span(1) ele score *backward *forward span(1) *forward span(2) *forward span(2) ele score *backward *forward span(1) ele score *backward *forward span *forward span *forward span *forward span zskiplist zskiplistNode zskiplistNode zskiplistNode zskiplistNode zskiplistNode 0 1 2 3
  31. Section 05 Zip List vs Skip List zlbytes zltail zllen

    Entry 1 Entry 2 Entry 3 zlend Entry 127 · · · *header *tail length(5) level(4) ele score *backward *forward span(1) *forward span(1) *forward span(2) *forward span(4) ele score *backward *forward span(1) *forward span(1) ele score *backward *forward span(1) *forward span(2) *forward span(2) ele score *backward *forward span(1) ele score *backward *forward span *forward span *forward span *forward span
  32. Redis Queue - Sorted Set public void addQueue(Apply apply){ String

    people = Thread.currentThread().getName(); long now = System.currentTimeMillis(); redisTemplate.opsForZSet().add(apply.getEventUuid(), people, (int) now); } public void publish(String event){ final long start = FIRST; final long end = SIZE - LAST; Set<Object> queue = redisTemplate.opsForZSet().range(event, start, end); for (Object people : queue) { if(applyRepository.count()==100) Break; Apply entity = new Apply(); entity.setEventUuid(event); entity.setUserUuid((String)str); applyRepository.save(entity); redisTemplate.opsForZSet().remove(event, people); } }
  33. Redis Distribution Lock Section 06 Redis 데이터베이스를 기반으로 여러 개의

    서버 또는 클라이언트 간에 동시에 접근하는 리소스를 보호하기 위한 분산 환경용 동시성 제어 메커니즘
  34. Redis Lettuce Spin Lock public boolean acquireLettuceSpinLock(String lockKey, long timeout,

    long startTime) throws InterruptedException { boolean locked = redisTemplate.opsForValue() .setIfAbsent(lockKey, "locked", timeout, TimeUnit.MILLISECONDS); while (!locked && System.currentTimeMillis() - startTime < timeout) { Thread.sleep(50); } if (!locked && System.currentTimeMillis() - startTime >= timeout) { return false; } return locked; } public void releaseLock(String lockKey) { redisTemplate.delete(lockKey); }
  35. Redis Redisson Spin Lock - RedissonLock.java redisson.getSpinLock(lockKey).tryLock(timeout, timeUnit); /** *

    Returns Spin lock instance by name. * <p> * Implements a <b>non-fair</b> locking so doesn't guarantees an acquire order by threads. * <p> * Lock doesn't use a pub/sub mechanism * * @param name - name of object * @return Lock object */ RLock getSpinLock(String name);
  36. Redis Redisson Distribution Lock - RedissonLock.java redisson.getLock(lockKey).tryLock(timeout, timeUnit); <T> RFuture<T>

    tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) { return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command, "if (redis.call('exists', KEYS[1]) == 0) then " + "redis.call('hincrby', KEYS[1], ARGV[2], 1); " + "redis.call('pexpire', KEYS[1], ARGV[1]); " + "return nil; " + "end; " + "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " + "redis.call('hincrby', KEYS[1], ARGV[2], 1); " + "redis.call('pexpire', KEYS[1], ARGV[1]); " + "return nil; " + "end; " + "return redis.call('pttl', KEYS[1]);", Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId)); }
  37. Base Condition Test Condition /api/apply { "userUuid":"070bbd9f-58c4-4291-8680-0b15e526b3c2", "eventUuid":"75e764ae-cc22-4318-be42-ad5897d937a0" } {

    "6180cd00-a346-458e-8f56-5ae1912c2113" } Request Response { "name":"gdg i/o extended", "period":2, "limitPerson":100 } 등록된 이벤트
  38. Section 07 Jmeter - 5,000명의 동시 접속자 Number of Treads

    (users) : 5,000 Ramp-up period (sec) : 1 Loop Count : 1
  39. HttpHostConnectException, RedisTimeoutException org.apache.http.conn.HttpHostConnectException: Connect to localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection

    refused: connect at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClien tConnectionOperator.java:156) at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl$JMeterDefaultHttpClientConnectionO perator.connect(HTTPHC4Impl.java:409) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClient ConnectionManager.java:376) at org.redisson.client.RedisTimeoutException: Command execution timeout for command: (PING), params: [], Redis client: [addr=redis://localhost:6379] at org.redisson.client.RedisConnection.lambda$async$0(RedisConnection.java:244) ~[redisson-3.18.0.jar:3.18.0] at io.netty.util.HashedWheelTimer$HashedWheelTimeout.run(HashedWheelTimer.java:715) ~[netty-common-4.1.94.Final.jar:4.1.94.Final] at io.netty.util.concurrent.ImmediateExecutor.execute(ImmediateExecutor.java:34) ~[netty-common-4.1.94.Final.jar:4.1.94.Final]
  40. Section 07 Distribution Lock의 한계 해결방안 • 단일장애 지점 관리

    • 메모리 모니터링 • 로그 레벨 & 알림 설정 • Timeout 설정 • AWS 키네시스와 같은 이벤트 브로커