1 . DB 관점에서의 파싱
파싱의 종류 | 주요 내용 | 비고 |
공통 파싱 | Syntax check (문법체크) Semantic check (Object : 테이블, 컬럼 등등 및 권한 유뮤체크) Library cache check (쿼리가 저장 되어 있는지 검사) |
Library cache 에 쿼리가 있는 경우 : 저장된 쿼리 실행계획 사용, soft parsing Libarary cache 에 쿼리가 없는 경우 : 실행 계획과 소스코드를 만듦, Hard parsing |
하드 파싱 (Hard parsing) | Library cache 에 이전에 실행했던 쿼리가 없는 경우 진행 쿼리 실행 계획을 만들기 위해 아래 두 가지 과정을 거침 Optimization : 최적화한 쿼리 실행 계획을 만듦 Row source generation : DB 엔진이 실행할 수 있는 프로그램 소스 생성 |
Optimization 과정 상세 1. Query Transform : 쿼리 블록 단위로 변형된 쿼리 생산 ex. 서브쿼리를 조인으로, 인라인뷰 해체 등등 2. Plan Generator : 쿼리 실행 계획 생성 3. Estimator : 쿼리 실행 계획들의 Cost 를 계산하고 최소 비용의 SQL 문장을 RowSource generator 에게 전달 |
소프트 파싱 (Soft Parsing) | Library cache 에 이전에 실행했던 쿼리가 있는 경우 소프트 파싱 진행 | Library cache 에 저장되어있는 쿼리 실행계획을 바로 사용 |
이해를 돕기 위한 쿼리 처리과정도
참고 : Library cache는 해시 구조로 관리 된다. SQL 쿼리마다 해시값을 매핑해서 저장하고 SQL 쿼리를 Library cache에서 찾을 때는 SQL 문장을 해시 함수에 적용하여 반환 되는 해시 값으로 탐색한다.
2. MyBatis 관점에서 파싱
2-1. ibatis #data# , Mybatis #{data}
<select id="bind" resultType="String" parameterType="Map">
SELECT user_name, user_address, user_phone
FROM user
WHERE user_id = #{userId}
</select>
위와 같이 Mybatis 에서 '#{parameter}' 이 사용된 쿼리문이 실행되면 쿼리문에 '?' 가 생기며 파싱이 된다.
SELECT user_name, user_address, user_phone
FROM user
WHERE user_id = ?
아마도 JDBC를 MyBatis 를 이용하지 않고 statement 클래스나 preparedStatement 클래스를 사용해 봤다면 이해가 더 빠를 것이다.
쿼리문을 작성할 때 #{}를 사용하는 경우 PreparedStatement 를 생성하게 되는데 위의 ? 에 파라미터를 binding 하여 수행된다. 또한 PreparedStatement 는 한번 수행한 쿼리를 캐싱하는 객체이다. (Soft Parsing)
(1) #{id}의 값이 juhanKim이라면 쿼리문에는 id= 'juhanKim'의 형태가 된다.
* 변수 자료형이 String이면, 자동으로 작은 따옴표를 붙여 쿼리가 수행되기 떄문에 '#{id}'와 같은 식으로 쿼리문을 작성하지 않아도 된다.
(2) SQL injection 을 예방할 수 있어 보안측면에서 유리하다.
2-2. ibatis $data$ , Mybatis ${data}
<select id="literal" parameterType="Map" resultType="Map">
SELECT ${colum}
FROM ${table}
</select>
값이 넣어진 채로 쿼리문이 수행된다. 그렇기 때문에 파라미터의 값이 바뀔 때마다 항상 쿼리문 파싱을 진행해야 한다.(Hard Parsing)
(1) 해당 컬럼의 자료형에 맞추어 Parameter 자료형이 변경된다.
(2) 쿼리 주입을 예방할 수 없어 보안측면에서 불리하다. 그러므로, 사용자의 입력을 전달할때는 사용하지 않는 편이 낫다.
(3) 테이블이나 컬럼명을 Parameter로 전달하고싶을 때 사용한다.
* 변수의 자료형과 관계없이 작은 따옴표(' ')를 parameter에 감싸지 않기 때문에, Parameter$ 및 ${Parameter}를 사용할 때 작은 따옴표로 처리를 하지 않으면 Column이나 이름을 찾지 못한다는 Error가 발생한다.
결론
#Parameter# 및 #{Parameter} 같은 경우에는 PreparedStatement를 사용하게 되면서, 변수를 Bind를 하게 된다. 이 경우에는 바인딩한 것들을 계속해서 바꿔줄 수 있기 때문에 코드의 재사용성이라는 장점이 있고, 변수를 Bind하면서, SQL Injection을 예방을 할 수 있다.
$Parameter$ 및 ${Parameter}는 변수에 저장된 값을 Bind하지 않고 Statement를 사용하게 되면서 쿼리문에 들어가는 값을 수정할 수 있고, 이 경우에는 우리가 흔히 알고 있는 SQL Injection의 공격을 받을 수 있다. 또한, 쿼리문이 변경되면 쿼리문을 다시 파싱하는 작업을 진행하기 때문에 속도가 저하된다는 단점이 있다.
* DB입장에서는 $$은 이미 문자열로 치환되어서 쿼리가 수행되기 때문에 $$값이 달라지면 하드파싱하게 된다.
그래서 대부분 Query 문을 짤 때, $ 방식보다는 # 방식을 사용하라고 권장을 하고 있습니다.
참고 사이트
1. 정희락, 불친절한 SQLs프로그래밍, 데비안 2. 구루비, SQL 파싱 부하 http://www.gurubee.net/lecture/2392 |
'DB & SQL' 카테고리의 다른 글
DATE, DATETIME, TIMESTAMP 의 차이점 (1) | 2024.06.13 |
---|