데이터베이스의 사이즈를 모니터링 하는 것은 DBA의 중요한 업무중 하나 인데요. 트랜잭션 로그가 채워지지 않도록 트랜잭션 로그를 정기적으로 잘라야 합니다. 잘라야 합니다. 자르다... 어감이 마치... 10기가 짜리 로그 파일을 1기가로 줄이는... 그런거 같습니다. 근데 아니에요. 10기가를 1기가로 줄이는것은 로그 자르기가 아니고 파일 축소 작업입니다.

DBCC SHRINKFILE

명령을 이용해서 줄입니다. 이것도 맘대로 줄일 수 있는게 아니고 비활성 로그 부분만 줄일 수 있습니다. 로그를 자르고 나면 그 잘린 부분이 "비활성 로그"입니다.  그럼 로그는 언제 잘라질까요? 단순 복구 모델에서는 Checkpoint 발생이후 잘립니다. 전체 복구 모델이나 대량 로그 복구 모델에서는 일단 로그 백업이 한번 되고 난 후 Checkpoint가 발생하면 잘립니다. 로그가 잘렸다... 라는 것은 재사용 가능하게 바뀌었다라는 것을 뜻합니다.  트랜잭션 로그 파일은 내부적으로는 VLF 라는 걸로 나우어져서 사용됩니다. 예를 들어서 8기가 짜리 로그 파일을 만들었다면 대략 512메가 짜리 16개의 VLF 파일이 생깁니다. 그리고 처음부터 하나씩 사용되어 집니다. 로그가 잘릴일이 없으면 트랜잭션 로그는 계속 늘어나게 됩니다. 그러다 보면 데이터 파일은 1기가인데 로그 파일이 100기가가 되는 그런 기현상도 벌어지죠.  로그파일에 대한 정보는

DBCC LOGINFO WITH TABLERESULTS, NO_INFOMSGS

명령을 이용해서 볼 수 있습니다. 결과는 다음과 같습니다.

  각각의 열은 하나의 VLF를 나타냅니다. FileSize는 KB입니다. Status가 2인 VLF가 현재 사용중이거나 아직 잘리지 않은 로그입니다. Status가 0인 VLF는 비활성 로그입니다. 잘린거죠. 잘린 로그는 재사용되어집니다.

1

DBCC SHRINKFILE (LogFileName, TRUNCATEONLY)

명령을 사용하면 마지막 활성 로그 이후의 공간을 운영체제에 반환하게 됩니다. 10기가 짜리 로그 파일이 1기가가 되는게 이 상황입니다. 만약 잘린 로그가 없다면 로그 파일은 무한정 커지게 됩니다. 무한용량 하드가 있다면 걱정없겠지만.... 포멧은 언제하나...  앞서 로그백업 후 Checkpoint가 발생하면 로그가 잘린다고 했었는데요. 로그가 잘린 이후 활성로그가 하나만 남는것이 이상적이랍니다. 활성로그가 여러개인 경우는 여러가지 경우가 있을 수 있겠는데 엄청나게 긴 트랜잭션이 VLF 여러개에 로그를 기록중인데 아직 트랜잭션이 끝나지 않은 경우 또는 그리 길지 않은 트랜잭션인데 VLF크기가 너무 작아서 좀 길다... 싶으면 여러개 잡아 먹는 경우가 있을 수 있겠는데요. 긴 트랜잭션이면 짧게 끝나도록 수정하면 되겠지만 VLF가 작은 경우는 문제가 좀 있습니다. 남아 있는 비활성 로그가 없을 경우 트랜잭션 로그를 기록 하기 위해서 로그파일을 늘려야 할텐데요. 이때 OS에 파일 크기를 늘려달라는 요청을 하게 될겁니다. 로그 기록하기도 바쁜데 파일 크기까지 늘려야 하니 쿼리 수행은 당연히 느려질겁니다.  그래서 제일 이상적인 상태는 로그 백업을 하는 주기 동안 더 이상 늘어나지 않아도 될만큼 충분한 로그 파일을 확보하고 그 안에서 VLF의 숫자가 적당히 존재하는게 최적일 겁니다. 적당히... 적당한 연봉은?? 그리고 로그 백업 후에는 활성 로그가 하나만 남아 있는것이 좋을것입니다. VLF의 크기를 수동으로 조절하면 좋겠지만 아쉽게도 그런 옵션은 없습니다. VLF의 크기는 처음 로그 파일을 만들때 결정됩니다. 대략 8기가 짜리 로그 파일을 만들면 512메가로 16개가 생깁니다. 그래서 처음부터 용량 계획을 하고 적당한 크기로 만들어 두는게 좋습니다. 만약 처음부터 적당한 용량으로 만들지 못했다면 일단 무슨 수를 쓰던 모든 로그를 비활성으로 만든 후  (백업을 하고 Checkpoint 수행) 파일을 줄이고 적당한 크기로 늘려놓으면 됩니다. 그래도 처음 두개의 vlf는 크기를 조정할 수 없습니다.  이상은 로그파일에 대한 설명이었고 다음 스크립트는 모든 DB에 대한 VLF의 갯수와 활성VLF 숫자를 조회하는 쿼리입니다.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

SET NOCOUNT ON

 

IF OBJECT_ID('tempdb..#LOGINFO', 'U') IS NOT NULL DROP TABLE #LOGINFO

IF OBJECT_ID('tempdb..#LOGINFOTemp', 'U') IS NOT NULL DROP TABLE #LOGINFOTemp

 

CREATE TABLE #LOGINFO (

  RecoveryUnitID TINYINT,

  FileID INTEGER,

                      FileSize DECIMAL(28, 0),

                      StartOffset DECIMAL(28, 0),

                      FSeqNo DECIMAL(28, 0),

                      Status TINYINT,

                      Parity TINYINT,

                      CreateLSN VARCHAR(30),

                      DatabaseName VARCHAR(1000),

                      DatabaseID INTEGER)

CREATE TABLE #LOGINFOTemp (

  RecoveryUnitID TINYINT,

  FileID INTEGER,

                          FileSize DECIMAL(28, 0),

                          StartOffset DECIMAL(28, 0),

                          FSeqNo DECIMAL(28, 0),

                          Status TINYINT,

                          Parity TINYINT,

                          CreateLSN VARCHAR(30),

                          DatabaseName VARCHAR(1000),

                          DatabaseID INTEGER)

DECLARE @SQL VARCHAR(MAX)

SET @SQL = ''

