DB 로직 최적화, 쿼리 최적화, 부족한 금액 계산하기

김민준·2023년 11월 2일
0

DB 로직 최적화
쿼리 최적화
부족한 금액 계산하기 - 프로그래머스

참조한 페이지

DB 로직 최적화

  • 작업할 데이터를 줄인다.
  1. DB 테이블과 엔티티를 일관성 있게 설계하여 중복 데이터를 피하고 정규화를 적용한다.

  2. 데이터 파티셔닝
    대용량 DB를 파티션으로 나누어 쿼리 성능을 최적화한다.

  3. 캐싱 : 자주쓰는 쿼리 결과를 캐싱한다.
    캐싱의 주기와 데이터 유효성을 고려해야한다.

  4. 배치 처리 Batch Processing
    리소스 여유가 많은 시간대에 미리 데이터를 처리해주는 것이다.
    예) 비-클러스터형 인덱스의 경우 물리적인 인덱스가 없기 때문에 대용량 데이터를 처리하기 불리하다. 사용자가 적은 시간대에 미리 마이그레이션을 해두자

  • 작업량을 줄인다.
  1. 쿼리 분리 및 쿼리 최적화
    복잡한 쿼리를 단순하게 만들어서 총 작업 비용을 낮춘다.
    예) 불필요한 조인을 제거한다.

  2. 트랜잭션
    적절한 범위내에서 필요한 트랜잭션만 적용한다.

  3. 타임아웃 설정, 데드라인 설정
    장기 실행되는 작업에 적용하여 시스템 안정성을 챙긴다.

  4. 요청 최소화 : 프론트엔드를 최적화하여 데이터 요청을 한번에 필요한 만큼만 하도록한다.

쿼리 최적화

  • 작업할 데이터 크기를 줄이자.

    1. *로 모든 필드를 부르지 말고 필요한 필드만 지정해서 부르자.

    2. GROUP BY 연산 시 HAVING 보다는 WHERE 절을 사용하자.

      같은 조건이라도 WHERE 절이 먼저 실행되기 때문에 데이터크기를 줄일 수 있다.

    3. INNER JOIN 시 큰 테이블을 FROM절에 배치하자.
      최소한의 콤비네이션을 탐색하도록한다.
      이너 조인 시 대부분 쿼리 플래너에서 효율적인 순서로 이너 조인의 순서를 바꾸기 때문에 차이가 없을 수 도 있다.
      반대로 조인할 테이블이 많아진다면 플래너에도 부하가 걸리기 때문에 되도록이면 미리 잘 구성하는 것이 좋다.

  • 연산량을 줄이자

    1. 조건에 연산을 걸지 않는다.
      원하는 조건이 나오는 계산식 보다는 원하는 조건의 범위를 지정해서 가져오자. 이렇게 해야 모든 필드값을 탐색하지 않는다.

    2. 와일드 카드 문자열(%)을 스트링 앞에 붙이지 않는다.
      value LIKE "%..."는 풀 테이블 스캔(full table scan)을 사용하지만 뒤에 붙이면 인덱스를 활용할 수 있다.

  • 연산이 빠른 기능을 사용하자

    1. DISTINCT는 느리다.
      EXIST를 이용하자.
-- 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);
  • 전처리한 테이블을 만들자
    데이터가 바뀌지 않거나 실시간 분석을 하지 않는다면 유용한 방법이다.

부족한 금액 계산하기 - 프로그래머스

부족한 금액 계산하기

n(n+1)2\frac {n(n+1)} {2} 하면 간단할것같다.

나의 풀이

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)과 같은 내용이다.

n(n+1)2\frac {n(n+1)} {2} - 예산 과 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?

쿼리 최적화: 빠른 쿼리를 위한 7가지 체크리스트 ✅
Query Planning

profile
node 개발자

0개의 댓글

관련 채용 정보