DB를 관리/운영 하다보면 점점 DB가 느려짐을 느끼는데...

 

여러가지가 있지만 DB 측면에서만 확인 해 보자면 아래와 같은 원인을 찾을 수 있다.

 

1) Data 누적에 따른 IO 증가

     - Data가 늘어난다고 해서 비례해서 속도가 느려지는건 아니다.

        인덱스가 정상적으로 잡혀 있고 정상적인 쿼리 계획에 의한 실행이라면

        거의 동일한 속도를 유지할수 있다.

        인덱스 관리, 쿼리 확인 하여 IO 높은 쿼리를 수정하는 등의 지속적인 관리 필요

 

2) 인덱스 단편화에 따른 실행비용 증가

    - 쿼리 실행계획 확인했는데 모두 seek로 클러스터, 넌클러스터 인덱스를 잘 타고 있는데도

        인덱스의 단편화 현상이 심화 되어 속도 지연현상이 일어날 수 있다.

        보통 인덱스가 10% 이상 깨지면 재구성을 해줘야 한다고 함

 

      o 인덱스 조각화 확인

        DBCC SHOWCONTIG (테이블명)

        SELECT * FROM sys.dm_db_index_physical_stats

        결과 항목 중 scan Density(스캔밀도) 항목이 90% 이상에 미치지 못하면 성능에 좋지 않으므로

        인덱스 재구성 해줘야함

         DBCC DBREINDEX (테이블명, 인덱스명) -- 주의! 테이블 사이즈가 클경우에는 인덱스 단위로 재구성해주는게

         좋고 테이블단위, DB 단위로 할 경우에는 온라인으로 진행하도록 해야함

 

     o 인덱스 사용량 분석

         select * from sys.dm_db_index_usage_stats

 

3) 사용량 증가에 의해 컴파일(cpu) 증가

      - DB서버 cpu를 증가시키는 원인중에 하나가 쿼리 재컴파일에 있다.

          쿼리가 호출되면 해당 쿼리를 컴파일 하게 되는데 문제는 무거운 쿼리의 컴파일 횟수가 많아지면

          자연스레 cpu가 증가 하게 되는것이다.

         이를 방지하기 위해 캐쉬된 쿼리를 사용하는것인데, 캐쉬된 쿼리를 사용하기 위해서는 Stored Procedure,

         동적쿼리 실행 중 sp_executeSql 이다.  (exec(@sql) 로는 사용을 자제하자! ^^)

         실행계획에 쿼리를 캐쉬해서 재사용을 하도록 하여 재컴파일도 하지 않고 이에따라 속도향상도 할 수 있다.

 

         o 캐쉬 정보 확인

         -- DBCC FLUSHPROCINDB(8) -- 선택DB 캐쉬 삭제

         -- DBCC FREEPROCCACHE -- 모든 DB의 캐쉬 삭제

         select objtype, dbid, usecounts, status, sqlbytes, sql from master.dbo.syscacheobjects

         objType에 'Adhoc', Prepared', 'Proc' 등을 확인 할 수 있다, Prepared가 sp_executeSql 이다.

        

         o MSSQL은 실행계획에 의해 쿼리를 실행한다. 비용을 기반으로 하여 최적화 된 실행 계획을

             생성하는데, Ad-hoc 쿼리는 매번 실행계획을 생성하는데 반해 Sotred Procedure는 캐쉬에 저장하여

             재사용을 할 수 있음

             Profiler 에서 cpu가 높은 쿼리 를 확인 해보고 이벤트에 SP:CacheHit, SP:CacheInsert 를 확인 체크하여

             추적을 하여 해당 쿼리들에 대해 튜닝 여뷰를 결정 하도록 한다.

 

4) 통계 미 업데이트로 인한 최적화되지 못한 실행계획 생성

      - 통계는 테이블의 테이터 분포 상태를 나타내는 정보이고, 최적의 실행 계획을 생성하기 위해 이용되는

           테이블 정보이다.

           통계는 기본적으로 테이블의 데이타 변경이 10% 이상 되었을경우 자동 갱신 되는데 문제는 데이타가

           1천만건 이상이라던지 대용량 일경우에 100만건이 되기전까지 업데이트를 하지 않는다는 것이다.

           실행계획에 의해 쿼리를 실행하는데 현재 데이타가 100만건 가까이 실데이타와 계획된 데이타가 다를경우

           예상된 결과보다 많은 데이타가 나오면서 계획되지 않은 결과를 출력하면서 성능이 저하될 수 있다.

          이럴때는 통계 업데이트를 하여 정상적인 실행계획을 갖도록 처리해줘야한다.

 

         o DB의 모든 통계 정보 갱신

            sp_updatestats 

            (주위! 운영중에 갱신을 하게되면  실행계획을 잡아야 하므로 느려질수 있으므로 한가할때 실행해주도록 )

         o 통계 정보 확인

            sp_helpstats 테이블명

            DBCC SHOW_STATISTICS (테이블명, 통계명)

 

