Skip to content

refactor : 보조인덱스 삽입속도 vs 복합키 random IO #11

@seonghooncho

Description

@seonghooncho

보조인덱스 삽입속도 vs 복합키 random IO

좋아요 개수와, member가 좋아요 했는지 여부를 확인하는 쿼리이다.

SELECT pl.post_id,
       COUNT(*) AS like_count,
       MAX(CASE WHEN pl.member_id = ? THEN TRUE ELSE FALSE END) AS amILiking
FROM post_likes pl
WHERE pl.post_id IN (?, ?, ?, ...)
  AND pl.deleted_at IS NULL
GROUP BY pl.post_id

위 쿼리를 수행하는데 가장 최적화 된 테이블 구조를 작성하려고 한다.

추가로 데이터 삽입 시의 성능까지 함께 고려해야한다.

  1. 복합키 전략.
@Table(name = "post_like"
public class PostLike {

    @EmbeddedId
    private PostLikeId id;
    
    @Column(name = "deleted_at")
    protected LocalDateTime deletedAt;

}

public class PostLikeId implements Serializable {

    @Column(name = "post_id")
    private String postId;

    @Column(name = "member_id")
    private Long memberId;
}
  1. 단일 키 + 보조인덱스 전략.

    @Table(
            name = "post_likes",
            indexes = {
                    @Index(name = "idx_post_likes_post_id", columnList = "postId", "memberId", "deleted_at")
            }
    )
    public class PostLike {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "post_id")
        private String postId;
        
        @Column(name = "member_id")
        private Long memberId;
    
        @Column(name = "deleted_at")
        protected LocalDateTime deletedAt;
    
    }

조회 쿼리비교 :

  • 복합키 :
    • 조회 시 버퍼풀 올리고, 클러스터드 인덱스의 postId와 memberId를 검색하여 찾아낸다.
    • 좋아요 개수와 내가 좋아요 했는지 여부는 빠르게 인덱스로 찾아낼 수 있고, 그 내부에서 raw 데이터인 삭제여부를 체킹해야한다. (그래봐야 10번 조금 넘게 비교 수행, 무시가능)
  • 단일키 + 커버링인덱스
    • 쿼리에 포함된 조건이 모두 포함된 커버링인덱스 이므로, 보조인덱스만 버퍼풀에 올려서 작업을 마칠 수 있다.
    • 삭제여부도 인덱스에 포함되어있으므로 바로 구분할 수 있다.

조회 쿼리의 경우, 비슷한 속도로 나올 것으로 추정된다.

그렇다면 삽입 시엔 어떻게 될까.

삽입 쿼리비교 :

  • 복합키 :
    • 현재 postId 가 문자열이기 때문에, 랜덤 IO 가 크게 발생할 수 있다.
    • mongoDB _id의 시작 8자는 16진수 time stamp 값이다.
    • 고로 binary 값으로 변환하여 저장한다면, 랜덤 io를 조금 줄일 수는 있을 것이다.
  • 단일 키 + 커버링인덱스 :
    • 삽입, 수정마다 보조인덱스까지 함께 수정해야하기 때문에 오래 걸린다.
    • 저장된 데이터의 양이 두배가 넘게 뛰는 문제가 있다.
      • 데이터 계산

        • 클러스터드 인덱스 (66B)
          • id : 8B
          • postId : 12B
          • memberId : 8B
          • deletedAt : 8B
          • 기본 구성요소 : 20B~
        • 보조인덱스 (56B)
          • postId… deleted_at + id == 46
          • 기본 구성요소 : 10B~

        100만 mau 이므로 한명당 좋아요 1000회 가정 → 1억건

        56억 B → 5.6기가 정도.

        디스크 입장에선 그리 큰 양은 아닌 것 같다.

        버퍼풀에도 보조인덱스만 올릴 것이니, 오히려 이득이거나 비슷할 것으로 추정된다.

데이터의 양이 더욱 많아질 수록, random IO가 심해질 것이므로, 상대적으로 튜플의 크기는 작고, 개수가 많은 좋아요의 경우 랜덤 IO 를 방지하는 단일키 방식을 사용하겠다.

복합키 케이스에 비해서는 미약한 성능이나, 보조인덱스 삽입시에서도 랜덤 io가 발생할 수 있으므로, postId를 바이너리로 치환 후 저장하겠다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions