ERP를 설계하다보면, 수많은 테이블의 조인연산이 이뤄지고, 원하는 데이터를 얻기위해 서브쿼리도 자주 사용하였습니다. 이렇게 되다 보니, 조인을 10~20개씩 걸어서 데이터를 조회 했습니다.
예를들어, 프로젝트 관련된 정보를 조회하고 싶다.
프로젝트 테이블 - 영업기회 테이블 - 수주 테이블 - 사원 테이블 - 부서 테이블 - 거래처 테이블 - 신용정보 테이블 - 프로젝트 타입 테이블 - 라이선스 테이블...
원하는 데이터를 얻고자 쿼리는 계속 늘어나게 됐습니다.
처음에는 성능상의 이슈가 없다가도 데이터가 늘어나게 되면 WAS서버가 다운되는 일까지 발생했습니다.
이를 해결하기 위해, 다양한 방법으로 쿼리를 튜닝해보고 비즈니스에 맞게 DB 아키텍쳐도 구성해보는 등 의미 있는 시간을 가져봤습니다.
인덱스는 데이터 검색 속도를 크게 향상시키는 데이터베이스 구조입니다. 하지만 모든 컬럼에 인덱스를 추가하는 것은 성능 저하를 초래할 수 있습니다.
user_id
컬럼에 인덱스를 추가.status
)를 기준으로 조회하는 경우 status
컬럼에 인덱스를 설정.CREATE INDEX idx_user_id ON users(user_id);
CREATE INDEX idx_status ON orders(status);
예시: B-Tree 인덱스 구조
비효율적인 쿼리는 데이터베이스 리소스를 과도하게 소모합니다. 이를 방지하기 위해 쿼리를 간소화하고 최적화해야 합니다.
SELECT *
는 모든 컬럼을 불필요하게 조회하므로 특정 컬럼만 선택.order_items
테이블에서 수량(quantity
)이 10 이상인 데이터를 조회.-- 비효율적인 쿼리
SELECT * FROM orders WHERE id IN (SELECT order_id FROM order_items WHERE quantity > 10);
-- 최적화된 쿼리
SELECT o.id, o.date
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
WHERE oi.quantity > 10;
데이터 정규화는 중복을 최소화하고 데이터 일관성을 유지하는 데 효과적입니다. 반면, 비정규화는 읽기 성능을 높이는 데 유리합니다.
-- 정규화된 테이블 설계
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
date DATE,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);
-- 비정규화된 테이블 설계
CREATE TABLE orders_with_customer (
order_id INT PRIMARY KEY,
customer_name VARCHAR(100),
date DATE
);
예시: 정규화와 비정규화 비교
데이터베이스가 쿼리를 처리하는 과정을 분석하여 병목 지점을 식별할 수 있습니다.
EXPLAIN SELECT o.id, o.date FROM orders o WHERE o.status = 'completed';
최신 통계 정보는 쿼리 최적화 도구가 효율적인 실행 계획을 수립하는 데 도움을 줍니다.
ANALYZE
명령어로 통계 정보 갱신.ANALYZE TABLE orders;
예시: 옵티마이저와 통계 정보