ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 쓰기에 시간을 쓸 것인가? 읽기에 시간을 쓸 것인가?
    운영 중인 서비스/Coconut. 2024. 3. 21. 15:15

    (소스코드: https://github.com/blog-example/-DB-Write-vs-Read)

     

    이번에 서비스 배포하면서 전 회사에서 같이 일하던 선배 개발자분들에게 부탁해서 피드백을 받아보았습니다.

    여러가지 피드백이 있었지만 이글에서는 게시글과 댓글의 ERD를 보고 주신 의견을 확인해보고자 글을 작성해봅니다.

     


     

    글은 크게 5가지 항목으로 진행됩니다.

     

    1. 실험의 배경

    2. 테스트 세팅

    3. 게시글 조회 성능 비교

    4. 댓글 생성 성능 비교

    5. 결론

     

     

    1. 실험의 배경

    1-1. 구현해야하는 요구사항

    서비스는 기본적인 게시글과 댓글을 보여주는 UI를 가지고 있습니다.

    구현해야하는 요구사항은 '게시글 목록에서 각 게시글에 달린 댓글의 숫자를 보여준다.' 였습니다.

    이번 피드백에서 언급이 된 부분은 게시글 조회시에 각 게시글에 달린 댓글의 개수를 어떻게 DB에서 가지고 오고 있는가 였습니다.

     

     

     

    1-2. 지금의 설계와 구현

    테이블 설계를 ERD를 통해서 살펴보겠습니다. 게시글과 댓글이 1:N의 관계로 연관관계를 가지고 있습니다. 

     

    그리고 게시글 목록을 불러오는 시점에 개별 게시글의 댓글의 숫자도 함께 조회합니다.

    '댓글은 상태가 active이거나, inactive 이면서 하위 댓글이 있는 댓글만 조회되어야한다' 조건이 있습니다.

     

    길고 긴 쿼리를 작성하면서 '이렇게 작성하는 것이 맞는가?' 라는 의구심이 들어서 이번 피드백을 요청했었습니다.

    '피드백으로 데이터 많이 넣고 작동시켜 본 적있느냐'는 이야기를 들어서 실험을 직접 해보려고 합니다.

     

     

    2. 테스트 세팅

    프로젝트에는 다양한 의존성이 포함되어있기 때문에, 테스트를 위해 좀 더 간결한 테스트 환경을 구성하였습니다.

    우선 '게시글 목록을 조회할 때 개별 게시글의 댓글의 개수를 확인할 수 있어야한다'는 요구사항을 만족시키는 2가지 경우의 테이블 설계를 합니다. 그리고 각 설계의 성능 차이를 비교해보겠습니다.

     

    2-1. ERD

    게시글과 댓글로 테이블을 구성했습니다. 게시글: 댓글 은 1:N의 관계입니다.

    두 설계의 차이는 게시글 테이블에 댓글의 개수를 필드로 가지고 있는지 여부입니다.

    댓글의 개수를 필드로 가지고 있는 경우를 Read-Performanced 반대의 경우를 Write-Performanced라고 부르도록하겠습니다.

     

     

     

    3. 게시글 조회 성능 비교

    아파치 벤치 마크를 사용해서 요청을 밀어넣고 동시성을 고려한 Time per request 지표를 비교해보겠습니다.

    클라이언트로부터 동일한 요청이 들어올 때 데이터베이스의 데이터 양에 따라 실행속도가 어떻게 변하는지 확인해보겠습니다.

     

    # 쓰기 우선
    # ab -n 1000 -c 100 http://localhost:8080/w/posts
    
    # 읽기 우선
    # ab -n 1000 -c 100 http://localhost:8080/r/posts

     

    3-1. 게시글 조회 구현 비교

    우선 write-performanced의 게시글 목록 조회 쿼리입니다.

    댓글 개수를 조회하기 위해서 COUNT와 GROUP BY 를 사용하고 있습니다.

     

     

    다음은 read-performanced의 게시글 목록 조회 쿼리입니다.

    commentCount라는 필드를 가지고 있기 때문에 별도의 join이나 count 연산을 하지 않습니다.

     

     

     

    3-2. 게시글 조회 실행 비교

    이제 각각을 실행시켜서 실행속도를 비교해보겠습니다.

     

     

    3-2-1. 댓글 테이블의 row 10,000개

     

    댓글 테이블에 1만개의 데이터를 추가한 상태로 시작해보겠습니다.

    각 경우를 동일한 방법으로 5회씩 호출한 후 Time per request를 집계해보았습니다.

    write-perf의 1차가 조금 높게 나온 것을 제외하면 평균적으로 2배가 조금 안되는 정도의 성능 차이를 보여줍니다.

    목록 조회시 read-performanced가 약 2배 빠른 것을 확인할 수 있습니다.

     

    3-2-2. 댓글 테이블의 row 1,000,000개

     

    이번에는 댓글 테이블에 10만개의 데이터를 추가한 상태로 시작해보겠습니다.

    실행 방법은 이전과 동일합니다.

    이번에는 read-perf의 1차가 조금 평균에서 벗어나는 것을 제외한다면 평균적으로 5 ~ 6배 정도의 성능 차이를 확인할 수 있습니다.

    목록 조회시 read-performanced가 약 5~6배 정도 빠른 것을 확인할 수 있습니다.

     

    3-3. 게시글 조회 성능 비교 결과

    자체적으로 카운트를 하는 필드를 가지고 있는 read-performanced 구현이 조회시에 최소 2배 이상의 성능을 보여주는 것을 확인할 수 있었습니다.  댓글 테이블의 데이터가 수가 늘어날 수록 두 경우의 조회 성능 차이가 더 크게 벌어지는 것 역시 확인할 수 있었습니다.

     

     

     

    4. 댓글 생성 성능 비교

    조회와 마찬가지로 아파치 벤치 마크를 사용해서 요청을 밀어넣고 동시성을 고려한 Time per request 지표를 비교해보겠습니다.

    클라이언트로부터 동일한 요청이 들어올 때 데이터베이스의 데이터 양에 따라 실행속도가 어떻게 변하는지 확인해보겠습니다.

    // comment.json
    
    {
      "postId": 1,
      "comment": "dummy"
    }
    # 쓰기 우선
    # ab -n 1000 -c 100 -p "$PWD/scripts/comment.json" -T application/json http://localhost:8080/w/comment
    
    
    # 읽기 우선
    # ab -n 1000 -c 100 -p "$PWD/scripts/comment.json" -T application/json http://localhost:8080/r/comment

     

     

    4-1. 댓글 생성 구현 비교

    그렇다면 이번에는 쓰기에 해당하는 댓글 생성 케이스를 살펴보겠습니다.

    우선 write-performanced의 댓글 생성 구현을 살펴보겠습니다. 특별할 것 없이 연관관계를 가지고 있는 게시글을 조회해서 가져온 뒤, 댓글 엔티티를 생성해서 save하고 있습니다.

     

    이번에는 read-performanced의 댓글 생성 구현을 살펴보겠습니다.

    기본적으로 게시글을 조회 후, 댓글을 생성해서 저장하는 것은 동일하나 게시글의 count를 올려주는 로직이 추가되었습니다.

     

    게시글을 수정할 때 동시성 이슈가 발생할 수 있기 때문에 비관적 락을 사용해서 다른 트랙잭션에서는 수정을 못하도록 설정해주었습니다.

    (댓글 개수는 오차가 조금은 발생해도 되는 데이터라고 생각하여 PESSIMISTIC_READ를 설정하였습니다)

     

     

    4-2. 댓글 생성 실행 비교

    이제 각각을 실행시켜서 실행속도를 비교해보겠습니다.

     

     

    4-2-1. 댓글 테이블의 row 10,000개

     

    댓글 테이블에 1만개의 데이터를 추가한 상태로 시작해보겠습니다.

    각 경우를 동일한 방법으로 5회씩 호출한 후 Time per request를 집계해보았습니다.

    write-performanced가 평균적으로 4 ~ 5배 가량 빠른 것을 확인할 수 있습니다.

     

    4-2-2. 댓글 테이블의 row 1,000,000개

     

    이번에는 댓글 테이블에 데이터를 10만개를 추가한 상태로 시작해보겠습니다.

    쓰기의 경우에는 데이터 크기에 영향을 받지 않는 것으로 보여집니다.

    10,000개일 때와 동일하게 4 ~5배 가량 write-performanced가 빠른 것을 확인할 수 있습니다.

     

    4-3. 댓글 생성 성능 비교 결과

    read-performanced의 경우, 조회에서 이점을 가져다 주는 count 필드가 생성시에는 성능을 저하시키는 요인으로 작용하여서 평균적으로 4 ~ 5배 가량의 성능차이를 발생시켰습니다.

     

     

    5. 결론

    두 가지 설계가 각각 장단을 가지고 있음을 확인할 수 있었습니다. 유저의 사용패턴을 고려해서 읽기가 더 중요한 서비스라면 read-performance와 같은 방향의 설계를 고려할 수 있을 것이고, 반대로 쓰기가 더 빈번한 서비스라면 write-performanced의 방향에서 데이터베이스 테이블 설계를 가져가는 것이 좋을 것 같습니다!

     

     

     

     

    감사합니다.

     

     

     

     

     

     

     

Designed by Tistory.