반응형

1. ALTER TABLE 테이블명_1 NOLOGGING;

    - 해당 테이블에서 작업시 훨씬 적은 양의 redo log만을 생성한다.

    - NOLOGGING으로 변경했던 테이블은 사용 후 다시 LOGGING 상태로 변경하는게 좋다.

 

2. insert /*+append*/ into 테이블명_1

    - APPEND 힌트를 줌으로써 DIRECT PATH INSERT가 가능하도록 한다.


3. insert /*+ PARALLEL(테이블명 프로세스수) */

    - 병렬처리 힌트를 줌으로 작업 속도가 증가한다.


4. CREATE INDEX 인덱스 명 ON 테이블명 (필드)

   TABLESPACE 테이블스페이스명

   NOLOGGING

   PARALLEL 8;

    - 인덱스 생성시에도 NOLOGGING과 PARALLEL을 적용하여 사용 할 수 있다

       다만 인덱스 생성 후 LOGGING 처리와 NOPARALLEL로 변경 적용 하여야 로깅처리 및 프로세스 사용을 적게 하게 된다.

       PARALLEL을 변경하지 않으면 생성시 사용한 프로세스 단위로 조회도 하게 되니 참고 !

    - ALTER INDEX 인덱스명 LOGGING NOPARALLEL;



5. 대량 테이블 SELECT 쿼리 수행시 시스템 자원을 활용하여 수행시간을 단축 시킬때 PARALLEL 힌트를 많이 사용한다.

   SELECT /*+ PARALLEL(테이블명) */ FROM 테이블명


6. DML 문장 (INSERT, DELETE, UPDATE)를 병렬로 수행하기 위해서는 현재 접속중인 세션을 DML로 지정해야만 병렬처리가 가능하다.

   ALTER SESSION ENABLE PARALLEL DML;

   사용 후 해당 세션의 DML을 해제 해주는게 좋다.

   ALTER SESSION DISABLE PARALLEL DML;


7. 

-- 1. 테이블 스페이스 사용량
SELECT   SUBSTR(A.TABLESPACE_NAME,1,30) TABLESPACE,
         ROUND(SUM(A.TOTAL1)/1024/1024,1) "TOTALMB",
         ROUND(SUM(A.TOTAL1)/1024/1024,1)-ROUND(SUM(A.SUM1)/1024/1024,1) "USEDMB",
         ROUND(SUM(A.SUM1)/1024/1024,1) "FREEMB",
         ROUND((ROUND(SUM(A.TOTAL1)/1024/1024,1)-ROUND(SUM(A.SUM1)/1024/1024,1))/ROUND(SUM(A.TOTAL1)/1024/1024,1)*100,2) "USED%"
FROM
         (SELECT   TABLESPACE_NAME,0 TOTAL1,SUM(BYTES) SUM1,MAX(BYTES) MAXB,COUNT(BYTES) CNT
          FROM     DBA_FREE_SPACE
          GROUP BY TABLESPACE_NAME
          UNION
          SELECT   TABLESPACE_NAME,SUM(BYTES) TOTAL1,0,0,0
          FROM     DBA_DATA_FILES
          GROUP BY TABLESPACE_NAME) A
GROUP BY A.TABLESPACE_NAME
ORDER BY TABLESPACE;

-- 2. 테이블 스페이스별 전체 용량 확인
SELECT TABLESPACE_NAME, SUM(BYTES)/1024/1024 TOTAL_SIZE
FROM    DBA_DATA_FILES
GROUP BY TABLESPACE_NAME;

-- 3. 테이블 스페이스 파일별 남은용량 확인
SELECT TABLESPACE_NAME, SUM(BYTES)/1024/1024 FREE_SIZE
FROM    DBA_FREE_SPACE
GROUP BY TABLESPACE_NAME;

-- 4. 테이블 스페이스 파일 확인
SELECT * FROM SYS.DBA_DATA_FILES;

-- 5. 테이블 스페이스 추가
ALTER TABLESPACE 테이블 스페이스명 ADD DATAFILE '데이터파일 경로 및 파일명.DBF' SIZE 30720M AUTOEXTEND ON;

-- 6. TEMP 테이블 스페이스 추가
ALTER TABLESPACE 테이블 스페이스명 ADD TEMPFILE '데이터파일 경로 및 파일명.DEF' SIZE 30720M AUTOEXTEND ON;

-- 7. 쿼리 실행 시간 확인
SET TIMING ON;

-- 8. 대량 데이터 인덱스 생성시 Sort area size 조절
ALTER SESSION SET SORT_AREA_SIZE = 10000000; -> 10M

--9. 실행중인 SQL확인용
SELECT a.osuser, a.SID, a.serial#, a.status, b.sql_text
FROM v$session a, v$sqlarea b
WHERE a.sql_address = b.address


반응형

사용방법

원격지에서도 사용가능합니다.

 

 


1. 디렉토리 조회하기

  select * from dba_directories;


2. 디렉토리 추가하기

  create directory dpump_dir as '/DATA/oracle';


3. 디렉토리 삭제 (잘못 입력 했을 경우 삭제)

  drop directory dpump_dir;


4. 디렉토리에 대한 권한 설정

grant read, write on directory dpump_dir to 사용자(또는 public);


5. expdp (oracle 계정 접속 후 사용 -> # su - oracle)

스키마(owner) 기준

$ expdp 사용자/비번 directory=2번에서 추가한 디렉토리 schemas=스키마명 dumpfile=파일명.dmp logfile=파일명.log

전체

$ expdp dpsp/dpsp dumpfile=파일명.dmp directory=2번에서 추가한 디렉토리 full=y logfile=파일명.log job_name=dpspfullexp

 

5.1 cmd 에서 사용방법

스키마(owner) 기준

expdp 사용자/비번@111.111.11.1:1521/orcl directory=2번에서 추가한 디렉토리 schemas=스키마명 dumpfile=파일명.dmp logfile=파일명.log

전체

expdp 사용자/비번@111.111.11.1:1521/orcl directory=2번에서 추가한 디렉토리 full=y dumpfile=파일명.dmp logfile=파일명.log


* expdp 실행모드


 - full 모드 : 데이터베이스 전체를 export 받을수있다

 - schema(owner) 모드 : schema 파라미터를 사용하여 특정 스키마 전체를 받을수있다.

 - tablespace 모드 : 해당 tablespace에 속한 모든 테이블을 받을수있습니다.

   transport_tablespace파라미터를 사용한다면 테이블과 테이블스페이스의 메타데이터도 받을수있습니다.

   단!!! transport_tablespace를 사용하려면 양쪽 OS가 같고 blocksize와 characterset 이 같아야 합니다.


 * expdp 파라미터


 - directory : 디렉토리 오브젝트를 지정하여 덤프 파일의 위치로 로그파일의 위치 지정가능

 - dumpfile : 파일시스템에 저장될 덤프파일의 이름을 지정해주는 파라미터로 파일이 여러개로 나누어질때 %U로 파일 이름을 고유하게 구분해 줄수 있다.

 - filesize : 한개 파일의 최대 크기를 지정

 - parfile : 파라미터 파일을 참조하여 사용

 - logfile/nologfile : 작업내용을 저장 할 로그파일명을 지정

 - content: 작업시 어떤 내용을 포함시킬 것인가를 결정

     All :테이블과 메타데이터를 모함한 모든것

     DATA_only:테이블 데이터만 포함 

     Metadata_only:메타데이터만 포함


 - exclude/include : 작업시 원하는 오브젝트만 선택하여 작업

   ex)scott schema 의 모든 것을 expdp 받되 emp,dept 테이블만 제외할경우

   expdp scott/tiger directory=datapump dumpfile=scott01.dmp schemas=scott exclude=table:\"IN\(\'EMP\',\'DEPT\'\)\"


 - query : 특정 조건에 맞는 데이터만 expdp 를 수행할 경우

 - sample : 데이터를 export할때 데이터가 너무 많을 경우 특정 퍼센트를 지정해서 추출 범위는 0.000001~100%

 - network_link : 원격으로 expdp할때 이기능은 DB link가 생성되어있어야 합니다

 - encrytion_password : expdp받은 테이블중에 특정 컬럼이 encryption되어있는경우 작업시 암호를 설정가능

 - job_name : 작업 수행시 job에 이름을 설정할수있습니다. 설정을 하지않으면 오라클이 자동으로 설정

 - status : 이 파라미터는 작업시 갱신된 내용을 status 설정된 시간 간격으로 진행상태를 보여주게 됩니다.

 - parallel : 작업수행시 프로세스를 몇 개를 사용 할 것인가를 지정하는 파라미터입니다. 주의할점은 지정된 개수만큼의 데이터 파일을 만들어 주어야하는데 앞에서 배운 %U 옵션을 주면 자동으로 생성 

 - attach : 일시 중단된 작업에 다시 접속 할때 사용

     ex) expdp scott/tiger job_name=dp1

       attach 모드 옵션

       add_file : 덤프파일을 추가

       exit:job 의 작업에서 빠져나감 

       kill_job : 해당 작업을 삭제

       parallel : 현재 작업중인 프로세스의 개수를 조절

       start_job :중단된 작업을 다시 시작 

       status : 현재 작업을 모니터링하는 시간조절

       stop_job : 현재 작업을 중단


6.impdp

  impdp 사용자/비번 dumpfile=파일명.dmp directory=dpump_dir2 schemas=스키마명 logfile=파일명.log


 * impdp 관련 파라미터


 * impdp 관련 파라미터는 거의 expdp와 비슷한 것이 많아서 중복설명은 생략하겟으니 expdp를 참고하세요


 - content

 - include 

 - exclude

 - table_exists_action : impdp 에 만 있는 옵션으로 동일한 이름의 테이블이 존재할 때 테이블의 데이터가 다를 경우가 있습니다.

     skip : 같은 테이블을 만나면 건너뛰고 다음테이블을 impdp 함

     append : 같은 테이블을 만나면 기존내용에 데이터를 추가

     truncate : 같은 테이블을 만나면 기존 테이블을 truncate 하고 새로 impdp 합니다

     drop : 기존 테이블을 drop하고 테이블을 새로 만들어서 새로운 내용을 impdp 합니다


 - remap_schema : scott user로 expdp 받은 테이블을 다른 유저로 impdp 할때 사용

     ex)impdp system/oracle directory=datapump dumpfile=scott.dmp schemas=scott include=table:\"\=\'emp\'\" remap_schema=scott:hr


 - remap_datafile : 기존 서버의 데이터파일 정보를 이전 후 서버의 데이터파일로 매핑해주는 파라미터

   expdp 된 덤프 파일 안에 데이터 파일 정보를 가지고 있는 경우만 사용가능

     ex)impdp system/oracle full=y directory=datapump dumpflie=full01.dmp  remap_datafile='/data1/users01.dbf':'/data2/users01.dbf'


 - remap_tablespace : 기존 테이블스페이스에서 다른 테이블스페이스로 테이블을 impdp시킬때 사용

     ex) impdp system/oracle directory=datapump dumpfile=scott.dmp remap_tablespace ='users1':'users2' schemas=scott;


     ex) impdp system/oracle directory=datapump dumpfile=scott.dmp remap_tablespace =users1_data:users2_data, users1_index:users2_index schemas=scott;


     ex) impdp system/oracle directory=datapump dumpfile=scott.dmp schemas=scott remap_schema=scott:scotttest remap_tablespace=users1_data:users2_data include=table:"in ('EMP','DEPT','SAL')" logfile=scott_impdp.log


     ex) impdp system/oracle directory=datapump dumpfile=scott.dmp schemas=scott remap_schema=scott:scotttest remap_tablespace=users1_data:users2_data include=table:\"in \(\'EMP\',\'DEPT\',\'SAL\'\)\" logfile=scott_impdp.log


     ps : 테이블명 대문자~~


- network_link



출처: https://cofs.tistory.com/72 [CofS]

반응형


[출처] Oracle DataBase 문자셋과 언어셋 확인 및 변경하기|작성자 시골사람


A db에서 export 한 것을 B db로 import 하려니 한글이 들어있는 칼럼의 경우에 사이즈가 맞지 않다고 나온다.
디비에 대해서 전혀 고려하지 않고 내리고 올리려고 했던터라 혹시나해서 보니 역시나 디비의 캐릭터가 다르더라.

그나저나 이 경우에는 기존에 데이터가 없어서 그냥 바꿔도 괜찮더라.

잘 정리된 글을 보고 따라했기에 그 글을 올린다.

Oracle DataBase 문자셋과 언어셋 확인 및 변경하기Oracle DataBase 문자셋과 언어셋 확인하기

 
[ 문자셋 확인 ]
SELECT name, value$
FROM sys.props$
WHERE name = 'NLS_CHARACTERSET';

SELECT name, value$
FROM sys.props$
WHERE name = 'NLS_NCHAR_CHARACTERSET';

[ 언어셋 확인 ]
SELECT name, value$
FROM sys.props$
WHERE name = 'NLS_LANGUAGE';

Oracle DataBase 문자셋과 언어셋 변경하기

 
- 문자셋 변경 : Oracle DataBase 관리자로 접속하여 NLS_CHARACTERSET, NCHAR의 CHARACTERSET에 원하는 문자셋을 변경할 수 있다.
- 언어셋 변경 : 문자셋과 마찬가지로 오라클 데이터베이스 관리자로 접속하여 NLS_LANGUAGE에 원하는 언어셋을 변경할 수 있다.

[ 문자셋 변경 ]
UPDATE sys.props$
SET value$ = '[ 변경을 원하는 문자셋 (KO16KSC5601, AL32UTF8 등)]'
WHERE name = 'NLS_CHARACTERSET';

UPDATE sys.props$
SET value$ = '[ 변경을 원하는 문자셋 (KO16KSC5601, AL32UTF8 등)]'
WHERE name = 'NLS_NCHAR_CHARACTERSET';

[ 언어셋 변경 ]
UPDATE sys.props$
SET value$ = '[ 변경을 원하는 문자셋 (AMERICAN_AMERICA.KO16KSC5601, AMERICAN_AMERICA.AL32UTF8등)]'
WHERE name = 'NLS_LANGUAGE'';
Oracle Character set 변경 자세히...............
####################################################
#### .bash_profile 환경변수
export ORACLE_BASE=/home/oracle
export ORACLE_HOME=$ORACLE_BASE/oracle/product/10.2.0/db_1
export ORACLE_SID=ora10
export LD_LIBRARY_PATH=$ORACLE_HOME/lib
#export LD_ASSUME_KERNEL=2.4.19
export PATH=$PATH:$ORACLE_HOME/bin

export NLS_LANG=KOREAN_KOREA.KO16KSC5601 #한글
export NLS_LANG=KOREAN_KOREA.KO16MSWIN949 #한글(추천:지원캐릭터가 더 많음 -뷃,숖..)
export NLS_LANG=AMERICAN_AMERICA.UTF8 #유니코드



####################################################
#### 캐릭터 셋 설정 확인

SELECT NAME,VALUE$ FROM PROPS$ WHERE NAME ='NLS_LANGUAGE' OR NAME ='NLS_TERRITORY' OR NAME ='NLS_CHARACTERSET';


####################################################
#### 오라클 캐릭터 셋 변경

update props$ set VALUE$='UTF-8' where name='NLS_CHARACTERSET';
update props$ set VALUE$='KO16MSWIN949' where name='NLS_CHARACTERSET';
update props$ set VALUE$='KO16KSC5601' where name='NLS_CHARACTERSET';

update props$ set VALUE$='KOREAN' where name='NLS_LANGUAGE';
update props$ set VALUE$='KOREA' where name='NLS_TERRITORY';



####################################################
#### 캐릭터 셋 변경후에 확인 사항(필수)

에러 유형 :
EXP-00008: ORACLE 오류 6552가 발생했습니다
ORA-06552: PL/SQL: Compilation unit analysis terminated
ORA-06553: PLS-553: 알 수 없는 문자 집합 이름입니다


Problem description
===================
You receive the following error when (re)compiling or calling a piece of pl/sql:
ORA-06550: line <num>, column <num>: ....
or
ORA-06552: PL/SQL: Compilation unit analysis terminated
followed by
ORA-06553: PLS-553: character set name is not recognized
에러 원인 : character set 이 섞여있음.



캐릭터셋 확인 쿼리 :
select distinct(nls_charset_name(charsetid)) CHARACTERSET,
decode(type#, 1, decode(charsetform, 1, 'VARCHAR2', 2, 'NVARCHAR2','UNKOWN'),
9, decode(charsetform, 1, 'VARCHAR', 2, 'NCHAR VARYING', 'UNKOWN'),
96, decode(charsetform, 1, 'CHAR', 2, 'NCHAR', 'UNKOWN'),
112, decode(charsetform, 1, 'CLOB', 2, 'NCLOB', 'UNKOWN')) TYPES_USED_IN
from sys.col$ where charsetform in (1,2) and type# in (1, 9, 96, 112);

캐릭터셋 확인 쿼리 결과(잘못된 경우): varchar2가 2개 의 캐릭터셋이 설정되어있음.
CHARACTERSET TYPES_USED_IN
-----------------------------------------------------
AL16UTF16 NCHAR
AL16UTF16 NVARCHAR2
AL16UTF16 NCLOB
US7ASCII CHAR
US7ASCII VARCHAR2
WE8DEC VARCHAR2
US7ASCII CLOB


캐릭터셋 확인 쿼리 결과(정상인 경우): TYPES_USERD_IN 하나당 하나의 캐릭터셋
CHARACTERSET TYPES_USED_IN
-----------------------------------------------------
AL16UTF16 NCHAR
AL16UTF16 NVARCHAR2
AL16UTF16 NCLOB
AL32UTF8 CHAR
AL32UTF8 VARCHAR2
AL32UTF8 CLOB


해결 방법 : 

1. INIT.ORA 안에 있는 parallel_server parameter 가 false 거나 아예 세팅되어있지 않은지 확인.
SQL>show parameter parallel_server

2. sqlplus "/as sysdba"로 다음 쿼리 실행(기존 데이터 백업 필수)
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER SYSTEM ENABLE RESTRICTED SESSION;
ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
ALTER SYSTEM SET AQ_TM_PROCESSES=0;
ALTER DATABASE OPEN;
COL VALUE NEW_VALUE CHARSET
SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER='NLS_CHARACTERSET';
COL VALUE NEW_VALUE NCHARSET
SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER='NLS_NCHAR_CHARACTERSET';

-- UTF8로 바꿀 경우(선택)
ALTER DATABASE CHARACTER SET INTERNAL_USE UTF8;
ALTER DATABASE NATIONAL CHARACTER SET INTERNAL_USE AL16UTF16;

-- 한글로 바꿀 경우(선택)
ALTER DATABASE CHARACTER SET INTERNAL_USE KO16MSWIN949
ALTER DATABASE NATIONAL CHARACTER SET INTERNAL_USE AL16UTF16;

-- oracle reboot 2번.
SHUTDOWN IMMEDIATE;
STARTUP;
SHUTDOWN IMMEDIATE;
STARTUP;

3. parallel_server parameter 수정한 경우 원복.




'SQL > Oracle' 카테고리의 다른 글

Oracle 관리시 유용한 쿼리  (1) 2019.02.13
오라클 데이터 백업 및 복원  (0) 2019.02.13
오라클 사용자 생성 및 권한주기  (0) 2018.08.27
반응형

출처: http://fordev.tistory.com/23 [개발자를 위하여...]


sql command line 접속시 sys 로그인 처리


connect sys/oracle as sysdba



1. 계정의 테이블 스페이스 생성

