⚡ 생각대로 살지 않으면 사는대로 생각한다.
⚡ 나는 어차피 잘 될 놈이다. 이미 잘 되고 있고, 계속해서 잘 되고 있다.
해당 포스팅은 내가 1장을 적고, 요약 및 정리한 것이다.
: SQL 파싱
+ SQL 최적화
- 프로시저 작성 : 절차 작성
- 컴파일 : 실행 가능한 상태로 만듬.
SQL 파서(Parser)
가 파싱을 진행SQL 옵티마이저
가 해당 역할을 함.SQL 옵티마이저
가 선택한 실행경로를 실행 가능한 코드 또는 프로시저 형태로 포맷팅하는 단계사용자가 원하는 작업을 가장 효율적으로 수행할 수 있는 최적의 데이터 액세스 경로를 선택해주는 DBMS의 핵심 엔진
옵티마이저의 최적화 단계
Decision Tree(의사 결정 트리)
를 통해 각 실행계획의 예상비용을 산정.❗단 실행계획에서 표시되는 비용들은 언제나 예상치다❗ 실수치가 아니다❗
+
를 붙이면 된다.Select /*+ INDEX(A 고객_PK) */
,
(콤마)를 사용할 수 있지만, 힌트와 힌트 사이에 사용 ❌라이브러리 캐시(Library Cache)
- SQL 파싱, 최적화, 로우 소스 생성과정을 거쳐 생성한 내부 프로시저를 반복 재사용할 수 있도록 캐싱해 두는 메모리 공간.
- 쉽게 보면, SQL 라이브러리다.
사용자가 SQL 명령을 하면,
사용자가 SQL문을 전달하면, DBMS는 SQL을 파싱한 후 해당 SQL이 라이브러리 캐시에 존재하는지 확인한다.
이 과정에서 캐시에서 찾아서 실행단계로 넘어가는 것을소프트 파싱
실패해서 최적화 및 로우 소스 생성 단계까지 모두 거치는 것을하드 파싱
사용자 정의 함수/프로시저, 트리거, 패키지 등은 생성할 때부터 이름을 갖는다.
컴파일한 상태로 딕셔너리에 저장되며, 사용자가 삭제하지 않는 한 영구적으로 보관된다.
실행할 때 라이브러리 캐시에 적재함으로써 여러 사용자가 공유하면서 재사용함.
!반면에 SQL은 이름이 따로 없다. 전체 SQL 텍스트가 이름 역할을 한다.
딕셔너리에 저장하지도 않는다.
처음 실행할 때 최적화 과정을 거쳐 동적으로 생성한 내부 프로시저를 라이브러리 캐시에 적재함으로써 여러 사용자가 공유하면서 재사용한다.
즉, 예를 들어 SELECT * FROM STUDENT
라는 쿼리를 날리면 SELECT * FROM STUDENT
이 자체가 라이브러리 캐시에 적재된다.
SELECT * FROM STUDENT WHERE NAME = SsangSu;
select * from STUDENT where NAME = SsangSu;
select * form student where name = SsangSu;
select * from student where name = SsangSu;
select * from student where name = SsangSu ;
select * from school.student where name = SsangSu;
위의 select 문은 의미상 같은 내용이고, 결과도 똑같이 나온다.(그렇다고 치자...)
그런데 자바에서도 문자열이 "ab".euqals("AB")
의 결과가 false이듯, 해당 명령문들은 다 다르게 인식하여 라이브러리 캐시에는 다 다르게 저장되어있다. 그래서 매번 하드 파싱이 일어나 성능적으로 문제가 생긴다.
그리고, SELECT * FROM STUDENT WHERE NAME = SsangSu;
해당 명령어에 NAME
값에 늘 다른 값을 주는 방식으로 하더라도,
라이브러리 캐시에는 아래와 같이 저장된다.
SELECT * FROM STUDENT WHERE NAME = SsangSu;
SELECT * FROM STUDENT WHERE NAME = oraking;
SELECT * FROM STUDENT WHERE NAME = javaking;
SELECT * FROM STUDENT WHERE NAME = tommy;
SELECT * FROM STUDENT WHERE NAME = karajan;
SELECT * FROM STUDENT WHERE NAME = Darren꼰;
매번 다른 SQL로 인식하여 하드파싱이 매번 일어나 성능에 문제가 생기는 것이다.
하지만 바인드변수(?
)를 사용하여 파라미터 Driven 방식으로 SQL을 작성하여 아래와 같이 SQL문을 작성하면,
SELECT * FROM STUDENT WHERE NAME = ? ;
한 번의 하드 파싱이 일어나고 그 이후엔 소프트 파싱이 되어 캐시 라이브러리에만 거치게 되어 SQL을 재사용하게 되어 성능이 좋아진다.
그리고 라이브 러리 캐시에는 이름이 매번 달라지더라도
SELECT * FROM STUDENT WHERE NAME = :1`
위와 같이 저장되어 있다. 즉, 하드파싱은 한 번 그 이후엔 계속해서 소프트파싱이 일어나게 되는 것이다.
SQL튜닝이 I/O튜닝이고, I/O튜닝이 곧 SQL 튜닝이다. 정확히는 디스크 I/O...
세그먼트
를 담는 콘테이너세그먼트 공간이 부족하면 익스텐트를 추가로 할당받긴하지만, 세그먼트에 할당된 모든 익스텐트가 같은 데이터파일에 위치하지 않을 수 있다.
서로 다른 데이터파일에 위치할 가능성이 더 높다.
하나의 테이플스페이스를 여러 데이터파일로 구성하면, 파일 경합을 줄이기 위해 DBMS가 데이터를 가능한 한 여러 데이터파일로 분산해서 저장하기 때문이다.
DBA
라고 부른다.ROWID
를 이용한다.ROWID
는 DBA + 로우 번호(블록 내 순번)로 구성되므로, 이를 분석하면 읽어야 할 테이블 레코드가 저장된 DBA를 알 수 있다.코드 캐시
라고 한다면, DB 버퍼캐시는 데이터 캐시
라고 할 수 있다.※참고 메모리 I/O(전기적 신호)에 비해 1,000배 이상 느림.
실제 SQL 성능을 향상하려면 물리적 I/O가 아닌 논리적 I/O를 줄여야함!
논리적 I/O는 통제가 가능하지만, 물리적 I/O는 통제가 불가능하다.
결국 통제 가능한 논리적 I/O를 줄여서 성능을 향상시킬 수 있다.
논리적 I/O를 줄이려면, 읽은 총 블록 개수를 줄인다.
Direct Path I/O를 제외한 모든 블록 I/O는 메모리 버퍼캐시를 경유한다.
위의 그림을 예시로 보면,
해시 체인
을 스캔하는 동안 다른 프로세스가 체인 구조를 변경하는 일이 생기면 곤란하기 때문에, 해시 체인 래치
가 존재하는데,해시 체인
에서 버퍼를 찾는동안 체인 구조를 변경하지 못 하도록 해시 체인
에 Lock을 건다.해시 체인
에 Lock을 거는 것을 캐시버퍼 채인 래치
라고 한다.읽고자 하는 블록을 찾았으면, 캐시버퍼 체인 래치(해시 체인에 Lock)
를 곧바로 해체해야, 다른 프로세스들이 해시 체인
을 통해 작업을 재개할 수 있는데,
이를 바로 해체하면, 후행 프로세스가 같은 블록에 접근해서 데이터가 조작될 수 있으므로, 데이터 정합성에 문제가 생김.
이를 방지 하기 위해 오라클에서 버퍼 Lock
을 사용하고,
캐시버퍼 체인 래치
를 해제하기 전에 버퍼 Lock
을 설정하여 데이터 정합성 문제를 방지한다.
캐시 버퍼 체인 래치 => 블록 찾음 => 버퍼 Lock => 캐시 버퍼 체인 래치 해체 => 후행 프로세스 해시 체인 접근.
-끝-