h1. 제 2절 SQL 파싱 부하 1. SQL 처리과정 2. 캐싱된 SQL공유 3. 바인드 변수 사용하기 4. Static SQL과 Dynamic SQL 5. 애플리케이션 커서 캐싱 {panel:title=1.SQL 처리과정 | borderColor=#ccc | titleBGColor=#B0C4DE | bgColor=#ffffff} * 실행계획이 만들어 지기까지의 과정(SQL파싱,SQL최적화(optimization) |
1. SQL을 실행하면 제일먼저 SQL 파서(parser)가 SQL 문장에 문법적 오류가 없는지 검사(Syntax 검사)
2. 문법적 오류가 없다면 의미상 오류가 없는지 검사(Semantic 검사, 오브젝트 존재유무등)
3. 사용자가 발생한 SQL과 그 실행계획이 라이브러리캐시(프로시저캐시)에 캐싱되어 있는지 확인
4. SQL과 실행계획이 캐싱되어 있다면 소프트파싱, 캐싱되어있지 않다면 하드파싱
2.캐싱된 SQL공유
3.바인드 변수 사용하기
Oracle | 바인드 변수 Peeking |
SQL Server | Parameter Sniffing |
>바인드변수 Peeking 기능 도입 : 첫번째 바인드 변수값을 살짝 훔쳐보고 그 값에 대한 분포를 이용하여 실행계획 결정하는 기능
>바인드변수 Peeking 기능 도입 : 첫번째 바인드 변수값을 살짝 훔쳐보고 그 값에 대한 분포를 이용하여 실행계획 결정하는 기능
>오라클은 11g부터는 적응적 커서공유(Adaptive Cursor Sharing)를 도입하여 칼럼 분포에 따라 다른 실행계획이 사용되도록 처리하였지만 이또한 완전한 기능이 아니므로 주의해서 사용 필요
4.Static SQL과 Dynamic SQL
Proc*C 구분으로 Static SQL 작성한 예시
int main()
{
printf("사번을 입력하십시오 : ");
scanf("%d", &empno);
EXEC SQL WHENEVER NOT FOUND GOTO notfound;
EXEC SQL SELECT ENAME INTO :ename
FROM EMP
WHERE EMPNO = :empno;
printf("사원명 : %s.\n", ename);
notfound:
printf("%d는 존재하지 않는 사번입니다. \n", empno); }
-SQL문을 String 변수에 담지 않고 마치 예약된 키워드처럼 C/C++ 코드 사이에 섞어 기술
-구문분석, 유효 오브젝트 여부, 오브젝트 엑세스 권한등의 체크 가능
int main()
{
char select_stmt[50] = "SELECT ENAME FROM EMP WHERE EMPNO = :empno";
// scanf("%c", &select_stmt); → SQL문을 동적으로 입력 받을 수도 있음
EXEC SQL PREPARE sql_stmt FROM :select_stmt;
EXEC SQL DECLARE emp_cursor CURSOR FOR sql_stmt;
EXEC SQL OPEN emp_cursor USING :empno;
EXEC SQL FETCH emp_cursor INTO :ename;
EXEC SQL CLOSE emp_cursor;
printf("사원명 : %s.\n", ename);
}
-조건에 따란 SQL이 동적으로 바뀔수 있으므로 syntax, semantics 체크 불가능
5. 애플리케이션 커서 캐싱
for(;;) {
EXEC ORACLE OPTION (HOLD_CURSOR=YES);
EXEC ORACLE OPTION (RELEASE_CURSOR=NO);
EXEC SQL INSERT ...... ; // SQL 수행
EXEC ORACLE OPTION (RELEASE_CURSOR=YES);
}
call count cpu elapsed disk query current rows
----- ------ ----- ------- ----- ------- ------ -----
Parse 1 0.00 0.00 0 0 0 0
Execute 5000 0.18 0.14 0 0 0 0
Fetch 5000 0.17 0.23 0 10000 0 5000
----- ------ ----- ------- ----- ------- ------ -----
total 10001 0.35 0.37 0 10000 0 5000
Misses in library cache during parse: 1
public static void CursorCaching(Connection conn, int count) throws Exception{
// 캐시 사이즈를 1로 지정
((OracleConnection)conn).setStatementCacheSize(1);
// 묵시적 캐싱 기능을 활성화
((OracleConnection)conn).setImplicitCachingEnabled(true);
for (int i = 1; i <= count; i++) {
// PreparedStatement를 루프문 안쪽에 선언
PreparedStatement stmt = conn.prepareStatement(
"SELECT ?,?,?,a.* FROM emp a WHERE a.ename LIKE 'W%'"); stmt.setInt(1,i);
stmt.setInt(2,i); stmt.setString(3,"test");
ResultSet rs=stmt.executeQuery();
rs.close();
// 커서를 닫더라도 묵시적 캐싱 기능을 활성화 했으므로 닫지 않고 캐시에 보관하게 됨
stmt.close();
}
}
public static void CursorHolding(Connection conn, int count) throws Exception{
// PreparedStatement를 루프문 바깥에 선언
PreparedStatement stmt = conn.prepareStatement(
"SELECT ?,?,?,a.* FROM emp a WHERE a.ename LIKE 'W%'");
ResultSet rs;
for (int i = 1; i <= count; i++) {
stmt.setInt(1,i);
stmt.setInt(2,i);
stmt.setString(3,"test");
rs=stmt.executeQuery();
rs.close();
} // 루프를 빠져 나왔을 때 커서를 닫는다.
stmt.close();
}
|