DB 로직 최적화
쿼리 최적화
부족한 금액 계산하기 - 프로그래머스참조한 페이지
DB 테이블과 엔티티를 일관성 있게 설계하여 중복 데이터를 피하고 정규화를 적용한다.
데이터 파티셔닝
대용량 DB를 파티션으로 나누어 쿼리 성능을 최적화한다.
캐싱 : 자주쓰는 쿼리 결과를 캐싱한다.
캐싱의 주기와 데이터 유효성을 고려해야한다.
배치 처리 Batch Processing
리소스 여유가 많은 시간대에 미리 데이터를 처리해주는 것이다.
예) 비-클러스터형 인덱스의 경우 물리적인 인덱스가 없기 때문에 대용량 데이터를 처리하기 불리하다. 사용자가 적은 시간대에 미리 마이그레이션을 해두자
쿼리 분리 및 쿼리 최적화
복잡한 쿼리를 단순하게 만들어서 총 작업 비용을 낮춘다.
예) 불필요한 조인을 제거한다.
트랜잭션
적절한 범위내에서 필요한 트랜잭션만 적용한다.
타임아웃 설정, 데드라인 설정
장기 실행되는 작업에 적용하여 시스템 안정성을 챙긴다.
요청 최소화 : 프론트엔드를 최적화하여 데이터 요청을 한번에 필요한 만큼만 하도록한다.
작업할 데이터 크기를 줄이자.
*
로 모든 필드를 부르지 말고 필요한 필드만 지정해서 부르자.
GROUP BY 연산 시 HAVING 보다는 WHERE 절을 사용하자.
같은 조건이라도 WHERE 절이 먼저 실행되기 때문에 데이터크기를 줄일 수 있다.
INNER JOIN 시 큰 테이블을 FROM절에 배치하자.
최소한의 콤비네이션을 탐색하도록한다.
이너 조인 시 대부분 쿼리 플래너에서 효율적인 순서로 이너 조인의 순서를 바꾸기 때문에 차이가 없을 수 도 있다.
반대로 조인할 테이블이 많아진다면 플래너에도 부하가 걸리기 때문에 되도록이면 미리 잘 구성하는 것이 좋다.
연산량을 줄이자
조건에 연산을 걸지 않는다.
원하는 조건이 나오는 계산식
보다는 원하는 조건의 범위
를 지정해서 가져오자. 이렇게 해야 모든 필드값을 탐색하지 않는다.
와일드 카드 문자열(%)을 스트링 앞에 붙이지 않는다.
value LIKE "%..."
는 풀 테이블 스캔(full table scan)을 사용하지만 뒤에 붙이면 인덱스를 활용할 수 있다.
연산이 빠른 기능을 사용하자
-- Inefficient
SELECT DISTINCT m.id, title
FROM movie m
INNER JOIN genre g
ON m.id = g.movie_id;
-- Improved
SELECT m.id, title
FROM movie m
WHERE EXISTS (SELECT 'X' FROM rating r WHERE m.id = r.movie_id);
하면 간단할것같다.
function sol01(price, money, count) {
var answer = -1;
var accumulate = count*(count+1)/2
var change = money - accumulate*price
if (change < 0){
answer = -change
} else {
answer = 0
}
return answer;
}
무난하게 풀어낸 것 같다.
아래는 약간 개선한 버전
function sol02(price, money, count) {
var answer = 0;
var accumulate = count*(count+1)/2
var change = accumulate*price - money
if (change > 0){
return change
}
return answer;
}
const sol1 = (..._) => Math.max(_[0]*_[2]*++_[2]/2-_[1], 0);
같은 논리지만 Math.max
객체를 사용하였다.
..._
: 함수에 입력한 매개 변수의 목록
Math.max()
: 괄호 안 조건에서 큰 값을 반환
_[0]
: 입력된 매개변수중 0번째 값
즉, 위의 내가 짠 sol0(price, money, count)
과 같은 내용이다.
- 예산 과 0 중 큰것을 내보내는 것, 일부러 요금에서 예산을 빼서 -로 할 필요가 없다.
아래의 코드로 테스트 진행함
// 솔루션0
function sol01(price, money, count) {
var answer = -1;
var accumulate = (count * (count + 1)) / 2;
var change = money - accumulate * price;
if (change < 0) {
answer = -change;
} else {
answer = 0;
}
return answer;
}
function sol02(price, money, count) {
var answer = 0;
var accumulate = (count * (count + 1)) / 2;
var change = accumulate * price - money;
if (change > 0) {
return change;
}
return answer;
}
// 솔루션1
const sol1 = (..._) => Math.max((_[0] * _[2] * ++_[2]) / 2 - _[1], 0);
//////////////////////////////////////////////////////////
async function runSolutionWithTiming(solutionFn, a, b, c) {
const startTime = new Date();
for (let i = 0; i < 1000000; i++) {
await solutionFn(a);
}
const endTime = new Date();
const executionTime = endTime - startTime;
console.log(`${solutionFn.name} 실행 시간: ${executionTime}ms`);
}
async function main() {
const a = 5646513246845654657432413;
const b = 1234563456465456423;
const c = 1456372;
await runSolutionWithTiming(sol01, a, b, c);
await runSolutionWithTiming(sol02, a, b, c);
await runSolutionWithTiming(sol1, a, b, c);
// await runSolutionWithTiming(sol1, a);
// await runSolutionWithTiming(sol2, a);
// await runSolutionWithTiming(sol3, a);
}
main()
.then(() => {
console.log("모든 실행이 완료되었습니다.");
})
.catch((error) => {
console.error("에러 발생:", error);
});
if문을 통과하고 변수에 할당하고 그걸 return 하는 것보다는 바로 return하는 것이 더 빠를 것이라고 생각했는데 아니었던것같다. if문 안에서 return을 내보낸게 문제일까?
Math 객체를 쓴건 예상대로 느렸다.
신입 개발자, DB를 최적화 하다! 1편
신입 개발자, DB를 최적화 하다! 2편
[백엔드] 기술 면접 Top30 - #26 DB 로직 최소화
What is Batch Processing?