create tablespace [tablespace_name] 
datafile '/home/oracle/oradata/DANBEE/[file_name].dbf' size 500m;

예)
CREATE TABLESPACE ADMIN DATAFILE 'D:\ORACLE\ORADATA\XE\ADMIN.dbf' SIZE  500M ;


2. 오라클 유저 만들기

CREATE USER [user_name] 
IDENTIFIED BY [password]
DEFAULT TABLESPACE [tablespace_name]
TEMPORARY TABLESPACE TEMP;

예)
CREATE USER nextree IDENTIFIED BY nextree DEFAULT TABLESPACE NEXTREE TEMPORARY TABLESPACE TEMP;


3. 생성한 USER에 권한주기

GRANT connect, resource, dba TO [user_name];

예)
grant connect, dba, resource to 유저명; (모든 권한 주기)

GRANT CREATE SESSION TO 유저명         // 데이터베이스에 접근할 수 있는 권한
GRANT CREATE DATABASE LINK TO 유저명
GRANT CREATE MATERIALIZED VIEW TO 유저명
GRANT CREATE PROCEDURE TO 유저명
GRANT CREATE PUBLIC SYNONYM TO 유저명
GRANT CREATE ROLE TO 유저명
GRANT CREATE SEQUENCE TO 유저명
GRANT CREATE SYNONYM TO 유저명
GRANT CREATE TABLE TO 유저명             // 테이블을 생성할 수 있는 권한
GRANT DROP ANY TABLE TO 유저명         // 테이블을 제거할 수 있는 권한
GRANT CREATE TRIGGER TO 유저명 
GRANT CREATE TYPE TO 유저명 
GRANT CREATE VIEW TO 유저명

GRANT  
 CREATE SESSION
,CREATE TABLE
,CREATE SEQUENCE   
,CREATE VIEW
TO 유저명;

4. 생성한 USER로 ORACLE에 접속하기

sqlplus nextree/nextree[@db_sid]


5. 계정 삭제하기

drop user 사용자계정 cascade;

-- 테이블 스페이스 크기 확장해주는 쿼리문
alter database 
datafile 'D:\oracle\oradata\XE\ADMIN.DBF'  resize 900M;

--테이블 스페이스 정보 보는 쿼리문 
SELECT file_name, tablespace_name, bytes, status FROM  DBA_DATA_FILES;

--테이블 명시적 인덱스 생성
--1번째 방법
CREATE INDEX MSID_IDX1 ON TEST(MSID)
--2번째 방법
create index test1_test on test1(test) 
tablespace users 
storage 

initial 10k 
next     10k 
pctincrease 0) 
pctfree 10

--테이블 정보 보는 쿼리
select * from user_constraints-- where table_name = upper('test1');

--ORA-00054: 자원이 사용중이고, NOWAIT가 지정되어 있습니다 해결 방법 
select a.sid, a.serial# 
from v$session a, v$lock b, dba_objects c 
where a.sid=b.sid and 
b.id1=c.object_id and 
b.type='TM' and 
c.object_name='CAR_INFO';

alter system kill session '12, 27846';

-- CAR_INFO에는 있는 값을 DASH_BOARD에 넣기
INSERT INTO DASH_BOARD(CAR_LICEN_NUM)
SELECT CAR_LICEN_NUM FROM CAR_INFO
MINUS
SELECT CAR_LICEN_NUM FROM DASH_BOARD


SYSTEM 계정 패스워드 변경하기
사용자계정 : /as sysdba

alter user system identified by "암호";
일반 스트링은 관계없지만 특수문자가 있을경우 반드시 "" 따옴표로 감싸준다.


1. 전체 단위(오라클 DB 전체)


덤프(익스포트) |

C:\) exp userid=system/비밀번호 file='C:\파일명.dmp' full=y


임포트 |

C:\) imp userid=system/비밀번호 file='C:\파일명.dmp' full=y



2. 사용자 단위(특정 계정/사용자의 DB )


덤프(익스포트) |

C:\) exp userid=계정명/비밀번호 file='C:\파일명.dmp' 


임포트 |

C:\) imp userid=계정/비밀번호 file='C:\파일명.dmp' 



3. 테이블 단위(특정 계정의 특정 테이블만을 추출)


덤프(익스포트) |

C:\) exp userid=계정명/비밀번호 file='C:\파일명.dmp' tables=테이블명


임포트 |

C:\) imp userid=계정명/비밀번호 file='C:\파일명.dmp' 

반응형

출처 : itworld

링크 : http://www.itworld.co.kr/print/105792



누구나 더 빠른 데이터베이스 쿼리를 원하며, SQL 개발자와 DBA 모두 이런 목표를 달성하기 위해 오랜 사용으로 효과가 입증된 여러 가지 방법에 의지한다. 하지만 안타깝게도 그 어떤 방법이라도 하나만으로는 완벽하거나 확실하지 않다. 이처럼 모든 쿼리를 미세조정, 즉 튜닝하기 위한 정답이 존재하지는 않지만, 길을 밝히는데 도움이 되는 풍부한 관례가 있다. 몇 가지는 RDMBS에 관련된 사항들이지만, 대부분의 팁은 모든 관계형 데이터베이스에 적용된다.


SQL 서버, 오라클, DB2, 사이베이스, MySQL, 아니면 다른 관계형 데이터베이스 플랫폼 중 어디에서 코딩을 하고 있더라도, 목표는 같다. 데이터베이스가 가능한 빨리 쿼리를 처리하는 동시에 현실적으로 가능한 많은 동시 사용자를 지원하는 것이다. 이는 공간과 자원 관리를 최적화하는 동시에 잠금(Locking), 입출력, 그리고 네트워크 트래픽은 최소화해야 한다는 것을 의미한다.


데이터베이스 튜닝은 기술인 동시에 과학이다. 다음은 사용 중인 데이터베이스를 더 빠르고 더욱 효과적으로 만들어주는 21가지 입증된 규칙이다.


1. 가능하면 커서(Cursor)를 피하라.

커서는 일련의 데이터에 순차적으로 액세스할 때 검색 및 현재 위치를 포함하는 데이터 요소를 말한다. 커서를 피하는 것은 아주 쉬운 결정이다. 커서는 속도 문제를 겪을 뿐 아니라, 다른 작업을 필요 이상 지연시킬 정도로 하나의 작업을 블록(Block)시킬 수도 있다. 이는 시스템의 동시성을 크게 저하시킨다.


2. 커서를 피할 수 없다면, 임시 테이블(temp table)을 사용하라

커서를 사용해야만 할 때가 있다. 그런 경우, 라이브 테이블(Live Table)보다는 임시 테이블에 대한 커서 작업을 수행하는 것이 더 낫다. 훨씬 더 작은 라이브 테이블에 대한 하나의 UPDATE 문이 있을 수 있다. 짧은 시간 동안에만 잠금(Lock)을 유지하게 되어 동시성을 크게 증진시켜 준다.


3. 임시 테이블을 현명하게 사용하라

다른 여러 가지 상황에서도 임시 테이블을 사용할 수 있다. 예를 들어, 어떤 테이블을 더 큰 테이블에 조인(Join) 시켜야만 한다면, 더 큰 테이블에서 필요한 일부 데이터만 임시 테이블로 끌어(Pull)와서 대신 그것과 조인시킴으로써 성능을 개선할 수 있다. 이는 필요한 처리 능력을 크게 줄여주며, 프로시저에 같은 테이블에 대해 유사한 조인을 해야만 하는 여러 개의 쿼리가 있는 경우 유용하다.


4. 데이터를 미리 준비하라

흔히 간과되는 예전의 기법이다. 커다란 테이블에 대해 비슷한 조인 작업을 할 보고서(Report)나 프로시저가 있다면, 미리 테이블을 조인시키고 테이블들을 하나의 테이블에 영속화(Persisted)시킴으로써 데이터를 사전 준비하라. 그렇게 하면, 사전 준비된 해당 테이블에 대한 보고서 작업을 실행할 수 있어서, 대규모 조인 작업을 피할 수 있다.


항상 이 기법을 사용할 수는 없지만, 대부분의 환경에는 늘 조인되는 인기 테이블이 있기 마련이다. 이런 테이블들을 사전에 준비하지 못할 이유가 전혀 없으며, 서버 자원을 절약하기 위한 훌륭한 방법이다.


5. 복합 뷰(Nested View)를 최소화하라

뷰는 엄청난 쿼리를 사용자들로부터 가리는데 훌륭하지만, 하나의 뷰 안에 또 다른 뷰와 내부에 있는 다른 뷰를 (계속해서) 중첩시키다 보면 심각한 성능 저하를 유발할 수 있다. 너무 많은 수의 복합 뷰는 모든 쿼리에 대해 엄청난 양의 데이터가 반환(Return) 되는 결과를 초래해서, 데이터베이스 성능을 말 그대로 기어 다니게 만들 수 있다. 혹은, 더 나가서, 쿼리 최적화기(Optimizer)가 포기해서 아무것도 반환되지 않을 수도 있다.


복합 뷰를 풀어내는 것으로 쿼리 응답 시간을 몇 분에서 몇 초로 줄일 수 있다.


6. UPDATE 문 대신 CASE 문을 사용하라

다음 시나리오를 살펴보자. 임시 테이블에 데이터를 삽입하고 있으며 다른 값이 존재할 경우 해당 데이터가 특정 값을 표시하도록 해야 한다. Customer 테이블에서 데이터를 끌어오고 있으며 주문 액수가 100,000달러 이상인 고객에 대해서 “우대”라는 라벨을 붙이고 싶어한다고 하자. 그래서, 100,000달러 이상의 주문 금액을 보유하고 있는 모든 고객에 대해서 CustomerRank 열에 “우대”라고 설정하기 위해 테이블에 데이터를 삽입하고 UPDATE 문을 실행한다.


논리적으로 보인다, 그렇지 않은가? 문제는 UPDATE 문이 로그된다는 것이다 즉, 테이블에 대한 모든 한 번의 쓰기 작업 당 두 번의 쓰기 작업이 일어난다는 의미이다. 물론, 이 문제를 피하는 방법은 SQL 쿼리 자체에서 인라인(Inline) CASE 문을 사용하는 것이다. 이는 모든 행에 대해 주문량 조건을 확인하고 테이블에 쓰기 전에 “우대” 라벨을 설정한다. 성능 증가는 깜짝 놀랄 정도이다.


7. 스칼라(Scalar) 대신 테이블 반환 함수(Table-Valued Functions)를 사용하라

전문가들이 사용하는 팁이 있다. 쿼리의 SELECT 목록에서 스칼라 함수를 사용할 경우, 그 대신에 쿼리에서 테이블 반환 함수를 사용하고 CROSS APPLY 문을 사용하면 성능을 개선할 수 있다. 이는 쿼리 시간을 절반으로 대폭 줄여줄 수 있다.



8. SQL 서버에서 분할(Partition)을 활용하라

SQL 서버 엔터프라이즈 사용자들은 성능을 가속화하기 위해 데이터 엔진의 자동 분할 기능을 활용할 수 있다. SQL 서버에서는 간단한 테이블조차도 하나의 분할로 생성되며, 사용자는 나중에 그것을 필요에 따라 여러 개의 분할로 쪼갤 수 있다. 테이블 간에 많은 양의 데이터를 옮겨야 할 경우, INSERT와 DELETE 문 대신에 SWITCH 명령을 사용할 수 있다. 테이블 간에 많은 양의 데이터를 삭제하고 삽입하는 대신, 단일 테이블에 대한 메타데이터만 변경하는 것이기 때문에, 실행하는데 몇 초 밖에 걸리지 않는다.


9. 배치 모드로 삭제(Delete)와 갱신(Update) 작업을 하라

거대한 테이블에서 많은 양의 데이터를 삭제하거나 업데이트하는 작업은 악몽일 수 있다. 문제는 이 두 가지 명령문 모두가 하나의 트랜잭션으로 실행되는 것이며, 프로세스를 중지시켜야 한다거나 작업 도중에 어떤 일이 일어진다면, 시스템은 전체 트랜잭션을 복원(Roll Back)시켜야만 한다. 이 작업은 진행 중인 다른 트랜잭션들을 블록 시킬 뿐 아니라, 많은 시간이 걸릴 수 있어서, 기본적으로 시스템 병목을 일으킨다.


해결책은 작은 배치 단위로 삭제나 업데이트 작업을 하는 것이다. 트랜잭션이 중지돼도, 소수의 행만 복원하면 되므로, 데이터베이스는 훨씬 더 빨리 온라인으로 돌아온다. 그리고 더 작은 배치작업들이 디스크에 커밋(Commit)하는 동안, 다른 작업들이 끼어들어서 어느 정도의 작업을 할 수 있어서 동시성이 크게 개선된다.


10. 서두르지 말고 천천히 하라

일부 개발자들은 이런 삭제와 업데이트 작업이 같은 날 완료되어야만 한다는 사실을 머리 속에 새겨놓고 있다. 늘 그런 것은 아니다. 특히, 아카이빙 작업은 더욱 그렇지 않다. 이 작업은 필요한 만큼 늘일 수 있으며, 이 작업을 완료하는 데는 더 작은 배치작업들이 도움이 된다. 이런 집약적인 작업을 더 천천히 할 수 있다면, 여분의 시간을 시스템이 다운되지 않도록 하는 데 투여하기 바란다.


11. ORM을 피하라

ORM(Object-relational Mapper: 객체 관계형 매퍼)는 지구상에서 최악의 코드를 만들어 내고 있으며, 개발자가 직면할 가능성이 있는 대부분의 성능 문제에 책임이 있다. 그렇지만, ORM을 피할 수 없다면, 스스로 자체적인 저장 프로시저를 작성하고 ORM이 자체 쿼리를 작성하는 대신 사용자가 작성한 쿼리를 호출하게 함으로써 부정적인 측면을 최소화할 수 있다.


12. 가능한 경우, 저장 프로시저(Stored Procedure)를 사용하라

더 훌륭한 코드로 이끄는 것 외에, 저장 프로시저는 다른 많은 장점도 가지고 있다. 저장 프로시저는 호출이 더 짧을 것이기 때문에, 트래픽을 크게 줄여준다. 프로파일러(Profiler) 같은 도구를 사용해서 추적하기가 더 쉬워서 사용자가 성능 통계치를 확보하고 잠재적인 문제를 더 빨리 규명할 수 있게 해준다. 더욱 일관성 있는 방식으로 정의할 수 있으며, 이는 실행 계획(Execution Plan)을 재사용할 가능성이 더 높으며, 임의 쿼리에 비해 엣지 케이스(Edge Case)와 감사용으로 사용하기가 더 쉽다는 의미이다.


많은 닷넷 코더들은 비즈니스 로직이 데이터베이스가 아닌 애플리케이션의 프론트 엔드에 속한다고 믿고 있다. 그렇지만, 그들은 틀렸다(대부분의 경우).


13. 더블 디핑(Double-Dipping: 중복 처리)을 피하라

저장 프로시저 사용은 때로 “더블 디핑”으로 이어질 수 있다. 대규모 테이블에 대해 별개의 쿼리를 여러 개 실행하고, 그것들을 임시 테이블에 넣은 다음에, 테이블들을 다시 조인하는 것이다. 이는 성능에 커다란 방해물이 될 수 있다. 가능한한 대규모 테이블을 한 번만 쿼리 하는 것이 훨씬 더 낫다.


조금 다른 시나리오는 한 프로세스의 몇 가지 단계에서 커다란 테이블의 일부가 필요한 경우로, 이는 매 번 커다란 테이블에 대한 쿼리를 유발한다. 일부에 대한 쿼리를 실행하고 그것을 다른 곳에 영속화 시킨 다음에, 후속 단계를 영속화된 더 작은 데이터 세트로 유도하라.


14. 커다란 트랜잭션은 작은 트랜잭션 여러 개로 쪼개라

단일 트랜잭션에서 여러 개의 테이블을 처리하는 작업은 해당 트랜잭션이 끝날 때까지 모든 테이블을 잠글 수 있기 때문에, 다수의 블로킹으로 이어진다. 해결책은 이 트랜잭션을 각각이 개별적으로 단일 테이블에 대한 작업을 하는 여러 개의 루틴(Routines)으로 쪼개는 것이다. 이는 블로킹 횟수를 줄여주고 다른 작업들이 계속해서 이루어질 수 있도록 다른 테이블들을 풀어준다.


15. 트리거(Trigger) 사용을 자제하라

하려고 하는 작업이 무엇이든, 원래 작업의 동일한 트랜잭션에서 수행될 것이기 때문에 트리거 사용도 비슷한 문제로 이어질 수 있다. 이는 트리거가 완료될 때까지 여러 개의 테이블을 잠그는 결과를 초래할 수 있다는 의미이다. 이런 트리거를 별개의 트랜잭션들로 쪼개면 더 적은 수의 자원을 잠그게 돼서 필요한 경우 변경사항 복원을 쉽게 만들어준다. 가능하면 트리거를 피하라.



16. GUID에 대한 클러스터링을 피하라

테이블 데이터 정렬을 위해 GUID(Globally Unique Identifier: 범용 고유 식별자)를 사용하지 말라. 임의로 생성되는 이런 16비트 숫자는 사용자의 테이블을 훨씬 더 빨리 파편화한다. DATE나 IDENTIFY 같은 값을 점진적으로 증가시켜서 데이터를 정렬하는 것이 훨씬 낫다. 휘발성 있는 모든 열에 대해서도 갖은 규칙이 적용된다. 단 몇 분 만에 극적으로 테이블들이 파편화될 수도 있다.


17. 테이블에 있는 모든 것을 카운트(Count)하지 말라

테이블에 데이터가 존재하거나 어떤 고객에 대한 데이터가 존재하는 지를 확인할 필요가 있으며, 확인 결과에 따라, 어떤 조치를 취해야 한다고 가정하자.


필자는 그런 데이터의 존재를 확인하기 위해 누군가가 SELECT COUNT(*) FROM dbo.T1 명령을 실행하는 것을 자주 보았다.


SET @CT = (SELECT COUNT(*) FROM dbo.T1);

If @CT > 0

BEGIN <Do something>

END


전혀 불필요한 명령이다. 존재 여부를 확인하고 싶다면, 다음과 같이 하라:


If EXISTS (SELECT 1 FROM dbo.T1)

BEGIN

<Do something>

END


다른 말로 하면, 테이블에 있는 모든 것을 카운트하지 말라는 것이다. 첫 번째 행으로 돌아가면 찾을 수 있다. SQL 서버는 EXIST 문을 제대로 사용할 수 있을 정도로 똑똑하며, 두 번째 블록의 코드는 아주 빠르게 결과를 돌려준다. 테이블이 크면 클수록, 더 많은 차이를 낼 것이다.


18. 행을 카운트하려면 시스템 테이블(System Table)을 사용하라

커다란 테이블의 행을 정말로 카운트할 필요가 있다면, 시스템 테이블에서 끌어 올 수 있다. ‘SELECT rows from sysindex’ 명령문은 모든 인덱스에 대한 열의 수를 알려줄 것이다.


그리고 클러스터된 인덱스가 데이터 자체를 나타내기 때문에, ‘WHERE indid = 1’을 추가하면 테이블 행을 얻을 수 있다. 그 다음에는 그냥 테이블 이름을 추가하기만 하면 만사형통이다. 이렇게 하면, 최종 쿼리는 다음과 같다


SELECT rows FROM sysindexes WHERE object_name(id) = ‘T1’ AND indexid = 1


19. 필요한 수의 열만 끌어오라

