SQL 공유 및 재사용
SQL을 한번 호출하면 SQL 파싱 -> SQL 최적화 -> 로우 소스 생성을 통해서 우리에게 결과 셋을 보여주게 됩니다 이때 한번 만들어진 로우 소스 같은 경우는 반복적인 재사용 SQL에 대해서는 파싱과 최적화 과정을 거치지 않고 바로 로우 소스 생성을 호출해서 우리에게 결과를 가져다주게 됩니다 즉 SQL 을 호출하면 DBMS는 먼저 캐싱 된 SQL 을 찾고 존재하면 결과 셋을 가져다주는 방식을 소프트 파싱이라고 하고 없으면 다시 SQL 파싱부터 진행을 하는데 이를 하드 파싱이라고 합니다
이름 없는 SQL의 문제
만약 하드 파싱부터 시작하게 되면 옵티마이저는 후보군이 될만한 무수히 많은 실행 경로를 분석해서 제일 적은 비용으로 결과 셋을 가져다주어야 하는데 이러한 높은 비용을 단 한 번만 실행하고 폐기하는 것은 비용적인 측면으로 불합리하기에 옵티마이저는 이들을 캐싱을 해두어서 저장을 하는데 문제는 함수 같은 것은 이름이 있어서 저장이 편리하지만 SQL 은 이름이 없는 것이 문제다 즉 SQL 은 캐싱 될 때 SQL 문구 자체가 저장이 된다 아래의 SQL 을 보자
1
2
3
4
5
6
7
8
SELECT * FROM emp WHERE empno = 7900;
SELECT * FROM emp WHERE Empno = 7900;
SELECT * FROM emp WHERE EmpNo = 7900;
SELECT * FROM emp WHERE EmpNo = 7900 ;
SELECT * FROM emp WHERE EmpNo = 7900 ;
모두 같은 결과를 가져다주는 SQL이다 하지만 옵티마이저가보기에는 이 SQL 들은 전부 다른 SQL임으로 모두 캐싱 된 것을 사용하지 못하고 하드 파싱 작업을 거치게 된다 즉 SQL 은 문구 자체가 이름이기 때문에 기존 실행된 것과 완벽히 일치해야만 캐싱 된 것을 사용할 수 있다
Oracle 과 DB2 전략
Oracle 오라클사에서 만든 DBMS이고 DB2는 IBM에서 만든 DBMS입니다 문제는 Oracle 은 SQL 자체를 저장하는 한편 DB2는 SQL에 이름을 붙여서 저장할 수 있습니다 그렇기에 DB2 환경에서는 위에처럼 오라클은 다르게 인식하는 쿼리도 하나로 인식시켜 호출할 수 있습니다 그럼 DB2 가 좋을 것이라고 생각하지만 이는 소규모 DBMS 일 때는 유리하지만 대규모 환경일 때는 저 SQL이 있는지 없는지 찾는 것도 비용의 하나로 들어가기 때문에 대용량 환경에서 DB2 SQL 이름 붙이는 것은 효용성이 떨어질 수 있습니다
프로그램 코딩 전략략
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void login(String login_id) throws Exception {
String sql = "SELECT * FROM CUSTOM WHERE LOGIN_ID = '" + login_id + "'";
Statment st = con.createStatment();
ResultSet res = st.executeQuery(sql);
}
public void login(String login_id) throws Exception {
String sql = "SELECT * FROM CUSTOM WHERE LOGIN_ID = ?";
PreparedStatment st = con.prepareStatment(sql);
st.setString(1 , login_id)
ResultSet res = st.executeQuery()
}
다음과 같은 2개의 소스가 있다고 가정을 해보자 이 사이트는 일별 로그인 통계 백만 회가 넘는 트래픽이 동작한다고 할 때 위의 코드와 아래 코드의 비용 차이는 압도적으로 아래가 유리하다 위의 쿼리를 돌리다 보면 캐싱에는 다음과 같은 캐싱 SQL 이 쌓이게 된다
1
2
3
4
5
6
SELECT * FROM CUSTOM WHERE LOGIN_ID = 'a';
SELECT * FROM CUSTOM WHERE LOGIN_ID = 'b';
SELECT * FROM CUSTOM WHERE LOGIN_ID = 'c';
SELECT * FROM CUSTOM WHERE LOGIN_ID = 'd';
이렇게 말이다 이는 LOGIN_ID 단 한 번만 사용되는 PK임으로 이렇게 쌓이게 되면 옵티마이저는 먼저 캐싱 된 SQL 이 있는지 없는지 살펴보게 되는데 저렇게 단 한 번만 쓰이게 되는 쿼리는 매우 매우 비효율적이다 그래서 JAVA 동적 변수 처리는 위가 아닌 아래처럼 처리를 해야 한다
1
2
3
SELECT * FROM CUSTOM WHERE LOGIN_ID = :1
아래 쿼리처럼 처리하면 캐싱 SQL 은 단 한 개만 존재할 것이고 옵티마이저는 이 쿼리를 반복해서 사용하게 된다 변수만 계속해서 바꿔서 말이다