⚡ 지금 SQL 바꾸면 서버 두 배 빨라진다?! 실무 SQL 꿀팁 9가지 대공개 ⚡

김동혁·2025년 1월 20일
43

데이터베이스

목록 보기
1/2
post-thumbnail

안녕하세요! 오늘은 SQL을 더 빠르고 안전하게 만드는 9가지 꿀팁을 총정리해보려 합니다.
처음부터 끝까지 읽고 나면, DB 쿼리 때문에 골치 앓는 일이 훨씬 줄어들 거예요!
(최대한 쉽게, 그리고 재미있게 설명해볼 테니 편하게 봐주세요! 😉)


목차

  1. 초간단 SQL 최적화 – "SELECT * 대신 필요한 컬럼만!"
  2. DELETE vs TRUNCATE vs DROP 차이점, 몰라서 서버 터질 뻔?!
  3. 왜 내 쿼리는 이렇게 느릴까? EXPLAIN 실행 계획 3분 정리
  4. DB 락(Lock) 때문에 멈춘다고? 락 걸리는 이유 & 해결법
  5. SQL Injection 해킹 시도? 이렇게 막으면 안전합니다!
  6. 이 인덱스가 왜 안 먹히지? - 인덱스 무용지물 사례 모음
  7. 숫자가 클수록 무조건 빠를까? - INT vs BIGINT vs UUID 성능 차이
  8. 날짜별 매출 통계? 이렇게 하면 10배 빠름! - GROUP BY 최적화
  9. 가장 많이 팔린 상품 TOP 10 구하기, 실수하는 코드 vs 최적화 코드

1. 초간단 SQL 최적화 – "SELECT * 대신 필요한 컬럼만!"

“SELECT *은 편리하지만, 최적화의 적!”

✅ 왜 안 좋을까?

  • SELECT * 로 모든 컬럼을 가져오면 불필요한 데이터까지 함께 전송됩니다.
  • 네트워크 트래픽 증가 + 서버 메모리 낭비 = 쿼리 속도 저하!

✅ 간단 예시

-- 잘못된 예시
SELECT * 
FROM orders 
WHERE order_date = '2025-01-01';

-- 개선된 예시(필요한 컬럼만 명시)
SELECT order_id, order_date, total_price
FROM orders
WHERE order_date = '2025-01-01';
  • 필요한 컬럼 수가 많지 않다면 반드시 명시해주자!
  • 이렇게만 해도 쿼리 속도가 확 달라지는 걸 실감할 수 있습니다.

✅ 실제 체감 예시

“컬럼 수 많은 대형 테이블에서 SELECT * → 특정 컬럼만 조회로 변경 후 처리 속도가 2~3배 빨라진 적도 있었어요!”


2. DELETE vs TRUNCATE vs DROP 차이점, 몰라서 서버 터질 뻔?!

“데이터 날리는 건데 뭐가 다른가요?” 라고 묻기 전에 꼭 알아둬야 할 핵심!

✅ 주요 차이점 한눈에 보기

아래는 세 명령어의 주요 특징을 간단히 정리한 표입니다:

✅ 헷갈리지 말자!

  • DELETE: WHERE 절로 골라서 지울 수 있으며, 트랜잭션을 걸어두면 실수 시 롤백 가능.
  • TRUNCATE: 전체 데이터를 한 방에 지우는 명령. 롤백 안 됨!
  • DROP: 테이블을 아예 날려버리는 명령. 구조까지 없어지니 정말 조심해야 함!

✅ 실수로 DROP? 어떻게 복구하지?

  • 백업이 중요합니다.
  • 실수로 DROP TABLE을 날려버린 경우, 백업이 없다면 복구가 거의 불가능!
  • "한 줄 잘못 입력했다가 팀장님한테..." 이런 사례, 현실에서도 자주 일어납니다.

    백업 자동화, 이중화 전략이 필요한 이유!


3. 왜 내 쿼리는 이렇게 느릴까? EXPLAIN 실행 계획 3분 정리

“EXPLAIN 한 번만 써도, 왜 느린지 바로 감 잡힌다.”

✅ EXPLAIN이란?

  • DB가 쿼리를 어떻게 실행할지 보여주는 실행 계획을 확인하는 명령어
  • MySQL 기준 예시:
EXPLAIN SELECT order_id, total_price
FROM orders
WHERE customer_id = 1234;
  • 결과를 보면 type, possible_keys, key, rows, Extra 등을 확인할 수 있습니다.

