---
description: 친절한 SQL 튜닝 1장
---

# 1장 SQL 처리 과정과 I/O

## 1. SQL 최적화

DBMS 내에서 DBMS 내에서 SQL을 분석하고, 실행 가능한 최적의 형태로 변환하는 전 과정을 의미한다.

주요 단계는 다음과 같다

* **SQL 파싱**: SQL 문을 구성 요소로 분해하고, 문법적·의미적 오류 검사
* **SQL 최적화**: 옵티마이저가 여러 실행 계획 중 가장 효율적인 방법 선택
* **로우 소스 생성**: 선택된 실행 계획을 실제 실행 가능한 코드 또는 프로시저 형태로 변환

이 중 SQL 최적화는 데이터베이스 성능을 결정하는 핵심 절차이다.

### SQL 옵티마이저

사용자가 원하는 작업을 가장 효율적으로 수행할 수 있는 최적의 데이터 엑세스 경로를 선택해주는 DBMS 핵심 엔진이다.

**SQL 최적화 단계**

* 쿼리를 수행하는 데 후보 실행 계획을 찾아낸다.
* 데이터 딕셔너리에 미리 수집해둔 오브젝트 통계 및 시스템 통계 정보를 통해 각 실행 계획의 예상 비용을 산정한다.
* 최저 비용의 실행 계획을 선택한다.

### 실행 계획

**AUTOTRACE**

Oracle은 AUTOTRACE를 통해 확인할 수 있다. Cost는 쿼리를 수행하는 동안 발생할 것으로 예상하는 I/O 횟수 또는 예상 소요 시간을 표현한다. 이 값은 어디까지나 예상치이므로 실제로 발생하는 I/O, 시간과 많은 차이가 날 수 있다.

```sql
SQL> SET AUTOTRACE TRACEONLY EXPLAIN;
SQL> SELECT * 
FROM t 
WHERE deptno = 10 AND no = 1;

---------------------------------------------------------------------------------
| Id | Operation                   | Name  | Rows | Bytes | Cost (%CPU)| Time  |
---------------------------------------------------------------------------------
|  0 | SELECT STATEMENT            |       |    5 |   125 |     2   (0)| 00:00:01 |
|  1 | TABLE ACCESS BY INDEX ROWID | T     |    5 |   125 |     2   (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN            | T_X01 |    5 |       |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------
```

**옵티마이저 힌트**

옵티마이저는 보통 효율적인 실행 계획을 선택하지만, 항상 최선이라고 보장할 수는 없다. 이럴 때는 **옵티마이저 힌트**를 사용해 특정 인덱스를 선택해 데이터 액세스 경로를 바꿀 수 있다. 쿼리가 시스템에서 중요한 역할을 한다면, 힌트를 통해 실행 계획을 직접 제어해보는 것도 좋은 방법이다.

```sql
SELECT **/*+ index(t t_x02) */** 
고객명, 연락처
FROM 고객 A
WHERE 고객ID = 10;
```

## 2. SQL 공유 및 재사용

### 라이브러리 캐시

사용자가 SQL 문을 입력하면, SQL 파싱, 최적화, 로우 소스 생성 과정을 거쳐 실행 계획과 관련된 정보가 **라이브러리 캐시**라는 메모리에 저장된다. 이는 옵티마이저의 최적화 과정에서 많은 연산이 필요하기 때문에, 동일한 SQL에 대해 반복적인 작업을 줄이기 위함이다.

* **소프트 파싱**: SQL 문이 라이브러리 캐시에 존재할 경우, 파싱과 최적화 과정을 생략하고 곧바로 실행 단계로 넘어간다.
* **하드 파싱**: SQL 문이 캐시에 없을 경우, 파싱부터 최적화, 로우 소스 생성까지 모든 단계를 새로 수행한다.

<figure><img src="https://3852524589-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MiwItDEqm8vP-F4sJ6W%2Fuploads%2FCbBlyBCDzpH4Spm7YaUC%2Fimage.png?alt=media&#x26;token=d0369795-f940-40b5-a091-0b5a05958cf9" alt=""><figcaption></figcaption></figure>

### 바인드 변수

**이름없는 SQL 문제**

SQL 문은 별도의 이름이 없고, 텍스트 자체가 고유 식별자의 역할을 한다. 이 때문에 텍스트의 아주 작은 부분만 달라져도, 전혀 다른 객체로 인식되어 새로운 SQL로 처리된다.\
라이브러리 캐시에서도 SQL 텍스트를 키 값으로 사용하기 때문에, 의미는 동일한 쿼리라 하더라도 텍스트가 다르면 매번 별도로 최적화 과정을 거치게 된다.

```sql
SELECT * FROM emp WHERE empno = 100;
select * from EMP where EMPNO = 100;
select * from emp where empno = 100;
```

<pre class="language-sql"><code class="lang-sql"><strong>SELECT * FROM CUSTOMER WHERE LOGIN_ID = 'hyeon';
</strong>SELECT * FROM CUSTOMER WHERE LOGIN_ID = 'minsoo';
SELECT * FROM CUSTOMER WHERE LOGIN_ID = 'signature';
</code></pre>

**바인드 변수**

바인드 변수는 파라미터 기반 방식으로 SQL을 작성하도록 한다. 이를 통해 하드 파싱은 최초 한 번만 발생하며, 캐싱된 SQL를재사용할 수 있다.

```sql
SELECT * FROM CUSTOMER WHERE LOGIN_ID = :1;
```
