바인딩 변수를 사용하면 하드 파싱을 줄이고 실행 계획 안정성, CPU 효율, 보안을 동시에 높일 수 있습니다.
핵심 개념
- 커서(Cursor): DB에서 SQL 실행을 위해 유지하는 객체
- 하드 파싱(Hard Parse): 새로운 실행 계획 생성 → 비용 높음
- 소프트 파싱(Soft Parse): 기존 커서 재사용 → 비용 낮음
- 커서 공유(Cursor Sharing): 동일 SQL이면 커서 재사용
- 바인딩 변수(Bind Variable): 값 자리 플레이스홀더로 SQL 동일성 보장
바인딩 변수 효과
- 커서 캐시 히트율↑ → 소프트 파싱 증가
- 라이브러리 캐시 경합↓ → 동시성 향상
- 실행 계획 안정화
- 보안 강화 → SQL 인젝션 예방
- 네트워크/메모리 효율
DBMS별 적용 예시
Oracle
SELECT * FROM orders WHERE customer_id = :cust_id AND order_dt BETWEEN :from_dt AND :to_dt;
모니터링: v$sqlarea
, v$librarycache
바인드 피킹/ACS로 값 분포 따른 계획 관리
PostgreSQL
PREPARE get_orders(int, date, date) AS SELECT * FROM orders WHERE customer_id=$1 AND order_dt BETWEEN $2 AND $3; EXECUTE get_orders(1001,'2025-01-01','2025-01-31');
모니터링: pg_stat_statements
로 실행 통계 확인
MySQL
PREPARE stmt FROM 'SELECT * FROM orders WHERE customer_id=? AND order_dt BETWEEN ? AND ?'; EXECUTE stmt USING @cid,@from,@to;
모니터링: performance_schema.events_statements_summary_by_digest
로 SQL 정규화 확인
SQL Server
EXEC sp_executesql N'SELECT * FROM Orders WHERE CustomerId=@cid AND OrderDt BETWEEN @from AND @to', N'@cid INT,@from DATE,@to DATE', @cid=1001,@from='2025-01-01',@to='2025-01-31';
파라미터 스니핑 이슈 존재 → 통계/힌트 조절
좋은 예 / 나쁜 예 (JDBC)
나쁜 예: String sql = "SELECT * FROM orders WHERE customer_id=" + customerId; Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(sql);
좋은 예: PreparedStatement ps = conn.prepareStatement("SELECT * FROM orders WHERE customer_id=? AND order_dt BETWEEN ? AND ?"); ps.setInt(1, customerId); ps.setDate(2, Date.valueOf(from)); ps.setDate(3, Date.valueOf(to)); ResultSet rs = ps.executeQuery();
체크리스트
- 소프트 파싱 비율 ≥ 95%
- 전 구간 파라미터 바인딩 적용
- 리터럴 폭증 SQL 제거
- 통계 최신화 & 히스토그램 관리
- 프레임워크 PreparedStatement/바인딩 활용
FAQ
- 모든 쿼리 바인딩해야 하나? → 예, 일관성이 핵심
- LIKE 검색도 바인딩? → 접두 고정 시 인덱스 사용 가능
- 리터럴 상수 강제 인덱스? → 계획 고착 위험, 바인딩+통계 권장
- 부분만 바인딩? → 커서 파편화 증가, 전체 적용 필요
- CURSOR_SHARING=FORCE → 임시 조치, 앱 레벨 바인딩 우선
마무리
- 바인딩 변수는 성능·안정성·보안 삼박자 해결책
- 실무는 전 구간 파라미터화 + 모니터링 루틴이 정석
- 다음 학습: 파티셔닝과 커서·바인딩 상호작용 또는 통계 수집 전략