열을 개별적으로 나열하는 대신 모든 쿼리를 SELECT * 명령문으로만 코딩한다면 너무 쉬울 것이다. 또 다시 문제는 필요한 것보다 더 많은 데이터를 끌어 온다는 것이다. 개발자가 120개의 열과 수 백만 개의 행을 가지고 있는 테이블을 대상으로 SELECT *를 실행하고는, 겨우 3~5개만 사용하고 말았다. 그 시점에, 개발자는 필요한 것보다 훨씬 더 많은 데이터를 처리시켰을 뿐만 아니라 다른 프로세스들로부터 자원을 뺏어가기도 한 것이다.


20. 네거티브 검색(Negative Search)를 피하기 위해 쿼리를 재 작성하라

인덱스를 사용할 수 없는 쿼리를 사용해서 데이터를 행 별로 비교할 필요가 있을 때, 예를 들어 FROM Customers WHERE RegionID <> 3 같은 경우는 인덱스를 사용할 수 있도록 쿼리를 재작성하는 것이 더 낫다.


SELECT * FROM Customers WHERE RegionID < 3 UNION ALL SELECT * FROM Customers WHERE RegionID


데이터 세트가 큰 경우, 인덱스를 사용하는 것이 테이블 스캔 버전을 크게 능가하는 결과를 내 놓을 수도 있다. 물론, 더 열악한 결과를 낼 수도 있으니 구현에 앞서 시험해보라.


필자는 이 쿼리가 팁 13번(중복 처리를 피하라)을 어긴다는 것을 알았지만, 융통성 없는 규칙은 없다는 것을 보여주는 것이기도 하다. 여기서는 중복 처리를 했지만, 대가가 큰 테이블 스캔을 피하기 위해서이다.


21. 맹목적으로 코드를 재사용하지 말라

필요한 데이터를 끌어온다는 것을 알기 때문에 다른 누군가의 코드를 복사하기가 십상이다. 문제는 종종 필요한 것보다 훨씬 더 많은 데이터를 끌어오고 있으며, 개발자들이 양을 줄이려 하는 경우는 거의 없어서, 거대한 데이터 상위 집합에 이르고 만다. 이는 대개 추가적인 외부 조인(Outer Join)이나 WHERE 문에서 추가 조건 형태로 나타난다. 재사용된 코드를 꼭 필요한 수준으로 줄일 수 있다면 커다란 성능 이득을 볼 수 있다.


이런 기법들 모두가 모든 상황에서 작동하지는 않는다는 것만 명심하라. 어떤 기법이 가장 잘 동작하는지를 알기 위해 실험을 해야만 할 것이다. 그렇지만, 일반적으로 언급한 SQL 팁들을 잘 사용하면 동시성을 증가시키고, 성능을 가속화시키며, DBA부터 최종 사용자들까지, 모든 사람의 삶을 훨씬 더 쉽게 만들어 줄 것이다.

반응형


SQL SERVER 관련해서 검색 하다가 나온 자료....

하늘몽구님 감사합니다...


출처 : 하늘몽구

링크 : http://skymong9.egloos.com/v/1841645


ORCLE이 정말 좋은 RDB라는 것을 알게 하는 것이 바로 페이징 기법일거라 생각되네요.
MySQL도 내부적으로 페이징이 가능한 쿼리를 지원해주지만 MSSQL은 최근 2005버전까지도... 좋은 페이징 기법을 소개하지 못하고 있는 것 같네요. 성능 좋은 페이징 기법이 공개된 것이 있기도 하지만... 초심자에게 쉽지 않은 쿼리들인 것 같습니다.
이에 조금 쉽게... 어떻게 하면 성능이 향상되는지를 설명해 보려고 합니다.

아래의 내용은 온라인상에서 바로 작성하는 내용이고 맞춤법등이 틀릴 수 있기 때문에 copy해서 사용하지 마시기 바랍니다.

개념을 잡는 정도로 활용하시면 좋을 것 같습니다.

MS SQL의 페이징 기법의 키는 TOP 키워드입니다.

#1. TOP과  클러스터드 인덱스

SELECT TOP 10 ID, subject, contents FROM TBL

이 쿼리는 누구나 알고 있는 쿼리입니다. 여기서 중요한 것이 TOP 10 입니다.  상위 10개만 갖고 오겠다는 뜻입니다.MSSQL은 내용저장을 클러스터드 인덱스 순으로 저장을 하게 됩니다. 만약 ID를 PK로 지정하셨거나 따로 클러스터드 인덱스로써 ID를 지정해 놓으셨다면 ID순으로 자동으로 정렬이 되어 상위 10개만 가져오게 됩니다.

기본키는 반드시 설정할 필요는 없습니다. 다만 레코드를 구분하는 아이디값을 대게의 경우 기본키로 놓게 되는데요.

사실 기본키가 성능에 있어서의 의미는 없다고 봅니다.(확인된 바 없음)

기본키는 자동으로 인덱스 컬럼이 된다는게 의미가 있겠죠. 그것도 테이블당 딱 하나 사용할 수 있는 클러스터드 인덱스로 자동 설정됩니다. 헌데 문제는 ASC로 설정된다는 것입니다.

웹페이지에서 최근 글의 경우는 대부분 DESC로 정렬합니다. 즉 최근 글을 먼저 표시해주지요. PK로 만들더라도 꼭 ID를 DESC로 인덱스하도록 만들어야 합니다.

바로 이 유일 인덱스 컬럼이 성능을 좌우하는 핵입니다.

초심자의 경우 인덱스의 중요성을 넘기는 경우가 많은데요. PK가 중요한 것이 아니라 정렬을 원하는 컬럼을 클러스터드 인덱스로 만들어 놓는 것이 성능에 가장 중요합니다.(레코드 수가 늘 수록 엄청난 성능 향상이 있습니다.)

팁) TOP 10 PERCENT라는 키워드도 가능합니다. 말 그대로 전체의 10%만 가져온다는 것입니다. 쓸모가 많은 팁이라 생각되네요.


#2. 가장 많이 쓰이는 페이징 쿼리

SELECT TOP 10 ID, subject, contents FROM TBL where ID not in (SELECT TOP 현재페이진 이전까지의 모든 게시물 수 ID FROM TBL order by ID DESC) order by DESC

참 좋은 쿼리입니다. MS SQL에서 나올 수 있는 가장 간결하고 좋은 쿼리가 아닌가 싶습니다.

저역시 작은 규모의 게시판 종류는 무조건 이 쿼리를 이용합니다. 유지보수가 편하기 때문입니다. 누구나 쉽게 알아 볼 수 있기 때문에 제가 도저히 못 봐줄 유지보수 프로젝트라면 다른 사람이 대신할 수 있는 쿼리이기 때문에... 이 쿼리를 즐겨사용합니다.

다만 이 쿼리에는 조건이 하나 붙습니다.

사용자들이 100페이징 이하(게시물 수로 2000개 이하정도)의 글을 되도록 조회한다.

이 쿼리는 2만건 이하의 테이블에 적당하다고 생각됩니다.

1페이지라고 하면 성능이 최고가 되며
2페이지라면 not in 안의 쿼리문에 의해 한페이지가 10개의 글이라고 가정하면 10개를 일단 불러 들이게 됩니다. 제거를 위해서죠.
3페이지라면 20개를 불러들여서 제거를 해야 하겠고
4페이지라면 30개를...

그럼 100페이지라면 990개의 글을 읽어 들여야 겠군요. 990개를 먼저 불러 들이는 것이 문제입니다.

가장 심플한 쿼리이지만 DB에 어느정도의 부하를 주는 쿼리라는 것을 알 수 있습니다.

개인적으로 레코드셋종류의 객체에 내용을 불러오는 쿼리는 0.1초 이전에 끝나야 한다고 생각됩니다. 물론 검색이 들어가면 이야기가 달라지지만 기본적인 형태(아무런 검색이 없는 경우)에서는 0.1초 이하에서 OPEN을 끝내는 것이 좋다고 생각되네요.

 

#3. ORDER BY문

아시다시피 ORDER BY문은 정렬의 조건입니다. 위 쿼리의 경우 ORDER BY ID DESC이니깐 ID에 대해 내림차순으로 정렬하겠네요. 사실 ORDER BY 문은 안써주는 것이 성능에 가장 좋습니다.

만약 ID를 클러스터드 인덱스로 지정해 놨다면 안써도 될 것 같습니다. 클러스터드 인덱스를 내림차순으로 지정해 놨다면 ORDER BY ID DESC는 성능에 전혀 영향을 미치지 않습니다. 가끔 MSSQL에서 서브쿼리를 썼을 경우 원하는 값을 리턴하지 않습니다. 이유는 모르겠습니다. 서브쿼리문을 가져오는 방법에서 더 빨리 가져오려고 하는 그 부분에서 나오는 문제일거라 생각되네요.

클러스터드 인덱스로 지정했을 경우 ORDER BY ID DESC는 되도록이면 넣으시는게 좋습니다. 정확성을 위해서!;


#4. 성능저하 요소 SELECT COUNT(*) FROM TBL

NOT IN 쿼리보다 심플한 페이징 쿼리는 MSSQL에서 없는 것 같습니다. 개발자 입장에서 프로그래밍하기도 정말 쉬운 쿼리입니다. 현재 페이지 번호만 넘겨주면 모든 것이 가능합니다. 가장 큰 장점이 바로 개발하기 편하다는 것이 겠고, 사용자의 웹접근 액션과도 상당히 잘 맞아 떨어집니다. 사람들은 10페이지 이상은 잘 보지 않으려는 경향이 있기 때문입니다.

NOT IN 쿼리는 생각보다 좋은 쿼리라는 것을 일단 말씀 드립니다. 단 조건은 INDEX를 잘 설정해줬다는 조건이 붙습니다.


게시판 등을 개발할 때 전체 게시물 수를 구해오는데 가장 많이 쓰는 쿼리문이 SELECT COUNT(*) FROM TBL입니다. 페이지 바로 가기 버튼을 위해서도 필요하고 게시물 번호를 붙히는 데도 쓰이기 때문에 쓰는 경우가 많습니다.

헌데 이 쿼리문 속도가 상당히 늦습니다. 100만건을 조회할 경우 1초가 넘어가 버리는 무식한 쿼리입니다. 야야~ 그럼 IDEX행을 가져와봐... 라고 하실 것 같습니다. SELECT COUNT(ID) FROM TBL ... 허나 애석하게 속도가 똑같습니다.

그리고 COUNT함수는 가능하면 (*)를 사용하시기 바랍니다. 이것이 정확한 방법입니다. COUNT(*)에서 속도향상을 위한 방법은 솔직히 말씀드려서 없습니다.

가장 좋은 방법은 테이블 하나를 만들어서 데이타 입력/삭제시마다 업데이트 하면서 게시물 수를 저장해 놓는 방법입니다. MSSQL 내에서는 트리거라는 기능을 제공합니다. INPUT/DELETE 시 UPDATE TBL_SETTING set TBL_COUNT = xxx 뭐 이런 식으로 짜 놓으면 되겠죠.

전체 게시물 수를 가져올 때는 되도록이면 트리거를 쓰거나 프로그램을 통해 정보를 저장하는 테이블에 업데이트하고 이 자료를 페이징시 가져오는 것이 성능을 위한 좋은 방법입니다.

1만건 이하라면 COUNT(*)를 쓰던 NOT IN을 쓰던 별 지장이 없다는 게 제 생각입니다. 고로, 자꾸 써먹어도 좋은 쿼리 들임을 일단 알려드립니다.


#5 성능향상에 가장 좋은 방법

1000만건을 테스트 해보지는 않았지만, 1페이지와 100만 페이지가 같은(해보진 않았죠^^)-혹은 비슷한- 성능을 보이는 쿼리는 다름 아닌 NOT IN보다 훨씬 더 간결한 쿼리입니다.

SELECT TOP 11 ID, subject, contents FROM TBL where ID <= 현재페이지 최상위 ID (order by ID DESC)

이 쿼리에서 가장 중요한 것은 ID가 클러스터드 인덱스로 ID를 설정하고 내림차순으로 지정되어 있어야 한다는 것입니다.

아무리 많은 글들도 0.1초안에 해결될만한 가장 성능이 좋은 쿼리문입니다. 동접자수 엄청나고 글 수가 많다면 이방법 이외의 방법은 사용하지 마시기 바랍니다.

허나 장점이 있으면 단점도 있습니다. 이 쿼리를 사용할 경우 ID값에 바로 접근하긴하기 때문에

이전 1 2 3 4 5 6 7 8 9 10 다음 <- 이와 같은 구성이 불가능하다는 것입니다.

그래서 TOP 10이 아닌 11을 사용한 것입니다. 1부터 10까지 구성은 힘들지만 다음페이지 버튼은 가능하기 때문입니다. 갯수가 11개면 다음 페이지가 있다는 이야기가 되며, 다음페이지의 최상위 ID값도 가져올 수 있게 됩니다.

어쨌거나 위 쿼리는 성능에 있어서는 더이상 좋을 수 없는 쿼리입니다. 분명 글이 많고 사용자 액션이 단순한 사이트라면 반드시 고려해보셔야 할 쿼리입니다. 이전 페이지 구현은 프로그램적으로 머리를 좀 굴려야 할 부분이긴 합니다.

어짜피 다음 페이지 버튼을 눌러야만 가능하니깐 이전 페이지의 ID값은 무조건 가지고 갈 수 있습니다. POST방식으로 이전 페이지 정보를 계속 넘기는 방법도 괜찮은 방법일 것입니다.


SELECT TOP 10 ID FROM TBL where ID > 현재페이지 최상위 ID order by ID ASC 를 어쩔 수 없이 쓰는 것도 한 방법이겠구요. 그래도 다른 쿼리들보다는 빠른 방식이니깐요. <- 이건 저도 테스트 해보진 않았습니다.


#6. 사용자 편의성도 좀 생각해 보자

대부분 웹사이트에서는 NOT IN쿼리가 좋은 방법입니다. 사용자 편의성에 있어서 좋은 선택이니깐요.

이전 1 2 3 4 5 6 7 8 9 10 다음 <- 이게 얼마나 편한 방법입니까^^; 그리고 사람들은 10페이지 이상 조회를 거의 하지 않기도 합니다.

성능을 생각한다면 #5번의 방법이 정말 좋은 방법이지요. 다만 사용자 편의를 위한 인터페이스 구현은 사실상 불가능 합니다. 이전 페이지 구현도 쉽지 않죠.

목표가 생겼습니다.

이전 1 2 3 4 5 6 7 8 9 10 다음

이 기능을 한번 구현해 보죠. 약간 성능 저하가 있더라도... 전체 NOT IN보다는 훨씬 더 빠르게 한다는 목표를 가지고...

일단 쿼리를 하나 보죠.

SELECT TOP pageSize*10+1 ID from TBL where ID <= 1페이지 11페이지 21페이지 등 각 1페이지의 처음 ID

이 쿼리는 페이지 바로가기 버튼 구현을 위한 쿼리입니다.

페이지 사이즈가 10개라고 하면 101개를 가져옵니다. 101개면 너무 많지 않냐 하겠지만 적은 갯수입니다.^^

컬럼이 하나밖에 없기 때문에 속도 저하가 거의 없는 쿼리죠.

이 결과를 가지고 1 2 3 4 5 6 7 8 9 10 다음 버튼 구현이 가능합니다. 이전 버튼 구현은 역시 약간 복잡하죠?

PageInfo = rs.getrows() 등의 좋은 메소드 등을 통해 배열로 만든 후 이 기능을 구현하는 것이 가장 좋은 방법일거라 생각됩니다.


SELECT TOP 10 ID, subject, contents from TBL where ID not in(SELECT TOP pageSize*(현재페이지의한자리숫자-1 / 0일때는 10) ID from TBL  where ID <= 페이지 11페이지 21페이지 등 각 1페이지의 처음 ID order by ID DESC)  and ID <= 페이지 11페이지 21페이지 등 각 1페이지의 처음 ID order by ID DESC


이 쿼리는 실제로 글을 뿌려주는 쿼리입니다. NOT IN이 쓰였네요. 하지만 10페이지 단위로 끊어서 10페이지 글 내에서 NOT IN을 사용하기 때문에 항상 빠른 속도를 내줄 수 있는 쿼리입니다.

페이지당 글 수가 아무리 많아도 100개 이하가 대부분이기 때문에 10페이지 단위로 끊는다고 해도 1000개의 글 내에서 모든 작업이 이루어지기 때문에 성능 저하는 거의(아예) 없다고 보시면 됩니다. 1000개정도 레코드는 아무것도 아니지요.


다만 걸리는 것은 페이지 바로 가기 버튼을 구현하기 위해 비슷한 쿼리를 두번 날렸기 때문에 두배의 비용이 든다는 거겠지요.


#7. 검색에 대한 이야기

검색 속도를 위한 가장 좋은 방법은 고가의 검색 엔진을 사용하는 것입니다. 검색엔진에 DB를 설정하고 URL 저장 방법만 설정해 놓고 스케쥴링만 해 놓으면 검색엔진은 알아서 DB를 검색하고 고쳐진 값에 대해서 URL 링크를 인덱싱해 놓습니다.

속도도 빠르고 한글의 경우 형태소 분석기를 통해 별에 별 검색도 가능하며 서비스 측면에서는 하일라이트 생성기등을 통해 사용자 편의성을 제공해주고... 뭐... 문제는 쩐이군요^^;

MSSQL에서 우리는 like '%xxx%' 검색을 많이 활용하게 됩니다.

SELECT ID, subject, contents from TBL where subject like '%xxx%'

이 쿼리는 xxx를 가지는 모든 subject 컬럼에 대해 검색을 하게 됩니다. 그럼 어떻게 해야 할까요. 넵~ 인덱스를 설정해야 합니다. subject에 대해서 넌클러스터드 인덱스를 설정해 주어야 합니다. 그러면 엄청난 속도 향상을 느낄 수 있습니다.

subject 는 varchar(255)형입니다. 그렇기 때문에 인덱스 설정이 가능합니다.

그럼 contents like '%xxx%'는?

헌데 contents가 text형이거나 varchar(max) (이건 2005에서 지원)라면?...

불행히도 인덱스를 줄수가 없습니다. MSSQL은 그다지 많은 인덱스 공간을 지원해주지 않습니다.

이경우 가능하면 input쪽에 varchar(4000)정도 만큼만 글을 입력하도록 제한하거나 varchar(4000)을 몇개 더 만들어서 DB저장시 나누어서 넣어주는게 좋습니다.

편법이고 지저분한 방법입니다.^^; 그래도 뭐 속도 향상이 있다면야... 모두들 어쩔 수 없이 text형도 검색을 하지만, 좋은 방법이 아닙니다. 글이 1만개만 넘어가다 상당한 부하가 걸릴 거라 생각됩니다.

넌클러스터드 인덱스만이라도 걸려 있다면, 성능은 무척 좋아집니다.

하지만 너무 방법이 지저분 하네요.

더좋은 방법이 있겠죠? MSSQL은 이런 경우를 위해 풀텍스트검색서비스를 지원합니다. varchar(MAX)나 text형 등을 시중의 검색엔진과 비슷한 방식으로 인덱싱하고 검색할 수 있는 방법은 제공합니다. 속도가 그렇게 좋지는 못합니다만...  형태소 분석기도 들어 있고 유사어 검색도 가능하고 정확도 정렬도 가능합니다. 다만 문제는 속도가 생각처럼 나오지는 않는 다는 것입니다. 특히 정확도 정렬을 위한 정렬의 경우 100만건 이상으로 테스트시 1초가 넘어가더군요.