5) block, lock 에 서비스 지연

     o 블로킹 모니터링

         sp_lock (status 열)

         sp_who or sp_who2 (blk, blkby 열 0 이상이면 블로킹 있는거임)

           sp_who2 실행시 이상있는 ID가 발견되면, ProgramName 확인 -> .Net Sqlclient Data Provider 일경우 아래 쿼리

           실행하여 쿼리 상세 정보 확인 

           

           => dbcc inputbuffer(SPID)

           

     o 블로킹 방지

          - with (nolock), with(readpast) 옵션 사용

          - lock timeout 설정 - set lock_timeout <시간

 

6) 쿼리 실행계획 관련 옵션

    Set Statistics Profile { ON | OFF } -- 쿼리 결과에 실행 계획을 포함
    Set Statistics IO {ON | OFF}     -- 페이지의 입출력 수를 알 수 있다.
    Set Showplan_All { ON | OFF }   -- 실행 계획만 보는 옵션 (실행계획을 도식화로 보여주지 않고 텍스트로 출력)

 

※ 성능 모니터를 이용한 분석

    - SQLServer:Statistics : Batch Requests/sec (초당 실행되는 쿼리)

    - SQLServer:Statistics : SQL Compilations/sec, Recompilation(재 컴파일이 낮아야 함)

    - SQLServer:Plan Cache: Cache Hit Ratio (캐시 적중률)

      -> 90% 이상으로 유지되어야함, 수치가 낮을수록 캐쉬는 의미가 없음

         적정 수준 98%를 유지할 수 있도록 해야함. 이를 위해서는 데이터 참조의 지역성을 잘

         고려하여 알맞은 캐시 공간을 확보해야한다.

    - SQLServer:Memory

    - Processor : % Processor Time 등  

[출처] MS-SQL 운영 팁|작성자 연우빠

https://docs.microsoft.com/ko-kr/sql/ssms/download-sql-server-management-studio-ssms


SQLSERVER 2008~2017까지 커버됩니다.


필요에따라 링크에 포함된 보안패치를 설치해야할 필요가 있습니다.

--------------- 1.  DB 분리하기

 

sp_detach_db DB명

 

-- 혹시 사용중이라고 나오면 로그인 사용자 kill
-- sp_who -- 로그인 사용자 확인
-- kill spid--로그인 사용자 쥑이기
-- 그리고 SMS DB같은 경우 문자보내기 서비스가 실행중이므로 서비스를 중지 시키고,
-- 로그인사용자 kill 시키고 분리

 

----------------- 1. 분리 끝

 

 

-----------------  2. DB 연결
-- 로그 파일이 있는 경우 sp_attach_db 사용
 sp_attach_db @dbname='DB명', @filename1='파일경로\ball.mdf', @filename2='파일경로\ball_log.ldf'

 

--데이터 파일만 있은 경우 sp_attach_single_file_db 사용
sp_attach_single_file_db @dbname='DB명', @physname='파일경로\baseball.mdf'

-----------------  2. DB 연결 끝


select 

case when (money - cashys) > 0 then '앞' 

when (money - cashys) = 0 then '중' 

else '뒤' 

end  as 'DIFF_CHK' ,money,cashys

from mem_Cashs

다른 서버의 동일한 조건의 테이블에 대량의 데이터를 옮겨야 할 때가 가끔 있다.

그때 BULK INSERT를 사용하면 한방에 슉~ 넣어줄 수 있다.

 

나는 다른 서버 DB의 데이터를 csv로 저장하여 가져왔다.

그냥 select 하고 나온 결과에 마우스 우클릭해서 다른이름저장 눌러서 csv 로 저장할 수 있다!

 

  1 use DB_name 
  2 BULK INSERT [table_name] 
  3 from 'C:\test.csv' 
  4 with ( 
  5     CODEPAGE = 'RAW', 
  6     FIRSTROW = 2, 
  7     MAXERRORS = 0, 
  8     FIELDTERMINATOR = ',', 
  9     ROWTERMINATOR = '\n', 
 10     tablock 
 11     ) 
 12     

 

csv 파일 위치 잘 지정해주시고

FirstRow = 2  는 첫번째 컬럼명 라인 짤라내기 위해 2번째 줄부터 입력하라 입니다.

FIELDTerminator = ','  필드 구분자는 콤마

ROWTerminator = '\n' 라인 구분자는 뉴라인

[출처] [MSSQL] BULK INSERT |작성자 종벌


해당 테이블에 존재하는 Identity 필드의 누적값을 알 수 있다.


SELECT IDENT_CURRENT('테이블명') 

타 서버간의 쿼리문을 사용하고 싶을때 이용한다.



EXEC sp_addlinkedserver 
@server='서버별칭',
@srvproduct = '',
@provider = 'SQLOLEDB',
@datasrc = 
'서버주소',@provstr='Provider=SQLOLEDB;Data Source=서버주소;Initial Catalog=데이터베이스명;User id=사용자명; Password=비밀번호;',
@catalog='데이터베이스명'
GO


EXEC sp_addlinkedsrvlogin '서버별칭', 'false', NULL, '사용자명', '비밀번호' 
GO


확인방법

SELECT * FROM [서버별칭].[데이터베이스명].[dbo].[sysfiles]
GO


+ Recent posts