SELECT @SQL = @SQL + 'USE [' + NAME + ']' + CHAR(10) +

                    'INSERT INTO #LOGINFOTemp ( RecoveryUnitID, FileID, FileSize, StartOffset, FSeqNo, Status, Parity, CreateLSN)' + CHAR(10) +

                    'EXEC( ''DBCC LOGINFO WITH TABLERESULTS, NO_INFOMSGS'')' + CHAR(10) +

                    'UPDATE #LOGINFOTemp SET DatabaseName = ''' + NAME + ''', DatabaseID = DB_ID()' + CHAR(10) +

                    'INSERT INTO #LOGINFO SELECT * FROM #LOGINFOTemp' + CHAR(10) +

                    'DELETE #LOGINFOTemp' + CHAR(10)

  FROM SYS.DATABASES WHERE DATABASE_ID != DB_ID('tempdb')

EXECUTE (@SQL)

SELECT DatabaseName, DatabaseID, FileID, FileCount = COUNT(1), ActiveCount = COUNT(CASE WHEN Status > 0 THEN 1 END)

  FROM #LOGINFO

GROUP BY DatabaseName, DatabaseID, FileID

ORDER BY CASE WHEN DatabaseID < 5 THEN 0 ELSE 1 END, DatabaseName

결과는 다음과 같습니다.

 

'Database > SQL Server' 카테고리의 다른 글

인덱스  (0) 2020.08.27
쿼리 저장소 (Query Store)  (0) 2020.08.27
VIEW에 있는 WITH SCHEMABINDING  (0) 2020.08.27
SP 검수 시 체크해야 할 리스트  (0) 2020.08.27
데이터 대량 변경 작업 시 체크해야 할 리스트  (0) 2020.08.27

이번에는 뷰를 생성할 때 WITH SCHEMABINDING 옵션에 대해서 얘기를 꺼내볼까 합니다.

 

BOL에 찾아보면...

 

 

SCHEMABINDING

기본 테이블의 스키마에 뷰를 바인딩합니다. SCHEMABINDING을 지정하면 뷰 정의에 영향을 미치는 방법으로 기본 테이블을 수정할 수 없습니다. 뷰 정의 자체를 먼저 수정하거나 삭제하여 수정할 테이블에 대해 종속성을 제거해야 합니다. SCHEMABINDING을 사용하는 경우 select_statement에 참조되는 테이블, 뷰 또는 사용자 정의 함수의 두 부분으로 구성된 이름(schema.object)이 있어야 합니다. 참조된 개체는 모두 같은 데이터베이스에 있어야 합니다.

SCHEMABINDING 절로 만든 뷰에서 사용하는 뷰 또는 테이블은 뷰가 삭제되거나 변경되어 스키마 바인딩이 더 이상 존재하지 않는 경우에만 삭제할 수 있습니다. 그렇지 않으면 데이터베이스 엔진에서 오류가 발생합니다. 또한 ALTER TABLE 문이 뷰 정의에 영향을 미치는 경우에는 스키마 바인딩이 있는 뷰에서 사용하는 테이블에서 이러한 문을 실행할 수 없습니다.

뷰에 별칭 데이터 형식 열이 있는 경우 SCHEMABINDING을 지정할 수 없습니다.

 

 

 

이 글만 읽게 되면... "아... 스키마 바인딩은.. 원래 테이블을 수정할 수 없게 하기 위해서 만드는 거구나" 라는 것만 생각 하게 됩니다.

 

그래서.. 뷰의 스키마 바인딩에 대해서 좀 더 자세히 알아보기 위해.. 이번 아티클을 작성하게 되었습니다.

 

 

뷰는 가상의 테이블입니다... 데이터가 존재하지 않는다고 생각합니다.

하지만 스키마바인딩 옵션으로 뷰에도 데이터를 가질 수 있습니다. 

즉.. 테이블과 뷰에 동시에 같은 데이터를 가질 수 있게 설정할 수 있다는 말입니다.

 

예제를 통해 보도록 하겠습니다.

 

일단 스키마 바인딩 되지 않는 뷰!

 

 

SET NOCOUNT ON

GO

IF OBJECT_ID('T1','U') IS NOT NULL

       DROP TABLE t1

GO

IF OBJECT_ID('VI_T1','V') IS NOT NULL

       DROP VIEW VI_T1

GO

CREATE TABLE t1

(

       idx                              int    identity

,      ClusteredValue             int    not null

,      ViewClusteredValue  int not null

)

GO

 

--//데이터1000건삽입

INSERT INTO t1(ClusteredValue, ViewClusteredValue)

SELECT CONVERT(INT,RAND()*10000), CONVERT(INT,RAND()*10000)

GO 1000

 

--//ClusteredValue에Clustered Index 생성

CREATE CLUSTERED INDEX CL_T1_ClusteredValue ON T1(ClusteredValue)

GO

 

CREATE VIEW VI_T1

AS

       SELECT * FROM t1

GO

SET NOCOUNT OFF

GO

 

SELECT * FROM t1

GO

 

SELECT * FROM VI_T1

GO

 

SELECT * FROM t1 WHERE ClusteredValue BETWEEN 100 AND 200

GO

 

 

SELECT * FROM VI_T1 WHERE ClusteredValue BETWEEN 100 AND 200

GO

 

 

4가지 쿼리에서 알 수 있듯이.. view를 조회하든.. table을 조회하든.. 실제 테이블은 t1 테이블을 참조해서 실행 계획을 만듭니다.

 

즉 .. 이 말은 스키마 바인딩이 안된 view는.. view에 데이터를 저장하는 것이 아니라.. 실제 테이블과 연결되는 하나의 인터페이스가 됩니다.

 

DBCC IND 명령어로.. 실제 테이블과 뷰에 데이터 페이지가 할당 되었는지 보도록 하겠습니다.

 

 

DBCC IND('tempdb','t1',0)

 

DBCC IND('tempdb','VI_T1',0)

 

 

 

Msg 5239, Level 16, State 1, Line 1

개체 ID 1755153298(개체 'VI_T1')을(를) 처리할 수 없습니다. 이 DBCC 명령이 이 유형의 개체를 지원하지 않습니다.

 

역시.. VIEW에는 데이터 페이지가 할당되지 않았습니다.

 

이제 스키마 바인딩으로 뷰를 생성해보도록 하겠습니다.

 

CREATE VIEW VI_SCHEMABINDING_T1

WITH SCHEMABINDING

AS

       SELECT * FROM t1

GO

 

Msg 1054, Level 15, State 6, Procedure VI_SCHEMABINDING_T1, Line 4

스키마 바운드 개체에는 구문 '*'을(를) 사용할 수 없습니다.

 

스키마 바인딩 된 뷰는 반드시 컬럼명을 명시해야 생성이 가능합니다.

 

CREATE VIEW VI_SCHEMABINDING_T1

WITH SCHEMABINDING

AS

       SELECT idx, ClusteredValue, ViewClusteredValue FROM t1

GO

 

Msg 4512, Level 16, State 3, Procedure VI_SCHEMABINDING_T1, Line 4

이름 't1'이(가) 스키마 바인딩에 적합하지 않으므로 뷰 'VI_SCHEMABINDING_T1'을(를) 스키마 바인딩할 수 없습니다. 이름은 두 부분으로 구성되어야 하며 개체는 자체 참조할 수 없습니다.

 

스키마바인딩 된 뷰를 사용할 때는.. 반드시 테이블에 소유자를 넣도록 해야되는군요 ^^

 

CREATE VIEW VI_SCHEMABINDING_T1

WITH SCHEMABINDING

AS

       SELECT idx, ClusteredValue, ViewClusteredValue FROM dbo.t1

GO

 

이제 제대로 생성이 되었습니다. 스키마가 바인딩이 되었으므로, 한번 t1의 idx라는 컬럼을 삭제해보도록 하겠습니다.

(스키마 바인딩이 되었으므로 삭제가 안되는 것이 맞습니다!)

 

 

ALTER TABLE t1

DROP COLUMN idx

 

Msg 5074, Level 16, State 1, Line 1

개체 'VI_SCHEMABINDING_T1'은(는) 열 'idx'에 종속되어 있습니다.

Msg 4922, Level 16, State 9, Line 1

하나 이상의 개체가 이 열에 액세스하므로 ALTER TABLE DROP COLUMN idx이(가) 실패했습니다.

 

역시.. 스키마 바인딩이 되어있으모로 예상이 맞았습니다.

이제 아까 제가 말씀드린 것 처럼.. 스키마 바인딩이 되었으므로 DBCC IND를 통해 뷰에도 데이터가 있는지 조사해보겠습니다.

 

 

 

 

 

DBCC IND('tempdb','t1',0)

 

 

DBCC IND('tempdb','VI_SCHEMABINDING_T1',0)

 

 

 

 

Msg 5239, Level 16, State 1, Line 1

개체 ID 1915153868(개체 'VI_SCHEMABINDING_T1')을(를) 처리할 수 없습니다. 이 DBCC 명령이 이 유형의 개체를 지원하지 않습니다.

 

헉?? 왜 데이터가 존재하지 않을까요??.. 아까 날렸던 SELECT 문장을 다시 수행해보도록 하겠습니다.

 

 

SELECT * FROM VI_SCHEMABINDING_T1

GO

 

SELECT * FROM VI_SCHEMABINDING_T1 WHERE ClusteredValue BETWEEN 100 AND 200

GO

 

 

 

 

스키마 바인딩이 되었음에도 불구하고.. 아까랑 별반 다를게 없습니다. 왜 이런것 일까요??

그럼 단지 스키마 바인딩은 컬럼을 삭제 못하게끔 하기 위해서 사용하는 것일까요?

 

혹시 공부하시다가.. 인덱싱된 뷰라고 들어보셨나요?? 바로... 스키마 바인딩된 뷰에는.. 인덱스를 만들 수 있습니다.

이제 뷰에 인덱스를 만들어 보도록 하겠습니다.

참고로.. 스키마 바인딩 된 뷰에는.. 유니크 클러스터드 인덱스를 먼저 만들어야.. 넌 클러스터드 인덱스를 만들 수 있습니다.

 

 

CREATE CLUSTERED INDEX CL_VI_SCHEMABINDING_T1_ViewClusteredValue ON VI_SCHEMABINDING_T1(ViewClusteredValue)

GO

 

Msg 1941, Level 16, State 1, Line 1

고유 클러스터형 인덱스 하나만 허용되므로 뷰 'VI_SCHEMABINDING_T1'에 비고유 클러스터형 인덱스를 만들 수 없습니다. 대신 고유 클러스터형 인덱스를 만드십시오.

 

유니크 클러스터형 인덱스를 만들라고 합니다.

그럼 그냥 넌클러스터드 인덱스를 만들면??

 

 

 

CREATE INDEX NC_VI_SCHEMABINDING_T1_ViewClusteredValue ON VI_SCHEMABINDING_T1(ViewClusteredValue)

GO

 

 

Msg 1940, Level 16, State 1, Line 1

뷰 'VI_SCHEMABINDING_T1'에 인덱스을(를) 만들 수 없습니다. 고유 클러스터형 인덱스가 없습니다.

 

 

 

 

 

따라서 유니크한 idx 컬럼에 만들어보도록 하겠습니다.

 

 

 

 

 

CREATE UNIQUE CLUSTERED INDEX NC_VI_SCHEMABINDING_T1_idx ON VI_SCHEMABINDING_T1(idx)

GO

 

이제 만들어진 인덱스에 page가 할당되었는지 확인해보겠습니다.

 

DBCC IND('tempdb','VI_SCHEMABINDING_T1',1)

 

 

뷰에 있는 인덱스에 페이지가 할당되었습니다!!

 

그렇다면... 아래 쿼리는 view에 생성한 Clustered Index를 탈까요?

 

SELECT IDX FROM VI_SCHEMABINDING_T1 WHERE idx between 100 and 200

 

 

 

헉!! idx에 인덱스를 만들었는데도 불구하고.. 뷰가 아닌 t1 테이블에 Clustered Index scan(=full scan)을 합니다.

뷰에 있는 Clustered Index를 전혀 이용하지 못했다는 말입니다.

 

그렇다면.. 뷰에 있는 클러스터드 인덱스를 사용하려면 어떻게 해야 할까요?

바로 NOEXPAND 옵션입니다.

 

 

 

 

SELECT IDX FROM VI_SCHEMABINDING_T1 WITH(NOEXPAND) WHERE idx between 100 and 200

  

 

 

이제 최종 뷰에 있는 Clustered Index Seek 한 것을 확인할 수 있었습니다. ^^

 

그렇다면.. t1 테이블에 데이터를 넣는다면??? 당근.. view에도 데이터가 들어가야하기 때문에.. I/O가 증가할 것입니다.

 

수정이 되는 경우도 마찬가지일테고요. 테스트는??? 한번 직접 해보십시요~~ㅎㅎㅎ

 

 

정리를 드리면..

WITH SCHEMABINDING 옵션은...

1. 반드시 컬럼명을 기술 해야 만들 수 있다.

2. 생성 된 뷰에 대한 컬럼은 삭제하거나 수정할 수 없다.

3. 테이블  명은 반드시 소유자 명을 지칭해야된다.

4. 인덱스를 만들면 page를 할당해서 데이터를 저장한다.

5. 인덱스가 없다면, 원래 뷰와 동일하게 작동한다.

 

 

인덱싱 뷰는..

1. WITH SCHEMABINDING 옵션으로 생성 된 VIEW에만 인덱스를 만들 수 있다.

1. 반드시 첫번째는 유니크한 클러스터드 인덱스를 만들어야 한다.

2. 뷰의 인덱스를 이용하기 위해서는 WITH(NOEXPAND)를 사용해야 한다.

 

참고로 NOEXPAND는 엔터프라이즈급에서는 인식한다고 합니다만... 힌트를 박아서 사용해야 확실하다고 합니다. ^^ (강산아님 댓글)

 

NOEXPAND 관련 민석님의 글: http://www.sqler.com/105239

SP 검수 시 체크해야 할 리스트


 

* SQL Server 기준으로 작성하였습니다.

 

1. Ad-hoc, 동적 쿼리 제거

1. Ad-hoc, 동적 쿼리는 실행 시 쿼리가 어떻게 구성되는지 알 수 없습니다.

   구성을 알 수 없으면 인덱스가 적절하게 구성되었는지, 실행계획이 잘못풀려 악성쿼리인지 아닌지를 판단할 수 없습니다.

 

 

2. where 절 체크 사항

1. 동일한 데이터 타입끼리 비교되는지 확인합니다.

   서로다른 데이터 타입끼리 비교 시 CONVERT 함수가 사용되기 때문에 인덱스 사용을 할 수 없습니다.

2. OR 문은 UNION ALL 로 변경

   OR 문은 상황에 따라 Scan 으로 풀릴 가능성도 있습니다.

   UNION ALL 로 쿼리를 Seek + Seek 하는 방식으로 변경이 가능한지 확인합니다.

3. CASE 문은 분기처리로 변경

   처음 쿼리가 실행될 때의 CASE 조건으로 실행계획이 풀리게 됩니다.

   이 후 CASE 절의 다른 조건으로 실행되는 경우 처음 설정된 실행계획을 계속사용하면서 잘못된 실행계획으로 풀릴 수 있습니다.

   CASE 문을 IF 분기처리하여 하나의 조건절로만 풀릴 수 있도록 가능한지 확인합니다.

4. NOT 구문이 사용되지는 않았는지 확인합니다.

5. OLTP 인 경우 전체 데이터를 조회하게 하는 조건이 없는지 확인합니다.

 

 

3. 조인을 사용한 경우 ANSI 조인 및 적절한 조인방식이 사용되었는지 확인

1. OLTP 인 경우 보통은 NL 조인을 사용

 

 

4. 조회 시 * 가 사용되었는지 확인

1. * 는 불필요한 데이터를 조회하여 리소스를 더 많이 사용하게 됩니다.

2. * 를 사용하는 경우 이후 테이블의 컬럼이 추가되거나 제거되면서 이슈가 발생할 가능성이 있습니다.

 

 

5. 대량 조회를 해야하는 경우 Paging 이나 TOP 으로 데이터를 제한

1. TOP 에 변수처리가 가능한데 이런 경우 데이터 조회 제한 건수를 알 수 없습니다. TOP 에는 변수를 사용하지 말아야 합니다.

2. Paging 의 경우에도 읽기 수를 제한하는 기법으로 가이드 할 수 있어야 합니다.

   Paging 은 https://mozi.tistory.com/312 링크를 참조하도록 합니다.

 

 

6. 리소스 사용 제한

1. 백오피스나 배치성 쿼리의 경우 OPTION (MAXDOP 1) 을 부여하여 서비스 리소스에 부하를 최소화 시켜야 합니다.

 

 

7. Lock 레벨에 대해 확인

1. 서비스에 사용되는 DML 에서 TAB Lock 이 발생할 수 있는 이슈가 없는지 확인합니다.

2. 조회 쿼리에서 WITH(NOLOCK) 이 사용되었는지 확인합니다.

 

 

8. 서비스에 사용되는 DML 의 경우 확인사항

1. DML 의 조건절 인덱스가 PK 이거나 UK 인지 확인합니다.

   PK, UK 가 아닌 경우 테이블의 전체 데이터가 쿼리대상에 포함되는지 개발팀에 확인합니다.

2. 배치성인 경우에는 단위처리로 하도록 합니다.

 

 

9. 임시 테이블이나, 커서는 사용하지 않도록

1. 임시 테이블, 커서는 자원을 많이 사용합니다.

   커서 대신 Identity 테이블을 사전에 생성하여 재활용 할 수 있도록 합니다.

 

 

10. 스키마 명시

1. owner 스키마에서 객체 검사 후 없는 경우 dbo 스키마에서 객체가 있는지 재검색합니다.

   재검색하는 비효율을 제거할 수 있도록 합니다.

 

 

11. 쿼리 실행계획 확인

SP 에서 사용된 쿼리 실행계획에 문제가 없는지 확인합니다.

 

 

 

 

SP 가 잘못 되면 서비스에서 장애가 발생하게 됩니다.

꼼꼼하게 검사하여 안정적인 서비스를 운영할 수 있도록 합니다.

데이터 대량 변경 작업 시 체크해야 할 리스트


 

 

1. 서비스 영향도 확인

1. 시간이 얼마나 소요될지에 대해 파악되면 이에대한 영향도를 개발팀에 확인 요청합니다.

 

 

2. 데이터 프리징이 가능한지 확인

1. TAB LOCK 으로 대량 변경 작업 진행 시 더 빠르게 처리할 수 있습니다.

   이는 ROW Lock 과 TAB Lock 에서 트랜잭션 로그가 쌓이는 방식에 대해 이해하고 있어야 합니다.

2. 이 때는 점검을 걸어야 하고, 서비스가 불가능 합니다.

 

 

3. 대량 DELETE 작업 시 INSERT 작업이 더 효율적인지 비교

1. ROW 별로 DELETE 작업을 진행하는 것보다 DELETE 대상이 아닌 데이터를 새로운 테이블에 INSERT 하는 작업이 효율적일 수 있습니다.

   프리징이 되었다는 가정에서 새로운 테이블로 INSERT 할 때는 원본 테이블에 Lock 을 잡지 않아도 됩니다.

   DELETE 원본 테이블   vs   INSERT INTO 신규 WITH(TABLOCK) SELECT 원본 테이블 WITH(NOLOCK)

2. DELETE 시 빈 페이지가 생기게 되는데, INSERT 로 새로 구성하는 경우 빈 페이지가 없기 때문에 부분범위 처리시 읽어야 하는 페이지를 줄일 수 있습니다.

3. 단, 새로운 테이블로 구성하게 되면 메타정보가 변경되면서 기존 쿼리의 실행계획이 변경될 수 있습니다.

 

 

4. 대량 UPDATE 작업 시 INSERT 작업이 더 효율적인지 비교

위와 비슷한 이유입니다.

 

 

5. 단위처리 할 수 있는지 확인

1. 현재 처리되고 있는 진행상황을 알 수 있어야 합니다.

   한번에 처리 도중 예측한 시간보다 작업시간이 훨씬 오래 걸려 취소해야 하는 경우 Rollback 에 걸리는 시간또한 예측할 수 없습니다.

 

 

6. 변경 시 원본 데이터를 보관

1. 작업이 예측과 다르게 된 경우, 해당 이유에 대해서 추적할 수 있어야 합니다.

 

 

7. 작업으로 쌓이는 트랜잭션 로그 크기 확인

1. 트랜잭션 로그가 많이 생성되는 경우 로그파일 크기를 키우는 것 또한 비용입니다.

   로그가 생성이 많이 될 것으로 보이면 크기를 미리 키워두는 것도 좋은 방법입니다.

'Database > SQL Server' 카테고리의 다른 글

VIEW에 있는 WITH SCHEMABINDING  (0) 2020.08.27
SP 검수 시 체크해야 할 리스트  (0) 2020.08.27
INDEX 인덱스 삭제 시 체크해야 할 리스트  (0) 2020.08.27
조인 조건자 없음  (0) 2020.08.27
DB 보안  (0) 2020.08.27

인덱스 삭제 시 체크해야 할 리스트


 

* SQL Server 기준으로 작성하였습니다.

 

1. 쿼리 힌트에 인덱스가 사용되지는 않았는지 확인

 

 

2. 쿼리 실행계획이 변경될 가능성이 있는지 확인

1. 기존 쿼리가 삭제될 인덱스를 사용하는 경우 실행계획이 변경되게 되고 이로인해 이슈가 될 수 있는지 확인합니다.

 

 

3. 인덱스에 접근하는 쿼리가 있는지 확인

1. dm_db_index_usage_stats 의 동적뷰에 마지막 접근 시간이 기록됩니다. 해당 뷰를 조회해서 인덱스가 지금도 사용되고 있는지 확인합니다.

 

 

4. ★ 인덱스를 바로 삭제하려고 하지마십시오.

1. rename 으로 인덱스 명을 변경하여 삭제와 같은 상황만 만들어 둡니다.

  유사 시 빠르게 복원이 가능하도록 준비하고 있어야 합니다.

  ( 인덱스 재생성 시, 테이블 Lock 이 걸리며 데이터가 많은 경우 인덱스 생성 시간도 오래걸립니다. )

 

 

생성과 다르게 삭제는 위험도가 높은 작업입니다.

서비스에 영향이 없는지 꼼꼼한 체크가 필요합니다.

'Database > SQL Server' 카테고리의 다른 글

SP 검수 시 체크해야 할 리스트  (0) 2020.08.27
데이터 대량 변경 작업 시 체크해야 할 리스트  (0) 2020.08.27
조인 조건자 없음  (0) 2020.08.27
DB 보안  (0) 2020.08.27
Missing Index  (0) 2020.08.27

우선 해당 실행계획을 보면 다음과 같습니다.

 

 

 

 

 

처음에 튜닝 할 당시에 이게 뭐하는 실행계획인지 잘 몰랐습니다. 조인 시에 Miss? / 인덱스 오류? 이런 저런 생각을 하였는데 정말 이유는 간단했습니다. 

 

조인의 조건자가 정말 없습니다. 그런데 상식적으로 조인을 걸고 ON 절을 해주지 않으면 조인이 안되지 않나? 이런 생각을 했는데... 잘 됩니다. 음...없다기 보다는

 

같은걸 2번 쓰거나 엉뚱한(?)것을 입력 했을때 쿼리가 잘 실행됩니다. 

 

그렇다면 뭘로 조인을 할까요? 그래서 테스트를 해봤습니다. 

 

 

 

준비한 테이블은 골키퍼 축구 선수들의 트레이닝 강의에 관한 내용입니다. 발 설계 및 발 쿼리니 이 자료는 참고만 부탁드립니다. 

 

테이블은 간단합니다. 4개의 테이블이 존재하며, 각각 선수정보(info) / 트레이닝항목(Class) / 수강현황(Register_info) / 코치(Teacher) 

 

개인적으로 수강현황은 N:N의 내용을 해소하기 위한 테이블로 만들었습니다. 한 트레이닝을 여러명이 들을수 있으며, 여러명이 여러개의 트레이닝을 받을 수 있기에...

 

 

 

 

 

Step 1 - 테이블 만들기

 

 

 

- 인덱스를 보고 이건 뭐야?라는 분들이 계실텐데 실행계획에서 NL조인에 X표기를 보기위한 것이니 너무 신경 쓰지 않으시길 바랍니다. ^^....

 

- 테이블은 위에 언급처럼 총 4개를 만듭니다. 선수정보/트레이닝항목/수강현황/코치 입니다. 

 

USE StudyDB

GO



create table info

(

User_key int identity(1,1)

,ID varchar(10)

,Name varchar(8)

,Hp char(11)

constraint PK_info primary key clustered (User_key)

)

go



create table Class

(

Class_key int identity(1,1)

,Class_name varchar(20)

,Class_code int

,teacher_key int

constraint PK_Class primary key clustered (Class_key)

)

go



create table Register_info

(

register_key int identity(1,1)

,User_key int

,Class_key int

,register_regdate datetime

,flag char(1)

constraint PK_Register_info primary key clustered (register_key)

)

go

create nonclustered index NC_1 on Register_info(User_key)

create nonclustered index NC_2 on Register_info(Class_key)

go



create table Teacher

(

teacher_key int

,teacher_name varchar(8)

,Hp char(11)

,teacher_class varchar(30)

constraint PK_Teacher primary key clustered (teacher_key)

)

go

 

 

 

 

 

Step 2 - 데이터 입력

 

- 이제 몇건의 데이터를 넣어보겠습니다.

 

insert into [dbo].[info]( ID, Name, Hp)  values ('AAAA','데헤아','01012341234')

insert into [dbo].[info]( ID, Name, Hp)  values ('BBBB','노이어','01023143532')

insert into [dbo].[info]( ID, Name, Hp)  values ('CCCC','카시야스','01033313643')

insert into [dbo].[info]( ID, Name, Hp)  values ('DDDD','체흐','01012346643')

insert into [dbo].[info]( ID, Name, Hp)  values ('EEEE','부폰','01023535036')

go

select * from [info]

go



insert into [dbo].[Class](Class_name, Class_code, teacher_key) values ('피지컬',1001,1)

insert into [dbo].[Class](Class_name, Class_code, teacher_key) values ('골키퍼',1002,2)

go

select * from [Class]

go



insert into [dbo].[Teacher] ([teacher_key],teacher_name, Hp, teacher_class) values(1,'무리뉴','01035530455','골키퍼 전문코치')

insert into [dbo].[Teacher] ([teacher_key],teacher_name, Hp, teacher_class) values(2,'클롭','01035340455','체력 전문코치')

go

select * from [Teacher]

go



insert into [dbo].[Register_info] ( User_key, Class_key, register_regdate, flag) values (1,1,getdate(),'Y')

insert into [dbo].[Register_info] ( User_key, Class_key, register_regdate, flag) values (1,2,getdate(),'Y')

insert into [dbo].[Register_info] ( User_key, Class_key, register_regdate, flag) values (2,1,getdate(),'Y')

insert into [dbo].[Register_info] ( User_key, Class_key, register_regdate, flag) values (3,2,getdate(),'Y')

go

select * from [Register_info]

go

 

 

 

Step 3 - 정상적인 조인

 

- 여기서 우리는 Select를 통해서 어떤 선수가 어떠한 트레이닝을 받고 있는지 확인 하고자 합니다.

 

select I.User_key,I.Name,C.Class_name

from info as I

inner join Register_info as R

on I.User_key = R.User_key

inner join Class as C

on R.Class_key = C.Class_key

order by User_key asc

 

 

 

- 실행계획에 아무런 지장이 없습니다. 

 

- 왜? on절도 정확히 명시되어있고, 정확한 Key로 묶었습니다.

 

 

 

- 그럼 선수가 받는 트레이닝은 무엇이며, 그 트레이닝의 담당자는 누구인지도 확인해 보겠습니다.

 

select I.User_key,I.Name,C.Class_name,T.teacher_name

from info as I

inner join Register_info as R

on I.User_key = R.User_key

inner join Class as C

on R.Class_key = C.Class_key

inner join Teacher as T

on C.teacher_key = T.teacher_key

order by User_key asc

 

- 역시 별다른 특이점이 없습니다. 왜? ON절을 정확히 명시했으니... 

 

- 그럼이제 NL 조인 부분에 X를 나타나게 해보겠습니다.

 

 

 

Step 4 - 조인 조건자 없음 

 

아까랑 똑같이 하돼 약간의 ON의 변화를 주겠습니다.

 

- 쿼리에서 표시한 부분의 ON절을 보면 2번째 Inner Join부분과 ON절이 똑같습니다. 그럼 Teacher 테이블은 어디갔어? ON절에 없잖아?

 

  뭐랑 조인하지? 하겠지만 조인이 됩니다.

 

select I.User_key,I.Name,C.Class_name,T.teacher_name

from info as I

inner join Register_info as R

on I.User_key = R.User_key

inner join Class as C

on R.Class_key = C.Class_key

inner join Teacher as T

on R.Class_key = C.Class_key -- 문제가 되는 부분

order by User_key asc

 

 

 

- NL 조인 부분에 X표시가 생겼습니다. 

 

  이 경고는 말그대로 조건자가 없음을 말합니다. 그렇다면 이것이 왜 문제가 되는가? 이것이 핵심입니다.

 

  바로 데이터가 틀리게 나오기 때문입니다. 우리가 바로 위에서 했던 정상 쿼리문과 결과를 비교해보겠습니다.

 

 

 

- 비교된 결과를 보면 다음과 같습니다.

 

 

 

 

 

 

 

- 그림을 통해서 확실히 보면 다음과 같습니다.

 

- 결과를 보면 이상한 점을 볼 수 있습니다. 

 

  각각의 데이터가 2배로 들어났습니다. 데이터도 이상합니다. 데헤아는 골키퍼,피지컬 1번씩 듣는데 2번씩 듣도록 되어있고, 노이어 / 카시야스는 수업이 총 1회씩인데

 

  골키퍼 2번 / 피지컬 2번씩 되어 있습니다. 이것은 무엇을 의미 할 까요?

 

 

 

- 3번째 Inner 조인까지 정확히 되었으나, 4번째 Teacher 테이블을 조인 시 Cross 조인을 하는 것임을 알 수 있습니다.

 

- 결과적으로 이것은 잘못된 데이터죠. 

 

  그렇다면 언제 이러한 실수를 많이 저지를 까요?

 

 

 

Step 5 - 우리는 언제 이러한 실수를 많이 하는가?

 

- 에이 누가 쿼리를 그렇게 짜요? 할 수 있습니다. 그런데 제가 현재 회사에서 튜닝을 하면 20건 중에 1건 정도는 이러한 결과를 보여줍니다. 왜 일까요?

 

- 개발자 분들은 주로 Where 조건을 이용해서 Join을 합니다. ON절을 잘 안쓰더군요.(물론 캐바캐입니다.)

 

  Where 조건에 넣음으로써 쿼리에서 결과가 맞다고 판단하고 그냥 넘어가는 것이죠. Where 조건으로 위의 내용을 재현하면 다음과 같습니다.

 

 

 

select I.User_key,I.Name,C.Class_name

from info as I

,  Register_info as R

,  Class as C

,  Teacher as T

where  I.User_key = R.User_key

and R.Class_key = C.Class_key

and R.Class_key = C.Class_key

order by User_key asc

 

 

 

흔히들 Where 조건에 기재하면서 Where 조건에 조건을 잘못 입력하는 경우가 있습니다. 

 

위의 조건 처럼 사용하면 실수를 많이 범합니다. 하지만... 조인이 잘되니 아 ! 이상이 없구나 하고 넘기는 경우가 많죠.

 

 

 

이러한 실수를 방지하기 위해서 꼭 ON절을 이용한 조인을 해야합니다. 뭐.. 꼭은 아니지만 권고하는 것이죠.

 

 

 

 

 

감사합니다.

DB 보안 설정

- 인증 방법에 따른 차이점

- 윈도우 인증과 로그인 인증의 차이점

1. 윈도우 인증은 SQL Server로 하여금 운영체제의 보안 기능 (암호 암호화(encryption), 암호 사용 기간, 암호의 최소/최대 길이 제한) 이 사용 가능합니다.

2. 윈도우 인증은 트러스트된 연결을 사용하여 기존 윈도우의 연결 방식에 의존한다.

3. 윈도우 인증은 액티브 디렉터리 사용자 및 로컬 사용자 인증이 가능합니다.

- 계정 관리

1. DB 최소권한의 유저로 운영하라.

2. 사용하지 않는 저장된 프로시저와 기능들은 제거하거나 관리자에게 제한된 접근권한을 주어라.

3. 퍼미션을 변경하고, 공개된 시스템 객체에 접근을 제거 하라.

4. 모든 사용자 계정의 패스워드를 강화 시켜라

 

- SQL Injection

1.저장된 프로시저를 사용할 때 매개변수화 된 API를 이용하라.

모든 입력 값 검증은 일반적인 루틴을 이용하고, 최소한의 권한을 DB 사용자 에게 적용하라.

2.프로시저로 변경 하고 동적 쿼리를 사용하지 않는다.

3.데이터베이스와 연동을 하는 스크립트의 모든 파라미터들을 점검하여 사용자의 입력 값이 SQL injection을 발생시키지 않도록 수정한다.

4.사용자 입력이 SQL injection을 발생시키지 않도록 사용자 입력 시 특수문자(' " / \ ; : Space -- +등)가 포함되어 있는지 검사하여 허용되지 않은 문자열이나 문자가 포함된 경우에는 에러로 처리한다.

5.SQL 서버의 에러 메시지를 사용자에게 보여주지 않도록 설정한다.

공격자는 리턴 되는 에러 메시지에 대한 분석을 통하여 공격에 성공할 수 있는 SQL Injection 스트링을 알아낼 수 있다. 따라서 SQL 서버의 에러 메시지를 외부에 제공하지 않도록 한다.

6.웹 애플리케이션이 사용하는 데이터베이스 사용자의 권한을 제한한다.

가능하면 일반 사용자 권한으로는 모든 system stored procedures에 접근하지 못하도록 하여 웹 애플리케이션의 SQL Injection 취약점을 이용하여 데이터베이스 전체에 대한 제어권을 얻거나 데이터베이스를 운용중인 서버에 대한 접근이 불가능하도록 한다.

7. 위험한 확장 저장 프로시저들을 제거한다. (확장 프로시저를 생성 할수 있는 dll도 같이 삭제)

(xp_cmdshell, Xp_regread, xp_regwrite , Xp_makewebtask ,Xp_enumdsn)

 

1. 인증

윈도우 인증

  윈도우의 인증 방법만을 사용한 인증

사용자가 SQL 서버에 연결할 때는 별도의 로그인 절차 없이 윈도우 계정 정보로 SQL 서버에 연결한다.

SQL 서버 MASTER 데이터베이스 안에 있는 syslogin 테이블이 모든 윈도우 사용자 계정이나 그룹 계정에 관한 정보들을 SQL 서버 로그인 계정으로 가지고 있다.

윈도우의 액티브 디렉토리 서비스에 지원되는 단일 로그인 기능을 사용할 수도 있다.

SQL 서버 인증

  운영체제와는 별개의 아이디 패스워드를 가지고 있으면서 사용자의 인증을 관리하는 방식

  운영체제의 강력한 보안체계를 전혀 활용할 수 없다는 단점이 있다.

혼합 인증

  윈도우, SQL 서버 인증 방식을 동시에 사용 하는 방식

  2가지의 장점을 잘 조화하여 사용 하는 방식

2. 로그인 과 유저

로그인 : SQL 서버에 접속한다는 의미

유저: 데이터베이스를 사용하기 위한

로그인 관련정보는 syslogins 테이블에 저장

유저의 관련정보는 sysusers 테이블에 저장

SQL 서버 설치시 기본적으로 만들어지는 로그인 계정

- Sa: System Administrator, sql 서버와 모든 데이터베이스에서 사용 가능한 모든 권한 소유

- BUILTIN/Administrator : Adminstrator 그룹으로 SQL 서버에 접속하는 사용자들을 위한 윈도우 인증 방법으로 접속할 때의 최고의 권한을 가지는 계정이다. (윈도우를 서버로 사용할 때만 등록되어 있다.)

- 새로운 로그인 생성

1. SP

sp_addlogin [ @loginame = ] 'login'

    [ , [ @passwd = ] 'password' ]  -- 빈암호 사용을 자제해야함 Default NULL

    [ , [ @defdb = ] 'database' ]  - Default master

    [ , [ @deflanguage = ] 'language' ] Default NULL,  서버의 현재 기본 언어

    [ , [ @sid = ] sid ] Default Null

    [ , [ @encryptopt= ] 'encryption_option' ]

2. CREATE

CREATE LOGIN login_name { WITH <option_list1> | FROM <sources> }

<sources> ::=

    WINDOWS [ WITH <windows_options> [ ,... ] ]

    | CERTIFICATE certificateName

    | ASYMMETRIC KEY asym_key_name

<option_list1> ::=

    PASSWORD = { 'password' | hashed_password HASHED } [ MUST_CHANGE ]

    [ , <option_list2> [ ,... ] ]

<option_list2> ::=

    SID = sid

    | DEFAULT_DATABASE = database 

    | DEFAULT_LANGUAGE = language

    | CHECK_EXPIRATION = { ON | OFF}

    | CHECK_POLICY = { ON | OFF}

    | CREDENTIAL = credential_name

<windows_options> ::=   

    DEFAULT_DATABASE = database

    | DEFAULT_LANGUAGE = language

  SQL 서버 인증 방식

USE [master]

GO

CREATE LOGIN [SA_TEST] WITH PASSWORD=N'1235_111', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=ON, CHECK_POLICY=ON

GO

  윈도우 인증 방식

USE [master]

GO

CREATE LOGIN [TAEK\TaekSu] FROM WINDOWS WITH DEFAULT_DATABASE=[test], DEFAULT_LANGUAGE=[한국어]

GO

USE [test]

GO

CREATE USER [TAEK\TaekSu] FOR LOGIN [TAEK\TaekSu]

GO

- 유저 생성

모든 데이터베이스 마다 자동적으로 만들어지는 유저는 dbo 하나 밖에 없다.

모든 데이터베이스에 공통적으로 생성해주어야 하는 유저가 있다면 model 데이터베이스 안에 원하는 유저를 생성해주면 된다.

SP

sp_adduser [ @loginame = ] 'login'

        [ , [ @name_in_db = ] 'user' ]

        [ , [ @grpname = ] 'role' ]

CREATE

CREATE USER user_name    [ { { FOR | FROM }

      {

        LOGIN login_name

        | CERTIFICATE cert_name

        | ASYMMETRIC KEY asym_key_name

      }

      | WITHOUT LOGIN

    ]

    [ WITH DEFAULT_SCHEMA = schema_name ]

동일한 SP에 대해 2000, 20005 , 2008의 처리 방식이 다름.

SYSTEM 테이블

master.dbo.syslogins

master.dbo.syslanguages

master.dbo.sysxlogins

sys.schemas

sys.database_permissions

3. 역할 ROLE

- 시스템 정의 역할

시스템 정의 역할은 SQL 서버에서 미리 정의해 놓은 역할.

- 서버역할

서버 역할은 SQL 서버에서 로그인 계정들을 그룹화 하고 정해진 권한을 부여하기 위해 사용

서버 역할은 사용자가 새로운 역할을 추가하거나 기존의 역할에 설정되어 있는 권한을 변경할 수 없다.

역할 설명

Bulkadmin BULK INSERT 문을 실행

Dbcreator 데이터베이스 생성, 삭제, 변경, 복원 가능

Diskadmin 디스크 파일 관리

Processadmin 프로세스 종료 가능

Securityadmin 로그인 및 속성 관리, 데이터베이스 수준의 사용 권한 허가 및 거부 등 가능

Serveradmin 서버 차원의 구성 옵션 변경 가능, 서버 종료 가능

Setupadmin 링크드 서버 추가 및 제거, 일부 시스템 저장 프로시저 실행 가능

sysadmin 서버의 모든 작업 수행 가능

SP_ADDSRVROLEMEMBER 시스템 저장 프로시저를 사용하는 문법

Sp_addsrvrolemember [ @loginame = ] 'login'

          , [ @rolename = ] 'role'

- 데이터베이스 역할

DB role이라고 부르며 각 데이터베이스 안에서 유저에 부여하는 것이다.

권한 변경이 불가능한 고정된 역할과, 권한을 변경할 수 있고 모든 유저가 속해 있는 public 역할이 있다.

역할 설명

Db_accessadmin 윈도우 로그인, 그룹 및 sql 서버 로그인 관리

Db_backupadmin 데이터베이스 백업 관리

Db_datareader 테이블 조회 권한 관리

Db_datawriter 테이블 데이터 변경 가능

Db_ddladmin 모든 DDL 명령 실행 가능

Db_denydatareader 테이블 조회 구너한을 제거

Db_denydatawriter 테이블 데이터 변경 권한을 제거

Db_owner 데이터베이스 내 모든 권한 사용

Db_securityadmin 사용 권한 관리

Public 데이터베이스 사용자가 가지는 가장 최소한의 권리

SP_ADDROLEMEMBER

Sp_addrolemember [ @rolename = ] 'role' ,

        [ @membername = ] 'security_account'

SYSTEM TABLE , DBCC

2000 sysusers

2005 sys.database_principals

dbcc auditevent

- 사용자 정의 역할

동일한 권한을 가진 유저 그룹을 만들 수 있게 해주는 것으로서 실제로 db role의 일부분이

라고 할 수 있다.

SP

p_addrole [ @rolename = ] 'role' [ , [ @ownername = ] 'owner' ]

CREATE

USE [test]

GO

CREATE ROLE [MyRole] - Role 생성

GO

USE [test]

GO

EXEC sp_addrolemember N'MyRole', N'SA_TEST' - 데이터베이스 역할 생성

GO

use [test]

GO

GRANT ALTER ON [dbo].[a] TO [MyRole] - 권한 생성

GO

use [test]

GO

GRANT SELECT ON [dbo].[c] TO [MyRole] WITH GRANT OPTION

GO

use [test]

GO

GRANT VIEW DEFINITION ON [dbo].[d] TO [MyRole]

GO

Db_decurityadmin이나 db_owner 역할을 가진 유저만 추가 가능

SSMS , sp_addrole, sp_droprole 로 추가가 가능

- 애플리케이션 역할

애플리케이션 역할 역시 db Role의 일부분이라고 볼 수 있다.

특정한 응용 프로그램에 따라 사용할 수 있는 권한을 제한하는 것.

주의사항: 한번 설정된 애플리케이션 역할은 해당 세션이 끝날 때까지 계속 유효하며, 일반 역할이나 유저에 부여된 모든 권한이나 역할은 해당 세션에서 모두 무시된다.

SP 활용

sp_addapprole [ @rolename = ] 'role' , [ @password = ] 'password'

CREATE APPLICATION ROLE application_role_name

    WITH PASSWORD = 'password' [ , DEFAULT_SCHEMA = schema_name ]

USE [test]

GO

CREATE APPLICATION ROLE [AppRoleTest] WITH PASSWORD = N'1234'

GO

use [test]

GO

GRANT EXECUTE ON [sys].[sp_ActiveDirectory_SCP] TO [AppRoleTest]

GO

use [test]

GO

GRANT INSERT ON [dbo].[c] TO [AppRoleTest]

GO

 

EXEC sp_setapprole 'AppRoleTest','1234';

SETUSER [ 'username' [ WITH NORESET ] ]

데이터베이스에 대한 ALTER ANY APPLICATION ROLE 권한이 필요합니다.

보안 카탈로그 뷰

http://msdn.microsoft.com/ko-kr/library/ms178542.aspx

보안 저장 프로시저

http://msdn.microsoft.com/ko-kr/library/ms182795.aspx

4. 데이터베이스 암호화

SQL 서버 2000까지의 암호화

- 문서화되지 않은 PWDEncrypt 함수로 복호화 되지 않는 단 방향 해시 함수로, 해당 값의 비교를 위한 PWDCompare 함수를 따로 지원한다.

DECLARE @Ciphertext varbinary(20);

DECLARE @Plaintext varchar(20);

SET @Plaintext = 'Hello'

set @Ciphertext = PWDENCRYPT(@Plaintext)

SELECT [원문] = @Plaintext , [암호문] = @ciphertext

SQL 서버 2005 암호화 방식

- 내장된 암호화 함수가 제공되고 이 함수 (MD2,MD4,MD5SHA,SHA1) 지원 (단방향 해시)

DECLARE @Ciphertext varbinary(20);

DECLARE @Plaintext varchar(20);

SET @Plaintext = 'Hello'

set @Ciphertext = HashBytes('SHA1',@Plaintext)

SELECT [원문] = @Plaintext , [암호문] = @ciphertext

- ENCRYPTBYPASSPHRASE 패스워드를 가지고서 키를 암호화 하는 방식

DECLARE @CreditCardNo varchar(20);

SET @Creditcardno = '1234567890'

DECLARE @PASS nvarchar(128);

SET @PASS = '암호화문자입니다.'

DECLARE @Enctext varbinary(8000)

set @Enctext = EncryptByPassPhrase(@Pass,@creditCardNo)

DECLARE @Dectext varbinary(8000)

set @Dectext = DecryptByPassPhrase(@Pass,@Enctext)

SELECT @creditCardNo 카드번호,

  @PASS 암호화문자,

  @Enctext 암호화,

  @Dectext,

  CONVERT(VARCHAR,@Dectext) 복호화

복호화에 반환되는 데이터는 VarBinary 형태임에 주의하여 문자열로 변환 하여야 한다.

SQL SERVER 2008

TDE (Transparent Data Encrypt)

http://msdn.microsoft.com/ko-kr/library/bb934049.aspx

http://optimizer.tistory.com/48

5. 보안 이슈

1. Windows Server, SQL Server Patch

2. JS/Spida 1443포트를 사용하는 SQL 서버에 sa 계정의 패스워드가 NULL인 경우 감염되는 바이러스이다.

JS/Spida, JS.Spida, JS_SQLSPIDA, JScript/SQLSpida, Worm, SQLsnake, Digispid로 불리고 5월 말에 주로 행동했던 것은 이 웜 바이러스의 변종인 JS/Spida.B이다.

3. 윈도우 인증모드라고 하여도 SA 패스워드를 NULL로 남겨두지 않는다.

4. DBMS 포트를 기본 1433에서 다른 포트로 변경하거나 필터링 한다.

5. SA 패스워드를 잘 보관한다. ASP 혹은 ASA 파일들, Connect.inc와 같은 데이터베이스 연결 문자열을 담아주는 파일들, 레지스트리에 패스워드를 넣어놓는 것은 sa의 패스워드를 노출할 위험이 있는 것이다. 암호화 되거나 컴포넌트와 같은 컴파일 된 파일에 보관하도록 한다.

6. 위험한 확장 저장 프로시저들을 제거하라.

- Xp_cmdshell (쉘 상의 모든 명령들을 수행할 수 있다.)

USE master

GO

IF OBJECT_ID('[dbo].[xp_cmdshell]') IS NOT NULL BEGIN

  EXEC sp_dropextendedproc 'xp_cmdshell'

END

GO

-

- Xp_regread, xp_regwrite (레지스트리를 읽거나 쓰기가 가능)

- Xp_makewebtask (데이터베이스 내의 정보들을 인터넷에 게시할 수 있다.)

- Xp_enumdsn (DNS 정보들을 보고자 하는 경우 사용)

- Sp_dropextendedproc 시스템 프로시저로 제거 할수 있다. Ex) sp_dropextendedproc 'xp_cmdshell'

1) master database

·  확장 프로시저 퍼미션 삭제

·  REVOKE EXECUTE ON xp_regread FROM public

·  REVOKE EXECUTE ON xp_instance_regread FROM public GO

·  sp_runwebtask 접근을 통한 web task 실행 권한 획득 방지

·  REVOKE EXECUTE ON dbo.sp_runwebtask FROM public GO

2) msdb database

·  Agent Job 접근을 통한 권한 확대 방지

·  REVOKE EXECUTE ON sp_add_job FROM public

·  REVOKE EXECUTE ON sp_add_jobstep FROM public

·  REVOKE EXECUTE ON sp_add_jobserver FROM public

·  REVOKE EXECUTE ON sp_start_job FROM public

·  mswebtasks 테이블을 통한 web task 관리 권한 획득 방지

·  REVOKE ALL ON dbo.mswebtasks FROM public

·  DTS packages 를 통한 administrator 패스워드 유출 방지

·  REVOKE EXECUTE ON sp_enum_dtspackages FROM public

·  REVOKE EXECUTE ON sp_get_dtspackage FROM public

·  SQL Agent Password 노출 방지

·  REVOKE EXECUTE ON sp_get_sqlagent_properties FROM public

  프로시저들의 Drop후에도 dll 파일은 남아 있으니 아래 쿼리를 실행하여 제거한다.

·  dbcc xp_cmdshell(free)

·  dbcc xp_dirtree(free)

·  dbcc xp_regdeletekey(free)

·  dbcc xp_regenumvalues(free)

·  dbcc xp_regread(free)

·  dbcc xp_regwrite(free)

·  dbcc sp_makewebtask(free)

·  dbcc sp_adduser(free)

다음으로 SQL Injection을 유발시킬 수 있는 시스템 DB상에 존재하는 프로시저들의 EXCUTE권한을 DENY하겠다.  해당 프로시저들을 통해 시스템의 정보를 조회하고 변경할 수 있으니 필수적으로 DENY해야 할 권한들이다.

 

·  DENY  EXECUTE  ON [master].[dbo].[xp_subdirs] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_dirtree] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_availablemedia] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_regwrite] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_regread] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_regaddmultistring] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_regdeletekey] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_regdeletevalue] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_regremovemultistring] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_regaddmultistring] TO [guest] CASCADE

 

마지막으로 일반 User의 물리적 파일 접근을 제한하기 위해 아래 쿼리를 실행한다.

·  DENY  EXECUTE  ON [master].[dbo].[xp_fileexist] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_fixeddrives] TO [guest] CASCADE

·  DENY  EXECUTE  ON [master].[dbo].[xp_getfiledetails] TO [guest] CASCADE

7. SQL 서버 에러 로그를 자주 검사하고 잘 보관해라.

6. SQL Injection

1. 저장 프로시저를 작성하여 Spooky Loing 문제를 해결 할 수 있다.

2. 저장된 프로시저를 사용할 때 매개변수화 된 API를 이용하라. 모든 입력 값 검증은 일반적인 루틴을 이용하고, 최소한의 권한을 DB 사용자 에게 적용하라.

3. 프로시저로 변경 하고 동적 쿼리를 사용하지 않는다.

4. 데이터베이스와 연동을 하는 스크립트의 모든 파라미터들을 점검하여 사용자의 입력 값이 SQL injection을 발생시키지 않도록 수정한다.

5. 사용자 입력이 SQL injection을 발생시키지 않도록 사용자 입력 시 특수문자(' " / \ ; : Space -- +등)가 포함되어 있는지 검사하여 허용되지 않은 문자열이나 문자가 포함된 경우에는 에러로 처리한다.

6. SQL 서버의 에러 메시지를 사용자에게 보여주지 않도록 설정한다. 공격자는 리턴 되는 에러 메시지에 대한 분석을 통하여 공격에 성공할 수 있는 SQL Injection 스트링을 알아낼 수 있다. 따라서 SQL 서버의 에러 메시지를 외부에 제공하지 않도록 한다.

7. 웹 애플리케이션이 사용하는 데이터베이스 사용자의 권한을 제한한다. 가능하면 일반 사용자 권한으로는 모든 system stored procedures에 접근하지 못하도록 하여 웹 애플리케이션의 SQL Injection 취약점을 이용하여 데이터베이스 전체에 대한 제어권을 얻거나 데이터베이스를 운용중인 서버에 대한 접근이 불가능하도록 한다.

Sys.object의 public 권한 제거

7.  서버를 견고하게 운영하기

1. DB 최소권한의 유저로 운영하라.

2. 사용하지 않는 저장된 프로시저와 기능들은 제거하거나 관리자에게 제한된 접근

권한을 주어라.

3. 퍼미션을 변경하고, 공개된 시스템 객체에 접근을 제거 하라.

4. 모든 사용자 계정의 패스워드를 강화 시켜라

5. 미리 승인된 서버의 링크를 제거 하라.

6. 사용하지 않는 네트워크 프로토콜을 제거하라.

7. 신뢰할 수 있는 네트워크,웹 서버, 백업 서버만 접근을 허용하라.

 

 

출처: https://taeksu.tistory.com/entry/SQL-Server-로그인-및-보안 [아ㅏㅏㅏㅏㅏㅏㅏㅏㅏ 졸려]

SELECT  user_seeks * avg_total_user_cost * ( avg_user_impact * 0.01 ) AS [index_advantage]

, migs.last_user_seek 

, mid.[statement] AS [Database.Schema.Table] 

, mid.equality_columns 

, mid.inequality_columns 

, mid.included_columns 

, migs.unique_compiles 

, migs.user_seeks 

, migs.avg_total_user_cost 

, migs.avg_user_impact

FROM   sys.dm_db_missing_index_group_stats AS migs WITH ( NOLOCK )

        INNER JOIN sys.dm_db_missing_index_groups AS mig WITH ( NOLOCK )

           ON migs.group_handle = mig.index_group_handle

        INNER JOIN sys.dm_db_missing_index_details AS mid WITH ( NOLOCK )

           ON mig.index_handle = mid.index_handle

WHERE   mid.database_id = DB_ID()

ORDER BY index_advantage DESC

 

 

-- 인덱스에대해카운터가되었는지확인

-- Index_id NULL 인경우는인덱스를타지않은경우, NULL 이아닌경우는인덱스를탄경우

SELECT object_name( idx.object_id) as 'table',

idx.name as 'index',

idx.index_id as 'id',

idx.type_desc as 'type',

icol.column_id,

--col.name,

usg.user_seeks,

usg.last_user_seek,

usg.user_scans,

usg.last_user_scan,

usg.user_lookups,

usg.last_user_lookup

FROM sys.indexes idx inner join sys.dm_db_index_usage_stats usg

ON usg.object_id = idx.object_id and idx.index_id = usg.index_id and usg.database_id = db_id()

LEFT JOIN sys.index_columns icol on icol.index_id = usg.index_id and icol.object_id = idx.object_id

AND usg.index_id IS null

AND idx.type_desc not in ('heap','clustered')

 

'Database > SQL Server' 카테고리의 다른 글

조인 조건자 없음  (0) 2020.08.27
DB 보안  (0) 2020.08.27
SQL Server의 동적 데이터 마스킹  (0) 2020.08.27
인덱스 리빌드는 통계를 업데이트 할까?  (0) 2020.08.27
INDEX 인덱스 상세 정보 확인  (0) 2020.08.27

동적 데이터 마스킹 (DDM)SQL Server 2016에 추가 된 유용한 기능입니다. DDM을 사용하면 인증되지 않은 사용자의 민감한 데이터를 숨길 수 있습니다. 가장 중요한 것은 데이터 마스킹과 암호화는 데이터를 보호하는 두 가지 방법입니다. 동적 데이터 마스킹은 여러 가지 전략을 사용하여 데이터를 숨 깁니다. SQL Server에서 저장된 데이터를 암호화 기능으로 수정하지 않으면 도움이됩니다.

 

다이내믹 데이터 마스킹은 헬스 케어, 뱅킹 도메인에서 광범위하게 사용되어 권한이없는 엔티티의 데이터 기밀성을 유지하기위한 엄격한 조치를 취할 수 있습니다. DDM을 통해 우리는 응용 프로그램 코드 및 쿼리를 수정하지 않고 기존 저장된 데이터를 보호.

 

사례 연구로서 개발자는신원, 연락처, 신용 카드 번호 및 재무 기록과 같은 고객의 건강 및 은행 기록이있는 생산 데이터에 노출됩니다. 타사 영업 담당자는 자신의 제품을 마케팅하는 데 사용할 수있는 고객의 보험 내역을 알고 있어야합니다.

 

따라서 동적 데이터 마스킹은 기밀 데이터를 일반 텍스트 형식으로 무단 사용자에게 노출하는 것을 제한 읽기 권한이 있어도.

 

 

 

 

 

마스킹 기능을 테스트 할 새 사용자 작성

동적 데이터 마스킹 쿼리를 진행하기 전에. 우리는 새로운 사용자를 만들 것입니다 MaskedTestUser 갖는 것 고르다 아래에 생성 된 테이블에 대한 권한 dbo 사용자. 마스크 된 열 기능을 사용하여 dbo 사용자 아래에 테이블을 만들고 MaskedTestUser를 사용하여 쿼리하여 마스크 된 데이터를 봅니다. MaskedTestUser는 실제 데이터에 액세스 할 수없는 권한이없는 사용자라고 가정합니다.

 

DROP USER IF EXISTS MaskedTestUser;

CREATE USER MaskedTestUser WITHOUT LOGIN;

 

 

 

SQL Server에서 마스킹 기능 및 지원되는 데이터 형식

SQL Server는 서로 다른 네 가지를 통합했습니다민감한 데이터를 마스크하는 기능. XML, varbinary 및 hierarchyid와 같은 일부 특수 데이터 유형과 함께 문자열, 숫자 및 날짜와 같은 가능한 모든 데이터 유형과 작동하도록 설계되었습니다.

 

 

 

 

 

기능과 사용법을 이해하기 위해 각각을 살펴 보겠습니다.

 

1. 태만

데이터의 전체 마스킹 기본 마스킹 기능을 사용하여 달성 할 수 있습니다. 가능한 모든 데이터 유형과 호환 SQL Server에서. 마스크 된 데이터는 마스크 된 열의 데이터 유형에 따라 표시됩니다. 문자열 데이터 유형은 IdentityNumber 및 Phone 열과 같은 XXXX 문자를 사용하여 표시됩니다. 숫자 데이터 유형은 0으로 표시됩니다. 날짜는 DateOfBirth 열과 같은 기본 날짜 '1900-01-01'로 표시됩니다.

 

데모 목적으로 샘플 데이터를 마스킹 열로 채 웁니다.

 

DROP TABLE IF EXISTS DefaultMaskTest;

CREATE TABLE DefaultMaskTest

(

ID       INT              IDENTITY (1,1) PRIMARY KEY NOT NULL

,DefaultMask_Varchar  VARCHAR(255) MASKED WITH (FUNCTION = 'default()') NULL

,DefaultMask_Char    CHAR(1)         MASKED WITH (FUNCTION = 'default()') NOT NULL

,DefaultMask_Bit      BIT MASKED WITH (FUNCTION = 'default()') NOT NULL

,DefaultMask_Date    DATE MASKED WITH (FUNCTION = 'default()') NOT NULL

,DefaultMask_DateTime DATETIME MASKED WITH (FUNCTION = 'default()') NOT NULL

,DefaultMask_Time    TIME MASKED WITH (FUNCTION = 'default()') NOT NULL

,DefaultMask_Integer  BIGINT MASKED WITH (FUNCTION = 'default()') NOT NULL

,DefaultMask_Decimal  DECIMAL(9,2) MASKED WITH (FUNCTION = 'default()') NOT NULL

,DefaultMask_XML      XML MASKED WITH (FUNCTION = 'default()') NOT NULL

);

GO

INSERT INTO DefaultMaskTest

(

DefaultMask_Varchar, DefaultMask_Char, DefaultMask_Bit, DefaultMask_Date, DefaultMask_DateTime, DefaultMask_Time

, DefaultMask_Integer, DefaultMask_Decimal, DefaultMask_XML

)

VALUES

(

'Chetan Sharma', 'M', 1, '2020-06-12', '2021-06-12 12:23:32:543', '08:12:46:342'

, 5282991, 45628.39,'<root>Tech-Recipes</root>'

);

 

 

 

다음 쿼리는 MaskedTestUser 및 dbo 사용자에게 마스크 된 데이터가 표시되는 방법을 보여줍니다.

 

--Drop & Create User - MaskedTestUser

DROP USER IF EXISTS MaskedTestUser;

CREATE USER MaskedTestUser WITHOUT LOGIN;

--Query table using dbo user

SELECT * FROM DefaultMaskTest;

--Grant SELECT permission to MaskedTestUser

GRANT SELECT ON DefaultMaskTest TO MaskedTestUser;

--Query table using MaskedTestUser

EXECUTE AS USER = 'MaskedTestUser';

SELECT * FROM DefaultMaskTest;

--Revert user impersonation to dbo user

REVERT;

 

 

 

 

 

 

 

 

2.부분

부분 기능 문자열 데이터 유형에서만 작동. 데이터의 부분 마스킹에 문자가 거의 없음문자열의 시작 또는 끝에서 기본 'XXXX'문자를 사용하는 대신 맞춤 문자열을 사이에 삽입 할 수 있습니다. 위의 예에서 CreditCard 열은 partial (2,“XXXXXXX”, 0) 방법을 사용하여 마스킹되어 처음 두 문자 만 표시하고 사용자 정의 가능한 X 문자를 추가합니다.

 

데모 목적으로 샘플 데이터를 마스킹 열로 채 웁니다.

 

DROP TABLE IF EXISTS PartialMaskTest;

CREATE TABLE PartialMaskTest

(

ID          INT IDENTITY(1,1) PRIMARY KEY NOT NULL

,PartialMask_Varchar  VARCHAR(255) MASKED WITH (FUNCTION = 'partial(1, "XXXX",1)') NOT NULL

,PartialMask_Nvarchar NVARCHAR(255)MASKED WITH (FUNCTION = 'partial(2, "ABCDEFG",3)') NOT NULL

);

GO

INSERT INTO PartialMaskTest

(

PartialMask_Varchar

,PartialMask_Nvarchar

)

VALUES

(

'I am on tech-recipes.com'

,'SQL Server keep rocking'

);

 

 

 

다음 쿼리는 MaskedTestUser 및 dbo 사용자에게 마스크 된 데이터가 표시되는 방법을 보여줍니다.

 

--Drop & Create User - MaskedTestUser

DROP USER IF EXISTS MaskedTestUser;

CREATE USER MaskedTestUser WITHOUT LOGIN;

--Query table using dbo (owner) user

SELECT * FROM PartialMaskTest;

--Grant SELECT permission to MaskedTestUser

GRANT SELECT ON PartialMaskTest TO MaskedTestUser;

--Query table using MaskedTestUser

EXECUTE AS USER = 'MaskedTestUser';

SELECT * FROM PartialMaskTest;

--Revert user impersonation to dbo user

REVERT;

 

 

 

 

 

 

 

3.이메일

이메일, 중요한 기능을 고려하여 이메일 ID를 마스킹하는 특정 기능인 이메일. 이메일 기능은 문자열 데이터 유형에서만 작동합니다. 에 이메일 ID 및 도메인 이름 마스킹 ‘[이메일 보호]’ 첫 문자를 그대로 유지하고 도메인 이름을 .COM으로 변경합니다. 고객 테이블의 EmailId 열이에서 마스크되었습니다. [이메일 보호] 에 [이메일 보호]. 부분 기능조차도 이메일 기능 사용을 다음과 같이 복제 할 수 있습니다 부분적 (1, '[이메일 보호]’, 0).

 

이메일 기능이 작동하는지 확인하기 위해 샘플 데이터를 마스킹 열로 채 웁니다.

 

DROP TABLE IF EXISTS EmailMaskTest;

CREATE TABLE EmailMaskTest

(

ID   INT IDENTITY (1,1) PRIMARY KEY NOT NULL

,EmailMask VARCHAR(255) MASKED WITH (FUNCTION = 'email()') NOT NULL

);

GO

INSERT INTO EmailMaskTest

(

EmailMask

)

VALUES ('[email protected]'),

('[email protected]'),

('[email protected]');

 

 

 

다음 쿼리는 MaskedTestUser 및 dbo 사용자에게 마스크 된 데이터가 표시되는 방법을 보여줍니다.

 

--Drop & Create User - MaskedTestUser

DROP USER IF EXISTS MaskedTestUser;

CREATE USER MaskedTestUser WITHOUT LOGIN;

--Query table using dbo (owner) user

SELECT * FROM EmailMaskTest;

--Grant SELECT permission to MaskedTestUser

GRANT SELECT ON EmailMaskTest TO MaskedTestUser;

--Query table using MaskedTestUser

EXECUTE AS USER = 'MaskedTestUser';

SELECT * FROM EmailMaskTest;

--Revert user impersonation to dbo user

REVERT;

 

 

 

 

 

 

 

4.무작위

마스킹 숫자 데이터 유형 열 원래 값에 난수를 사용합니다. 정의 된 범위 사이에서 난수를 생성 할 수 있습니다. 고객 테이블의 급여 열이 랜덤 (1,10) – 1에서 10 사이의 난수 만 생성합니다. 우리는 다음을 사용하여 소수 범위를 정의 할 수 있습니다 무작위 (0.1,0.75).

 

DROP TABLE IF EXISTS RandomMaskTest;

CREATE TABLE RandomMaskTest

(

ID     INT IDENTITY (1,1) PRIMARY KEY NOT NULL

,RandomMask_INT     INT MASKED WITH (FUNCTION = 'random(1,999)') NOT NULL

,RandomMask_BIGINT  BIGINT MASKED WITH (FUNCTION = 'random(1000,2000)') NOT NULL

,RandomMask_DECIMAL DECIMAL(9,2) MASKED WITH (FUNCTION = 'random(1.1,10.5)') NOT NULL

);

GO

INSERT INTO RandomMaskTest

(

RandomMask_INT

,RandomMask_BIGINT

,RandomMask_DECIMAL

)

VALUES

(33405691, 401204193524, 311531.56);

 

 

 

다음 쿼리는 MaskedTestUser 및 dbo 사용자에게 마스크 된 데이터가 표시되는 방법을 보여줍니다.

 

--Drop & Create User - MaskedTestUser

DROP USER IF EXISTS MaskedTestUser;

CREATE USER MaskedTestUser WITHOUT LOGIN;

--Query table using dbo (owner) user

SELECT * FROM RandomMaskTest;

--Grant SELECT permission to MaskedTestUser

GRANT SELECT ON RandomMaskTest TO MaskedTestUser;

--Query table using MaskedTestUser

EXECUTE AS USER = 'MaskedTestUser';

SELECT * FROM RandomMaskTest;

--Revert user impersonation to dbo user

REVERT;

 

 

 

 

 

 

 

마스킹 기능 및 데이터 유형 호환성

마스킹 기능은 지원되는 데이터와 작동유형 만. 호환되지 않는 데이터 형식으로 마스킹 기능을 사용하려고하면 SQL Server에서 다음 오류가 발생합니다. 문자 데이터 유형에 임의의 함수를 사용하려고 할 때. 임의 함수는 숫자 데이터 유형과 만 호환됩니다.

 

16003 메시지, 수준 16, 상태 0, 줄 21열 'IdentityNumber'의 데이터 유형은 데이터 마스킹 기능 'random'을 지원하지 않습니다.</br>

 

데이터베이스에서 마스크 된 열 쿼리

추가 sys.masked_columns 뷰는 데이터베이스의 모든 마스크 열을 포함합니다. 이를 사용하여 마스크 된 열 이름, 마스킹 기능 및 관련 테이블 이름을 쿼리 할 수 있습니다.

 

SELECT  OBJECT_NAME(mc.object_id) as TableName

,mc.name as ColumnName

,TYPE_NAME(system_type_id) as DataType

,mc.is_masked as IsMasked

,mc.masking_function as MaskingFunction

FROM    sys.masked_columns as mc

WHERE  mc.is_masked = 1;

 

 

 

 

 

 

 

테이블 문을 만들기 위해 다중 마스킹 기능 추가

우리는 이미 유용한 예제를 통해 각 마스킹 기능을 자세히 살펴 보았습니다. 요구 사항에 따라 모든 마스킹 기능이 포함 된 테이블을 만들고 출력을 봅시다.

 

사용자 역할 및 권한 이해는중대한. MaskedTestUser는 실제 데이터를 볼 수있는 권한이 없으므로 마스크 된 데이터가 그에게 표시되지만 권한이있는 사용자 및 고객 테이블 소유자 인 dbo는 사용 가능한 모든 데이터를 일반 텍스트로 볼 수 있습니다.</br>

 

Use tempdb;

DROP TABLE IF EXISTS Customer;

CREATE TABLE Customer

(

Id INT IDENTITY(1,1)

,DateOfBirth DATE MASKED WITH (FUNCTION = 'default()') NOT NULL

,EmailId VARCHAR(255) MASKED WITH (FUNCTION = 'email()') NOT NULL

,IdentityNumber VARCHAR(11)  MASKED WITH (FUNCTION = 'default()') NOT NULL

,Phone VARCHAR(11) MASKED WITH (FUNCTION = 'default()') NOT NULL

,Salary INT MASKED WITH (FUNCTION = 'random(1,9)') NOT NULL

,CreditCard VARCHAR(20)    MASKED WITH (FUNCTION = 'partial(2,"XXXXXX",0)') NOT NULL

);

INSERT INTO Customer (DateOfBirth, EmailId, IdentityNumber, Phone, Salary, CreditCard)

VALUES ('1985-10-28', '[email protected]', 'BZVLPE1258Q', '98564533213', 85000, '9764-3451-0916-1047');

 

 

 

다음 쿼리는 MaskedTestUser 및 dbo 사용자에게 마스크 된 데이터가 표시되는 방법을 보여줍니다.

 

--Drop and Create MaskedTestUser

DROP USER IF EXISTS MaskedTestUser;

CREATE USER MaskedTestUser WITHOUT LOGIN;

--Query table using dbo user

SELECT * FROM Customer;

--Grant SELECT permission to MaskedTestUser

GRANT SELECT ON Customer TO MaskedTestUser;

--Query table using MaskedTestUser

EXECUTE AS USER = 'MaskedTestUser';

SELECT * FROM Customer;

--Revert user impersonation to dbo user

REVERT;

 

 

 

 

 

 

 

테이블의 기존 열에 마스킹 기능 추가

ALTER TABLE Customer DROP COLUMN IF EXISTS AccountNumber;

ALTER TABLE Customer

ADD AccountNumber INT;

ALTER TABLE Customer

ALTER COLUMN AccountNumber ADD MASKED WITH (FUNCTION = 'random(1000,5000)');

 

 

 

개요

동적 데이터 마스킹은 권한이 없는 사용자로부터 민감한 데이터를 숨기는 데 유용합니다. 데이터 기밀성이 비즈니스에 중요한 경우에 사용할 수 있습니다.

'Database > SQL Server' 카테고리의 다른 글

조인 조건자 없음  (0) 2020.08.27
DB 보안  (0) 2020.08.27
Missing Index  (0) 2020.08.27
인덱스 리빌드는 통계를 업데이트 할까?  (0) 2020.08.27
INDEX 인덱스 상세 정보 확인  (0) 2020.08.27
  • Version : SQL Server 2005, 2008, 2008R2, 2012, 2014

 

인덱스를 리빌드하면 통계가 업데이트 될까? 이 질문에 많은 사람들은 "YES"라고 답할 것이다. 사실은 모든 통계를 업데이트를 하지 않는다. 인덱스를 리빌드 하는경우 해당 인덱스와 관련된 통계만 업데이트 된다.

 

Index Stats

Non-index stats

ALTER INDEX REORG

NO

NO

ALTER INDEX <index_name> REBUILD

해당 인덱스와 관련된 인덱스 통계 업데이트 됨

NO

ALTER INDEX ALL REBUILD

모든 인덱스 통계 업데이트 됨

NO

DBREINDEX (old syntax)

YES

YES

 

위에서 볼 수 있듯이 모든 통계는 인덱스 리빌드 작업을 통해서 업데이트 된다. 때로는 비 인덱스 통계도 매우 중요하다. 비인덱스 통계는 자동 또는 수동으로 생성된 관련 통계를 의미한다. 임계값이 큰 테이블의 경우 수동 통계 업데이트가 필요할 수 있다. 추적플래그 2371이 도움이 될 수 있다.

 

  • Statistical maintenance functionality (autostats) in SQL Server :

 

http://support.microsoft.com/ko-kr/kb/195565

  • Changes to automatic update statistics in SQL Server – traceflag 2371 :

http://blogs.msdn.com/b/saponsqlserver/archive/2011/09/07/changes-to-automatic-update-statistics-in-sql-server-traceflag-2371.aspx

 

실습을 통해 모든 통계를 업데이트 하지 않는 경우를 확인 할 수 있다.

 

[샘플 데이터 생성]

if object_id ('t') is not null

drop table t

go

create table t(c1 int, c2 as c1 & 1)

go

 

create index t1_indx1 on t(c1 )

go

set nocount on

declare @i int

set @i = 0

while @i < 1000

begin

insert into t (c1) values (@i)

set @i = @i + 1

end

go

 

update statistics t with fullscan

go

 

go

--this will create a stats on c2

select count(*) from t where c2 =1

 

go

 

[통계 정보 확인]

SELECT

obj.name, stat.name, stat.stats_id, last_updated

FROM sys.objects AS obj

JOIN sys.stats stat ON stat.object_id = obj.object_id

CROSS APPLY sys.dm_db_stats_properties(stat.object_id, stat.stats_id) AS sp

where obj.name = 't'

 

 

[인덱스 리빌드 진행]

-- alter all indexes

alter index all on t rebuild

--re-organize won't update even stats of the index

--alter index all on t reorganize

 

통계 정보를 확인해 보면 t1_index1의 통계는 최근 업데이트가 반영된 것을 확인 할 수 있으며 __WA_Sys_00000002_34C8D9D1 은 업데이트가 되지 않은 것을 확인 할 수 있다.

SELECT

obj.name, stat.name, stat.stats_id, last_updated

FROM sys.objects AS obj

JOIN sys.stats stat ON stat.object_id = obj.object_id

CROSS APPLY sys.dm_db_stats_properties(stat.object_id, stat.stats_id) AS sp

where obj.name = 't'

 

 

 

 

 

[참고자료]

http://blogs.msdn.com/b/psssql/archive/2015/03/06/does-rebuild-index-update-statistics.aspx

 

 

출처: https://sungwookkang.com/1062 [Data Science Lab]

'Database > SQL Server' 카테고리의 다른 글

조인 조건자 없음  (0) 2020.08.27
DB 보안  (0) 2020.08.27
Missing Index  (0) 2020.08.27
SQL Server의 동적 데이터 마스킹  (0) 2020.08.27
INDEX 인덱스 상세 정보 확인  (0) 2020.08.27

+ Recent posts