✅ 핵심 포인트

  • Using index: 인덱스를 잘 타고 있다는 뜻. 대부분 GOOD!
  • Using filesort: 정렬을 추가로 해야 하는 상황. 대량 데이터에서는 성능 저하 우려 (BAD!)
  • JOIN 시 Nested Loop: 한 테이블을 기준으로 다른 테이블을 반복 검색
    • 작은 테이블부터 JOIN하는 게 유리할 때가 많음
  • JOIN 시 Hash Join(DB종류에 따라 다름): 메모리에 해시 테이블 만들어서 JOIN. 레코드 수 많으면 유리할 수도!

✅ 실제 업무 적용 예시

  • 만약 EXPLAIN 결과에 Using filesortUsing temporary가 보인다면 → 인덱스 최적화 혹은 쿼리 재작성 고려!
  • 한 번만 봐도 “아, 여기서 병목이 일어나는구나!” 를 쉽게 파악할 수 있어 실무에서 매우 유용합니다.

4. DB 락(Lock) 때문에 멈춘다고? 락 걸리는 이유 & 해결법

“서버가 안 돌아가는데 원인도 모름... 락이었네!”

✅ 락(Lock)이란?

  • 여러 트랜잭션이 동시에 접근하는 상황에서, 데이터의 무결성을 지키기 위해 걸리는 잠금
  • 예: SELECT ... FOR UPDATE를 사용하면, 해당 행(또는 테이블)에 대해 다른 트랜잭션이 수정할 수 없게 함

✅ Deadlock(교착 상태)란?

  • 두 트랜잭션이 서로가 가진 락을 기다리는 상황
    예시:
    1) 트랜잭션 A가 orders 테이블 락을 잡고, customers 테이블 락도 필요
    2) 트랜잭션 B가 customers 테이블 락을 잡고, orders 테이블 락도 필요
    3) 둘 다 상대방이 풀어줄 때까지 기다리므로 영원히 진행 불가

✅ 락 걸렸을 때 해결 방법

  • InnoDB 엔진 기준: SHOW ENGINE INNODB STATUS; 로 데드락 정보를 확인
  • 락이 자주 걸린다면,
    • 트랜잭션 범위 최소화 (한 번에 너무 많은 UPDATE/DELETE 금지)
    • 쿼리 순서 통일 (JOIN 순서 등)
    • 적절한 인덱스로 검색 범위를 좁히기

✅ 재미 포인트

“어쩐지 자꾸 서버가 느려지더라... 알고 보니 다 같이 SELECT ... FOR UPDATE 쓰고 있었음.”
실험으로 간단한 테이블에 트랜잭션 2개 열고 서로 교착상태 만들어보면 엄청 이해가 빨라집니다.


5. SQL Injection 해킹 시도? 이렇게 막으면 안전합니다!

“SELECT * FROM users WHERE username = '$input'” → 해킹 가능?!

✅ SQL Injection이 뭐죠?

  • 쿼리 안에 악의적인 입력값을 넣어 DB를 비정상 조작하는 해킹 기법
  • 예시:
    -- 만약 username에 "admin' OR '1'='1" 이 들어가면?
    SELECT * FROM users 
    WHERE username = 'admin' OR '1'='1' 
      AND password = '...';
    → 실제로는 'admin' OR '1'='1' 조건이 참이 되어 모든 user정보가 노출될 수도 있음!

✅ 어떻게 막을까?

  1. Prepared Statement(또는 파라미터 바인딩) 사용

    -- 예: PHP의 PDO 문법 예시
    $stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
    $stmt->execute([$username, $password]);
    • 쿼리와 변수를 분리시켜 입력값이 쿼리 문법을 오염시키지 못하게 함.
  2. ORM 사용 시도 마찬가지

    • 대부분의 ORM(예: JPA, Django ORM 등)에서는 바인딩 처리를 자동으로 해줌.
    • 직접 쿼리 넣을 땐 반드시 변수 처리를 주의!
  3. 입력값 검증

    • ', ;, -- 등 의심스러운 문자를 걸러내거나 이스케이프 처리
    • 프론트엔드/백엔드 모두 방어적 코딩

✅ 직접 해보면 재미있다!

"이렇게 하면 뚫린다 → Prepared Statement 쓰면 방어된다"
짧은 샘플 DB를 만들어 실제로 Injection 테스트 해보면 훨씬 기억에 오래 남아요!


6. 이 인덱스가 왜 안 먹히지? - 인덱스 무용지물 사례 모음

“인덱스 걸었는데 왜 속도가 똑같지?”