그리고 MSSQL 2000에서 한글은 제대로 지원되지 않았습니다. 2005에서 한글 지원이 되는데... 실제로 띄어쓰기를 하지 않아도 검색이 되는 정도를 확인하였습니다. 동의어 검색도 가능하다고 하는데... 저역시 많은 테스트가 필요할 것 같습니다.

2000에서는 전혀 안쓰는 기능이었지만, 2005에서는 충분히 활용가치가 있을 것 같네요. 다만 호스팅 업체에서 이 기능을 지원하지 않는 다는 것이 가장 큰 문제입니다.^^; 인덱싱 속도는 꽤 빠르나 하드디스크를 많이 차지하기 때문에, 그리고 인덱싱시 꽤 부하를 주기 때문에 지원하지 않는 것 같으며, 2000의 경우 한글 인덱싱 자체가 잘 안되기 때문에 지원을 하지 않는 것 같습니다.

다만 MSSQL 2005라면 프로젝트에 충분히 쓸 수 있을 것도 같습니다. 제가 이번에 활용해보도록 하겠습니다.

상용검색엔진에 비하면 못하겠지만 어느정도 흉내를 내주고 CONTAINS(TABLE)/ FREETEXT(TABLE)등의 4개의 함수를 통해 SQL쿼리문 내에서 사용하기 때문에 개발하기가 수월합니다.

풀텍스트 검색엔진에 대한 이야기는 또 다음에 계속 하도록 하겠습니다.



정리;
속도 향상을 위해 가장 중요한 것은 바로 INDEX 설정!;

반응형

SQL Server 대용량 서비스를 하려고 하다 보니 문제를 일으키는 상황이 발생하여 찾다가 찾다가 좋은 설정내용을 발견하여 공유 합니다.

출처: 군고구마 DBA

링크 : http://burning-dba.tistory.com/33

군고구마 DBA 님 좋은 정보 감사합니다 !!


처음에 SQL Server를 설치 한 후에 설정해야 하는 값들을 정리해봤습니다.설정 이외에 왜 설정을 하는지 간단한 이유를 기입하였습니다.

버전은 SQL Server STD 2008R2 위주로 작성되었으며,(제가 가장 많이써서...) 혹시 문제가 있거나 틀린 부분은 댓글을 남겨주세요. (_ _)

설정하는 방법이 아닌 왜 설정해야 하는가를 위주로 포스트를 작성 하였습니다.




목차 

Ⅰ. SYSTEM 설정

1. 하이퍼스레딩

2. 전원 계획 사용옵션 

3. 프로세서 사용계획

4. Lock Pages In Memory

5. RAID 구성

6. 바이러스 백신 프로그램 관리


. SQL Server 설정

1. 포트설정

2. 시작 계정 관리

3. Flag 설정

4. 구성관리자 설정

5. Memory 설정

6. 계정 설정

7. TempDB 설정

8. SQL Error Logs 설정

9. 데이터베이스 파일 설정



본론으로 들어가보겠습니다. 여기저기서 그림을 퍼온게 많습니다. 어디서 많이 보셨던 그림 일 수도 있습니다.





Ⅰ. SYSTEM 설정




1. 하이퍼스레딩

Hyper Threading은 가장 흔히 알듯이 CPU가 2배로 보입니다.

아래의 그림은 Windows 작업 관리자에서 보이는 CPU입니다. 하이퍼스레딩을 켤 경우 이 CPU의 코어 개수가 2배로 보입니다. 

아래의 초록색 꼬불꼬불 선이 나타나 있는 것들이 각각의 코어입니다. 현재 이 PC에는 8개의 코어가 있습니다. (하이퍼스레딩 안켰으므로)



하이퍼스레딩을 가장쉽게 이해 할수 있는 것이 아래의 그림입니다. 아래의 그림 중에 보면 우측에 그림에 그 설명이 매우 잘 나옵니다.

우측 그림에서 빨간색 선을 기준으로 첫번째 부터 싱글코어,듀얼코어,하이퍼스레딩 입니다. 

