페이지네이션
많은 양의 데이터를 어떻게 노출할 것인가?
다음 페이지(게시판 번호) vs 스크롤
페이지네이션 구현 - 오프셋 기반
✅ 3번,4번 데이터 찾아가는동안 DB는 1번,2번 데이터 스캔
✅ 마지막 페이지를 구하기 위해 전체 갯수 알아야함
sort
properties: 필드 (id정렬, 이름정렬)
direction : 오름차순, 내림차순 정렬
오프셋 기반 페이징 구현의 문제
마지막 페이지를 구하기 위해 전체 갯수를 알아야함 => 부하 발생 가능
반환하는 데이터 2개를 읽기위해 4개의 데이터를 읽고 버림
커서 기반 페이징
키를 기준으로 데이터 탐색범위를 최소화
전체 데이터를 조회하지않기때문에 게시판 조회 UI 구현이 어려움
중복 데이터 여부
오프셋 기반 페이징: 1페이지 보고있다가 2페이지로 넘어갈때 데이터가 많아지면 offset이 밀리면서 1번의 데이터를 2번에서도 볼 수 있다.
커서 기반 : 마지막으로 받은 키 고정, 최신 데이터 위로 올림(내림차순) => 같은 데이터를 두번 이상 볼 케이스가 거의 없다.
커버링 인덱스
검색 조건이 인덱스에 부합하다면, 테이블 바로 접근보다는 인덱스를 통해 접근하는것이 매우 빠르다.
=> 테이블에 접근하지않고 인덱스로만 데이터 응답을 내려줄수는 없을까?
✅ MYSQL에서는 PK가 클러스터 인덱스이므로 커버링 인덱스에 유리하다.
(인덱스로만 커버하겠다 : 훨씬 빠름)
✅ order by, offset, limit절로 인한 불필요한 데이터 블록 접근을 커버링 인덱스를 통해 최소화
타임라인 최적화
타임라인이란?
트위터, 페이스북, 인스타그램 등 SNS에서 팔로워들의 게시물을 보여주는 피드
요구사항
회원ID를 받아 해당 회원의 팔로워들의 게시물을 시간순으로 조회
타임라인 (Fan Out On Read (Pull Model)) : 조회시점에 부하
usecase에 만든다(게시물, 회원-팔로우 정보)
1. 회원의 팔로우 목록 조회
2. 1번의 팔로우 회원 id로 게시물 조회
서비스가 커질수록 느려지는 타임라인
타임라인 시간복잡도
log(Follow 전체 레코드) + 해당회원의 Following * log(Post 전체 레코드)
팬아웃 타임라인 이론 - Fan Out On Write (PushModel) : 작성시점에 부하
게시물 작성시, 해당 회원을 팔로우 하는 회원들에게 데이터를 배달한다.
✅ 타임라인 조회시에는 Timeline 테이블을 조회하여 게시물을 조회(배달이 온 것만 조회)
✅ Pull 모델에서의 조회시점의 부하를 쓰기시점의 부하를 치환
정합성과 성능의 트레이드 오프
Pull Model
- 시간복잡도를 희생(Follow 많을수록 더 높은 시간) - 원본 데이터를 직접 참조하므로 정합성 보장에 유리
- 페이스북(최대 5000명의 친구)
Push Model
- 공간복잡도를 희생(timeline 추가공간)
- 트위터(5000명이 최대, 나를 팔로우를 하는 사람이 늘어나면 추가로 계정 팔로우 가능)
- Push Model은 Pull Model에 비해 시스템 복잡도가 높다.
- ✅ 게시물 작성과 타임라인 배달의 정합성 보장에 대한 고민이 필요하다.
- Push Model은 Pull Model에 비해 시스템 복잡도가 높다. 하지만 그만큼 비즈니스, 기술 측면에서 유연성을 확보시켜준다.
모든 회원의 타임라인에 배달되기 전까지 게시물 작성의 트랜잭션을 유지하는 것이 맞을까?
CAP 이론
시스템은 일관성, 가용성, 분단 허용성 세가지 속성중에서 두가지만 가질 수 있다.
출처: https://dongwooklee96.github.io/post/2021/03/26/cap-%EC%9D%B4%EB%A1%A0%EC%9D%B4%EB%9E%80/
Push Model은 Pull Model에 비해 시스템 복잡도가 높다.
하지만 그만큼 비즈니스, 기술 측면에서 유연성을 확보시켜준다.
트랜잭션
트랜잭션이 필요한 이유
여러 SQL문을 마치 하나의 오퍼레이션으로 묶을 수 있어야한다.
트랜잭션 격리레벨
처리중인 데이터를 다른 곳에서 조회하게 되면 문제 발생
A, C ,I ,D
어떤 이유에서 나왔고 어떤 기술들이 ACID를 가능하게 하는지 이해하기
A
트랜잭션이 Atomicity한 단위가 된다.
중간에 연산이 수행되다가 멈추지 않는다.
=> MVCC를 통해 제공
C
트랜잭션 실행 완료 후에는 언제나 일관성있는 데이터베이스 상태 유지
제약조건을 통해 보장(유니크 제약, 외래키 제약)
I
많은 성능(높은 동시성 등..)을 포기해야하므로 개발자가 제어 가능
트랜잭션 격리레벨을 통해 제약 정할 수 있음 => MVCC를 통해
각각의 데이터베이스마다 조금씩 다르다.
D
WAL을 통해 제공
JDBC Template으로 트랜잭션 제어 방법
1. 트랜잭션 어노테이션 사용 - 선언적 어노테이션 @Transactional : 대부분 선호
2. 트랜잭션 템플릿 이용
@Transactional
proxy로 동작하므로 inner함수에서는 제대로 동작하지 않을 수 있다.
propagation(전파 레벨) : 트랜잭션 안의 트랜잭션들이 어떻게 동작하는지 방법 정의
자세한 내용은 : https://n1tjrgns.tistory.com/266
트랜잭션 범위
트랜잭션 범위는 짧게 가져가는 것이 좋다.
트랜잭션이 길어지면 데이터베이스 커넥션을 오랫동안 점유하게 되어 동시다발로 발생했을때 트랜잭션 pull 고갈 발생 가능
트랜잭션 격리레벨
ISOLATION : 트랜잭션은 서로 간섭하지않고 독립적으로 동작한다.
선택할 수 있는 격리레벨 종류
- READ UNCOMMITTED - 거의 안씀
- READ COMMITTED (커밋된 데이터 읽음)
- REPEATABLE READ (커밋된 데이터 읽음 + 트랜잭션마다 id부여, 항상 동일한 읽기 가능, 데드락 이슈 자주 발생)
- SERIALIZABLE READ (팬텀 Read 발생 X) - 거의 안씀
✅ 아래로 갈수록 이상현상이 없지만 동시 처리량이 낮다.
보통 read committed나 repeable read 사용, 데드락 이슈로 인해 read committed를 repeable read보다 더 자주 쓴다.
위 4가지는 아래 3가지 현상을 허용/격리하는지에 따라 구분 가능
- Dirty Read : 커밋되지 않은 데이터를 읽음
2. Non Repeatable Read : 같은 결과를 조회했지만 결과가 달라지는 것
3. Phantom READ : 같은 조건으로 데이터 읽었을때 없던 데이터가 생김
동시성 제어하기
멀티 스레드 환경
대부분 하나의 웹서버는 여러개의 요청을 동시수행할 수 있다.
작성 코드 한줄은 동시에 수행될 수 있다.
동시성
하나의 자원을 두고 여러개의 연산들이 경함
=> 데이터 정합성을 깨뜨릴 수 있다.
ex) 하나의 자원 : db의 하나의 레코드, 서버내의 하나의 전역변수..
동시성 이슈 발생 패턴
해결 방법
공유 자원에 대한 잠금을 획득하여 줄 세우기
동시성 이슈가 어려운 이유
쓰기락과 읽기락
동시성 제어를 위한 가장 보편적인 방법은 락을 통한 줄세우기(비관적 락 : 락이 발생할 것이라 예상하고 미리 줄을 세움)
MYSQL에서는 쓰기락과 읽기락을 제공한다.
쓰기락 : SELECT .... FOR SHARE
읽기락 : SELECT .... FOR UPDATE 또는 UPDATE, DELETE 쿼리
✅ 매번 잠금이 발생할 경우, 성능저하가 발생한다.
(MySQL에서 일반 SELECT는 nonblocking consistent read(대기없는 read)로 동작)
✅ 락을 통해 동시성을 제어할 경우에는 락의 범위를 최소화하는 것이 중요하다.
: 락의 범위가 길어질수록 connection pool 점유시간 증가 => connection pool 고갈 가능
MYSQL에서의 락의 범위
MYSQL에서는 트랜잭션의 커밋 혹은 롤백시점에 잠금이 풀린다.
=> 트랜잭션이 곧 락의 범위
✅ 락의 범위에 따라 여러 종류의 락이 존재한다. 테이블 락, 레코드락, gap락..
레코드 락
MySQL에서 잠금은 row가 아니라 인덱스를 잠근다.
=> 인덱스가 없는 조건으로 Locking Read시 불필요한 데이터들이 잠길 수 있다.
적절한 인덱스를 타지 않으면 락의 범위가 불필요하게 커진다.
락 확인하기
추가로 공부하면 좋은 것
비관적 락 (지금까지 설명한 락)
락을 통한 줄세우기(비관적 락 : 락이 발생할 것이라 예상하고 미리 줄을 세움)
☑️ 락을 통한 동시성 제어는 불필요한 대기상태를 만듬
☑️ 동시성이 빈번하지 않은 쿼리로 인해 다른 쿼리가 대기한다면?
낙관적 락
낙관적 락이란?
=> 동시성 이슈가 빈번하지 않길 기대하고, 어플리케이션에서 CAS(Compare and Set)를 통해 제어한다.
✅ 버전 필드 추가하여 버전을 통해 비교한다.
✅ 실패에 대한 처리를 직접 구현해야한다.
✅ JPA에서는 Optimistic Locking @Version 어노테이션 제공
강의를 끝내고 공부하면 좋은 것
'Spring > 카테캠 - TIL' 카테고리의 다른 글
TIL [0626] - 클론 코딩 1주차 (0) | 2023.06.26 |
---|---|
TIL [0612-0618] : 스프링 어노테이션 정리 (0) | 2023.06.18 |
TIL [0531 - 0604] : 대용량 처리를 위한 MySQL 이해 (0) | 2023.06.03 |
TIL [0526-0528] Spring MVC 4 (5) | 2023.05.28 |
TIL [0521] Spring MVC 3 (0) | 2023.05.21 |