✅ 대표 사례

  1. LIKE '%검색어%'
    • %가 앞에 있으면(좌측 와일드카드) 인덱스 사용 불가
    • 해결: 검색어% 형태로(접두어) 쿼리하거나, Fulltext 인덱스 활용
  2. WHERE DATE(created_at) = '2024-01-01'
    • 함수로 감싸면 인덱스 사용 불가
    • 해결: created_at >= '2024-01-01' AND created_at < '2024-01-02'
  3. OR 조건
    • WHERE col1 = 'X' OR col2 = 'Y' 는 인덱스가 둘 중 하나만 쓰이거나 비효율적
    • 해결: 가능하면 쿼리 분리(UNION ALL)하거나 다른 방식으로 조건 처리

✅ 인덱스 사용법 팁

  • 컬럼 가공(함수, 연산) 없이 비교해야 인덱스가 100% 활용 가능
  • 여러 컬럼을 함께 인덱스 걸 때, 컬럼 순서도 중요
  • 카디널리티(컬럼 값 분포)가 높은 컬럼부터 인덱스로 두는 것이 효과적

7. 숫자가 클수록 무조건 빠를까? - INT vs BIGINT vs UUID 성능 차이

“기왕이면 다 BIGINT, 그리고 UUID 쓰면 최고?” → 그렇지 않을 수도!

✅ INT vs BIGINT

  • INT(11)는 최대 4바이트, BIGINT는 8바이트
  • 데이터가 엄청나게 클 경우 BIGINT가 필요하지만, 불필요하게 큰 타입 쓰면 메모리/스토리지 낭비
  • 예:
    • 10만 건 정도로 ID 사용한다면 INT도 충분 (최대 21억까지 가능)
    • 수십억 건 이상이라면 BIGINT 고려

✅ UUID를 PK로 쓰면 왜 느려질까?

  • PK(Primary Key)는 일반적으로 정렬(인덱스) 구조
  • UUID는 랜덤성이 높아 인덱스 분포가 고르게 되지 않고, 페이지 스플릿도 자주 발생
  • 따라서 자동 증가 ID(AUTO_INCREMENT)에 비해 INSERT 성능이 떨어질 수 있음

✅ 어떻게 선택할까?

  1. 데이터 규모 추정 후, 필요 최소한의 타입 선택
  2. 이미 엄청난 데이터가 있다면, 확장성을 위해 BIGINT가 나을 수도
  3. UUID는 복제/분산환경에서 유니크 ID가 필요할 때 유용 (하지만 성능 트레이드오프 주의)

8. 날짜별 매출 통계? 이렇게 하면 10배 빠름! - GROUP BY 최적화

“GROUP BY 돌렸더니 쿼리 한번에 1분 걸려요...”

✅ GROUP BY의 문제점

  • 대량의 데이터를 그룹화하면서 임시 테이블을 생성하거나, 디스크 정렬을 수행할 수도 있음
  • 인덱스가 없으면 엄청 느려질 수 있음

✅ 해결 방법

  1. 미리 집계 테이블 만들어서 적절히 업데이트
    • 예: 매일 자정에 sales_summary 같은 테이블을 생성/갱신
    • 실시간성보다 조회 속도가 중요한 통계라면 매우 유효
  2. 인덱스 + GROUP BY
    • GROUP BY 대상 컬럼에 인덱스가 걸려 있으면 정렬이나 임시 테이블 생성을 최소화할 수 있음

    • 예:

      CREATE INDEX idx_order_date ON orders(order_date);
      
      SELECT order_date, SUM(total_price) 
      FROM orders
      GROUP BY order_date;
  3. 쿼리 구조 최적화
    • 불필요한 JOIN 빼기, WHERE로 대상 범위 줄이기

✅ 실제 사례

"매출 통계 쿼리를 GROUP BY로 매일 돌렸는데 1분 걸리던 게, 인덱스 + 집계 테이블로 1초로 줄었어요!"
이 차이는 실무에서 치명적일 수 있죠.


9. 가장 많이 팔린 상품 TOP 10 구하기, 실수하는 코드 vs 최적화 코드

“ORDER BY COUNT(*) DESC LIMIT 10이면 끝 아님?”

✅ 기본 쿼리

SELECT product_id, COUNT(*) AS cnt
FROM order_items
GROUP BY product_id
ORDER BY cnt DESC
LIMIT 10;
  • 간단해 보이지만, 테이블 전체를 스캔해야 할 수도 있음

