Database/SQL 42

SQL - LEFT OUTER JOIN 쿼리 Split 하기!

지난번 커버링 인덱스를 적용한 쿼리를 추가 개선한 이야기이다. 커버링 인덱스 적용 - https://jaehoney.tistory.com/333 지난번 조회 Latency가 너무 커서 커버링인덱스를 조회해서 2~3배, 최대 10배 효율적으로 개선을 할 수 있었다. 문제는 Latency는 큰 문제가 없음에도 TPS가 너무 안나왔다. HTTP 트랜잭션이 0.3s~0.4s 정도인 반면 TPS가 10.8 밖에 안나왔다. 결과를 먼저 소개하자면 LeftOuterJoin 구을 Split해서 TPS를 10.8에서 107.7로 개선했다. 아래는 문제 재현을 위해 임의로 구성한 환경에서 테스트를 진행한 부분이다. 기존 코드 먼저 기존 코드를 보자. @Repository public class ArticleReposito..

Database/SQL 2024.02.27

MySQL - 인덱스를 사용해도 느린 이유! (key_len, filtered)

Optimizer가 원하는 Index를 사용했으면 최적의 쿼리인걸까?! 그렇지 않다! 예시를 통해 알아보자. 아래는 예시를 위해 생성한 Index이다. 아래의 쿼리를 실행시키면 어떻게 될까? explain select * from employees where office_id = 1 and money = 50; key로 custom_index가 선택되었다. 글자가 너무 작아서 중요한 부분을 확대했다. 실행 시간 해당 쿼리의 실행 시간은 548ms 이다. Index도 원하는 대로 탔고, 매우 간단한 데 쿼리임에도 불구하고 매우 오래걸린다. 뭐가 잘못된 걸까..? 1. key_len 다시 실행 계획을 보자. 여기서 가장 중요한 지표는 key_len이다. key_len은 쿼리 조건으로 사용된 인덱스 컬럼들의 ..

Database/SQL 2023.11.27

SQL - WHERE 절, ON 절 제대로 이해하기!

최근에 팀원 분이 LEFT OUTER JOIN의 ON 절에 일반 조건이 포함된 쿼리를 작성하신 것을 봤다. SELECT * FROM team t LEFT OUTER JOIN member m ON t.id = m.team_id AND m.team_id = 4; 해당 부분이 예상대로 동작하지 않을 것을 예상하고 리뷰를 드리면서, 생각보다 잘 모르시는 분이 많으실 것 같아 정리하게 되었다. 미리 말하지만 해당 SQL은 의도대로 동작하지 않는다. ON 절과 WHERE 절의 차이에 대해 알아보자. Sample Data 삽입 테스트를 위해 데이터를 삽입했다. Team을 5개 삽입 각 팀별 멤버를 2개 삽입 WHERE SELECT * FROM team t LEFT OUTER JOIN member m ON t.id =..

Database/SQL 2023.11.21

DB 인덱스에 대한 오해 (컬럼 1개 vs 2개!)

어느 날 신입 분이 나한테 찾아와서 물었다. '메일 조회를 할 때 권한이 없으면 404가 아니라 401 또는 403이 나와야 하는 것 아닌가요?' (인덱스 얘기한대놓고 무슨 권한 얘기인지..) 나도 쌩신입때 선배분께 동일한 질문을 했었다. 인덱스 오해 아래 코드는 비즈니스에서 Mail을 조회하기 위해 사용되던 코드이다. mailRepository.findByUserIdAndId(userId, id) .orElseThrow(() -> new EntityNotFoundException("Mail", id)); 여기서 내가 생각한 문제는 3가지가 있다. 쿼리에 비즈니스 로직이 들어간다. 가독성이 나빠지고 객체지향적인 설계가 불가능해진다. 프로그래밍 초식의 쿼리에서 로직 빼기 부분을 참고하자. 자신의 소유가 아..

Database/SQL 2023.09.16

MySQL - 이모지(Emoji) 뒷 내용이 모두 잘리는 문제 해결!

개발 DB에는 데이터가 잘 저장되는데 실 서버 DB에서 데이터가 잘리는 문제가 발생했다. 확인해보니 문자열의 이모지 뒷 내용이 전부 잘린 것으로 확인해서 DDL을 각 DB의 DDL을 비교해봤다. (MySQL을 사용하고 있다.) 그런데 개발 서버 DDL과 실서버 DDL이 동일했다. 그런데 왜 데이터가 개발 서버에서만 잘렸지..? utf8 vs utf8mb4 개발 서버와 실서버의 컬럼은 모두 charset - utf8을 사용하고 있었다. utf8과 utf8mb4는 어떤 차이가 있을까? utf8 - 내부적으로 한 문자당 가변으로 최대 3바이트를 사용한다. utf8mb4 - 내부적으로 한 문자당 가변으로 최대 4바이트를 사용한다. 여기서 이모지는 4바이트를 차지한다. 즉 utf8mb4로는 표현할 수 있고, ut..

Database/SQL 2023.04.05

MySQL - 락 불필요한 데이터를 잠그는 문제 정리! (+ Index)

