Framework/Database2009. 3. 13. 07:31

키셋이나 다이나믹 커서는 최소한 connectionless 웹환경이라면 거의 쓰이지 않기 때문에 잘 보이지 않지만 동시성이 매우 중요한 일부에서는 고려될수 있는 커서 방식이다.



  1. WAS는 쿼리 실행을 요청한다.  
  2. DB는 쿼리의 "모든" 결과의 "Key 셋(unique index)"을 메모리에 snapshot 형태로 만든다. 모든 데이타의 load를 완료하면 Client의 신호를 기다린다
  3. Client(WAS)는 Fetch(rs.next())를 요청한다.
  4. DB는 cursor 정보를 유지하면서 current Key의 정보로 DB를 lookup해서 row의 정보를 알아낸다
  5. 알아낸 row를 Client에게 보낸다. 
  6. 패킷(1개의 row만 들어있음)을 받았다는 confirm 메시지를 보낸다.
  3,4,5,6의 동작을 9번 더 반복한다

KeySet은 Static과 상당히 비슷하지만( 실제 메모리에 로드하는 것은 전체 결과셋이 아니라 결과셋의 Key만을 메모리에 올린다.(만약 테이블에 unique index가 없다면 이 방식은 실행할 수 없으므로 이 시점에서 자동으로 static cursor 모드로 변경된다.) 메모리에는 Key들의 정보만 있기 때문에 매 Fetch 시마다 데이타베이스 lookup을 해서(해당 키로 DB를 다시 Access read한다.) 해당 Row의 정보를 리턴한다. (물론 select 컬럼에 key가 아닌 컬럼이 있을 경우이다. 만약 select 컬럼이 모두 key의 부분이라면 역시 static mode와 동일하다.)

여기서 맨처음의 예제처럼 하나의 테이블에서라면 문제없겠지만 만약 쿼리가 2개이상의 테이블을 조인하는 경우라면 어떻게 될까? 조인된 테이블은 실제 존재하는 것이 아니므로 Key 정보를 유지할수 없다. 여기서의 구현은 사실 DB와 드라이버 마다 틀리기는 하지만 대부분은 이경우 keyset이 작동하지 않는다.

키셋을 사용한다면 가장 큰 이유는 Key정보만을 메모리에 유지하고 나머지 Data는 매번 새로 read를 하기 때문에 다른 세션에서 수정한 값을 실시간으로 읽을 수 있는 장점이 있다. 사실 JDBC의 sensitive 방식은 바로 이 keyset을 뜻한다. 다른 Session의 update에 sensitive 하기 때문에 붙여진 이름이다. 하지만 실제로 돌려보면 이 방식을 지원하는 JDBC 드라이버는 많지 않고 그 일부도 제각각 특정 조건을 만족해야만 동작하는 경우가 많다.(이를테면 단일테이블 억세스일때만..) 그리고 만약 사용자 업데이트가 키 컬럼을 수정하였다면 역시 Keyset은 제대로 동작하지 않는다.


단점은
  - WAS에서 필요한 양과 상관없이 해당 쿼리의 모든 Key셋 데이타를 읽어야 한다.
  - DB에서 읽은 모든 Row를 KeySet을 Snapshot 형태로 저장해야 하기 때문에 Static보다는 적지만 공유 자원인 DB 메모리는 여전히 많이 소모된다.
   - 매 fetch마다 DB Read가 일어난다.
   - 동기적으로 작동하기 때문에 Client의 fetch 요구시마다 row단위의 데이타 전송이 이루어진다.(패킷의 크기가 작다.)
   - cursor를 해당 프로그램이 종료될때까지(DB close 할때까지) 유지해야 하고 더 많은 정보를 관리해야 한다.
   - 첫번째 Row가 Client에 전송되는 시간도 Static 보다는 나을지라도 여전히 느리다.
   - RR의 동시성 문제로 다중 사용자 환경에서 느리다.

장점 
   - Client가 필요한 양만 네트워크에 전송한다. 
   - Repeatable Read가 가능하다.
   - 일부만 억세스한다면 세번째 단점에도 불구하고 Static 보다는 더 빠르다.







마지막으로 Dynamic 커서 방식을 보자. 위 그림을 보면 먼가 이상하다고 생각하겠지만 이는 실수가 아니다. 

  1. WAS는 쿼리 실행을 요청한다.  
  2-3 DB는 쿼리 결과의 메모리에 로드한후 첫번째 Row의 정보를 WAS에게 보낸다. 
  WAS는 fetch를 요청할때마다 해당 SQL은 다시 실행되서 다시 메모리에 올린후 cursor 정보대로 x번째 로우를 보내준다.


몰랐던 사람에게는 당황스럽겠지만 Dynamic 커서로 실행되면 rs.next()때마다 매번 쿼리가 다시 실행된다. 2번 load Data때 전체를 올리느냐 혹은 일부만 올리는가는 역시 DB 마다 구현이 다르지만 보통은 전체를 다시 올린다. 이런 제정신이 아닌듯해 보이는 짓을 하는 이유는 Pantom을 read하기 위해서이다. 앞의 Keyset 커서는 쿼리가 실행된 이후 다른 사용자가 업데이트 한 정보는 볼수 있지만 사용자가 새로이 insert한 row(일명 pantom row)는 보지 못한다. 다이나믹 커서는 매 패치시마다 쿼리가 다시 실행되기 때문에 사용자가 insert한 정보를 억세스 가능하게 한다.

사실 만약 당신이 DB 전문가가 아니라면 Dynamic 커서는 쓸 필요도 없고 써서도 안된다. Dynamic Cursor가 사용하는 잠금 수준은 Serializable 이고 이는 극히 제한적이며 특수한 용도로 사용되며 이 모드는 워낙 DB마다 구현이 천차만별이기 때문에 전용 드라이버의 API를 사용하지 않으면 JDBC API로 이 cursor 방식을 실행시키는 방법도 없다.




여러번 강조했다시피 단순히 설정한다고 해당 커서 모드로 동작하는게 아니다. 위에서 말한 것 말고도 MSSQL의 경우 Select문이 트리거가 있는 테이블과 트리거가 없는 테이블을 조인하면 커서가 static 커서로 변환된다. Forward Only 커서가 읽기 전용이 아니라면 Dynamic 커서로 변환된다. 원격 테이블을 참조하는 분산쿼리라면 커서는 Keyset 방식으로 바뀐다 등등 수많은 예외 사항이 존재한다.

대부분의 프로그래머가 자신도 모르게 사용하는 커서 수준은 Forward Only, Static, Keyset 정도이다. 현재 프로그래밍에서 사용하는 커서 모드를 모른다면 몇가지 확인 방법이 있다. 

  - 첫째 rs.first(), rs.absolute(), rs.last() 같은 메소드가 정상작동한다면 static 아니면 keyset이다. Foward Only는 비동기적으로 작동하기 때문에 위 메소드의 커서이동이 불가능하고 Dynamic는 멤버 row가 고정되어 있지 않기 때문에 fetch absolute가 지원되지 않는다. 커서 용어로 Scroll이 가능한 커서는 static과 keyset이다.
  - 둘째 SQL에 group by 나 max 혹은 union, minus 같은 집계함수나 구문을 사용하였다면 static 이다. 집합의 레벨에 변형이 있다면 Forward Only로 작동할 수 없다. 
  - 셋째 구문에 Order by를 사용했다면 그리고 Where 구문을 위해 인덱스가 사용되었다면 static일 확률이 높다. 정렬정보를 유지하기 위해 Set정보를 유지해야 하기 때문이다. 임시 테이블을 만들 필요가 있는 Select문은 암시적으로 static 커서로 변환되는 경우가 많다.
  - 넷째 static과 keyset 모두 가능성이 있다면 아마도 static 일 것이다. keyset은 잘못 사용했을때 다중사용자 환경에서 잠금으로 인한 속도저하가 있기 때문에 대부분의 Driver는 static을 선택한다.

사실 몇가지 더 있겠지만 원리를 이해하였다면 쿼리만 보고도 이게 어떤 커서로 실행될 것인지 짐작할 수 있다. 단순하게 말하자면 아주 초보적이고, 하나의 테이블만을 대상으로 하고, 집계함수나 Order by 등의 구문을 쓰지 않으면 Forward Only로 동작할 확률이 높고 그 밖의 경우는 대부분 static 으로 동작한다고 해도 거의 틀리지 않는다.

사실 여기까지 소개한 Forward Only나 Static 모두 심각한 단점이 있기 때문에 - 라기 보단 대부분 적절히 피할 방법을 알지 못하기 때문에 - 어느 모드든 그리 좋아 보이지는 않는다. 그리고 이 말은 사실 대부분의 환경에서 심각한 문제를 겪고 있다는 뜻이기도 하다. 만약 정확하게 커서에 대한 지식을 쌓고 적절하게 프로그래밍 한다면 DB 서버는 다중환경에 훨씬 더 최적화 되어 있기 때문에 DB 사이즈와 상관없이 같은 부하에서 웹서버가 다운되면 다운됐지 DB 서버가 다운되는 일은 결코 없다. 


다음글에는 오라클과 MSSQL의 차이점과 오라클과 MSSQL은 이런 문제를 어떻게 피해가는가에 대하여...

'Framework > Database' 카테고리의 다른 글

프로시저 vs SQL  (0) 2009.03.23
Framework - 커서(Anonymous)  (0) 2009.03.19
Framework (Servant)  (0) 2009.03.12
Framework (DBController)  (0) 2009.03.12
Framework -커서(ForwardOnly, Sensitive(=Static))  (0) 2009.03.12
Posted by bleujin