✅ 인덱스 활용 TOP-N 최적화

  • 인덱스(product_id) 에 있고, order_items 레코드가 어마어마하게 많다면?

  • GROUP BY product_id 시 인덱스 정렬 사용 → 임시 테이블 없이 빠르게 계산 가능

  • 또는 집계 테이블(예: product_sales_count)을 주기적으로 업데이트

    -- 집계 테이블 예시 (product_id별로 누적 판매량을 따로 관리)
    CREATE TABLE product_sales_count (
      product_id INT NOT NULL PRIMARY KEY,
      total_sold INT NOT NULL
    );
    
    -- 주문이 들어올 때마다 혹은 일정 주기로 total_sold를 업데이트
    UPDATE product_sales_count
    SET total_sold = total_sold + {판매 수량}
    WHERE product_id = {해당 상품ID};
    
    -- TOP 10 조회 시
    SELECT product_id, total_sold
    FROM product_sales_count
    ORDER BY total_sold DESC
    LIMIT 10;
  • 이렇게 하면 조회 시엔 순식간에 끝남!

✅ 실험해서 비교하면 재밌다!

"기본 쿼리 vs 인덱스/집계 테이블" 각각 실행 시간 체크해보면, 100배 이상 차이가 날 때도 있습니다.


마무리: 재미있게, 그리고 실무에서 바로 써먹을 수 있게!

오늘은 SQL 최적화, 데이터 삭제 방식, 락, 인덱스, Injection, 타입 선택, GROUP BY, TOP-N 쿼리 등등 실무에서 가장 많이 만나는 문제들을 한 번에 훑어봤습니다.

💡 포인트 정리

  1. 필요한 컬럼만 조회해서 성능 향상
  2. 데이터 삭제 시 DELETE / TRUNCATE / DROP 차이를 꼭 이해하고 쓰기
  3. EXPLAIN으로 실행 계획을 보는 습관 기르기
  4. 락(특히 Deadlock) 문제는 트랜잭션 범위쿼리 순서로 예방
  5. SQL InjectionPrepared Statement로 막을 수 있음
  6. 인덱스가 어디서, 어떻게 동작하는지 정확히 알아야 함
  7. INT vs BIGINT vs UUID는 데이터 특성에 맞춰 선택
  8. GROUP BY는 인덱스 및 집계 테이블로 최적화
  9. TOP-N 쿼리도 큰 테이블에서는 집계 테이블 활용 고려

☑️ 어떻게 활용할까?

  • 너무 심오한 이론보다는, 지금 당장 내 프로젝트에 적용해볼 만한 짧은 팁부터 시도해보세요!
  • 특히 인덱스EXPLAIN 적용, SELECT * → 필요한 컬럼만 변경은 당장 5분 안에 실행 가능합니다.
  • 작은 변경이라도 실제 서버에서 2~3배 속도 향상을 체감할 수 있어요.

✨ 엔딩

SQL 최적화는 계속해서 배워가야 할 영역이에요.
작은 팁 하나로도 우리 애플리케이션이 훨씬 빨라지고, 사용자 만족도가 쑥쑥 올라갑니다.
조금씩 개선해나가다 보면, 어느새 "나 DB 좀 아는 개발자"가 되어 있을 거예요!

“오늘의 글이 도움이 되었다면, 좋아요와 댓글 부탁드립니다! ❤️

읽어주셔서 감사합니다. 다음에 또 재미있고 유익한 주제로 만나요! ٩(๑˃̵ᴗ˂̵)و

profile
🐱 도쿄에서 활동 중인 웹 개발자 🇯🇵💻 🧑‍💻 최근에는 요즘IT에서 작가로도 활동 중입니다! 📝 요즘IT 글 모음: https://yozm.wishket.com/magazine/@donghyuk65/

3개의 댓글

comment-user-thumbnail
2025년 1월 31일

좋은 내용입니다 많은 도움이 되었어요 !

답글 달기
comment-user-thumbnail
2025년 2월 3일

Livonia Locksmith, your safety and convenience are our top priorities. From automatic locks to emergency lockout services, we provide fast, reliable, and professional locksmith solutions tailored to your needs. Whether you’re at home, running a business, or dealing with an unexpected lock issue, we’re here 24/7 to ensure you’re secure and stress-free. Don’t wait for a problem to escalate—give us a call today at 313-329-6063 https://locksmithlivonia.com/

답글 달기
comment-user-thumbnail
2025년 2월 4일

Locksmith Novi is a trusted provider of comprehensive locksmith services in Novi, Michigan. They offer a wide range of solutions tailored to meet the needs of residential, commercial, and automotive clients.

Emergency Services: Recognizing that lock-related emergencies can occur at any time, Locksmith Novi provides 24/7 emergency locksmith services. Whether you're locked out of your home, office, or vehicle, their team is available around the clock to offer swift and reliable assistance.

For more information or to request their services, you can visit their website at
https://locksmithnovi.net/.

답글 달기