(출처 : http://smsinfo.tistory.com/m/post/514)


하이퍼 스레딩은 CPU가 마치 2개 인것 처럼 놀고 있는 부분을 1개의 코어가 처리하는 것으로써 배타적 작업이 많을 때에 유리 합니다.

위의 그림에서 1개의 작은 네모칸이 1개의 연산을 할 수 있는 곳이라고 할때 여러개의 연산이 가능 한 부분중에 항상 모든 곳이 작업을 

하고 있는 것은 아니므로, 놀고 있는 곳을 사용 함으로써 마치 2개의 코어가 일하는 것 처럼 보이는 것 입니다.


보통 (작은)OLTP 환경 즉, 게임 같은 서버에서는 하이퍼스레딩을 켜주면 성능 향상에 효과를 볼 수 있습니다.

하지만 큰 대용량 작업이 많은 OLAP 같은 환경에서는 CPU의 코어를 나눠서 파워를 분산함으로써 더욱 처리가 늦어 질 수 있습니다.

하여, 결론적으로 작은 OLTP환경에서는 사용을 하며, OLAP등 (DW,로그)등의 환경에서는 사용을 하지 않습니다.





2. 전원계획 사용옵션

설정법 : 실행 -> 전원 옵션


전원계획은 설정에 따라서 그 파워의 차이로 인하여, 성능에 영행을 줄 수 있습니다.

총 3가지 형태가 있는데, 균형조정/절전/고성능이 있습니다. 무조건 SQL Server가 있는 서버에서는 고성능을 선택해야지 100%의 성능을

발휘 합니다. 균형조정과 절전모드는 최소 프로세스 상태이므로, 5%의 파워를 고성능은 100%의 파워를 냅니다. 

결론적으로 고성능 옵션을 사용하는 것이 바람직 합니다.


물론 전기세가 증가하지만.. 성능을 포기하고 전기료를 아낄분은 없을것이라 생각 됩니다.


참조 사이트 

㉠전원 계획 사용옵션에 따른 성능 차이 테스트 링크

https://sqlserverperformance.wordpress.com/2010/09/28/windows-power-plans-and-cpu-performance/




3. 프로세서 사용계획

설정법 : 제어판 -> 성능 정보 및 도구 -> 시각효과 조정 -> 고급탭 



프로세서 사용계획은 2가지로 성능이 가능 합니다. 프로그램/백그라운드 서비스로 설정이 가능한데, 보통 SQL Server만 사용하는 서버에서는 

백그라운드 서비스를 사용합니다. 그 이유를 대부분이 SQL Server는 Service에 등록되어 백단에서 움직이므로, 그렇게 설정한다고 알고 있는데

잘못된 내용입니다.


백그라운드 서비스로 설정하는 것은 맞으나, 이렇게 설정하는 이유는 Context Switch 를 최소화 시키기 위해서 입니다. 결론적으로 백그라운드 

서비스로 설정하는 것이 바람직 합니다. Context Swith의 내용 및 자세한 사항은 아래의 링크를 참조하시면 좋을 듯 합니다.


참조 사이트

㉠ 백그라운드 서비스 관련 링크

http://blogs.technet.com/b/sankim/archive/2009/06/10/windows-vs.aspx





4. Lock Pages In Memory 설정

설정법 : 실행 -> gpedit.msc -> Windows 설정 -> 보안설정 -> 로컬정책 -> 메모리에 페이지 잠금


Lock Pages In Memory 는 매우 중요한 부분 입니다. 이것은 꼭 설정이 필요 합니다.


메모리에 페이지 잠금 속성 더블 클릭


사용자 또는 그룹추가 클릭하여, SQL Server 시작 계정 등록


이것을 설정하는 가장 큰 이유는 가상메모리 사용을 방지하기 위함인데, 가상 메모리란 메모리가 부족하여 디스크를 메모리 처럼 사용 하는 것을 말 합니다. 

디스크를 메모리 처럼 사용하다 보니, 그 성능이 매우 느려져서 성능 저하를 가져 오는 것 입니다. 메모리를 디스크 처럼 사용 하는 것을 Hard faults라고 하며,

Perfmon항목에서 Pages/sec를 확인하여, 이 수치가 높다면 Memory 항목에 Page faults/sec(Hard faults)를 유심히 봐야 합니다. 


Lock Pages In Memory 를 설정하지 않으면 다음과 같은 문제가 발생 할 수 있습니다.

1. SQL Server 성능이 갑자기 저하됨

2. SQL Server 실행하는 컴퓨터가 잠시 응답하지 않음

3. SQL Server 응용 프로그램에 대한 Time out 발생

4. 시스템에서 응용 프로그램을 사용 할 때 문제가 발생 함


참조 사이트

㉠ Lock Pages in Memory 설정 방법 및 설정하지 않을시 문제점

http://cclassic2.cafe24.com/?mid=SQL_Tip&search_target=tag&search_keyword=%ED%8E%98%EC%9D%B4%EC%A7%80&page=1&document_srl=8324

㉡ 가상 메모리 설명

http://egloos.zum.com/sweeper/v/2988689

㉢ 메모리 관리 및 Perfmon 보는 방법

http://blog.daum.net/question0921/1031



5. RIAD 

레이드는 SE 분들이 설정을 하시며 DBA는 DB서버에서 최적의 성능을 발휘할 레이드를 고려해야 합니다. 주로 게임환경에서 사용하는 레이드는 1+0 입니다. 

게임에서 가장 많이 사용하며, 안정성과 성능을 모두 확보할 수 있습니다. 우선 1+0을 이해하기 위해서는 레이드 0과 1을 알아야 합니다.


레이드 0 

레이드 0의 경우 빠른 속도가 강점입니다. 레이드 0의 경우 데이터를 스트라이핑을 통해 DISK 4개에 나누어 뿌리는 방식으로 데이터를 저장 하므로, 그 처리 속도가

매우 빠릅니다. 하지만 DISK 중에 1개라도 깨진다면 데이터가 모두 손실됩니다. (중간에 이빨이 빠진것으로 이해하면 좋을 듯 합니다.)

(출처 : http://www.acnc.com/raidedu/0_1)


㉡ 레이드 1

레이드 1의 경우 0과 다르게 안정성이 뛰어납니다. 레이드 1은 데이터를 미러링하여, 동시에 저장하기 때문에 1개의 DISK가 깨져도 복구가 가능합니다. 

하지만 250GB씩 4장의 DISK를 꽂아서 1TB로 사용할 경우 미러링을 위해 500GB밖에 사용 할 수 없습니다.

 또한 레이드 1을 구성하기 위해서는 최소 2장의 DISK가 필요 합니다. 

(출처 : http://www.acnc.com/raidedu/0_1)


㉢ 레이드 1+0 

레이드 1+0의 경우 레이드 0으로 묶여진 레이드를 다시 레이드 1로 묶는 것을 말 합니다. 이렇게 함으로써 스트라이핑으로 데이터를 저장 함과 동시에

미러링을 하므로, 그 안정성과 성능의 이점을 모두 얻을 수 있습니다. 


(출처 : http://cappleblog.co.kr/133)


㉣ 레이드 0+1

레이드 0+1의 경우 위의 1+0과 반대로 생각하면 됩니다. 레이드 1로 묶여진 것을 다시 레이드 0으로 묶은 것 입니다. 위와 마찬가지로 속도 및 안정성을

모두 확보 가능 하지만 이것이 깨졌을때 그 문제점이 나타므로, 사용하지 않습니다.

(출처 : http://cappleblog.co.kr/133)



* 레이드 1 + 0을 사용하는 이유 

1. 0 + 1로 묶은 레이드에서 디스크가 깨졌다고 가정 할때 위의 그림에서 첫번째 레이드의 A1 데이터가 손상 된다면, 전 데이터를 사용 불가능 합니다. 

하지만 반대로 1 + 0의 경우 A1의 데이터가 있는 디스크가 깨져도 옆에 그대로 미러링 된 데이터가 있으므로, 지장이 없습니다.


2. 복구시에도 그 차이가 분명히 드러납니다. 만약에 위와 마찬가지로 첫번쨰 레이드가 깨졌다고 가정 할 경우 0 + 1의 경우 데이터를 정렬 및 재 구성을

위해서 전부 복원 해야 하지만, 1 + 0 의 경우 아직 살아있는 옆의 미러링된 디스크에서 데이터를 복사하여 복원 하므로, 처리 시간 및 리소스에 비용이

현저하게 줄어 듭니다.


이밖에 레이드는 레이드5 등도 자주 사용하지만 게임회사인 저는 1 + 0을 거이 대부분 사용하였기에 나머지 자료는 링크를 참조 부탁드립니다.


참조 사이트

㉠ 레이드 동작 플래쉬

http://www.acnc.com/raidedu/0_1

㉡ 레이드 1 + 0과 0 + 1의 차이점

http://lab.gamecodi.com/board/zboard.php?id=GAMECODILAB_Lecture&page=1&sn1=&divpage=1&sn=off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=103

 레이드 설명 

http://cappleblog.co.kr/133




6. 바이러스 백신 프로그램 관리

바이러스 및 백신 프로그램은 주로 일 단위, 주단위로 검사를 수행 합니다. 하지만 데이터베이스의 데이터파일 및 백업 파일등은 그 검사 대상에서 제외되어야 합니다.

만약 MDF , NDF , LDF 파일등이 있는 폴더를 검사하게 된다면, 검사 시 대기현상이 발생하거나 성능에 영향을 줄 수 있습니다. 


버전별로 정리된 바이러스 백신 관련 설정은 MSDN에 설정을 참조하시길 바랍니다.


참조 사이트

㉠ 백신 프로그램 관리

https://support.microsoft.com/en-us/kb/309422/ko








. SQL Server 설정



1. 포트 설정

설정 법 : 실행 -> SQL Server 구성 관리자 -> SQL Server 네트워크 구성 -> (인스턴스명)에 대한 프로토콜 -> TCP/IP -> IP주소 탭 -> TCP포트 부분 변경


포트를 설정하는 가장 큰 이유는 보안의 문제가 나타나기 때문입니다. 보통 SQL Server를 처음 설정 했을 경우 기본 포트는 1433입니다. 하지만 이 1433포트를 그대로

사용 하는 경우는 극히 드문 경우 입니다. 그 이유는 1433 포트는 알려진 포트이므로, 공격의 대상이 됩니다. 이러한 공격의 한 종유로 브루트 포스(Brute Force Attack)공격이 있습니다.

이것은 무차별 적으로 암호를 변경해가며, sa 계정으로 1433 포트로 접근을 계속해서 시도하는 공격 입니다. 이로 인해 차후에 나오겠지만 sa 계정도 사용하지 않습니다.


이러한 공격이 초단위로 수백 ~ 수천번씩 공격하기 때문에 부하가 심할 수 밖에 없습니다. SQL Server의 포트는 기본 1433이 아닌 다른 포트로 변경 하며, 변경 후 에 SQL Server를 

재시작 해야 합니다.


참고 자료

㉠ 포트 관련 공격

http://ozit.tistory.com/35





2. 시작 계정 관리

설정 법(계정 생성) : 실행 -> 컴퓨터 관리 -> 로컬 사용자 및 그룹 -> 사용자 폴더 우클릭 -> 새 사용자

 

컴퓨터 관리로 접속하여, 로컬 사용자 및 그룹을 탭으로 갑니다.


로컬 사용자 및 그룹 탭에 사용자 폴더를 우클릭하여, 새 사용자를 추가합니다.


새 사용자는 SQL Server 시작 계정으로만 사용하므로, 네이밍을 SQL 또는 SQL Server등 분별하기 쉽게 주는게 좋습니다.


생성된 계정을 우클릭하여 속성을 누르고, 속성에서 소슥그룹탭에 있는 모든 소슥그룹을 제거합니다. 기본적으로 Users로 소속그룹이 되어 있습니다.

소속 그룹을 제거하는 이유는 Users 소속그룹에 속해 있을시 응용프로그램 실행, 로컬 및 네티워크 프린터 사용 , 컴퓨터 잠금과 같은 일반적인 작업을 수행 할 수 있기

떄문 입니다. 하여, SQL Server 만 수행하기에 아무런 소속그룹도 필요하지 않습니다. 


소속스룹에 따른 권한 관련 링크

https://technet.microsoft.com/ko-kr/library/cc771990.aspx



설정 법(시작 계정 설정) 

SQL Server 구성관리자 -> SQL Server 서비스탭 클릭 -> SQL Server(인스턴스명) 우클릭 -> 속성 클릭 -> 로그온탭에서 계정 변경 (생성한 계정으로)


SQL Server의 구성관리자를 실행하여 SQL Server 서비스 탭을 클릭하면 다음과 같이 서비스들이 나옵니다. 여기서 SQL Server (인스턴스명) 을 우클릭하여 속성을 들어갑니다.


계정을 변경하고, 적용 후 SQL Server를 재시작 합니다.

결론적으로 생성된 아무 권한이 없는 오직 SQL Server만 실행하는 계정을 이용해 SQL Server를 구동해야 합니다.





3. Flag 설정

설정법 : SQL Server 구성관리자 접속 -> SQL Server 서비스 -> SQL Server (인스턴스) -> 우클릭하여, 고급탭 클릭 -> 시작매개 변수 부분에 Flag 기입


시작 매개변수에 설정해주면 SQL Server가 시작되면서 플래그가 적용됩니다.


아래의 표에 설정이 필요한 플래그를 정리하였습니다.

T845 , T1118 을 제외한 플래그는 상황에 맞게 설정하는 것이 좋을 것으로 보입니다.





4. 구성 관리자 설정 


구성관리자는 상황 및 사용에 따라서, 중지,사용안함,자동으로 만들어야 합니다.

자동으로 설정 할 경우 SQL Server시작시 같이 올라 옵니다. 하지만 자동으로 설정하였다고해서 무조건 올라오는 것이 아니므로 (가끔 안올라옴) 점검 및 서버 재기동시

DBA가 함께 확인을 해줘야 합니다.


아래의 표는 현재 Live 중인 게임DB 서버에서 실행 운영중인 내용입니다. 


서버의 사용 용도 및 사용에 맞게 서비스를 변경해 주면 좋을 듯 합니다.




5.  Memory 설정 

설정법(UI) : SSMS 접속 -> 최상위 SQL Server 로 되어 있는 부분 우클릭 -> 속성 -> 메모리 부분 클릭 -> 최소,최대 서버 메모리 부분 변경



최소 서버메모리는 SQL Server가 최소 유지할 메모리를 뜻하여, 최대 서버 메모리는 최대 SQL Server가 사용 할 수 있는 메모리를 뜻 합니다.



설정법 (쿼리) : 

sp_configure 'max server memory (MB)', 14000    --최대메모리지정

sp_configure 'min server memory (MB)', 12000      --최소메모리지정


SQL Server는 메모리를 동적으로 관리합니다. 필요 할때마다 메모리를 점유하며, OS에서 필요하다고 하기 전까지 그 메모리를 반환하지 않습니다. 그 이유는 재사용성 때문입니다.

SQL Server가 프로시저 및 파라미터 쿼리에 속도가 빠른 이유는 컴파일 이후에 메모리에 상주시켜놨다가 바로 실행 시키기 때문입니다. 


SQL Server에서 최소/최대 메모리를 꼭 설정해야 하는 이유는 설정하지 않을 시 SQL Server는 기본적으로 최소/최대에 대한 제한이 없습니다. 

이렇게 제한이 없으므로, 부하가 많은 시스템에서 I/O가 지속적으로 발생하면 SQL Server는 OS에 메모리를 계속 요구하며 할당받아 사용하게 됩니다. 하지만 이는 OS 및 다른 응용

프로그램에 동작에 영향을 줄 수 있습니다. 반대로 OS에서 다른 응용프로그램에서 많은 공간의 메모리를 사용하고 있다면 SQL Server가 할당 받아 사용할 메모리가 줄어들게 됩니다.

SQL Server의 버퍼풀 메모리가 부족 할 경우 잦은 I/O를 발생시키고 쿼리를 실행하는데 메모리를 얻지 못하여, 대기가 발생 할 수 있습니다.


주로 SQL Serve가 설치된 서버라면 SQL Server 이외에 다른 프로그램을 돌리지 않는 것이 일반적입니다. 하여, SQL Server 전용 서버라면 80~90%까지 (전체 메모리에) SQL Server가 점유하게 합니다. 


참고 자료

㉠ 메모리 관련 

http://kajin7.tistory.com/entry/MS-SQL-Server-2008-%EB%A9%94%EB%AA%A8%EB%A6%AC%EA%B4%80%EB%A6%AC




6. 계정 설정

설정 법 : SSMS 실행 -> 보안 -> 로그인 


위에서 언급하였듯이 포트를 변경하는 이유에서 처럼 계정을 관리해야 하는 이유도 보안상 이유입니다. 기본적으로 변경해야 할 것은 다음과 같습니다.


## 시작 및 ##으로 끝나는 계정 관련한 MSDN 링크 

https://msdn.microsoft.com/ko-kr/library/ms181127.aspx





7. Temp DB 설정

설정 법 

아래의 설정은 TempDB를 8개로 나누는 것이며, 사이즈 및 경로를 서버의 상황 및 구성에 맞게 변경 해야 합니다.

use master

go

 

alter database tempdb modify file

 (name=tempdev, filename='D:\MSSQL_TempDB\tempdb.mdf', SIZE = 1024MB, FILEGROWTH = 128MB)

go

alter database tempdb modify file

(name=templog, filename='D:\MSSQL_TempDB\templog.ldf', SIZE = 1024MB, FILEGROWTH = 128MB)

Go

 

alter database tempdb add  file

(name=tempdev01, filename='D:\MSSQL_TempDB\tempdev01_Data.ndf', SIZE = 1024MB, FILEGROWTH =128MB)

go

alter database tempdb add file

(name=tempdev02, filename='D:\MSSQL_TempDB\tempdev02_Data.ndf', SIZE = 1024MB, FILEGROWTH =128MB)

go

alter database tempdb add file

(name=tempdev03, filename='D:\MSSQL_TempDB\tempdev03_Data.ndf', SIZE = 1024MB, FILEGROWTH =128MB)

go

alter database tempdb add file

(name=tempdev04, filename='D:\MSSQL_TempDB\tempdev04_Data.ndf', SIZE = 1024MB, FILEGROWTH =128MB)

go

alter database tempdb add file

(name=tempdev05, filename='D:\MSSQL_TempDB\tempdev05_Data.ndf', SIZE = 1024MB, FILEGROWTH =128MB)

go

alter database tempdb add file

(name=tempdev06, filename='D:\MSSQL_TempDB\tempdev06_Data.ndf', SIZE = 1024MB, FILEGROWTH =128MB)

go

alter database tempdb add file

(name=tempdev07, filename='D:\MSSQL_TempDB\tempdev07_Data.ndf', SIZE = 1024MB, FILEGROWTH =128MB)

go

 alter database tempdb add  file

(name=tempdev08, filename='D:\MSSQL_TempDB\tempdev08_Data.ndf', SIZE = 1024MB, FILEGROWTH =128MB)

go

 


TempDB는 SQL Server에서 아주 중요한 역할을 하며, 몇가지 역할을 살펴보면 다음과 같습니다.

1. 정렬 시 중간 결과값 저장

2. 임시 테이블 생성 시 저장

3. Hash join 등에서 사용


일부의 역할이며, 더욱 많은 역할을 담당합니다.


하지만 TempDB는 SQL Server 설치 초기에 아무런 정보 없이 설정하기 매우 어렵습니다. MSDN에서는 코어의 갯수데로 TempDB를 쪼개라고 권장하고 있습니다.

TempDB의 경우 그간의 시스템을 모니터링 하여, 적절하게 쪼개주면 좋습니다. 용량의 증가율 및 i/O의 경합률등을 고려하여 개수를 쪼개는 것이 바람직 합니다.


TempDB의 경우 너무 많이도, 너무 적게도 쪼개는 것이 좋지 않으므로, 초기에 4~8개 정도 적절한 용량을 세팅한 뒤에 차후 모니터링을 통해서 그 개수를 측정

하는 것이 좋습니다.



참조 자료

㉠ TEMP DB성능 최적화 MSDN

https://technet.microsoft.com/ko-kr/library/ms345368(v=sql.105).aspx

㉡ SQL TAG 2

TempDB 부분




8. SQL Error Logs 설정

설정 법 : SSMS -> 관리 -> SQL Server 로그 우클릭 하여 구성 클릭



SQL Server 오류로그 구성에서 개수를 조절 합니다.



SQL Server 오류로그의 수는 아래에 보이는 로그의 개수를 뜻 합니다.

이 개수는 99개 까지 조절이 가능 합니다. 이 개수를 너무 적게 설정하면, 그 히스토리가 적게 남으므로 많이 설정하는 것을 권장 합니다.

예전에 제가 실제로 오류 내용을 봐야하는데, 개수를 6개만 저장하여, 히스토리를 확인하지 못한 경우가 있었습니다. 





9. 데이터베이스 파일 성정

설정 법 : SSMS -> 설정 할 DB 우클릭 -> 속성 -> 파일


데이버테이스에서 용량 산정 및 증가값을 설정하는 것은 매우 중요합니다. 딱 정해진 값이 없고, 이것도 TempDB와 마찬가지로 기존에 사용률 및 증가값을

모니터링 하여, 설정해야 합니다. 처음 세팅시에 기본적인 값을 설정(예상하여) 해야 합니다.


여기서 중요한것은 증가값을 설정할때 기본적으로 LDF 파일의 경우 퍼센트(%) 단위로 지정되어 있는데 이것은 MB 단위로 변경해야 합니다.

MB 단위로 지정해야지 일정 단위로 증가되며 퍼센트로 지정 할 경우 차후에 그 용량이 커지면 기하 급수적으로 용량이 증가 할 수 있습니다.


또한 MDF,NDF와 LDF는 각각 다른 디스크에 구성하여, 그 성능을 높여야 합니다. 그리고 백업 파일등도 다른 디스크에 보관하여, 혹시 모를 MDF,NDF의 깨짐이

발생하면, 백업 파일을 통해 복원을 해줘야 합니다.

반응형

제목

MS-SQL - SQL서버 환경 위주의 핵심 튜닝 정리

작성자

DEV.WON ( http://blog.naver.com/jadin1 )

작성일

2010-10-14

배포

https://blog.naver.com/jadin1/70096018381

 

 

시작하며)

정말 오랫만이네요.. 강좌를 쓰는게...

그동안 있었던 일을 모두 열거하긴 너무 힘들것 같습니다따로 DEV.WON 썰에 쓰도록 하지요..

요즘 화두가 되고 있는 클라우드 컴퓨팅환경을 지원하기 위한 MS 사의 Azure를 보고 있는데가격정책이 그리 나쁘진 않을꺼 같단 생각이 듭니다.

오늘은 데이터베이스의 튜닝에 대해서 간략히 설명해 보려합니다.

사실 DB튜닝의 정답이 어디있겠고 끝이 어디 있겠습니까..그리고 쉬운작업도 분명히 아니겠지요.

이 강좌의 목적은 초절정 튜닝 고수가 되어 DB컨설팅을 다니며 화려한 스포트 라이트를 받자는게 아닙니다.

다만 개발자로써 DBA한테 무시 받지 않을정도로만또는 DBA가 없는 회사에서 독고다이 DB를 관리하셔야 하는 분이거나,

아님 우리 회사 시스템이 너무 느려 터져서 본인이 손수 고쳐 보겠다고 하시는 분들에 한해서 입니다.

전문 DBA 로써 강좌를 작성한것이 아니니강좌의 퀄리티를 기대하진 마세요..

하지만 난 아무리 DB튜닝에 대해서 공부해 봐도 모르겠다는 분들을 위하여 최대한 재믿고 쉽게 쓰도록 노력해보겠습니다.

마지막으로 이 강좌는 MS-sQL 2005 기반에서 작성되었습니다.

 

튜닝을 위한 사전 지식)

튜닝을 하려면 몇가지 바탕이 되는 기술이 필요합니다.

아주 간략히 얘기해보죠..

 

[1] PROCEDURE 의 쿼리 처리 과정과 캐쉬에 대한 이해

 mssql의 쿼리처리 과정은 다음 5단계를 거친다.

1.       구문 분석 (Parsing)

2.       표준화 (Standardization)

3.       최적화 (Optimization)

4.       컴파일 (Compilation)

5.       실행 (Execute)

매번 저 단계를 거치는 것은 부하가 크므로 메모리에 캐시하게 되며
syscacheobjects 
테이블을 보면 그 캐시에 저장된 쿼리 내역을 알 수 있다.
아래처럼 간단하게 호출을 한다.

select * from sys.syscacheobjects;

 

 

<v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f><o:lock aspectratio="t" v:ext="edit"></o:lock>

(자세한 컬럼은 여기를 참조 한다http://msdn.microsoft.com/ko-kr/library/ms187815.aspx)

dbcc freeproccache; --프로시저 캐시에서 모든 요소를 제거하는 구문

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

실행계획 보는 방법 인덱스 스캔 vs 테이블 스캔

 

(쿼리 선택후 Ctrl + L 또는 위의 아이콘을 클릭하면 예상 실행 계획을 볼수 있다)

이 탭을 선택하여 내역을 보도록 하자탭을 선택하게 되면두 개의 아이콘이 보이게 된다여기서 오른쪽 아이콘에 마우스를 가져간 다음, 1~2초간 머무르면 다음의 그림과 같이 팝업이 뜨게 된다.

 

 

내용을 보면여러 가지의 항목이 숫자 값으로 표현이 되고 있다각 항목들은 쿼리를 수행할 때 SQL Server가 내부적으로 어떠한 작업을 수행하였는지를 통계적인 수치로 보여주는 것이다우선 물리적인 연산과 논리적인 연산을 보면, Clustered Index Seek라고 되어 있다다음의 행 개수 는 수행된 쿼리에 의해서 반환된 행의 개수를 말한다.

I/O비용은 해당 작업즉 아이콘이 표시하고 있는 내부적인 작업에 의해서, SQL Server가 설치된 서버의 하드디스크에 대해서 물리적으로 읽기작업이 일어난 비용을 말한다. SQL Server가 데이터를 반환하는데 있어서가장 많은 시간을 필요로 하는 부분이 바로 물리적인 하드 디스크로부터 데이터를 읽어 들이는 것이기 때문에좀 더 빨리 데이터를 반환하기 위해서는 SQL Server가 하드 디스크로부터 데이터를 읽어 들이는 작업 I/O의 횟수를 줄이는 것이 필요하게 된다때문에 I/O 비용에 나타난 값이 작으면 작을수록 데이터를 빠르게 반환하게 되며효율적인 쿼리문을 작성한 것이라고 할 수 있다.

또한 비용 은 쿼리문의 실행에 필요한 비용을 말하는 것이다즉 쿼리를 수행하는 데 소요되는 비용을 말한다따라서 이 수치 역시 작을수록효율적인 쿼리가 수행되는 것이다.

자 이제 방금전 언급되었던 물리적 연산/논리적 연산에서 나왔던 clustered index seek 에 대해서 알아볼 필요가 있다이말인 즉슨테이블에 있는 인덱스를 이용하여 쿼리를 수행하였다는 것을 말한다왜 인덱스를 이용하면 테이블의 일부만을 검색하게 될까테이블의 일부만을 검색하고도정확한 데이터를 반환할 수 있을까그렇다테이블의 일부만을 읽어서도 정확하게 데이터를 반환할 수 있다이것이 바로 인덱스의 장점이다.

사실 위의 쿼리 select * from dbo.Customers where customerID = 'CACTU'

에는 눈치챈 독자도 있겠지만 customerID index가 걸려있다그래서 당연히 index seek를 할수 있었던 것이다.

그렇다면 해당 인덱스를 drop해 볼까나..하지만 테이블 생성시 primary key clustered 로 생성하여 drop이 되지 않으니똑 같은 스키마 구조로 인덱스만 제거하여 새롭게 테이블을 생성하였다.(customers_have_not_index)

이제 두 번째 문장을 실행하여 첫 번째 문장의 실행 계획과 비교하여 보도록 하자.

 

 

 

우선 I/O 비용을 비교하여 보도록 하자각각 0.003125 0.004685로 수치상에서 격차를 보이고 있음을 알 수 있다또한 쿼리의 연산자 비용과 하위 트리 비용에서도 차이가 나느것을 알수 있다.

또한 매우 중요한 사실인데 위의 그림에서 정렬됨을 보면 customers 테이블의 실행 계획은 true이다하지만 customers_have_not_index 에서는 false인 것을 알수 있다.

그 이유는 clustered index는 내부적으로 인덱스 생성시 인덱스 데이터를 정렬 하는 특징을 가지고 있기 때문이다.

 

 

정리를 좀 해볼까.

두번째 테이블은 table scan이다앞서 첫번째 테이블은 index scan이다.

Index scan : 색인을 가지고 데이터를 조회해온다(첫번째 테이블의 실행계획)

Table scan : 색인이 없는 컬럼으로 조회를 하는 경우원하는 레코드 의 값을 가져오기 위해 처음부터 끝까지 하나하나 읽어가면서 원하는 정보를 찾는다. ( full scan이라고도 함)

이렇게 인덱스가 설정된 테이블과 인덱스가 설정되지 않은 테이블에 대한 데이터 반환 요구 시필요한 비용만을 보아도 성능의 차이를 느낄 수 있다인덱스가 설정된 테이블의 경우에는 인덱스를 이용하여 데이터가 있는 위치를 쉽게 찾을 수 있기 때문에필요한 비용도 작게 나타났다반면에 테이블에 인덱스가 설정되어 있지 않은 경우에는테이블 전체를 읽는 작업을 수행하기 때문에 그 만큼 많은 비용을 필요로 하게 되고 데이터가 반환되는 시간까지도 늦어지게 되는 것이다.

 

[2] 인덱스에 대한 이해]

인덱스는 다음과 같은 구분 기준에 따라 나눌수 있다

             1) 구조적인 기준

n         Clustered Index

n         Non-clustered Index

2) 인덱스 구성 컬럼의 수

u        Single-Column Index

u        Composite Index

3) 유일값 유무에 따른 기준

u        Unique Index

u        Non-unique Index

 

[2-1] 차례 와 색인

인덱스를 이해하기 위해서는 차례와 색인을 먼저 이해 하는 것이 중요하다.

우리는 데이터 베이스 관련 서적 한권쯤은 가지고 있을 것이다그 책의 맨 앞장을 보라!

저자 소개 및 머리말이 지나가고 ( 근데 대부분 머리말에 보면 마지막에 사랑하는 가족에게 감사하다는 말을 <st1:personname w:st="on" o:ls="trans">남기전</st1:personname>에 묵묵히 몇일의 원고 마감 밤샘작업을 묵묵히 지켜봐준.. 이라는 문구는 자주 들어본거 같다.. 아마도 저자분들 모두 힘들게 집필하신 것이 틀림없다.다음 장을 넘겨 보면 차례가 나오는 것이 대부분이다.

차례의 특징이 몬가이 책이 1페이지부터 1000페이지까지 있다면. 1페이지부터 마지막 페이지까지 차례대로 중요한 부분 부분에 제목과 페이지 숫자가 적혀있다.

1. 데이터 베이수 개론                                ---------          .1

             1-1 데이터 베이수 의 개념   --------              .2

             1-2 데이터 베이수 의 역사   --------              .4

2. 데이터 베이수 SQL                      --------                 .21

             2-1 ANSI SQL 이란             --------                 .22

중략..

모 대략 이런식이다.

차례의 특징은 독자들은 서적을 처음부터 끝까지 뒤지지 않아도찾고자 하는 내용을 찾을수 있다.(순서대로 구성되어 있는 제목과 소제목들)

 

이제 서적의 맨 마지막으로 가 보도록 하자.

맨 마지막 페이지는 가격과 출판일시등이 나와있다맨마지막 바로 전 페이지를 보자

어라색인 또는 index 이라는 제목으로 약 3페이지 정도 걸쳐서한글의 자모순서 또는 알파벳 순으로 해당 내용이 어디에서 설명이 되고 있는지를 페이지의 번호와 함께 제공이 되고 있다이게 색인이다.

 

클러스터된 인덱스

먼저 클러스터된 인덱스를 살펴보도록 하자클러스터된 인덱스는 설명한 것과 같이 인덱스로 설정된 컬럼에 있는 데이터의 순서대로하드 디스크 상에 물리적으로 저장되는 것을 말한다바로 위에서 말한 "차례"에 해당한다는 것을 의미한다.

  • 차례
  • 테이블 당 하나만 설정가능
  • 클러스터 되지 않은 인덱스보다 빠르다
  • 유니크 인덱스로 설정된다
  • 데이터 입력 시비교적 느리다.
  • 범위에 의한 쿼리

클러스터된 인덱스는 클러스터 되지 않은 인덱스를 설정한 경우보다도 더 빨리 데이터를 반환할 수 있다이는 실제로 데이터가 순서대로 저장이 되기 때문에물리적인 하드 디스크 공간에 여기저기 분포하고 있는 데이터를 찾아서 반환하지 않고순서대로 데이터를 읽어오기만 하면 되기 때문이다.

이렇게 클러스터 인덱스는 데이터를 사전식으로 저장하기 때문에테이블 당 하나만을 설정할 수 있다.

(엄연히 말하면 클러스터드 인덱스로 설정된 컬럼의 데이터는 사전식 순서대로 물리적인 공간에 저장되는 것은 아니다실제로 그렇게 되면 서버측에 부하를 초래 한다만약 1억건의 데이터가 입력된 테이블에서 클러스터드 된 인덱스의 순서에 의해 첫번째 순서로 저장되어야 하는 데이터가 입력되면.. 기존 데이터는 차례로 뒤로 밀려야 하며이러한 작업은 엄청한 부하를 초래 하기 마련이다그렇기 때문에 내부적으로는 페이지라는 작은 단위로 나뉘어 데이터가 저장된다이때 각 페이지 간의 다음에 위치하게될 페이지가 어디에 저장되는지에 대한 정보가 필요하다이 정보를 페이지 체인(page chain) 이라고 부르며실제로 데이터가 순서대로 저장된다는 의미는 이 페이지 체인이 순서대로 연결된다는 것을 뜻한다.)

클러스터 되지 않은 인덱스

다음에는 클러스터 되지 않은 인덱스를 살펴보도록 하자.

  • 목차
  • 물리적으로 순서에 따라서 데이터가 저장되지 않는다
  • 데이터 반환 시클러스터된 인덱스와 비교하여 느린 편
  • 데이터 입력수정삭제 시클러스터 인덱스에 비하여 빠른 편
  • 테이블 당 249개까지 설정이 가능
  • 적은 범위의 데이터에서 특정 데이터를 반환하는 쿼리

클러스터 되지 않은 인덱스는 앞서 설명을 하였던 "목차"와 같은 기능을 가지고 있다데이터가 순서대로 저장이 되어 있지는 않지만어디에 저장이 되어 있다는 정보를 데이터가 저장되는 저장장소와는 별도의 공간에 따로 저장을 하는 것을 말한다.

클러스터 되지 않은 인덱스는 클러스터된 인덱스와는 달리물리적으로 데이터가 사전식 순서에 의해서 저장되지 않는다따라서 데이터를 반환하는데 있어서는 클러스터된 인덱스보다는 느리지만반대로 데이터의 입력수정삭제작업에 대한 성능은 클러스터된 인덱스에 비하여 빠른 편이다.

이렇게 클러스터 되지 않은 인덱스는 물리적으로 서버의 하드 디스크에 아무런 변화도 주지 않기 때문에테이블 당 249개의 클러스터 되지 않은 인덱스를 설정할 수 있다클러스터 되지 않은 인덱스는 클러스터된 인덱스와는 달리, "아이디가 100인 값을 반환하라"와 같이 조건으로 하나의 값만이 반환되는 쿼리에 사용하는 것이 성능에 도움이 된다또한 클러스터 되지 않은 인덱스는 선택성(Selectivity)가 높은 경우에는 그 성능을 십분 발휘할 수 있게 되기 때문에클러스터 되지 않은 인덱스를 선정하는 경우에는 선택성을 고려하여 선정하기 바란다.

 

 

 

 

             Clustered index non Clustered index의 비교

 

Clustered Index

Nonclustered Index

최대 Index 

1

249

Index 지정에 따른 크기

Table 크기의 1~5%

Table 크기의 10~20%

조회성능

빠름

Clustered Index보다 느림

Data 수정

Nonclustered Index보다 느림

빠름

사용

영역을 지정한 조회

하나의 값을 반환하는 조회

 

 

 

 [2-2] 단일 컬럼 인덱스 & 복합 컬럼 인덱스

인덱스는 인덱스를 구성하는 컬럼의 수에 따라서도 나눌 수 있다앞서 설명을 드린 예에서는 하나의 컬럼만으로 인덱스를 설정하였다이를 단일 컬럼 인덱스라고 한다반대로 하나 이상의 컬럼으로 인덱스를 설정하는 경우에는 복합 컬럼 인덱스(Composite Index)라고 한다복합 인덱스는 쿼리가 이루어질 때하나 이상의 컬럼에 대해서 자주 쿼리가 발생되는 경우에 유용하게 사용될 수 있다즉 복합 컬럼 인덱스는 T-SQL 구문을 이용하여 쿼리를 작성하는 경우, WHERE절에서 자주 사용되는 컬럼들을 복합 컬럼 인덱스로 작성하면쿼리의 성능을 향상시키는데 많은 도움을 주게 된다.

복합 컬럼 인덱스의 경우 인덱스 생성시 컬럼의 순서가 중요하다.

예를들어아래와 같이 복합 인덱스를 생성한다면

CREATE INDEX ix_companyName_contactTitle ON customers(companyName , contactTitle)

첫번째 companyName  가 먼저 정렬되고 두번째로 contractTitle이 정렬된다 contactTitle where 쿼리 조회시 index seek가 일어나지 않을 수 있다.

따라서 복합 컬럼 인덱스의 컬럼에서 첫번째 컬럼으로 작성된 컬럼을 조건절에서 이용하여가능하면 데이터 중에서 찾을 테이터의 범위를 줄여나가도록 하는 것이 좋다.

이와 같이 복합 컬럼 인덱스를 사용하면인덱스 페이지에 해당 컬럼들의 데이터를 가지고 있게 되기 때문에 보다 빠른 검색을 할 수 있게 된다하지만 복합 컬럼 인덱스가 장점만 가지고 있는 것은 아니다만약 복합 컬럼 인덱스로 설정된 컬럼의 크기가 크다면그 컬럼들의 크기만큼 인덱스 페이지를 작성하여야 하기 때문에 저장공간의 낭비를 초래할 수도 있다또한 데이터가 입력수정삭제되는 작업이 발생할 때마다그 크기만큼의 인덱스 페이지를 수정하는 작업을 하여야 하기 때문에단일 컬럼 인덱스보다는 더 많은 시간과 비용이 필요로 하게 된다.

 

[2-3] 유일값 유무에 따른 기준 : 유니크 인덱스 & 중복 값 인덱스

다음에는 유니크 인덱스와 중복 값 인덱스이다이미 눈치가 빠른 독자들은 이 인덱스들이 어떠한 성격을 가지는지 눈치 챘을 것이다그렇다 유니크 인덱스와 중복 값 인덱스는인덱스로 설정된 컬럼에 데이터가 중복된 값을 가지는지아니면 유일값을 가지는 컬럼인지에 따라서 분류된 것이다.

클러스터된 인덱스는 가능하면 유니크 인덱스로 설정할 것을 권장한다. SQL Server 2000에서 클러스터된 인덱스를 설정할 때유니크 인덱스로 지정하지 않는다면 SQL Server는 자동으로 해당 테이블에 대해서 각 열(Row)마다 Uniqueidentifier 값을 추가하게 된다이는 각 열(Row)들을 유일하게 지정하여중복되는 값을 제거하기 위한 것이다따라서 가능하면 클러스터된 인덱스를 지정하면서유니크 인덱스를 지정하는 것이 좋다.

간략히 얘기한다고 했으나 이제 보니 양이 적지 않았다.

사전 배경 지식으로써 인덱스만 가지고 얘기했는데도 말이다사실 이렇듯 튜닝을 하기 위해서는 알아야 할것이 너무 많다. (당연한 소리겠지만)

사실 필자의 경험상으로는 대부분의 속도상의 이슈가 되는 DB상의 문제중 상당수는 SQL 실행계획에 문제가 있어서 이를 해결하면 대부분이 해결되었던 것이 많다그래서 인덱스만이라도 설명은 꼭하려고 했던것이다.

자 다음은실제로 이 인덱스를 가지고 데모 성격으로 실제 유사하게 튜닝을 해보도록 하자.

 

[3] INDEX SEEK 이해하기

SEEK의 사전적 뜻은 ~을 찾다. ~을 구하다 정도가 되겠다 index를 찾겠다는건데 이때

Index scan 처럼 쭈욱처음부터 끝까지 찾는게 아니라 콕콕 찝어서 원하는걸 바로 바로 찾아간다고 하는것에 좀 차이가 있다(물론 seek 가 훨 빠르다.)

INDEX SEEK는 클러스터드 유무에 따라서다음으로 분류된다.

1.       Clustered Index Seek

2.       Index Seek

 Index 종류에 따른 seek를 나눈것이다.

첨언을 하자면 Non-Clustered Index Dense Index이고 Secondary index 성격을 띄고 있다.

쉽게 말하면 Non-Clustered B트리 구조가 밀집되고 빡빡한 느낌이 있다는것인데 무슨말인지 지금 당장 이해 하지 않아도 좋다.

 Index Seek를 하는 하부 알고리즘을 이해해 보자.

모든지 그렇지만 해당 시스템에서의 핵심 원리를 알아야 다른 응용이 가능하고 확장이 가능하다.

Index seek를 이해 못하면서 하는 튜닝은 튜닝이 아니다원리와 동작 방식을 이해 하지 못하고 어떻게 속도 튜닝을 하겠다는 말이겠는가그만큼 강조를 하니 꼭 이해 해 주길 바란다.

 

 

 

, [CodeMaster] 라는 테이블이 있다고 가정하자.

그리고 해당 테이블에는 클러스터드 인덱스가 없다고 가정해보자.

다음과 같은 SQL문장이 있다.

SELECT   * FROM CodeMaster

WHERE code_1 IN (‘B105’ , ‘C101’)

그리고 code_1 라는 컬럼에는 Non-Clustered Index가 생성되어 있다고 가정해 보자.

Non-Clustered Index Data Page Heap 이다.(Heap은 클러스터 인덱스가 없는 상태를 말한다.)

(첨언이지만 거의 대부분의 DBMS는 데이터 입출력 은 Byte 단위로 일어나지 않는다각각의 DBMS 플랫폼마다 최소한의 입/출력 단위가 정해져 있는데 Oracle은 이러한 입/출력 의 단위를 직접 세팅 할수 있으나 MS-SQL Server 8KB fix 이다이말은 SQL Server에서는1Byte를 읽기 위해서는 어쩔수없이 8KB를 읽어야 한다는 뜻이다.

Index seek를 설명하기 위해서 아래 그림과 같이 인덱스 구조를 표현해보았다.

MS-SQL이라면 아래 그림의 Index Page, Data Page는 모두 8KB이다.

 

 

, MS-SQL 제품이라고 가정을 해보고 Index Seek의 절차를 살펴보자.

자 위의 쿼리가 동작을 하면 모르긴 몰라도 제일 처음 할 것은 ‘B105’ 라는 코드를 찾을려고 할것이다.

그런데 code_1 컬럼이 인덱스가 생성되어 있으니 인덱스를 이용하여 데이터를 찾으려 할것이다.

1.       제일먼저 Root Page를 읽어서 다음에 접근할 Index Page Number를 찾는다, ‘B102’로 시작되는 2번째 index page에는 ‘B102’ ~ ‘B105’ 까지 있음을 Root Page를 통해 알수 있다.

2.       Index Depth 2이므로 중간노드가 Leaf Node이다. Leaf Index Page에서 row locator가 가르키는 데이터 페이지의 해당 row를 읽는다.

     ‘C101’도 동일하다.

이해가 되지 않는 독자분들은 다른 책을 더 참고하는 것이 좋을것이다본 강좌에서는 아주 간략히 설명하였기 때문에 이해에 어려움이 있을수 있다.

Anyway, 위의 과정에서 총 몇번의 Page에 접근하였는가를 살펴보자.

‘B105’ 를 찾기 위하여 3Page(Index Page 2 + Data Page 1를 읽었고, ‘C101’도 마찬가지로 3번이다.

위의 Seek 의 중요한 전제는 바로 unique index일 경우라는 것이다.!

만약 위의 인덱스가 non-unique index였다면 얘기는 달라진다.

Non-unique index 라면 현재 위치 바로 다음의 데이터 값이 현재값과 같을수도 있으므로 ‘B105’ 다음 값을 계속 읽으면서 ‘B105’가 아닌 값이 나올 때 까지 순차적으로 읽어야 할것이다.

반면, Unique Index라면 현재 값이 ‘B105’이면 다음 값은 다른 값이 분명하므로 아예 다음 값을 읽지도 않는다.

그리므로 확실히 unique한 데이터의 값을 가지는 컬럼에는 unique index를 걸어주는 것이 성능상 이점이 많다.

 

그럼 어느게 best way인지 감이 올 것 같은데안타깝게도 계속 강조했지만 그때 그때 상황에 따라 다르다는것이다.

인덱스가 만병통치약은 아니라는것인데,

실제로 인덱스를 이용한 접근 방식은 Page access depth가 깊어진다.

/출력 cost가 상승하게 된다또한 index seek보다 차라리 어떨때는 Data Page를 순차적으로 scan하여 데이터를 찾는 것이 더 빠를때도 많다그 차이점은 바로 데이터 로우의 개수다.

이러한 이유로 많은 권고 사항 및 DB 관련 책에서는 적은 수의 Row를 가지는 테이블에는 인덱스 생성이 오히려 독이 될수 있다고 말한다하지만 시간이 지나면서 옵티마이저도 발전을 거듭해 왔다.

그래서 옵티마이저가 데이터 Row 수를 판단하여 인덱스가 있음에도 Seek를 수행하지 않고 바로 Table Scan으로 수행하는 경우가 빈번하다이게 바로 Cost Based Optimizer의 강점이자 똑똑한 점이다.

여러분 입장에서는 index Seek를 할지 Full Scan으로 풀지에 대한 고민은 Index 를 이용할 때 와 Table Full Scan을 할때의 Disk I/O CPU비용들을 고려하는 것이 가장 좋은 방법이 아닐까 생각든다.

 

 

 

인덱스 가지고만 튜닝해보기)

지금까지 언급한것만 가지고 실제로 튜닝을 해보도록 하겠다본 강좌 작성중 어려웠던 점은 실제로 속도상의 문제가 있는 DB의 데이터 상태를 만들어 놓는것이었다실제로 통계정보의 정확도도 떨어뜨려야 좀더 실전과 유사한 상황을 만들겠지만.. 어쨌든 문제가 되는(튜닝의 소지가 다분한) DB의 예제상태로 만드는 것은 다소 난해했다.

 

[1] Sort를 제거하라!

모르긴 몰라도 DB SQL 문장에 대한 처리는 간결하고 서버에 부담을 주지 말아야 한다는 것은 모두다 알고 있는 사실이다. DB입장에서 데이터 조회는 최소화 되어야 한다.

실제 어플리케이션에서 상당수 차지하고 있는 기능이 있다바로 정렬기능.

XX번가 쇼핑몰이나 x, Gx켓 등의 쇼핑몰들에 보면 모가 있는가?

가격순/정렬순/판매순

그러면 여러분의 머리속에는 자연스럽게 ORDER BY 절이 떠오른다이걸가지고 얘기해보도록 하자.

(본 예제에서 필자가 별다른 index create 구문이 없다면 해당 테이블에는 인덱스가 없다고 생각해 주길 바란다.)

그리고 예를 들 테이블에 데이터양에는 신경을 덜 써주길 바란다어차피 데이터양에 따라서 튜닝의 접근 시작부터가 달라지고 그때그때 대처해야하는 방식이 다르다그저 normal 한 상태정도라고 가정하고 봐주길 바란다그리고 테이블의 스키마 구조는 상식적인 수준이고 주문관련 테이블들도 구성하였으니 크게 어려움은 없을거라 믿는다.

 

아래 문장은 평범한 order by 절이 있는 SQL 이다.

자 우리가 눈여겨 보아야 할 것은 실행계획.!

 

 

실제로 상당히 퍼포먼스가 떨어지는 실행계획이다물론 대부분의 상황이 그렇지만 위의 테이블의 데이터 레코드가 모 10 100개 이런건 논의의 대상이 아니다.

만약 10만개면? 100만개면 어떻게 될까.. 문제가 심각해질것이다또한 위의 쿼리를 수행하기 위해서 DBMS는 메모리에 올릴것이고(query workspace) 그러다 메모리가 부족해지면 디스크 IO를 무쟈게 발생시키며 물리적 worktable을 만들어 작업을 수행할것이다동시 접속자가 많은 페이지에서 위의 문제가 되는 SQL를 지속적으로 호출하는 상황이라면불을 보듯 뻔하다이 사이트 왜이리 느려서버 증설하자이런 식의 ..

 

 

또 잡담이 길어졌다.

위의 3개의 동그라미가 시사하는 바가 있다.

우선 순서대로 분석을 해보자.

1.       table scan

2.       sort

3.       Parallelism

의 순서대로 수행이 되었다.

자 우리는 이제 각각이 의미하는 바를 이해해야 하고 어떤상황에 해당작업이 이루어지며더 나아가 데이터 양의 상태와 데이터 조각상태통계상태 에 따라 천차만별의 실행계획을 내밷어 낸다는 것을 느껴야 하고본인의 생각자체가 optimizer가 생각하는 거와 유사하게 사고가 이루어져야 한다.

그럼 왜 위의 3개의 처리가 이루어졌을까?

물론 별다른 create index구문이 없다면 별도의 인덱스가 해당 테이블에 없다고 했으니,

위의 테이블에는 인덱스가 없다.

하나하나의 의미와 왜 발생되었는지 확인해보자.

1. table scan

너무도 당연한거다데이터 search point도 없으며인덱스도 존재하지 않는다.

2. sort

- ORDER BY 절이 있으니까 SORT 작업이 이루지는건 당연한 걸까?

틀린말은 아니지만 맞는 말도 아니다. ORDER BY 절이 있기 때문이 아니라 clustered index가 없기 때문이 좀더 맞는 말이다.

참고로 DBMS 입장에서 sort작업은 엄청나게 high cost 가 발생되는 작업이다..

3. Parallelism

             - 병렬처리를 했다는 뜻이다처리할 데이터가 많으니 데이터 페이지 단위 또는 옵티마이저가 적절하게 data range segment 작업을 하여 parallel scan을 한다는것이다.

이유가 그러면 테이블에 데이터 양이 많아서그럴수도 있고 아닐수도 있다.

절대적 데이터양 이 많으면 병렬처리로 갈 확률이 높은 것은 사실이다.

때에 따라서 데이터양 자체가 많은데 병렬처리를 하지 않는 경우도 있다.

여러가지 이유가 있겠지만 이런 경우에 많이 속한다.

1.       데이터양은 많지만 인덱스로 인하여 검색 데이터 양이 적어진 경우

2.       실제 물리적 데이터 양은 많으나통계정보의 업데이트가 이루어지지 않아서 옵티마이저가 데이터 양이 적다고 인식하는 경우

3.       실제로 데이터 양도 많고 통계정보에도 그렇게 표기되어 있지만 병렬처리후 merge 하는 비용이 더 크다고 옵티마이저가 판단하는 경우

이거모야정답도 없고 그때 그때 다르고 모이래라고 불만을 터트릴수 있지만 옵티마이저는 거짓말을 하거나 절대 자기 맘대로 하지 않는다.

특별히 role based 로 옵티마이저 세팅을 지정하지 않는다면 언제나 옵티 마이저는

Cost based로 움직인다정말이다비용을 항상 계산하고 거기에는 항상 논리적 계산과 분석을 한후에 옵티마이저가 실행계획을 리턴한다.

 

위의 내용이 어렵고 이해되지 않는다 해도 실망하지 말자나중에는 자연스럽게 이해 될것이다물론 이번강좌만으로는 한계가 있는건 사실이다.

 

이제 그럼 엄청나게 비싼 비용이 드는 SORT작업을 DBMS로부터 덜게 해주어 전체적 속도를 향상 시켜보자.

..

지금 바로 몰 해야 한다 라고 머리속에 떠오른 사람은 굳이 이 강좌를 볼 필요가 없겠다.

자 생각이 안떠오른 사람은 다음 글을 계속 읽어주기 바란다.

모르긴 몰라도 SORT를 제거하는게 이번 미션이다가장 좋은 방법은 몰까?

ORDER BY 절을 제거 하는 것맞는 말이다;;; 하지만 정렬이 안되잖아.

 

가장 좋고 강력한 방법은 바로 위에서 배웠던 CLUSTERED INDEX 생성이다.

 

 

 

위의 문장을 실행해 보았다.

당당하게 CREATE CLUSTERED INDEX IX_ORDERS2_CLST ON ORDERS2(ORDERID)

 를 치고 F5를 누른후 똑 같은 SELECT  SQL의 실행계획을 봤더니왠걸?

떡하니 아직도 SORT가 있다!!!

클러스터드 인덱스는 왠만하면 PK , 즉 유니크 한 컬럼이 좋다고 해서 ORDER테이블이니까 ORDERID  IDNEX컬럼으로지정까지 했는데 말이다!

이유는클러스터드 인덱스 컬럼과 ORDER BY 절의 컬럼의 불일치 때문이다.

반드시 클러스터드 인덱스가 있다고 해서 SORT가 없어지진 않는다!

 

 

 

 

 

 

이번에는 위에서 생성했던 클러스터드 인덱스는 그대로 두고 새롭게 NON CLUSTERED INDEX로 추가 생성하면서 CUSTOMERID 컬럼을INDEX컬럼으로 지정하였다.

 

 

실행하였더니???? ! SORT가 제거 되었네!

꼭 클러스터드 인덱스로 지정하지 않아도 SORT는 제거 되네맞는말이다.

하지만 INDEX SCAN 으로 수행되었다. SEEK 가 아니란 말이다.

그럼 index scan index seek 의 차이는?

최대한 복잡한 말 제거하고 단순하게 말하면 다음과 같다.

index Scan : 인덱스의 전체 또는 한 범위를 찾는것 
index Seek : 
인덱스의 특정범위의 행을 찾는 것

 

대체적으로 seek가 성능상 유리하다.

[2] The sequence decision Of Composite Index Columns.

 

 

위의 쿼리처럼 ordered 컬럼과 customerid 컬럼 조합으로 복합 인덱스를 생성하고,

WHERE절에 customerid 컬럼으로 조회를 하였다.

분명히 복합 인덱스에 customerid컬럼도 포함하고 있어서 index seek로 될 것 같은데 index scan으로 되었다왜일까?

ORDER BY A , B

와 같은 쿼리절이 있을 때 어떻게 정렬이 되는가먼저 A 컬럼 그룹이 정렬되고 그 A그룹내에서 B 컬럼의 데이터가 정렬된다이와 비슷하게 복합인덱스도 그렇게 정렬이 된다고 보면 된다.

그렇기 때문에 순서가 뒤 순서인 customerid 만으로는 데이터를 찾는데 한계가 있으므로 index scan으로 쪽 찾는다고 보면 된다.

복합인덱스 생성시 순서가 중요하다 라는 사실이다.

 

 

 

 

 

이제 아래의 컬럼을 수행해보니 index seek가 된다.

이해가 될것이다.

 

 

방금 수행한 where 절 에는 2개의 컬럼으로 찾지만 만약

SELECT ORDERID,CUSTOMERID,EMPLOYEEID,ORDERDATE FROM DBO.ORDERS2 WHERE CUSTOMERID = 'VINET'

와 같이 1개의 컬럼으로 찾는데 복합인덱스밖에 없고 customerid가 위에 처럼 2번째 순서로 지정되어 있다면 어떻게 해서 튜닝을 해야 할까?

복합인덱스 생성시 순서를 바꾸어 생성하는것도 방법이고, customerid 만을 위한 단일 인덱스를 생성하는것도 방법일것이다상황마다 다르기 때문에 정답은 없다.

 

그리고혹 이렇게 질문하는 똑똑한 독자도 있을것이다.

WHERE ORDERID = '10248' AND CUSTOMERID = 'VINET' 에서

WHERE CUSTOMERID = 'VINET'  AND ORDERID = '10248'

로 쿼리를 변경하면 위에 처럼 index seek 로 수행이 되지 않는 것 아니냐그래서 SQL where절 기술할 때 순서도 중요하지 않느냐 라고 질문하실수 있다.

(복합인덱스 생성시 ORDERID,CUSTOMERID 순이었으므로)

 

아주 훌륭한 질문이다.

답은 몰까상황마다 다르다.

Optimizer type에 따라 달라진다.

rule based 였다면 where 절 기술 컬럼 순서가 중요하고그에 기반하여 실행계획이 수립된다.

         만약 cost base였다면 where 절 에 기술하는 컬럼순서는 중요치 않다.

             왜냐면비용기반으로 옵티마이저가 실행계획을 결정하기 때문이기도 하고바로 MS-SQL내부에서 SQL 절이 도착하면 내부적으로 수행되는 simplication 로 다시 SQL 이 조정되기 때문이다.( simplication 관련 참조 하시면 알수 있을것이다.)

 

또다른 독자는 이렇게 질문할수도 있겠다.

잘 모르겠고 모든 경우를 커버하려면 WHERE 절에 걸리는 모든 컬럼에 복합인덱스로 생성하면 되지 않느냐라고 반문할수도 있다.

절대 무작정 만드는 인덱스는 silver bullet이 아니다.

특히나복합 인덱스의 경우에는 상호 컬럼 index page depth가 지속적으로 늘어나 index seek  scan시 과도한 cost를 불러일으켜 조회 성능에 저하를 초래 한다.

또한 절대적인 디스크 용량 차지도 물론 많아 지게 되는건 당연한 사실.

 

 

[3] Corvering Index

Corvering Index  Composite index Query검색에 필요 한 모든 Column들이 포함되는 특별한 NonClustered Index 의 형태로 index 만으로 데이터를 조회하는 특징을 지닌다.

 

 

 

복합 인덱스를 만들고 아래 쿼리를 수행해 보자.

 

 

Index seek where절 조회 자체에 대한 부분은 올바르게 수행된 것 같다.

그런데 난데 없이 RID LOOKUP이 발생하였다.

RID Lookup 은 몰까?

RID Lookup 외에 SQL 2005 이전에서는 Bookmark Lookup 이라는게 있었다.

또한 클러스터드 인덱스가 존재할때는 key lookup형태로 바뀌어 표기된다(그 이유는 클러스터드 인덱스 생성시 각 컬럼에 클러스터드ID값을 참조하게 됨으로써 lookup 대상이 바뀌는 특성 때문인데 이는 이해하기에는 별도의 클러스터드 인덱스 강좌를 해야할 정도의 수준으므로 생략하겠다)

어쨌든 Lookup 이녀석의 주요 골자는 이렇다.

SELECT 하려는 컬럼이 인덱스에 없을경우 non-clustered index key값을 가지고 Heap Page(혹은 clustered index 가 있는 테이블일 경우에는 clustered index검색하는것을 의미한다.

그러므로 당연히 Heap 이나 clustered index 를 찾아가는 과정에서 추가적인 I/O cost가 증가한다.

Oracle로 예를들면 인덱스 영역에 저장되어있는 ROWID를 가지고 Data Block Access 와 흡사하다고 생각하면 된다.

 

이러한 배경으로 인하여당연히 SELECT하는 컬럼이 모두 인덱스에 존재하고 인덱스가 데이터 조회에 관련된 모든 사항을 corver 하여 최적의 데이터 조회 성능을 낼 때,

이를 Covered Index 라고 하는것이다.

당연히 Covered index 만으로 조회 될때는 별도의 RID Lookup은 발생하지 않는다.

.

아래와 같은 경우에는 SELECT 절에 나오는 컬럼을 모두 복합인덱스로 설정한 것이다.

 

 

 

당연히 RID Lookup은 발생하지 않는다.

하지만 이게 최적의 솔루션이 아니라는 것을 독자분들은 이미 알고 있다.

 

[INCLUDE 키워드를 활용]

 

 

INCLUDE SQL2005 부터 지원한다.

그리고 non-clustered index에만 생성해야 한다.

그리고 Index page depth가 깊어지거나 추가되는 것이 아닌, Leaf level 바로 하위에 데이터 정렬이 이루어진다.

그리하여 위의 실행계획에서는 lookup이 발생되지 않는다.

[4] Search Arguments with Index

SARG

Non-SARG

WHERE name = ‘DEV.WON’

WHERE name = member_mane

WHERE age < 30

WHERE age != 30

WHERE price = 100/12

WHERE price *12 = 100

WHERE name like ‘D%’

WHERE substring(name , 1, 1) = ‘D’

WHERE price between 9 AND 20

WHERE price < 2 AND price > 4

WHERE name like ‘DONG%’

WHERE name like ‘%DONG’

위의 표를 보면 바로 어떤걸 의미하는것인지 알수 있다.

왼쪽편의 SQL 문장과 같이.

데이터 검색 범위를 좁히고 되도록 이면 INDEX를 사용할수 있는 쿼리 문장을 사용하라는 뜻이다.

 

 

[5] Index Management

[5-1] DBCC CONTIG

이제 인덱스 관련된 관리를 통하여 튜닝을 해보자.

먼저 테이블의 데이터 현황과 인덱스에 대한 단편화가 어떻게 되어 있는지 살펴볼수 있어야 한다.

 

 

 

 

 

 

 

 

 

아래의 명령을 실행해보니 아주 잘 결과를 표현해 준다.

 

 

빨간 네모를 친부분을 중점적으로 보았으면 한다.

첫번째 빨간 네모 항목은 바로 검색밀도다 [최적 : 실제대비를 나타내고 있고 100%에 가까울수록 검색 밀도가 높아 좋은 상태를 의미한다.

두번째 빨간 네모 항목은 논리 검색 조각화 상태다. 0%에 가까울수록 조각화가 없는 좋은 상태다.

세번째 빨간 네모 항목은 익스텐트 검색 조각화 상태다.(익스텐트 내 빈공간 비율을 의미마찬가지로 0%에 가까울수록 좋다.

 

아래와 같이 table.. index단위로도 조회 가능하다.

명령어 예
DBCC SHOWCONTIG (Table)
DBCC SHOWCONTIG (Table, Index)
DBCC SHOWCONTIG (Table) WITH FAST

 

 

그리고 아래표는 각각의 항목과 설명을 나타내고 있다.

항목

설명

스캔한 페이지

1페이지 = 8KB 이므로 스캔한 페이지는 800KB

스캔한 익스텐트

익스텐트 = 1페이지 * 8 이므로 17 * 8KB * 8 = 1088 KB

전환된 익스텐트

익스텐트를 스캔하면서 익스텐트와 익스텐트 사이를 옮겨간 횟수를 의미.

익스텐트 당 평균 페이지 수

하나의 익스텐트는 8개의 페이지라고 볼 수 있는데 익스텐트가 조각나서 하나의 익스텐트에 평균 5.9개의 페이지가 있는 것

스캔밀도

스캔밀도가 100% 이면 조각난 상태가 아니며 100% 미만이면 그 만큼 조각난 상태그러므로 위의 경우 약 25%가 조각난 상태라고 볼 수 있다.|

논리 스캔 조각화 상태

논리적인 조각화 상태를 의미합니다삽입갱신 등의 DML문에 의해서 인덱스의 트리구조가 전환된 것을 의미.

익스텐트 스캔 조각화 상태

인덱스의 잎 페이지 스캔에서 순서가 바뀐 익스텐트의 비율.

페이지 당 사용 가능한 평균 바이트 수

하나의 페이지는 최대 8060바이트가 저장될 수 있다이것은 페이지들이 사용할 수 있는 가용 페이지의 공간을 의미.

평균 페이지 밀도(전체)

스캔한 페이지의 조각화 상태. 100%이면 조각난 상태가 아니며, 100% 미만이면 조각난 상태.

 

모든 튜닝의 시작은 정확한 상황 파악과 진단이다.

현상을 파악을 해야 튜닝의 계획이 잡히는 법이다.

 

[5-2] INDEX DROP&CREATE /  DBREINDEX / INDEXDEFLAG

경우에 따라서는 INDEX drop하고 새로 create 하는 방식을 쓴다다음과 같은 경우에 대표적이다.

1.       기존 과 신규 인덱스의 이름이 동일하게 사용될 경우

2.       NonClustered Index Clustered Index로 만들경우

(clustered Key ID 참조 재정렬로 인한 성능이슈 방지를 위하여)

 

 

DBCC DBREINDEX ([table_name] , [‘index_name’ ])

e      사용이유 : 해당테이블에 많은 트렌젝션(CUD) 발생이 되면 테이블 단편화 가 가속되어 검색 밀도가 떨어져 인덱스 검색에 의한 성능향상이 감소된다이 때문에 단편화된 인덱스의 조각화 상태를 낮추어 검색성능을 높인다.

(하지만 대용량 테이블의 경우 주의 해야 합니다데이터도 많고 넌클러스터드로 많은 인덱스가 걸려있다면 Re buiding되면서 폭발적 로그 증가로 인하여 disk full 등의 risk 가 발생할수 있습니다.

따라서 로그 의 폭발적 증가를 막기 위해서는 SELECT databasepropertyex(‘sampleDB’ , ‘recovery’)

를 통해 나온 값이 full 이라면 simple recovery mode로 변경해 주셔야 합니다.

Alter database sampleDB set recovery simple)

대용량 데이터 베이스의 경우 index rebuild  online 상태에서 함부로 했다간 난리 납니다.

 

DBCC INDEXDEFLAG ( db이름, table이름, index이름)

e      인덱스 스캔성능을 향상 시키기 위한 목적으로 페이지의 물리적 순서가 왼쪽에서 오른쪽에로 잎 노드의 논리적 순서와 일치하도록 인덱스 잎 수준의 조각을 모은다.

e      DBCC INDEXDEFLAG 는 온라인 상태에서도 가능이 작업은 lock cycle 이 짧으므로 실행중인 쿼리나 업데이트 lock를 차단하지 않는다.

e      진행도중 언제든지 종료할수 있으며 종료된 시점까지의 상태를 보존한다.

DBREINDEX  INDEXDEFLAG 의 비교

 

DBREINDEX

INDEXDEFLAG

구축시간

일정

조각화에 따라 다름

온라인서비스 운영

2005 부터 가능(제약 많음)

가능

효과

100%

상황에 따라 다름

 

 

마지막으로 인덱스 통계 명령어를 통해서 마지막 통계 업데이트 시간을 알수 있고 그 시간을 통해 적절하게 통계에 대한 재 생성의 여부를 결정할수 있다.

DBCC SHOW_STATISTICS(테이블명,인덱스명)

 

 

쿼리힌트)

쿼리 힌트에 대한 나열과 설명은 의미가 없을 것 같다.

그리고 필자는 정말 쿼리 힌트는 특수한 경우가 아니라면 쓰지 말라고 권장하고 싶다어차피 쿼리 힌트를 써서 옵티마이저가 선택한 실행계획보다 더 좋은 성능을 내는 경우가 그렇게 많지 않고 대부분 옵티마이저가 정한 실행계획이 올바른 경우가 많다.

(다만통계 상황이 최신으로 업데이트 되어 있어서 옵티마이저가 적절한 통계를 토대로 실행계획을 수집 한다는 전제가 있다!)

특수한 경우가 아니라면쿼리 힌트를 통한 튜닝보다는 인덱스 단편화 관리를 잘하고 통계 수집을 원활히 하고적적한 인덱스 수립을 하는 것이 훨씬 효과적이고 정석적인 방법이라 생각한다.

그리고 쿼리 힌트는 그때 당시에는 적절한 힌트를 썼을 지어도 시간이 지나 데이터양의 변화가 심해 졌을 경우에는 작성했던 쿼리 힌트가 올바르지 않을수 있다다시 작성하고 소스를 수정해야 하는 경우가 오지 않는다는 보장이 없다.

 

SQL 쿼리 튜닝)

이제 인덱스는 다소 지겨우니 SQL 쿼리 문장 자체를 튜닝해보자.

사실 개발 시간의 부족과, DB 쿼리 에 대한 이해 부족도 등으로 인하여 잘못된 쿼리 SQL 문장이 작성되는 것이 사실이다그러지 않도록 노력하고 방안을 찾아야 함은 개발자와 DBA 모두의 과제가 아닌가 생각이 든다.

 

단순하고 심플한 SQL 문장으로 튜닝해 보자.

 

--쿼리튜닝예제1

SELECT A.customerID  ,

             (

                           SELECT SUM(quantity)

                           FROM [OrderDetail2] B

                           WHERE

                                        A.orderid = B.orderid

             ) qty

FROM orders A

 

 

 

위의 SQL 문장을 보자.

Order 테이블과 orderDetail2 테이블은 서로 1:N의 관계를 가지고 있으며,

하나의 주문에 대한 상세 주문정보(예를들면 주문한 상품들의 관계를 가지고 있다.

하나의 주문에 대해서 주문한 상품등의 카운트를 구하기 위하여 위와 유사한 쿼리를 많이 수행하곤 한다.

하지만 무엇이 문제일까사실 orderDetail 테이블에 데이터 양이 많지 않으면 그다지 문제가 되지 않는다.

하지만 데이터양이 많아질수록 order 테이블 레코드 하나당 orderDetail테이블의 sum집계 연산 COST는 기하급수적으로 늘어날것이다.

이러한 문제성있는 쿼리에 대해서 다음과 같이 튜닝해 보았다.

SELECT A.customerID  , B.qty

FROM orders A    LEFT OUTER JOIN

             (

                           SELECT orderid , SUM(quantity) AS qty

                           FROM [OrderDetail2]

                           GROUP BY orderid

             ) B

ON A.orderid = B.orderid

위의 SQL 문장을 보도록 하자.

Group by 를 먼저 하여 SUM 값을 가져온후 join을 하고 있다어떤 효과가 발생하는지는 직접 독자들이 실행하여 보길 바란다.



다음은 흔하게 쓰곤 하는 OR 절에 대한 부분이다.

아래와 같은 SQL문장은 매우 흔하게 사용한다.

하지만 OR 절은 내부적으로 Table Full Scan을 자주 유발시키는 주범이다.

OR 절로 걸려야 하기 때문에 Data Scan 범위가 넓다는것이다.

SELECT * FROM dbo.OrderDetail2

WHERE productId = 11 or  quantity >3

 

 

 

 

 

 

위의 실행결과 약 4.8초 가 소요되었으며예상대로 Table scan이 이루어졌다.

 

아래와 같이 쿼리를 수정해 보았다물론 결과물은 동일하다.

SELECT * FROM dbo.OrderDetail2

WHERE productId = 11

UNION

SELECT * FROM dbo.OrderDetail2

WHERE  quantity >3

 

 

 

 

수행해본 결과 0.9초가 걸렸다여전히 튜닝해야 할 포인트가 많지만 SQL 문장만 바꾸었을 뿐인데 현격히 속도가 빨라졌음을 알수 있다.

또한 눈여겨 볼점은 병렬처리를 수행하여 속도가 빨라졌다는 점이다.

 

그외에도 SQL 문장이 DB로 하여금 무리하고 과도한 연산을 수행하게 하는 비효율적인 경우는 많다.

그 모든 것들을 나열하기에는 너무 많으며 때에 따라서는 어떤것도 정답이 될수 있고틀린것이 될수 있기 때문에 단언할 수는 없다.

 

 

 

Join 의 비밀)

사실 Oracle에서 얘기하는 Driving 테이블을 SQL서버에서는 OUTER테이블이라고 칭한다.

본강좌에서는 모두 통일하여 Driving 테이블이라 칭하겠다.

 

1. NESTED LOOP JOIN의 특징

① 순차적 : Driving Table의 처리범위에 있는 각각의 로우들이 순차적으로 수행될 뿐 아니라

테이블간의 연결도 순차적이다.

② 선행적 먼저 액세스되는 테이블의 처리범위에 의해 처리량이 결정.

③ 종속적 나중에 처리되는 테이블은 앞서 처리된 값을 받아 액세스.

즉 값을 받아서 처리범위가 결정됨

④ Random Access : Driving Table의 인덱스 액세스는 첫번째 로우만 Random Access이고,

나머지는 Scan, 연결작업은 Random Access .

⑤ 선택적 연결되는 방향에 따라 사용되는 인덱스들이 달라질 수 있다.

⑥ 연결고리중요방향성 연결고리의 인덱스 유무에 따라 액세스 방향 및 수행속도에 많은 차이가

있음.

⑦ 부분범위처리 가능 연결작업 수행 후 Check 되는 조건으로 부분범위처리를 하는 경우에는

조건의 범위가 넓거나 없다면 오히려 빨라짐

2. NESTED LOOP JOIN의 사용

① 부분범위처리를 하는 경우

- Nested Loop Join은 주로 전체가 아닌 부분범위를 처리하는 경우에 유리.

② Join 되는 테이블이 상호의존적인 경우

- Join 되는 어느 한 쪽이 상대방 테이블에서 추출된 결과를 받아야 처리범위를 줄일 수 있는

상태라면 항상 유리하지만, Driving Table의 처리가 많거나 연결 테이블의

Random Access 량이 많을 경우에는 Sort/Merge Join 보다 불리.

③ 처리량이 적은 경우

- Random Access를 많이 하므로 On-Line 애플리케이션처럼 처리량이 적은 경우에 유리.

④ Driving Table 의 선택이 관건

어느 테이블이 먼저 ACCESS 되는가가 수행속도에 큰 영향을 미치게 된다..

3. SORT MERGE JOIN의 특징

① 동시적 각각의 테이블이 자신의 처리범위를 액세스하여 정렬해 둔다.

② 독립적 각 테이블은 다른 테이블에서 어떠한 상수값도 제공 받지 않고 주어진 상수값에 의해서만

범위를 줄인다.

③ 전체범위처리 부분범위처리를 할 수 없다./3

④ Scan방식 자신의 처리범위를 줄이기 위해 인덱스를 사용하는 경우만 Random Access이고,

Merge작업은 Scan

⑤ 선택적 연결고리가 되는 칼럼은 인덱스를 사용하지 않는다..

⑥ 무방향성 : Join의 방향과는 무관.

4. SORT MERGE JOIN의 사용

① 전체범위처리를 하는 경우

- Sort/Merge Join은 주로 부분이 아닌 전체범위를 처리하는 경우에 유리.

② EQUI-JOIN에서만 가능

③ Join되는 테이블이 상호독립적인 경우

상대방 테이블에서 어떤 상수값을 받지 않고도 처리범위를 줄일 수 있는 상태에서 유리.

상수값을 받아 줄여진 범위가 30%이상이면 Sort/Merge Join이 유리.

④ 처리량이 많은 경우

- Random Access를 하지 않으므로 전체범위처리에 유리.

⑤ 효과적인 인덱스 구성이 관건

자신의 처리범위를 어떻게 줄이느냐가 관건이므로 효과적인 인덱스구성이 매우 중요

5 HASH JOIN의 특징

① 독립적 각 테이블은 다른 테이블에서 어떠한 상수값도 제공 받지 않고 주어진 상수값에 의해서만

범위를 줄인다.

② 전체범위처리 부분범위처리를 할 수 없다.

③ 선택적 연결고리가 되는 칼럼은 인덱스를 사용하지 않는다.

④ SORT 안함 : SORT를 하지 않으므로 SORT MERGE JOIN 보다 좋은 성능을 내며,

작은 table 과 큰 table 의 join시에 유리.

6. HASH JOIN의 사용

① 전체범위처리를 하는 경우

- HASH Join은 주로 부분이 아닌 전체범위를 처리하는 경우에 유리.

② EQUI-JOIN에서만 가능

③ 처리량이 많은 경우

- Random Access를 하지 않으므로 전체범위처리에 유리.

④ 효과적인 인덱스 구성이 관건

자신의 처리범위를 어떻게 줄이느냐가 관건이므로 효과적인 인덱스 구성이 매우 중요.

⑤ 작은 table 과 큰 table의 join시에 유리.__

 

 

 

튜닝에 유용한 것들)

튜닝을 하기 전에 가장 필요한 것은 무엇일까?

바로 현재 시스템에 어떤 문제가 있는지 어떤 것을 튜닝해야 할지 알아야 하는것이다.

이것은 마치 의사가 진찰 하기전에 정확하게 문진하고 어디가 아픈지를 알아야 다음 치료로 가기 위한 시간절약과 방향을 잡을수 있을것이다.

 

[의사에겐 청진기, DBA에겐 모니터링 툴]

PSSDiag – user 가 요청한 SQL 문장 수집.file save되며 시스템 부하가 profiler 보단 적다.

SQLDiag – SQL 2005부터 기본적으로 설치되어있다.

아래는 SQLDiag실행화면

 

 

 

SQLNexus – 유명한 Ken Handerson 가 남기고간 위대한 무료 툴. Trace파일 분석을 해주는 아주 유용한 툴이다.

 

자 다음은 각종 유용한 SQL 문장을 소개 한다.

 

[1] 사용하지 않는 인덱스를 찾아라

위에서도 언급했지만 무분별한 인덱스 생성은 결코 좋지 못하다사용하지 않은 인덱스를 추출하여 정리해 보자이를 위해서 아래의 쿼리를 공유하니 유용하게 사용하길 바란다.

 

---------------------------------------------------사용하지않은인덱스추출

------초기테이블생성

use master

go

DECLARE @curdb varchar(50)

set @curdb = 'master'

select @curdb as DBName, object_name(i.object_id) AS TAB, i.name AS IDXName into #unusedindex

from sys.indexes i, sys.objects o

where  i.index_id NOT IN (

      select s.index_id from sys.dm_db_index_usage_stats s

      where s.object_id=i.object_id and i.index_id=s.index_id

                  and database_id = db_id(@curdb)

              )

and o.type = 'U' and o.object_id = i.object_id

order by object_name(i.object_id) asc

go

------생성DB 확인

--select name from sys.sysdatabases

------사용하지않은인덱스추출

DECLARE @curdb varchar(50)

set @curdb = 'DBName'

INSERT INTO #unusedindex(DBName, TAB, IDXName)

select @curdb as DBName, object_name(i.object_id) AS TAB, i.name AS IDXName 

from sys.indexes i, sys.objects o

where  i.index_id NOT IN (

      select s.index_id from sys.dm_db_index_usage_stats s

      where s.object_id=i.object_id and i.index_id=s.index_id

                  and database_id = db_id(@curdb)

              )

and o.type = 'U' and o.object_id = i.object_id

order by object_name(i.object_id) asc

go

------결과조회

select * from #unusedindex

drop table #unusedindex

 

[2] 이 인덱스는 어디 테이블에 있고 이 테이블은 어디 DB에 있는겨

또 필요한 것이 있다바로 인덱스->테이블->DB 순으로 서로 object들을 찾는것이다.

----------------------------------------------------------------------------Show_InDEX 정보출력

Use master

go

SET NOCOUNT ON

CREATE TABLE #ShowIndexInfo (DBNAME VARCHAR(50), TableName VARCHAR (255),IndexName VARCHAR (255), IndexDesc VARCHAR(200), INDEXKEY VARCHAR(400) )

go

 

CREATE Proc sp_ShowIndex @CURDB VARCHAR(50)

AS

SET NOCOUNT ON

DECLARE @tablename VARCHAR(255)

CREATE TABLE #ShowIndexInfo2 (TableName VARCHAR(255),IndexName VARCHAR(255), IndexDesc VARCHAR(200), INDEXKEY VARCHAR(400))

CREATE TABLE #ShowIndexInfo3 (IndexName VARCHAR (255), IndexDesc VARCHAR(200), INDEXKEY VARCHAR(400))

DECLARE tables CURSOR READ_ONLY FOR SELECT Schema_Name(uid)+'.'+[Name] FROM SYS.SysObjects WHERE TYPE = 'U'

OPEN tables FETCH NEXT   FROM tables   INTO @tablename

WHILE @@FETCH_STATUS = 0

BEGIN

 

   INSERT INTO #ShowIndexInfo3 (IndexName, IndexDesc , INDEXKEY )

   EXEC ('sp_helpindex ''' + @tablename + '''')

   INSERT INTO #ShowIndexInfo2( TableName ,IndexName , IndexDesc, INDEXKEY )

   SELECT @tablename,* from  #ShowIndexInfo3

   DELETE FROM #ShowIndexInfo3

   FETCH NEXT  FROM tables INTO @tablename

END

INSERT INTO #ShowIndexInfo (DBNAME , TableName,IndexName, IndexDesc, INDEXKEY)

SELECT A.DBNAME,B.* FROM (SELECT @CURDB AS DBNAME) A , #ShowIndexInfo2 B

DROP TABLE #ShowIndexInfo2

DROP TABLE #ShowIndexInfo3

CLOSE tables

DEALLOCATE tables

GO

 

 

declare @name varchar(100), @cmd varchar(2000)

DECLARE dbname CURSOR READ_ONLY FOR  SELECT Name FROM SYS.sysdatabases where name notin('master','msdb','model','tempdb','pubs','northwind','DISTRIBUTE','SQLBPA')

OPEN dbname FETCH NEXT FROM dbname INTO @name

WHILE @@FETCH_STATUS = 0

BEGIN

  select @cmd = N'use ' +  quotename(@name) + N' exec sp_ShowIndex '+@NAME 

  exec (@cmd)

  FETCH NEXT FROM dbname INTO @name

END

CLOSE dbname

DEALLOCATE dbname

select * from #ShowIndexInfo

drop table #ShowIndexInfo

drop proc sp_ShowIndex

SET NOCOUNT OFF

GO

 

 

 

 

 [3] 테이블을 자세히 보라

중요한 테이블의 정보에 대해서 면밀히 살펴볼 필요가 있다.

------------------------------------------------------------------------ 테이블사용량정보

USE MASTER

GO

SET NOCOUNT ON

CREATE TABLE #tblSpace(   DBNAME VARCHAR(50), name        VARCHAR(50),   rows        INT,reserved    VARCHAR(30),data        VARCHAR(30), index_size  VARCHAR(30),unused      VARCHAR(30))

GO

CREATE PROC SP_TABLERECINFO @CURDB VARCHAR(100)

AS

 DECLARE RCur CURSOR READ_ONLY FOR select 'sp_spaceused ''' + SCHEMA_NAME(uid)+'.'+NAME + '''' AS SQL from SYSOBJECTS where XTYPE = 'U'

 DECLARE @strSQL     VARCHAR(2000),  @dtStart    DATETIME

 OPEN RCur FETCH NEXT FROM RCur INTO @strSQL

 CREATE TABLE #tblSpace2( name        VARCHAR(50),   rows        VARCHAR(30),reserved    VARCHAR(30),data        VARCHAR(30),index_size  VARCHAR(30),unused      VARCHAR(30))

 WHILE (@@fetch_status <> -1)

 BEGIN

     IF (@@fetch_status <> -2)

     BEGIN

          INSERT INTO #tblSpace2

          EXEC(@STRSQL)

     END

     FETCH NEXT FROM RCur INTO @strSQL

 END

 insert into #tblSpace(DBNAME,[name], [rows], reserved, data, index_size, unused)

 SELECT A.DBNAME,B.* FROM (SELECT @CURDB AS DBNAME) A ,#TBLSPACE2 B

 DROP TABLE #TBLSPACE2

 CLOSE RCur

 DEALLOCATE RCur

GO

declare @name varchar(100), @cmd varchar(2000)

DECLARE dbname CURSOR READ_ONLY FOR  SELECT Name FROM master.dbo.sysdatabases where name notin('master','msdb','model','tempdb','pubs','northwind')

OPEN dbname FETCH NEXT FROM dbname INTO @name

WHILE @@FETCH_STATUS = 0

BEGIN

  select @cmd = N'use ' +  quotename(@name) + N' exec SP_TABLERECINFO ' +@NAME 

  exec (@cmd)      

  FETCH NEXT FROM dbname INTO @name

END

CLOSE dbname

DEALLOCATE dbname

GO

SELECT * FROM #tblSpace ORDER BY DBNAME, ROWS DESC

DROP TABLE #tblSpace

DROP PROC SP_TABLERECINFO

SET NOCOUNT OFF

GO

 

 

 

 

 

마무리하며)

 정말 정신없이 튜닝에 대해서 정리해 보았다.

아직 다루지 않은것들이 너무 많다.

l         2개이상의 JOIN 쿼리에서의 인덱스 설정 및 구성

l         트렌젝션처리 방식을 이용한 튜닝

l         커서를 사용함으로써 발생된 이슈에 대한 튜닝

l         시스템 구성을 최적화하여 성능을 높이는 시스템 레벨 튜닝

 

너무 방대하고 상황마다 다르지만 끝으로 몇가지 를 권장한다.

1.       인덱스와 통계 자료에 대해서 주기적으로 업데이트하라.

이는 매우 중요하다옵티마이저에게 최신의 자료를 제공해야 할 DBA의 의무가 있다.

2.       AdHoc 쿼리를 지양하라. Trivial PLAN 을 갖을수 있도록 Prepared 타입의 SQL 로 작성하라.

사실 AdHoc 쿼리도 빈번한 수행 쿼리에 대해서는  proccache에 저장된다  하지만 where 절의 key값만 다르게 해서 다양한 Adhoc 쿼리가 수행요청이 온다면 proccache 에는 무수히 많은 complied plan이 쌓여 메모리 누수가 발생할수 있다매우 중요하다.

             3. 적절한 인덱스를 구성하라.

                - 너무 무분별하지 않게정확하게 필요한 인덱스를 만들어라

             4. 항상 문제가 되는 SQL 쿼리가 있는지 모니터링하고 지속적으로 튜닝하라.

                - 처음에는 별게 아닌 것 같지만 나중에 큰산이 되어 시스템을 망가트리게 될수 있다지속적인 관심과 튜닝은 필수다.

 

모 이정도로 본 강좌를 정리하려 한다.

회사다니며틈틈히 본 강좌를 작성하였으나역시 시간과 여유의 문제가 따른다좀더 완성도 있고 좋은 내용으로 구성하고 싶었으나

어쩌랴.. 지금 상황에서 최선을 다한것에 만족하려 한다.

 

끝으로)

부족한 강좌 끝까지 봐주셔서 감사드립니다편의상 짧은 어투도 양해부탁드려요.

욕심에는 Oracle과 비교하며 작성하려 했으나 너무 여의치 않네요튜닝은 결코 만만하거나 섣부르게 할수 있는 부분이 아닙니다정확히 알고 이해하고 있어야만 가능하지죠하지만 모든지 노력하고 연습하면 가능하겠죠저도 그러기 위해서 노력하렵니다..

대부분 자동차 튜닝의 끝을 본 사람들이 이런 얘기를 한다고 하죠자동차의 튜닝의 끝은 순정 부품이다.

저는 DB튜닝의 끝을 감히 이렇게 말씀 드리고 싶습니다.

“DB튜닝의 끝은 DB Modeling에 있다.”

이 말은 속도를 빠르게 하기 위해서 다시 DB모델링을 한다는게 아니고요 DB모델링이 처음부터 잘되어있으면 상당수의 많은 속도 이슈가 제거될수 있다는 뜻입니다.^^;

어쨌든 긴 강좌 봐주셔서 감사합니다.

다음에 또 뵈요.~~


반응형

--시스템 테이블을 이용한 각 테이블의 COUNT정보 

select o.name as TABLE_NAME, i.rows as ROW_COUNT_ALL
from sysindexes i, sysobjects o
where o.xtype = 'U'
and o.name <> 'sysdiagrams'
and o.id = i.id
order by o.name
go

 

 

--특정 저장 프로시져의 내용정보


select o.name,c.text from syscomments c , sysobjects o
where c.id = o.id
and o.name = 'SP_NAME'
order by c.id,c.colid

go

 

--뷰를 작성한 SELECT문장 정보


select o.name,c.text from syscomments c , sysobjects o
where c.id = o.id
and o.name = 'VIEW_NAME
order by c.id,c.colid

go

 

--특정 테이블의 컬럼 정보


select o.name,c.name 
from syscolumns c, sysobjects o
where c.id=o.id
and o.name = TABLE_NAME

go

 

--특정 테이블의 인덱스 정보 


select o.name,col.name 
from sysindexkeys ik, sysobjects o, syscolumns c,
   (select i.id,i.indid,i.status 
    from sysindexes i 
    where (i.status & 2048)<>0) sub_i
where ik.id=o.id
and ik.id=c.id
and ik.colid=c.colid
and ik.id=sub_i.id
and ik.indid=sub_i.indid
and o.name='TABLE_NAME' 
order by ik.id,ik.indid,ik.keyno

go

 

--현재 서버상에  존재하는 프로세서 정보


select 
spid,kpid,lastwaittype,status,hostname,program_name
from master..sysprocesses

go

 

--특정일 이후에 작성된 오브젝트 객체만을 알고 싶을때


select o.name table_name, substring(v.name + x.name,1,16) type , o.crdate create_date

,substring(user_name(uid),1,8) owner
from sysobjects o, master.dbo.spt_values v, master.dbo.spt_values x
where o.sysstat & 0xf = v.number
and v.type = 'O'
and x.type = 'R'
and o.userstat & -32768 = x.number
and o.crdate >= 'DATE_BASE'

order by 4,2,3,1

go

 

--현재 트랜잭션 락 현상을 일으키는 세션정보와 대상 오브젝트 정보


select  convert (smallint, req_spid) spid,
rsc_dbid dbid,rsc_objid objId,o.name objname,rsc_indid IndId,
substring (v.name, 1, 4) type,substring (rsc_text, 1, 16) resource,
substring (u.name, 1, 8) mode,substring (x.name, 1, 5) status
from master.dbo.syslockinfo,master.dbo.sysobjects o,
master.dbo.spt_values v,master.dbo.spt_values x,
master.dbo.spt_values u
where master.dbo.syslockinfo.rsc_type = v.number
and master.dbo.syslockinfo.rsc_objid *= o.id and v.type = 'LR'
and master.dbo.syslockinfo.req_status = x.number and x.type = 'LS'
and master.dbo.syslockinfo.req_mode + 1 = u.number and u.type = 'L'
order by spid

go

 

--테이블의 인덱스를 강제로 타게 만들기


select * from TABLE_NAME tablename WITH(INDEX(PK_NAME))

where tablename.empno = 1
go


--서버상의 쿼리 부하 확인


select p.status,  p.program_name, p.hostname

, p.spid, p.blocked, p.kpid, p.cpu, p.physical_io, p.waittype, p.waittime, p.lastwaittype, p.waitresource, p.dbid

, p.uid, p.memusage, p.login_time, p.last_batch

, p.ecid, p.open_tran, p.sid, p.hostprocess

, p.cmd, p.nt_domain, p.nt_username, p.net_address, p.net_library, p.loginame

, p.context_info, p.sql_handle, p.stmt_start, p.stmt_end

FROM master..sysprocesses p

where (status like 'run%' or waittime > 0 OR blocked <> 0 OR open_tran <> 0

OR EXISTS(SELECT * FROM master..sysprocesses p1 where p.spid = p1.blocked and p1.spid <> p1.blocked)

  )

--AND spid > 50

--AND spid <> @@spid

AND LTRIM(RTRIM(program_name)) = 'Microsoft JDBC Driver for SQL Server'

ORDER BY CASE WHEN status like 'run%' THEN 0 ELSE 1 END

, waittime DESC, open_tran desc


SELECT

[er].[session_id],

[es].[program_name],

[est].text,

[er].[database_id],

[eqp].[query_plan],

[er].[cpu_time]

FROM sys.dm_exec_requests [er]

INNER JOIN sys.dm_exec_sessions [es] ON

[es].[session_id] = [er].[session_id]

OUTER APPLY sys.dm_exec_sql_text ([er].[sql_handle]) [est]

OUTER APPLY sys.dm_exec_query_plan ([er].[plan_handle]) [eqp]

WHERE

[es].[is_user_process] = 1

AND [er].[last_Wait_type] = N'SOS_SCHEDULER_YIELD'

ORDER BY

[er].[session_id];

GO


--쿼리 실행 시간 확인


SELECT SUBSTRING(qt.TEXT, (qs.statement_start_offset/2)+1,

((CASE qs.statement_end_offset

WHEN -1 THEN DATALENGTH(qt.TEXT)

ELSE qs.statement_end_offset

END - qs.statement_start_offset)/2)+1),

qs.execution_count,

qs.total_logical_reads, qs.last_logical_reads,

qs.total_logical_writes, qs.last_logical_writes,

qs.total_worker_time,

qs.last_worker_time,

qs.total_elapsed_time/1000000 total_elapsed_time_in_S,

qs.last_elapsed_time/1000000 last_elapsed_time_in_S,

qs.last_execution_time,

qp.query_plan

FROM sys.dm_exec_query_stats qs

CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt

CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp

ORDER BY qs.total_worker_time DESC -- CPU time


+ Recent posts