락은 DBMS이나 애플리케이션에서 동시성을 제어할 수 있는 방법이다. 해당 포스팅에서는 MySQL의 락에 대해 다룬다. 락 이란? 락을 통해 동시성을 제어할 때는 락의 범위를 최소화하는 것이 중요하다. 락의 범위가 길어지면 대기중인 DB 커넥션이 많아지므로 커넥션 풀 고갈로 이어질 수 있다. MySQL에서는 트랜잭션의 커밋 혹은 롤백시점에 잠금이 풀린다. 즉, 트랜잭션이 곧 락의 범위가 된다. 트랜잭션과 락 예시를 통해 알아보자. 한 트랜잭션 내에서 DB에 Update를 하고 새로운 이미지를 업로드한다고 한다고 가정하자. 트랜잭션과 락은 각각 아래의 역할을 수행한다. 트랜잭션 업로드가 진행되는 동안에도 DB 커넥션을 유지하고 트랜잭션을 지속한다. 업로드가 성공하면 트랜잭션을 커밋한다. 업로드가 실패하면 ..

Database/SQL 2023.02.17

SQL - OneToOne 관계일 때 주의할 점 (+ Unique Key 설정)

SQL에서 1:1 (OneToOne) 관계를 정의할 때 반드시 고려해야 하는 부분이 있다. 예시를 통해 살펴보자. 주어진 상황 아래는 내가 설계한 DB의 일부이다. 계정과 회원을 굳이 분리한 이유는 성격이 다르다고 생각해서이다. 계정은 회원 정보에 속하기 때문에 보안 등급이 더 높고, 다른 테이블에서 Join을 할 때 회원 정보는 필요하지만 계정 정보는 필요하지 않기 때문에 해당과 같이 설계하였다. 계정과 회원은 1:1 (OneToOne) 관계를 맺는다. 이때 중요한 것은 서로에 대한 참조 키에 Unieque key를 걸어줘야 한다는 점이다. Unieque key 위 DB를 기준으로, User 테이블의 account_id 컬럼에 Unique Key를 걸어줘야 비로소 1:1이 된다. Unique key 조..

Database/SQL 2022.09.08

Real MySQL - Replication(복제)란 무엇인가?!

데이터베이스를 사용할 때 가장 중요한 두 가지는 확장성(Scalability)과 가용성(Availability)이다. 서비스에서 발생하는 대용량 트래픽을 안정적으로 처리하기 위해서는 서버의 확장이 필수적이며, 사용자가 언제든지 안정적인 서비스를 이용할 수 있게 하려면 DBMS 서버를 포함한 하위 시스템들의 가용성이 반드시 뒷받침되어야 한다. 이 두 요소를 위해 가장 일반적으로 사용하는 기술이 복제(Replication)이다. 복제 복제(Replication)는 한 서버에서 다른 서버로 데이터가 동기화 되는 것을 말한다. 원본 데이터를 가진 서버를 소스(Source) 서버, 복제된 데이터를 가지는 서버를 레플리카(Replica) 서버라고 부른다. 소스 서버에서 데이터가 변경되면 레플리카 서버에서는 변경 내..

Database/SQL 2022.07.07

MySQL - 비트마스크 컬럼을 사용하지 않아도 되는 이유 (+ SET Type)

MySQL에서 동일한 여러가지 플래그를 만들 일이 생긴다. 다음 그림은 내가 신입때 만든 프로젝트의 ERD 일부이다. 해당 테이블을 설계하면서 고려했던 방식은 두 가지이다. 비트마스크로 부모 테이블안에 1개 컬럼으로 저장하는 방식 테이블 1개로 추출해서 저장하는 방식 비트마스크로 처리하면 가독성을 망치고 구현이 힘들어진다. 그리고 DBMS에서 컬럼마다 데이터 크기를 정의할 수 있다. 그래서 두 번째 방식으로 테이블을 설계하게 되었다. (발표 때 아무도 태클 걸어 주지 않으셨다.) SET TYPE MySQL 데이터 타입 레퍼런스를 보던 중 SET이라는 것이 눈에 들어왔다. SET은 ENUM처럼 문자열 값을 MySQL 내부적으로 정수 값으로 매핑해서 저장하는 방식의 타입이다. 하지만 SET은 ENUM과 달리..

Database/SQL 2022.06.30

Real MySQL - 파티션이란 무엇인가?!

파티션 파티션 기능은 테이블을 논리적으로는 하나의 테이블이지만 물리적으로는 여러 개의 테이블로 분리해서 관리할 수 있게 해준다. 주로 대용량의 테이블을 물리적으로 여러 개의 소규모 테이블로 분산하는 목적으로 사용한다. 하지만 파티션을 사용하면 무조건 성능이 빨라지는 것은 아니다. 어떤 쿼리를 사용하느냐에 따라 오히려 성능이 더 나빠지는 경우도 자주 발생한다. 파티션의 동작 원리에 대해 더 깊게 알아보자. 인덱스와의 관계 데이터를 탐색할 때 DB는 인덱스를 메모리에 올려서 사용하게 된다. 인덱스가 커지면 SELECT는 물론이고, INSERT, UPDATE, DELETE 작업도 함께 느려진다. 특히 한 테이블의 인덱스 크기가 물리적 메모리 공간보다 크다면 그 영향은 더 심각해진다. 파티션은 데이터와 인덱스를..

Database/SQL 2022.06.29