Community

ERD 설계를 하면서 고민한

블로그 주소 : https://blog.naver.com/gomets_journey/223424921992 복합키보단 단일키를 사용하자 Comment 테이블은 자체적인 생성한 값을 pk로 잡는 설계를 하였다. 하지만 위 설계는 Review 테이블의 review_id(pk), Member 테이블의 member _id(pk)를 전부 KEY로 잡는 설계도 가능하다. 하지만 이렇게 설계를 하였을 때는 다음과 같은 문제점이 존재한다. 1. 변경의 유연성 및 확장성 감소 * Review 테이블의 review_id와 Member 테이블의 member_id를 각각의 키로 사용하는 방식을 선택하면, 이들의 구조가 변경될 때 전체 시스템에 영향을 미치게 된다. 즉, 이러한 변경은 시스템의 유연성과 확장성을 제한할 수 있다. 새로운 요구 사항이나 데이터 모델의 변화가 있을 때 전반적인 코드를 수정해야 할 수 있다. 2. Primary Key 변경에 따른 영향 * 만약 Review 테이블의 review_id나 Member 테이블의 member_id가 변경되어야 한다면, 이러한 변경은 전반적인 코드와 데이터베이스 스키마에 영향을 줄 수 있다. 특히 이러한 변경은 다른 테이블에서 이러한 키를 외래 키로 사용하는 경우에는 특히 민감할 수 있다. 3. 인덱스를 타지 않는 경우가 발생한다. SELECT * FROM COMMENT WHERE member_id=200 * 예를 들어, Comment 테이블에서 200번 회원이 작성한 댓글들을 가져오고자 하면 다음과 같은 SQL문이 필요할 것이다. 하지만 Comment 테이블의 키는 member_id, comment_id이다. 따라서 member_id만 조회하는 위의 쿼리는 인덱스를 타지 않게 된다. 인덱스가 없으면 데이터베이스가 테이블을 순차적으로 스캔해야 하므로 성능이 저하될 수 있다. 따라서 이러한 경우에는 별도로 member_id에 대한 인덱스를 추가로 생성해야하는 번거로움이 생긴다. 북마크 기능 설계 비교 카페 북마크는 on/off가 가능하다. 따라서 어떤 사용자가 어느 카페 번호를 북마크했는지에 대한 정보를 담고 있어야 한다. 이를 북마크 로그성 테이블로 설계하였고, 북마크 기능이 on/off 될 때마다 이 테이블에 insert/delete를 반복하도록 구현하였다. 카페를 찜하는 것이므로 카페 테이블에 들어가는 것이 맞다고 생각할 수도 있다. 하지만 그럴경우 어느 사용자가 북마크했는지에 대한 정보까지 카페 테이블에 들어가게 되어 불필요한 데이터가 저장되게 된다. cafe1을 member1, member2가 북마크한 경우 로그성 테이블에는 (cafe1, member1), (cafe1, member2)만 저장하면 되지만 카페 테이블에 들어가면 (cafe1, member_id, nickname, ... , created_date, modified_date), (cafe1, member_id, nickname, ... , created_date, modified_date)로 저장되게 되어 cafe1부터 modified_date까지 중복된 데이터가 2번이나 저장되게 된다. 위에서 알아본 것처럼 북마크 on/off 기능에서는 로그성 테이블의 설계가 효율적이다. 하지만 모든 카페들의 총 북마크 개수를 select하는 경우에 단점이 존재한다. 북마크의 경우 로그성 테이블에 몇몇 카페가 존재하지 않는다고 해서 카페를 누락시켜버리면 안된다. 즉, inner join이 아니라 outer join을 사용해야 한다. 카페 수와 로그성 테이블의 데이터 수가 적을 경우에는 큰 문제가 생기지 않을 수 있지만, 카페 수가 많아질 경우 문제가 생긴다. 예를 들어 카페 수가 천 개, 로그성 테이블에 10만개의 데이터가 있다고 생각해보자. 이 상황에서 outer join을 사용하게 되면 1,000 * 100,000 = 100,000,000번의 연산이 발생하게 된다. 또한 여기서 group by 연산이 이루어진다면 그 만큼 더욱 쿼리의 연산 속도는 느려지게 된다. 위 처럼 수정할 수 있을 것이다. 위와 같이 카페 테이블에 북마크 총 수를 두는 방법이다. 북마크 총 수를 카페 테이블에 같이 두게 된다면 select 쿼리를 실행 하였을 때, outer join과 group by 연산을 수행할 필요없이 한 테이블내에서 조회가 가능해진다. 하지만 이 역시 단점이 존재한다. 이 방법으로 테이블을 설계하면 유저가 북마크를 클릭하였을 때 로그성 테이블에 insert 하는 것과 카페 테이블에 북마크 총 수를 +1 하는 것이 하나의 트랜잭션이 된다. 하지만 동시에 다수으 ㅣ유저가 북카르를 클릭한다면 트랜잭션 밀림 현상이 발생하게 될 수 있다. 즉, transaction isolation 때문에 lock이 거리게 되어 유저의 대기시간이 길어지게 되는 것이다. 이 방법을 해결하기 위해 다음과 같은 방법들을 고려할 수 있다. 1. 분산 트랜잭션 관리 : 북마크 총 수를 업데이트 하는 직업과 북마크 로그를 삽입하는 작업을 별도의 트랜잭션으로 처리하고, 이를 분산 트랜잭션으로 관리하여 트랜잭션 밀림을 최소화한다. 2. 비동기 처리 : 북마크 총 수를 업데이트하는 작업을 비동기적으로 처리하여 사용자에게 즉시 응답을 반환한 후에 백그라운드에서 처리한다. 이를 통해 사용자 경험을 향상시키고 트랜잭션 밀림 문제를 완화할 수 있다. 3. 분산 락 사용 : 북마크 총 수를 업데이트하는 작업에 대한 분산 락을 사용하여 동시성을 제어하고 트랜잭션 밀림 문제를 완화할 수 있다. 4. 캐시 사용 : 북마크 총 수를 빈번하게 업데이트하는 대신, 캐시를 사용하여 일정 시간동안 북마크 총 수를 메모리에 유지하고 주기적으로 디스크에 업데이트한다. 이를 통해 트랜잭션 밀림 문제를 완화할 수 있다. 여기서 4번 캐시 사용의 경우 레디스(Redis)와 같은 인메모리 데이터베이스를 사용하여 북마크 총 수를 캐싱하는 것이 트랜잭션 밀림 문제를 완화하고 성능을 향상시키는 효과적인 방법 중 하나이다. 레디스는 메모리 내에 데이터를 저장하고 조회하기 때문에 데이터베이스에 비해 훨씬 빠르게 응답할 수 있다. 레디스를 사용하여 북마크 총 수를 캐싱하는 과정은 다음과 같다. 1. 북마크 총 수를 레디스에 저장한다. 2. 사용자가 북마크를 추가하거나 삭제할 때마다 레디스에 저장된 북마크 총 수를 증가 또는 감소시킨다. 3. 북마크 조회 요청이 들어올 때, 먼저 레디스에 해당 정보가 있는지 확인하고 있다면 캐시된 값을 반환한다. 만약 레디스에 해당 정보가 없다면 데이터베이스에서 값을 조회하고 캐시에 저장한 후에 반환한다. 4. 주기적으로 레디스에 저장된 북마크 총 수를 데이터베이스에 업데이트하여 캐시를 유지하고 데이터의 일관성을 유지한다. 이러한 방식으로 레디스를 사용하면 데이터베이스에 대한 요청을 줄이고 응답 시간을 단축하여 트랜잭션 밀림 문제를 완화할 수 있다. 또한, 레디스의 내구성 옵션을 활용하여 데이터의 손실을 방지할 수 있다.

알림

알림이 없습니다