2022-09-25-TIL
2022-09-25-TIL
Today I Learned
Redis failover
Redis의 Master-Replica 복제만으로는 마스터가 죽었을 때 누군가 레플리카를 새 마스터로 올려 줘야 한다. 이를 자동으로 해 주는 것이 Redis Sentinel이다. Sentinel은 마스터와 레플리카를 감시하고, 문제가 생기면 알리며, 마스터 장애 시 레플리카 하나를 마스터로 승격하고 나머지 레플리카와 클라이언트가 새 마스터를 보도록 재구성한다. 클라이언트는 마스터 주소를 직접 갖지 않고 Sentinel에게 현재 마스터를 물어본다.
공식 문서가 강조하는 점은 세 가지다(Redis: High availability with Redis Sentinel).
- 견고하게 운영하려면 Sentinel을 서로 다른 장비에 최소 세 개 둔다.
- quorum은 장애를 “감지”하는 데 필요한 Sentinel 수일 뿐이다. 실제 failover를 수행하려면 Sentinel 과반의 투표로 리더가 선출되어야 한다.
Redis 복제는 비동기라서, 클라이언트가 쓰기 성공 응답을 받았더라도 failover 과정에서 그 쓰기가 사라질 수 있다.
- https://velog.io/@limsubin/Redis-Master-Slave%EB%A5%BC-%EC%A0%81%EC%9A%A9-%EB%B0%8F-failover%EB%A5%BC-%ED%99%95%EC%9D%B8%ED%95%98%EC%9E%90
Spring Data JPA save
SimpleJpaRepository.save()는 엔티티가 새것인지 판단해서, 새것이면 persist(), 아니면 merge()를 호출한다. 판단 기준은 다음 순서다(Spring Data JPA: Persisting Entities).
- 기본형이 아닌
@Version필드가 있으면, 그 값이null일 때 새 엔티티로 본다. - 버전 필드가 없으면 식별자를 보고,
null이면 새 엔티티로 본다. - 엔티티가
Persistable을 구현하면isNew()의 결과를 따른다.
문제는 식별자를 직접 할당하는 엔티티다. @GeneratedValue 없이 ID를 넣고 save()를 호출하면 식별자가 null이 아니므로 새 엔티티가 아니라고 판단해 merge()가 호출된다. merge()는 먼저 SELECT로 기존 행을 찾고, 없으면 INSERT한다. 그래서 INSERT 한 번이면 될 일에 SELECT가 하나 더 나간다. 문서가 제시하는 해결책은 Persistable을 구현하고, @PostPersist와 @PostLoad에서 새 엔티티 표시를 끄는 방식이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Entity
public class Product implements Persistable<String> {
@Id
private String id;
@Transient
private boolean isNew = true;
@Override
public boolean isNew() { return isNew; }
@PostPersist
@PostLoad
void markNotNew() { this.isNew = false; }
@Override
public String getId() { return id; }
}
- https://insanelysimple.tistory.com/300
- https://leegicheol.github.io/jpa/jpa-is-new/
- https://jaime-note.tistory.com/65
- https://velog.io/@rainmaker007/spring-data-jpa-save-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC
Apache ShardingSphere
Apache ShardingSphere는 기존 데이터베이스 위에 샤딩, 읽기·쓰기 분리, 분산 트랜잭션 같은 기능을 얹어 분산 데이터베이스처럼 쓰게 해 주는 프로젝트다. 두 가지 형태로 제공된다(ShardingSphere Overview).
- ShardingSphere-JDBC: JDBC 드라이버를 감싸는 자바 라이브러리다. 애플리케이션에 내장되어 별도 배포가 필요 없고 오버헤드가 작지만 자바에서만 쓸 수 있다.
- ShardingSphere-Proxy: MySQL이나 PostgreSQL 프로토콜을 말하는 별도 프록시 서버다. 어떤 언어나 DB 도구에서도 접속할 수 있지만 홉이 하나 늘어 지연이 커진다.
애플리케이션 코드에서 샤드 라우팅을 직접 구현하지 않아도 된다는 것이 장점이고, 샤드 키를 포함하지 않는 쿼리나 샤드를 넘나드는 조인은 여전히 비싸다는 점은 샤딩 자체의 제약으로 남는다.
- https://blog.naver.com/asei/221511101848
Cache
AWS 문서는 캐시를 “데이터의 일부를 임시로 저장해 다음 요청을 더 빨리 처리하는 고속 저장 계층”으로 정의한다. 캐시는 여러 계층에 존재한다. 브라우저(HTTP 캐시 헤더), DNS, CDN과 리버스 프록시, 애플리케이션(Redis, Memcached 같은 키-값 저장소), 데이터베이스(쿼리 결과, 버퍼) 순이다. 어느 계층이든 TTL로 언제 만료시킬지, 원본이 바뀌었을 때 캐시를 어떻게 무효화할지를 함께 정해야 한다(AWS: Caching Overview).
쿠팡 엔지니어링 글은 대용량 트래픽에서 캐시로 성능을 높인 사례를 다룬다. 캐시가 효과를 보려면 같은 데이터를 반복해서 읽는 비율(적중률)이 높아야 하고, 캐시 만료 순간 요청이 한꺼번에 원본으로 몰리는 문제나 캐시 서버 장애 시 원본이 버틸 수 있는지도 설계에서 다뤄야 한다.
- https://aws.amazon.com/ko/caching/
- https://medium.com/coupang-engineering/%EC%BA%90%EC%8B%9C%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EB%8C%80%EC%9A%A9%EB%9F%89-%ED%8A%B8%EB%9E%98%ED%94%BD-%EC%B2%98%EB%A6%AC-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81-a274f4731d07
- https://oingdaddy.tistory.com/384
Spring Cache
Spring의 캐시 추상화는 메서드에 애너테이션을 붙여 캐시를 적용하고, 실제 저장소(EhCache, Caffeine, Redis)는 CacheManager 설정으로 고르는 구조다. @EnableCaching으로 활성화한다(Spring Framework: Declarative Annotation-based Caching).
@Cacheable: 같은 인자로 다시 호출되면 메서드를 실행하지 않고 캐시 값을 돌려준다.@CachePut: 메서드를 항상 실행하고 결과로 캐시를 갱신한다.@CacheEvict: 캐시를 지운다.allEntries = true로 전부 지울 수 있다.
키는 기본적으로 파라미터로 만든다. 파라미터가 없으면 SimpleKey.EMPTY, 하나면 그 값, 여러 개면 모두를 담은 SimpleKey다. 주의할 점은 기본 프록시 모드에서 같은 클래스 안의 메서드 호출(self-invocation)은 프록시를 거치지 않아 캐시가 적용되지 않는다는 것이다. @Transactional과 같은 이유다.
- https://hwannny.tistory.com/53
- https://jaehun2841.github.io/2018/11/07/2018-10-03-spring-ehcache/
- https://jojoldu.tistory.com/57
댓글
아직 댓글이 없습니다