Azure SQL Managed Instance에서 로컬 스토리지 사용량 모니터링

 

·       Version : SQL Server, Azure SQL Managed Instance

 

Azure SQL Managed Instance 사용할때, 선택한 인스턴스의 vCore 예약 스토리지에 따라 사전에 정의된 스토리지 공간이 있다. 이번 포스트에서는 Managed Instance 스토리지 공간을 모니터링하는 방법에 대해서 알아본다.

 

Managed Instance에서는 아래와 같은 제약이 있다.

·       인스턴스의 스토리지 제한은 Azure Portal에서 선택할 있다. 범용은 8TB, 비즈니스 크리티컬은 4TB 보다 없다.

·       (범용 전용) 로컬 SSD 디스크 스토리지의 제한 – Managed Instance 경우  tempdb 24GB X vCore 공간만큼 로컬 SSD 배치된다. 배치된 로컬 SSD 한계에 도달하면 tempdb에서 임시 객체를 만들수 없다.

 

스토리지 리밋에 도달하면 스토리지 공간/ vCore 수를 늘리거나 일부 리소스를 비워야 한다. 스토리지 변경은 오래 걸릴수 있기 때문에 스토리지 한계에 도달하기 전에 스토리지를 추가하는 것이 중요하다. sys.dm_os_volume_stats Managed Instance 스토리지를 포함하여 볼륨에 대한 사용 정보를 제공한다. 아래 스크립트를 사용하여 스토리지의 사용량 정보를 확인 있다.

SELECT

       volume_mount_point,

       used_gb = CAST(MIN(total_bytes / 1024. / 1024 / 1024) AS NUMERIC(8,1)),

       available_gb = CAST(MIN(available_bytes / 1024. / 1024 / 1024) AS NUMERIC(8,1)),

       total_gb = CAST(MIN((total_bytes+available_bytes) / 1024. / 1024 / 1024) AS NUMERIC(8,1))

FROM sys.master_files AS f

       CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.file_id)

GROUP BY volume_mount_point;

 

 

 

 

아래 그림에서 http:// 경우 원격 Azure Premium Disk 저장소를 사용하고 있는지 보여준다.

 

 

스크립트를 주기적으로 실행하여 available_gb 줄어드는 것을 확인하여 스토리지 공간이 부족할 경우 알림을 받을 있도록 한다.

 

[참고자료]

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2019/03/08/monitor-storage-local-storage-usage-on-general-purpose-managed-instance/

 

 

2019-09-06 / Sungwook Kang / http://sungwookkang.com

 

Azure SQL Managed Instance, Azure Local Storage, SQL Azure, sys.dm_os_volume_stats, 로컬 스토리지

SQL Server Enterprise Edition 에서 CPU 40 Core 이상 사용하지 못하는 현상

 

·       Version : SQL Server

 

호스트의 서버의 CPU96 Core 장비에서 SQL Server 2016 Enterprise Edition 설치 사용하는데 CPU 40Core 이상을 사용하지 못하는 문제가 있었다. 처음에는 NUMA 불균형이 발생하지 않았는지 의심했었지만, 증상을 보면 0 코어부터 39 코어까지만 정확히 사용하는것을 확인할 있었다. 여러가지 원인을 조사 결과 해당 문제는 SQL Server 라이선스 업데이트로 해결 되었다. MS 라이선스 정책은 많이 어려운 편인데 (서비스를 제공하는 형태에 따라 다양한 라이선스가 있음, 라이선스의 자세한 내용은 여기서 다루지 않는다.) 일반적인 SQL Server Enterprise 에디션을 설치 할경우 40Core 이상 사용할 없다고 한다. 40Core 이상 사용하려면 Core 라이선스를 선택해서 설치해야 한다.

 

이미 SQL Server Enterprise Edition 설치되어 있는 경우는 어떻게 할까? 이런 경우 SQL Server 재설치는 필요없이 라이선스 업데이트가 가능하다. 우선 SQL Server Enterprise Core Edition 다운로드 받아서, 호스트 서버에서 마운트 한다. 그리고 Install과정을 진행한다. 설치 과정중 나타나는 라이선스 코드를 복사하여, 아래 스크립트에서 PID 부분에 복사한 라이선스 코드를 입력하고 실행한다.

Setup.exe /q /ACTION=editionupgrade /INSTANCENAME=MSSQLSERVER /PID=XXXXX-XXXXX-XXXXX-XXXXX-XXXXX /IACCEPTSQLSERVERLICENSETERMS

 

라이선스 없데이트 과정은 수분의 시간이 필요하며, 과정중에 SQL Server 서비스가 중지되므로 반드시 점검때 하도록 한다.

 

 

2019-09-03 / Sungwook Kang / http://sungwookkang.com

 

SQL Server Enterprise Edition, SQL Server Core lincese, SQL 라이선스, SQL Server 40코어 이상 사용하기, SQL Server CPU Core

SQL Server Agent Job에서 sysploicy_purge_history  작업실패

 

·       Version : SQL Server

 

SQL Server 2008 이후로 SQL Server Agent Job에서는 syspolicy_purge_history라는 job 등록되어 있다. 해당 Job MSDB 쌓여 있는 히스토리를 정리하는 작업을 진행한다.  가끔 해당 Job 실행시 아래와 같은 오류가 발생할 있는데, 오류가 무엇인지 확인해보고 해결하는 방법에 대해서 살펴 본다.

 

Job 실패하였을때, 해당 로그를 살펴보면 아래와 같은 오류로그가 발생하였다.

Date                    7/29/2019 1:23:41 PM

Log                       Job History (syspolicy_purge_history)

 

Step ID                3

Server                

Job Name                         syspolicy_purge_history

Step Name                       Erase Phantom System Health Records.

Duration                           00:00:00

Sql Severity        0

Sql Message ID  0

Operator Emailed          

Operator Net sent         

Operator Paged

Retries Attempted          0

 

Message

Executed as user: NT Service\SQLSERVERAGENT. The job script encountered the following errors. These errors did not stop the script:  A job step received an error at line 1 in a PowerShell script. The corresponding line is 'import-module SQLPS'. Correct the script and reschedule the job. The error information returned by PowerShell is: 'The specified module 'SQLPS' was not loaded because no valid module file was found in any module directory.  '  A job step received an error at line 1 in a PowerShell script. The corresponding line is 'SQLSERVER:'. Correct the script and reschedule the job. The error information returned by PowerShell is: 'The term 'SQLSERVER:' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.  '.  Process Exit Code -1.  The step failed.

 

해당 Job 실행하는 명령을 직접 Powershell콘솔에서 실행하여도 아래와 같은 오류가 발생한다.

 

해당 오류는 명령이 참조하는 모듈의 PATH 설정이 누락되어 발생한 경우로, 시스템 변수에서 해당 변수값을 지정하면 해결이 가능하다. 모듈의 PATH 경로는 SQL 버전마다 조금씩 다르며 아래 경로를 참고할 있도록 한다.

C:\Program Files (x86)\Microsoft SQL Server\140\Tools\PowerShell\Modules

 

모듈이 위치한 경로를 확인하였으면,  [My Computer] – [right click] – [Properties]에서 [Advance] 탭을 선택하여 [Environmeont Variables] 클릭한다.  PSModulePath 이름으로 모듈이 위차한 PATH 등록(또는 수정)한다.

 

경로가 올바르게 수정되었으면, SQLPS 시작하여 정상적으로 PATH 설정이 완료되었는지 확인이 가능하다. 또는 SQL Agent에서 해당 Job 실행하여 정상적으로 실행이 되는지 확인할 있다.

 

 

[참고자료]

https://blog.sqlauthority.com/2016/03/08/sql-server-syspolicy_purge_history-job-failing-step-erase-phantom-system-health-records/

 

2019-08-07 / Sungwook Kang / http://sungwookkang.com

 

SQL Server, Agent job, sysploicy_purge_history , Agent 히스토리 삭제, PSModulePath, Erase Phantom System Health Records

SQL Server에서 테이블 이름을 변경하지 않고 테이터만 교체하기

 

·       Version : SQL Server

 

SQL Server에서 파티션 테이블을 사용하기 위해서는 SQL Server Enterprise Edition 사용해야한다. Standard Edition에서도 파티션 테이블 처럼 사용하기 위해서는 새로운 테이블을 생성하고 sp_rename 함수를 사용하여 기존테이블과 새로운 테이블의 이름을 변경하는 방식으로 사용하였다.

-- Replace live with staging

BEGIN TRAN

DROP TABLE DataTable;

EXEC sp_rename DataTable_Staging, DataTable;

COMMIT

 

-- Swap live and staging

BEGIN TRAN

EXEC sp_rename DataTable, DataTable_Old;

EXEC sp_rename DataTable_Staging, DataTable;

EXEC sp_rename DataTable_Old, DataTable_Staging;

COMMIT

 

위와 같은 방법을 사용하여 메타 데이터 레벨에서 테이블 이름을 변경하면 스키마가 일치하더라도 이름이 바뀐 테이블이 지정된 이름의 메타 데이터와 연결되지 않는다. 시스템은 캐시된 ObjectID 사용하는데 스왑아웃을 수행하려면 캐시 데이터를 업데이트하는 추가 단계가 필요하다. 스위치 아웃으로 인해 ObjectID 관련 메타정보가 그대로 유지되면 이러한 추가단계가 불필요하다.

 

ALTER TABLE SWITCH TO 명령은 기존 테이블을 유지하면 데이터 세트를 스와핑힌다. 명령을 사용하기 위해서는 스키마 정보가 호환 가능해야한다. 컬럼이름, 컬럼 순서, 조약 조건등 특성이 동일해야한다.

-- Replace live with staging

BEGIN TRAN

TRUNCATE TABLE DataTable;

ALTER TABLE DataTable_Staging SWITCH TO DataTable;

COMMIT

 

-- Swap live and staging

/* Note: An extra table, DataTable_Old, is required to temporarily hold the data being replaced before it is moved into DataTable_Staging. The rename-based approach did not require this extra table. */

BEGIN TRAN

ALTER TABLE DataTable SWITCH TO DataTable_Old;

ALTER TABLE DataTable_Staging SWITCH TO DataTable;

ALTER TABLE DataTable_Old SWITCH TO DataTable_Staging;

COMMIT

 

 

 

 

 

[참고자료]

https://bengribaudo.com/blog/2016/11/15/3521/swapping-data-sets

 

 

2019-05-02 / Sungwook Kang / http://sungwookkang.com

 

SQL Server, partition table, table swap, data set swap, alter table switch to, sp_rename, alter table, 테이블 변경, 테이블 이름 변경, 데이터 변경

SQL Server master 데이터베이스 정리

 

·       Version : SQL Server

 

SQL Server에서 master 데이터베이스의 역할은 시스템에 대한 모든 정보를 기록한다. 여기에는 로그인 계정, 끝점, 연결된 서버 시스템 구성설정과 같은 인스턴스 차원의 메타 데이터가 포함된다.

·       master database : https://docs.microsoft.com/en-us/sql/relational-databases/databases/master-database?view=sql-server-2017

 

우리는 가끔 의도하지 않게 master 데이터베이스에 개체를 생성하는 경우가 있다. 대부분 개체를 생성할 USE 문을 생략했을 수도 있고 많은 작업창을 띄어 놓고 사용하다가 실수할 수도 있다. 이번 아티클에서는 master 데이터베이스를 정리하는 방법에 대해서 알아본다. master 데이터베이스는 시스템의 설정을 저장하고 있으므로 해당 작업시 주의가 필요 하다.

 

[master에서 삭제하지 않을 목록]

아래 표에 정으되어 있는 오브젝트 유형은 시스템과 관련있기 때문에 삭제 목록에서 제외한다.

ET

External table

IT

Internal table

PC

Assembly (CLR) stored procedure

PG

Plan guide

RF

Replication-filter-procedure

S

System table

SQ

Service queue

TA

Assembly (CLR) DML Trigger

X

Extended procedure

-

CLR User-Defined Data Type (UDDT)

 

사용자는 자신만의 모듈을 생성하여 시스템 객체로 표시할 수도 있다. 경우 시스템 저장프로시저와 구별 있는 확실한 방법이 없기 때문에 주관적으로 식별해야 한다.

 

[master 에서 삭제할 목록]

오브젝트를 삭제하기 위해서는 약간의 순서가 필요하다. 예를 들어 테이블을 삭제하려면 외부키나 뷰를 제거해야하며, 테이블 함수를 사용하는 테이블을 삭제 때까지 파티션 함수를 제거할 없다. 또한 순환 참조가 있을 있으므로 많은 경우 스크립트를 여러번 실행해야 수도 있다.

 

아래 표시된 유형은 객체를 삭제하면 자동으로 삭제되기 때문에 크게 걱정할 필요가 없다.

C

Check constraint

D

Default constraint

PK

Primary key constraint

TR

DML trigger

UQ

Unique constraint

 

나머지는 아래 순서대로 삭제해야한다.

1

F

Foreign key constraint

Need to be dropped before tables, as described below.

2

V

View

Should be dropped before tables, since they can have direct or indirect SCHEMABINDING.

FN

SQL scalar function

IF

SQL inline table-valued function

TF

SQL table-valued-function

3

P

Stored procedure

Need to be dropped before tables, but after views and functions (since procedures can reference views and functions, but can't be referenced by them except in rare cases using OPENQUERY()).

4

U

Table (user-defined)

Dropped after foreign keys and any type of module that can reference them with SCHEMABINDING.

5

TT

Table type

Dropped after tables and modules, but before old-style rules that could be bound to them. Table types need to be dropped before other types.

Alias types

 

6

SO

Sequence object

Dropped after tables/modules because they can't be dropped if any table or module references them.

R

Rule (old-style, stand-alone)

D

Old-style CREATE DEFAULT

Partition functions

 

7

Partition schemes

 

Can't be dropped until after partition functions.

8

SN

Synonym

I drop these toward the end because they can reference many of the items above (though this entity is not really prone to any issues on its own, unless you have external references pointing at them).

9

Schemas

 

I drop these almost last because almost all entities above can belong to a certain schema. For any entities you want to keep but move to a different schema, you'll need to first use ALTER SCHEMA ... TRANSFER.

10

Roles and Users

 

For users you don't want to keep, you'll need to first remove them from user-defined role membership and ownership, as well as ownership of any schemas. 

(For users you do want to keep, you may also want to remove any inappropriate permissions, but I'll deal with that in a separate post.) 

For roles you don't want to keep, you'll need to first remove any members; but you can't remove members who also happen to own roles.

 

[기본 접근]

순환 참조는 여러 개체가 서로를 참조하므로 개체를 삭제하는 올바른 순서를 결정하기가 복잡하다. 그래서 스크립트가 실패하더라도 다시 실행하여 스크립트가 성공할때 까지 반복해서 실행 있는 스크립트로 작성해야 한다. 예를 들어 개의  SCHEMABINDING 뷰를 사용하는 경우 개의 뷰를 모두 삭제해야 테이블이 삭제된다.   순서가 명확하다면 순서대로 하면 되지만 뷰가 여러개 이면서 서로 뷰를 참조하는 경우 순서를 예측하기 어렵기 때문에 삭제 명령을 반복해서 실행한다.

try { drop view 1; go; drop view 2; }

 

 

[삭제 스크립트 (순서대로 실행)]

Foreign keys

-- script to drop Foreign Keys

USE [master];

GO

SET NOCOUNT ON;

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  ALTER TABLE ' + objectname + N'

    DROP CONSTRAINT ' + fkname + N';

END TRY

BEGIN CATCH

  SELECT N''FK ' + fkname + N' failed. Run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT fkname = QUOTENAME(fk.[name]),

    objectname = QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name])

  FROM sys.foreign_keys AS fk

  INNER JOIN sys.objects AS t

  ON fk.parent_object_id = t.[object_id]

  INNER JOIN sys.schemas AS s

  ON t.[schema_id] = s.[schema_id]

) AS src;

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

View and functions

-- script to drop views

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  DROP VIEW ' + objectname + N';

END TRY

BEGIN CATCH

  SELECT N''View ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(v.[name])

  FROM sys.views AS v

  INNER JOIN sys.schemas AS s

  ON v.[schema_id] = s.[schema_id]

  WHERE v.is_ms_shipped = 0

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

-- script to drop functions

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP FUNCTION ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Function ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(o.[name])

  FROM sys.objects AS o

  INNER JOIN sys.schemas AS s

  ON o.[schema_id] = s.[schema_id]

  WHERE o.is_ms_shipped = 0

  AND o.[type] IN ('FN','IF','TF')

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Stored Procedures

-- script to drop procedures

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP PROCEDURE ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Procedure ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(o.[name])

  FROM sys.objects AS o

  INNER JOIN sys.schemas AS s

  ON o.[schema_id] = s.[schema_id]

  WHERE o.[type] = 'P'

  AND o.is_ms_shipped = 0

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Tables

-- script to drop user tables

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP TABLE ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Table ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(o.[name])

  FROM sys.tables AS o

  INNER JOIN sys.schemas AS s

  ON o.[schema_id] = s.[schema_id]

  WHERE o.is_ms_shipped = 0

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Table Types

-- script to drop table types

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP TYPE ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Type ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(o.[name])

  FROM sys.types AS o

  INNER JOIN sys.schemas AS s

  ON o.[schema_id] = s.[schema_id]

  WHERE o.is_table_type = 1

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Alias Types

-- script to drop alias types

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP TYPE ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Type ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(o.[name])

  FROM sys.types AS o

  INNER JOIN sys.schemas AS s

  ON o.[schema_id] = s.[schema_id]

  WHERE o.user_type_id > 256

  AND o.is_table_type = 0

  AND o.is_assembly_type = 0  -- not a CLR UDT

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Sequences

-- script to drop sequences

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP SEQUENCE ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Sequence ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(o.[name])

  FROM sys.sequences AS o

  INNER JOIN sys.schemas AS s

  ON o.[schema_id] = s.[schema_id]

  WHERE o.is_ms_shipped = 0

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Old-style CREATE RULE

-- script to drop old-style Rules

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP RULE ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Rule ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(o.[name])

  FROM sys.objects AS o

  INNER JOIN sys.schemas AS s

  ON o.[schema_id] = s.[schema_id]

  WHERE o.is_ms_shipped = 0

  AND [type] = 'R'

  AND parent_object_id = 0

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Old-style CREATE DEFAULT

-- script to drop old-style Defaults

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP DEFAULT ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Default ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(o.[name])

  FROM sys.objects AS o

  INNER JOIN sys.schemas AS s

  ON o.[schema_id] = s.[schema_id]

  WHERE o.is_ms_shipped = 0

  AND [type] = 'D'

  AND parent_object_id = 0

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Partition functions

-- script to drop partition functions

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP PARITITON FUNCTION ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Partition function ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME([name])

  FROM sys.partition_functions

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Partition schemes

-- script to drop partition schemes

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP PARITITON SCHEME ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Partition scheme ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME([name])

  FROM sys.partition_schemes

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Synonyms

-- script to drop synonyms

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP SYNONYM ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Synonym ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name]) + N'.' + QUOTENAME(o.[name])

  FROM sys.synonyms AS o

  INNER JOIN sys.schemas AS s

  ON o.[schema_id] = s.[schema_id]

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Schemas

-- script to drop schemas

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  EXEC sys.sp_executesql N''DROP SCHEMA ' + objectname + N';'';

END TRY

BEGIN CATCH

  SELECT N''Schema ' + objectname + N' failed - run the script again.'',

    ERROR_MESSAGE();

END CATCH

' FROM

(

  SELECT QUOTENAME(s.[name])

  FROM sys.schemas AS s

  WHERE [schema_id] BETWEEN 5 AND 16383

) AS src(objectname);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

-- script to re-route default schemas

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  ALTER USER ' + QUOTENAME(name) + N' WITH DEFAULT_SCHEMA = dbo;

 END TRY

 BEGIN CATCH

  SELECT N''User ' + p.name + N' failed - run the script again.'',

     ERROR_MESSAGE();

 END CATCH

'

FROM sys.database_principals AS p

WHERE p.default_schema_name IS NOT NULL

AND NOT EXISTS

(

  SELECT 1

  FROM sys.schemas AS s

  WHERE name = p.default_schema_name

);

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

Roles and Users

-- script to change ownership of roles to dbo

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  ALTER AUTHORIZATION ON ROLE::' + QUOTENAME(r.name) + N' TO dbo;

 END TRY

 BEGIN CATCH

   SELECT N''Role ' + r.name + N' failed - run the script again.'',

     ERROR_MESSAGE();

 END CATCH

'

FROM sys.database_principals AS r

INNER JOIN sys.database_principals AS u

ON r.owning_principal_id = u.principal_id

WHERE r.[type] = 'R'

AND u.name NOT IN (N'public', N'dbo', N'guest', N'INFORMATION_SCHEMA', N'sys')

AND u.name NOT LIKE N'##%##' -- hopefully you don't name users/roles this way!

AND r.is_fixed_role = 0;

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

-- script to change ownership of schemas to dbo

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  ALTER AUTHORIZATION ON SCHEMA::' + QUOTENAME(name) + N' TO dbo;

 END TRY

 BEGIN CATCH

  SELECT N''Schema ' + name + N' failed - run the script again.'',

    ERROR_MESSAGE(); END CATCH

'

FROM sys.schemas

WHERE [schema_id] BETWEEN 5 AND 16383

AND principal_id BETWEEN 5 AND 16383;

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

-- script to remove members from roles

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

' + CASE WHEN CONVERT(tinyint,

  PARSENAME(CONVERT(nvarchar(128),

  SERVERPROPERTY(N'ProductVersion')),4)) >= 11 -- 2012+

 THEN

  N'ALTER ROLE ' + QUOTENAME(r.name) + N' DROP MEMBER '

   + QUOTENAME(m.name) + N';'

 ELSE

  N'EXEC [sys].[sp_droprolemember] @rolename = N''' + r.name

    + ''', @membername = N''' + m.name + N''';'

 END + N'

 END TRY

 BEGIN CATCH

   SELECT N''Role ' + r.name + N' failed - run the script again.'',

     ERROR_MESSAGE();

 END CATCH

'

FROM sys.database_role_members AS rm

INNER JOIN sys.database_principals AS r

ON rm.role_principal_id = r.principal_id

AND r.is_fixed_role = 0

INNER JOIN sys.database_principals AS m

ON rm.member_principal_id = m.principal_id;

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

-- script to drop roles

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  DROP ROLE ' + QUOTENAME(r.name) + N';

 END TRY

 BEGIN CATCH

  SELECT N''Role ' + r.name + N' failed - run the script again.'',

     ERROR_MESSAGE();

 END CATCH

'

FROM sys.database_principals AS r

WHERE r.[type] = 'R'

AND r.name <> N'public'

AND r.is_fixed_role = 0;

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

-- script to drop users

USE [master];

GO

DECLARE @sql nvarchar(max) = N'';

 

SELECT @sql += N'BEGIN TRY

  DROP USER ' + QUOTENAME(u.name) + N';

 END TRY

 BEGIN CATCH

  SELECT N''User ' + u.name + N' failed - run the script again.'',

     ERROR_MESSAGE();

 END CATCH

'

FROM sys.database_principals AS u

WHERE u.[type] IN ('U','S')

AND u.name NOT IN (N'public', N'dbo', N'guest', N'INFORMATION_SCHEMA', N'sys')

AND u.name NOT LIKE N'##%##';

 

SELECT @sql;

--EXEC sys.sp_executesql @sql;

 

 

지금까지  master 데이터베이스를 정리하는 작업 순서, 스크립트 생성에 대해서 알아보았다. 객체를 삭제하는 명령은 자동화 있지만 모든것은 완벽히 분석할 수는 없다. (사용자 모듈을 시스템 모듈로 생성한 경우 ) 스크립트를 사용하더라도 생성된 스크립트를 반드시 DBA 검토를 해야한다. master 데이터베이스를 정리하는것은 위험한 작업이므로 반드시 많은 테스트와 검증이 필요하다.

 

[참고자료]

https://www.mssqltips.com/sqlservertip/4530/cleaning-up-the-sql-server-master-database/

 

 

2019-04-18 / Sungwook Kang / http://sungwookkang.com

 

SQL Server, SSMS, mssql, DBA, master database, sys.database_principals, sys.database_role_members, sys.foreign_keys, sys.objects, sys.partition_functions, sys.partition_schemes, sys.procedures, sys.schemas, sys.sequences,

SQL Server 프로토콜과 SQLCMD 사용한 연결

 

·       Version : SQL Server

 

SQL Server 연결하는 것은 이미 많은 문서를 통해서 알려져 있으며 GUI 제공하는 툴들이 많아서 어렵지 않게 느껴질 수가 있다. 하지만 상황에 따라 복잡해 있다. 만약 TCP 포트에서 수신대기를 하지 않는 SQL Server 인스턴스에는 어떻게 연결할까? 호스트 컴퓨터가 서버  DNS 확인할 없는 경우 어떻게  SQL Server 연결할까? 명명된 인스턴스는 무엇일까? 이번 포트스에서 SQLCMD 사용하여 다양한 프로토콜의 의미와 사용법을 알아 본다.

 

SQL Server 관리하는 다양한 툴들이 있다. 특히SSMS(SQL Server Management Studio) Microsoft에서 공식으로 제공하는 SQL Server관리툴이며GUI 제공한다. 그리고 SQLCMD라는 CLI 있다. SQLCMD 경우 명령줄로 이루어져 있다. SQL Server Linux 지원하면서 우리는 GUI 뿐만아니라 환경에도 익숙해져야 하기 때문에 SQLCMD 대해 다루어 본다.

 

SSMS 사용하여 SQL Server 인스턴스에 연결할  특정 프로토콜을 사용하기 위해서 Connection Properties 탭에서 프로토콜을 선택할 있다.

 

대부분 SQL Server 연결할 , 프로토콜을 지정하지 않으면 TCP/IP 사용한다고 생각할 있다. 하지만 프로토콜을 지정하지 않으면 TCP/IP 사용한다는 보장을 없다. SSMS SQLCMD에는 SQL Server 구성관리자의 프로토콜에 따라 순서가 결정된다.  아래 그림은 SQL Server 구성관리자의 프로토콜 구성이다. 구성관리자에는 32bit, 64bit  가지 클라이언트 구성이 있다. SSIS 사용하는 경우 32bit 커넥터를 사용할 있기 때문에 중요하다. 패키자가 64bit 버전에서 실행되고 64bit 설정을 하지 않을 경우 패키지가 실패한다.

 

[SQLCMD 사용한 SQL Server TCP/IP 연결]

TCP/IP 프로토콜을 사용하여 SQL Server 인스턴스에 연결할 , 인스턴스가 기본 포트가 아닌 경우 서버 IP 주소, 또는 호스트 이름과 포트가 필요하다. 아래 스크립트는 TCP/IP 사용하여 연결하는 일반적인 구문이다.

sqlcmd -S tcp:<computer name>,<port number>

 

IP 주소를 입력하면 TCP/IP 프로토콜을 사용할 것이라는 보장을 없다. TCP/IP 보장하기 위해서 tcp:라는 접두어를 사용하여 연결한다. 예를 들어 Windows 인증을 사용하는 SQL Server 기본 인스턴스를 사용하여 SQL-A라는 서버에 TCP/IP 프로토콜을 사용하여 연결하려는 경우 아래 구문을 이용한다.

sqlcmd –S tcp:SQL-A

sqlcmd –S tcp:SQL-A –U sa –P Pa$$w0rd

 

IP 사용할 경우 아래 구문을 이용한다.

sqlcmd –S tcp:10.10.10.10

sqlcmd –S tcp:10.10.10.10 –U sa –P Pa$$w0rd

 

명명된 인스턴스를  사용하는 경우 아래 구문을 이용한다.

sqlcmd –S tcp:SQL-A\TEST

sqlcmd –S tcp:SQL-A\TEST –U sa –P Pa$$w0rd

 

포트를 지정해야 경우 콤마(,) 구분하며 포트를 지정해야 한다. 예를 들어 기본 인스턴스가 1433 포트를 사용하고 TEST 인스턴스가 51613포트를 사용하는 경우 연결 문자열을 아래와 같다.

sqlcmd –S tcp:SQL-A,1433

sqlcmd –S tcp:SQL-A,1433 –U sa –P Pa$$w0rd

 

sqlcmd –S tcp:10.10.10.10,1433

sqlcmd –S tcp:10.10.10.10,1433 –U sa –P Pa$$w0rd

 

sqlcmd –S tcp:SQL-A,51613

sqlcmd –S tcp:SQL-A,51613 –U sa –P Pa$$w0rd

 

sqlcmd –S tcp:10.10.10.10,51613

sqlcmd –S tcp:10.10.10.10,51613 –U sa –P Pa$$w0rd

 

 

[SQLCMD SQL Server 명명된 파이트 연결]

명명된 파이프는 서버와 클라이언트 간의 통신을 위해 명명된, 단방향, 또는 이중 파이프이다. 내부적으로 명명된 파이프의 모든 인스턴스는 동일한 파이프 이름을 갖지만 메시지 기반 통신과 클라이언트 가장을 허용하는 자체 버퍼를 유지한다. 명명된 파이프는 프로세스간 통신(IPC) 의존한다. 명명된 파이프를 사용하여 로컬 인스턴스에 연결하면 해당 파이프가 커널 모드에서 로컬 프로시저 호출(LPC) 실행된다는 점에 유의한다. 일반적으로 프로토콜은 원격 SQL Server 인스턴스에 연결할때 TCP/IP 사용하는것이 바람직하기 때문에 사용되지 않는다. 반면 로컬 인스턴스에 연결할 공유 메모리는 종종 선택되어 사용된다. 아래는 기본 인스턴스의 명명된 파이트 연결구문이다.

\\[COMPUTER NAME OR IP ADDRESS]\pipe\sql\query

 

예를 들어 서버 MYSERVER 기본 인스턴스에 대한 명명된 파이프는 다음과 같다.

\\ MYSERVER \ pipe \ sql \ query

 

명명된 인스턴스의 구문은 다음과 같다.

\\[COMPUTER NAME OR IP ADDRESS]\pipe\MSSQL$[SQL Server Instance Name]\sql\query

 

예를 들어 서버 SQL-A에서  SQL Server 인스턴스 TEST 명명된 파이프를 사용하려는 경우 구문은 아래와 같다.

\\SQL-A\pipe\MSSQL$TEST\sql\query

 

명명된 파이프 프로토콜을 사용하여 SQL Server 인스턴스에 연결하는 것은 TCP/IP 사용하는것과 크게 다르지 않다. 연결문자열에np: 접두어를 사용한다. 아래 구문은 Windows SQL Server 인증을 사용하여 SQL Server 기본 인스턴스와 명명된 인스턴스에 연결한다.

sqlcmd –S np:\\SQL-A\pipe\sql\query

sqlcmd –S np:\\SQL-A\pipe\sql\query –U sa –P Pa$$w0rd

sqlcmd –S np:\\SQL-A\pipe\MSSQL$TEST\sql\query

sqlcmd –S np:\\SQL-A\pipe\MSSQL$TEST\sql\query –U sa –P Pa$$w0rd

 

 

[SQLCMD 사용한  SQL Server 공유 메모리 연결]

프로토콜은 클라이언트 연결이 로컬 서버에서 실행될 때만 사용될 있다. 기본적으로 커널 모드에서 실행되는 로컬 프로시저 호출(LPC)이다. MDAC 2.8 이하를 사용하는 경우 공유 메모리 프로토콜을 사용할 없다. 경우 sqlcmd 자동으로 명명된 파이프로 전환한다. 공유 메모리를 사용하는 접두사는 lpc: 이다.

sqlcmd –S lpc:SQL-A

sqlcmd –S lpc:SQL-A –U sa –P Pa$$w0rd

sqlcmd –S lpc:SQL-A\TEST

sqlcmd –S lpc:SQL-A\TEST –U sa –P Pa$$w0rd

 

 

[참고자료]

https://www.mssqltips.com/sqlservertip/5133/different-ways-to-connect-to-sql-server-using-sqlcmd/

 

 

2019-04-17 / Sungwook Kang / http://sungwookkang.com

 

SQL Server, SSMS, sqlcmd, named pipe, sql server protocol, sql connection, mssql

SQL Server Edition 다운그레이드 확인사항

 

·       Version : SQL Server

 

SQL Server Enterprise Edition 에서 SQL Server Standard Edition으로 다운 그레이드 확인해야 가지 사항에 대해서 알아본다. SQL Server Enterprise Edition SQL Server Standard Edition으로 다운그레이드 일부 구성이 기본값으로 다시 설정된다.

 

[SQL Server 오류 로그 ]

SQL Server 오류 로그 파일의 수가 기본 6개로 재설정된다. 설정을 확인하고 필요한 수로 설정한다. SSMS GUI 사용할 수도 있으며 T-SQL 코드를 사용할 있다.

 

USE [master]

GO

 

EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'NumErrorLogs', REG_DWORD, 99

GO

 

 

[SQL Agent 메일 프로필]

SQL Server Agent 속성의 메일 프로필이 비활성 된다. 메일 프로필 사용 올바른 메일 프로필이 사용되는지 확인 한다. SSMS에서 GUI 사용하거나T-SQL 코드를 사용할 있다.

 

USE [msdb]

GO

 

EXEC msdb.dbo.sp_set_sqlagent_properties @email_save_in_sent_folder=1,

      @databasemail_profile=N'DBServerAlerts_Profile', -- replace with your Agent's profile

      @use_databasemail=1

GO  

 

[토큰 교체 설정]

“Replace tokens for all jobs responses to alert” 옵션을 사용하는 경우 다시 활성화 해야 한다.    그림(SQL Agent 메일 프로필 그림) 처럼 SSMS GUI 사용하거나 T-SQL 코드를 사용할 있다.

USE [msdb]

GO

 

EXEC msdb.dbo.sp_set_sqlagent_properties @alert_replace_runtime_tokens=1

GO

 

[기타 잠재적 문제]

위의 설정은 레지스트리를 통해 구성되는 설정이며 시스템 데이터베이스는 저장되지 않는다.

·       SQL Server 2014 레지스트리 위치는 아래와 같다.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL12.MSSQLSERVER\SQLServerAgent

 

·       SQL Server 2017 레지스트리 위치는 아래와 같다.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL14.MSSQLSERVER\SQLServerAgent.

 

레지스트리 아래 저장되는SQL Server 에이전트는 다음과 같다.

·       AlertFailSafeEmailAddress

·       AlertFailSafeNetSendAddress

·       AlertFailSafeOperator

·       AlertFailSafePagerAddress

·       AlertNotificationMethod

·       DatabaseMailProfile

·       ErrorLogFile

·       ErrorLoggingLevel

·       IdleCPUDuration

·       IdleCPUPercent

·       JobHistoryMaxRows

·       JobHistoryMaxRowsPerJob

·       JobShutdownTimeout

·       MonitorAutoStart

·       RestartSQLServer

·       UseDatabaseMail

 

SQL Server Edition 다운그레이드 사항을 확인하여 시스템 운영에 참고 있도록 한다.

 

 

[참고자료]

https://www.mssqltips.com/sqlservertip/4698/sql-server-edition-postdowngrade-steps/

 

2019-04-11 / Sungwook Kang / http://sungwookkang.com

 

SQL Server, Edition post-down grade, Job Agent, Mail profile, Error log number, Token replacement setting, SQL registry key, MS SQL, SQL Edition

AlwaysOn 구성환경에서 Server Role 체크 Job Agent 실행 중지하기

 

·       Version : SQL Server

 

SQL Server에서 AlwaysOn 구성하였을때, Failover 대비하여 Secondary 서버에서도 Primary서버와 동일하게 계정, Job Agent 등이 구성되어 있어야 한다. 그런데 Secondary 경우 DB 동기화 되고 있는 대기 서버이기 때문에 Job Agent Primary 동일하게 설정하면 Secondary에서 Job 실행 Job Fail 발생한다. 또한 Secondary에서 일부 Job 경우 실행이 되지 말아야 것들이 있다. 아래 스크립트는 AlwaysOn role 확인하여 서버가 Primary 때만 Job Agent 수행되도록 한다. 각각의 Job Agent 번째 단계에 추가하여 Primary Role 아닐때 Job 수행을 중지하도록 한다.

DECLARE @SERVER_ROLE nvarchar(50)

 

SELECT

    @SERVER_ROLE = A.ROLE_DESC

FROM sys.dm_hadr_availability_replica_states AS A

    INNER JOIN sys.availability_groups AS B ON A.GROUP_ID = B.GROUP_ID

WHERE A.IS_LOCAL = 1

 

IF @SERVER_ROLE <> 'PRIMARY'

       EXEC msdb.dbo.sp_stop_job N'Job Name' ;

 

Secondary에서 Job 실행되었을때, Role 확인하는 부분이 있어 Job 중지된것을 확인할 있다.

 

[참고자료]

https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-stop-job-transact-sql?view=sql-server-2017

 

2019-04-10 / Sungwook Kang / http://sungwookkang.com

 

SQL Server, AlwaysOn,  Job Agent, sys.dm_hadr_availability_replica_states, AG Role check

Azure SQL Managed Instance 에서 SQL Agent Job history 기록 보관하기

 

·         Version : Azure SQL

 

Azure SQL Managed Instance Azure 클라우드에서 호스트되는 완전히 관리되는 SQL Server 이다. Managed Instance 많은 SQL Server기능을 제공하지만 가지 제약이 있다. 제약사항 하나는 SQL Agent작업 실행의 히스토리 기록을 변경할 없다는 것이다. 이번 포트스에서는 제약 사항을 다른 방법으로 우회하여 해결하는 방법 하나를 소개한다.

 

Managed Instance SQL Agent 작업당 100개의 히스토리를 기록하며,  1000개의  히스토리 기록을 유지할 있다. 아래 프로시저를 실행하면 jobhistory_max_rows_per_job열에서 정보를 확인할 있다.

exec msdb.dbo.sp_get_sqlagent_properties

 

Azure SQL Managed Instance SQL Agent 히스토리를 기록하는 정보 값을 기본 레지스트리 값에 저장하므로 SQL Agent등록 정보를 변경할 없다. 그래서 SQL Agent 작업 히스토리에 대한 보존 정책 (작업 100개의 히스토리 또는  전체 히스토리 최대 1000개의 기록) 고정시켯다. 일부 직업의 경우 오래된 작업 기록을 확인하지 있다. 따라서 이러한 작업 히스토리 정보를 장기적으로 또는 삭제되기 이전에 정보를 보존하려면 sysjobhistory 테이블에서 정보를 다른 테이블에 저장해야 한다.  아래 예제를 통해서 어떻게 저장하는지 살펴본다.

 

Azure SQL Managed Instance Job history 테이블의 변경사항(삭제, 업데이트) 기록하기 위한 테이블을 생성하고 기존 테이블에 변경 사항이 있을때 버전 배치작업을 사용하여 히스토리를 새로운 테이블을 기록한다. sysjobhistory 테이블의 변경 사항을 다른 테이블(sysjobhistoryall) 저장하도록 변환하려면 아래 스크립트를 적용한다.

ALTER TABLE [msdb].[dbo].[sysjobhistory]

ADD StartTime DATETIME2 NOT NULL DEFAULT ('19000101 00:00:00.0000000')

GO

 

ALTER TABLE [msdb].[dbo].[sysjobhistory]

ADD EndTime DATETIME2 NOT NULL DEFAULT ('99991231 23:59:59.9999999')

GO

 

ALTER TABLE [msdb].[dbo].[sysjobhistory]

ADD PERIOD FOR SYSTEM_TIME (StartTime, EndTime)

GO

 

ALTER TABLE [msdb].[dbo].[sysjobhistory]

ADD CONSTRAINT PK_sysjobhistory PRIMARY KEY (instance_id, job_id, step_id)

GO

 

ALTER TABLE [msdb].[dbo].[sysjobhistory]

SET(SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[sysjobhistoryall], DATA_CONSISTENCY_CHECK = ON, HISTORY_RETENTION_PERIOD = 1 MONTH))

GO

 

스크립트를 살펴보면 PK_sysjobhistory라는 기본 키가jobhistory 테이블에 추가되고 변경사항을  추적하는데 필요한 개의 열을 추가한 것을 있다. 작업이 완료되었으면 Managed Instance 기록 정리 작업을 실행하면 변경 사항이 보존되는지 여부를 테스트 있다. 아래 스크립트르 차례로 실행하여 시뮬레이션 있다.

EXEC msdb.dbo.sp_purge_jobhistory

 

기존 sysjobhistory 테이블에서 작업 기록이 삭제되면 sysjobhistoryall 테이블에서 삭제된 작업 기록 확인할 있다.

select * from msdb.dbo.sysjobhistoryall

 

 

 

2018-10-08 / Sungwook Kang / http://sqlmvp.kr

 

Azure SQL, Managed Instance, Azure SQL Agent, jobhistory, sysjobhistory, SQL Agent

Azure VM에서 SQL Server 대한 저장소 구성 지침

 

·         Version : Azure VM

 

Azure Virtual Machines 에서 SQL Server 사용할 경우 VM SQL Server 완벽하게 제어하고 SQL Server 작업 부하를 위한 가장 간단한 클라우드 마이그레이션 경로를 제공한다.

·         SQL Server on Virtual Machines :  https://azure.microsoft.com/en-us/services/virtual-machines/sql-server/

 

SQL IaaS Extension 기본적으로 Azure Market Place 통해 생성된 SQL VM 설치 된다. SQL IaaS Extension 백업 관리, 자동 보안 패치, 연결 구성, AKV 통합 최적화된 스토리지 구성 기능을 제공한다. Azure Market SQL Server 이미지는 작업 부하 유형에 따라 SQL Server 팀이 성능에 맞춰 조절한다. 그러나 SQL Server 업무상 중요한 서비스를 하는 경우 최적의 저장소 구성은 특정 작업 부하의 I/O 특성 요구 사항에 따라 다르다. 이번 포스트는 Azure IaaS에서 수행된 테스트를 기반으로 SQL Server 팀의 지침을 설명한다.

 

[프리미엄 저장소 적절한 크기의 VM 선택]

프리미엄 스토리지는 낮은 대기 시간과 일관된 높은 I/O 성능을 제공하도록 설계되었다. 캐시되지 않은 읽기는 평균 4ms 대기 시간으로 작동하고 캐시되지 않은 쓰기는 VM 평균 2ms 대기 시간 최대 80,000 IOPS 작동한다. 또한 예측할 없는 작업에서도 호스트의 SSD 기반 BLOB 캐시 기술은 대기 시간이 1ms 미만이며 최대 160,000 IOPS 제공한다. 따라서 일관된 I/O 성능과 높은 성능으로 인해 Azure VM에서 SQL Server 서비스는 프리미엄 스토리지를 사용할 것을 권장한다.

프리미엄 스토리지는VM 따라IOPS, 대역폭 연결할 있는 디스크 수에 대한 크기 제한 성능 사양이 있다. 예를 들어 표준 DS14_v2 VM크기는 64개의 프리미엄 디스크를 사용하여 최대 51,200 IOPS또는 768MBps 디스크 처리량을 제공하며 로컬 SSD 576GB BLOB 캐시를 사용하여 최대 64,000 IOPS또는 512MBps 처리량을 제공한다. 아래 링크에서 모든 VM 크기에 할당된 제한 리소스를 확인할 있다.

·         Memory optimized virtual machine sizes : https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sizes-memory

 

[읽기가 많은 작업 경우 읽기 전용 캐시를 사용하여 처리량을 향상]

프리미엄 스토리지를 지원하는 Azure VM 캐싱을 위해 가상 시스템 RAM 로컬SSD 조합을 사용하는 다중 계층 캐싱 기술을 사용한다. SQL 서버 작업 부하를 가져오는 읽기 전용 캐시 이득은 작업량 특성에 따라 다르다.

아래의 테스트 결과는  10-P30 디스크가 장착된 DS14_v2 VM에서 최대 50,000 IOPS 사용하는 OLTP 작업 부하로 테스트했다. 데이터 로그 파일은 테스트를 위해 10-P30 디스크에서 동일한 저장 영역 풀에 있다.

읽기 80%, 쓰기 20%, 약간의 tempdb 사용하는 OLTP 작업 부하의 경우 캐시가 없는 동일한 구성에 비해 모든 프리미엄 디스크에 대해 읽기 전용 캐시가 활성화 되었을 처리량이 42% 증가했다. 읽기 50%, 쓰기 50%, 약간의 tempdb 사용하는 OLTP 작업 부하의 경우 캐시가 없는 구성에 비해 읽기 전용 캐시의 처리량이 25% 증가했다.

 

[데이터 로그 파일의 분리]

데이터 로그 파일을 분리한 별도의 저장 영역을 사용하여 읽기 전용 캐시의 이득을 최적화 하는 것이 좋다. 캐시가 없는 프리미엄 스토리지 디스크에 로그파일을 호스팅 하면 읽기 작업에 대해서는 호스트 BLOB 캐시의 사용 가능한 공간과 처리량이 절약되어 이점을 극대화 한다. 특히  SQL Server 데이터 파일을 호스팅하는 저장소에 포함될 프리미엄 저장소 디스크에는 읽기 전용 캐시를 사용하는 것이 좋다. 예를 들어 데이터 파일에 IOPS 요구사항이 30,000 이라면 모든 디스크에 RO 캐시를 활성화 6 - P30 디스크 또는 4 - P50 디스크를 스트라이핑 하는것이 좋다.

 

로그 파일을 호스팅 하는 저장소에 포함될 프리미엄 저장소 디스크는 읽기전용 캐시를 구성하지 않는것이 좋다. 또한 로그 파일의 디스크 처리량 요구 사항이 7500 IOPS 미만인 경우 단일 P50 디스크로 IOPS 달성할 있으므로 로그 파일에 대한 저장소 풀이 필요하지 않다. 특히 작은 호스트 BLOB 캐시가 있는 VM에서SQL Server 작업의 부하가 경우 로그 파일과 데이터 파일을  분리하였을때 테스트에서 상당한 처리량 증가를 보였다. 평균적인 예로 데이터를 호스팅하는것과 비교하여 RO 캐시가 있는 별도의 스토리지 풀에서 호스트되는 캐시 데이터 파일이 없는 스토리지 풀에서 로그 파일을 호스팅할  쓰기 50% 비율의 OLTP 테스트 경우 처리량이 35%  증가했다.  

 

[올바른 크기의 VM 선택  로컬 SSD tempdb 배치]

VM 크기에 따라 로컬 SSD BLOB 캐시에 대해 최대 대역폭, 최대 처리량 크기 제한이 가능한 스토리지 용량을 정의한다. Tempdb 사용량이 높은 업무용 SQL Server 경우 로컬 SSD temp db 호스팅하면 작업 부하 성능과 처리량에 영향을 준다. VM 로컬 SSD 대한 크기, 처리량 대역폭 제한이 tempdb IO 요구 사항을 허용해야 한다. 그리고  VM 호스트 BLOB 캐시 스케일 한계는 로컬 SSD 읽기 호스트 BLOB 캐시 읽기가 발생하는 읽기 활동을 허용할 만큼 충분히 커야 한다.

최대 로컬 디스크 + SSD 캐시 성능한계는 로컬 SSD 읽기 쓰기 작업 SSD 캐시의 읽기 작업에 대한 최대 작업을 정의한다. tempdb 로컬 SSD 디스크에 배치되고 SQL Server 데이터 파일을 호스팅하는 프리미엄 디스크에 RO 캐시가 사용되는 경우 tempdb에서 구동되는 읽기 + 쓰기 IOPS RO 캐시의 읽기 IOPS 공유된다. 예를들어 표준 DS14_v2 임시 디스크의 크기가 224GB이고 호스트 캐시가 576GB이며 로컬 캐시의 tempdb 활동과 함께 읽기 캐시에 사용할 있는 최대 처리량은 64,000 IOPS 524MB/s이다.

 

Storage Optimized L-Series 또는 Memory Optimized M-Series 로컬 SSD 최대 14TB이고 메모리가 최대 4TB 이다. 이러한 서버는 고부하의 SQL Server 작업을 처리하는데 가장 적합하다. 적절한 VM 크기 선택 구성으로 Azure VM SQL Server 작업 부하에 대해 미션 크리티컬 성능을 얻을 있다.

 

[참고자료]

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2018/09/25/storage-configuration-guidelines-for-sql-server-on-azure-vm/

 

 

2018-10-02 / Sungwook Kang / http://sqlmvp.kr

 

Azure SQL, Azure VM, BLOB Storage, local SSD, local cache, SQL Server On Azure VM, SQL Server on Azure

Azure SQL 도입된 새로운 집계 함수 APPROX_COUNT_DISTINCT

 

·         Version : Azure SQL

 

다양한 서비스를 운영하다보면 서비스에 따른 중요 포인트가 조금씩 다른 경우가 있다. 예를들어 정확한 숫자(결과값) 중요시하는 서비스(일일 매출, 재고량 ) 있으며, 빠른 응답을  (실시간 데이터 사용량, DB 전체 데이터 ROW 등의 대시보드 ) 중요시하는 서비스가 있다. 물론 응답성을 중시한다고 해서 데이터가 틀리면 안되겠지만 허용되는 오차범위에서 최대한 빠른 응답이 필요할 때가 있다.  이번 포스트에서는 매우 데이터 세트 전체에 대한 집계에 대해 빠른 응답을 수있는 Azure SQL APPROX_COUNT_DISTINCT 대해서 다루어 본다.

 

Azure SQL 새로운 집계함수인 APPROX_COUNT_DISTINCT NULL 아닌 고유한 값이 있는 그룹의 대략적인 수를 반환한다. 함수는 데이터 시나리오에 사용하도록 설계되었으며 아래 조건에 최적화 되어 있다.

·         수백만행 이상의 데이터 세트에 대한 액세스

·         많은 수의 고유한 값을 가진 열의 집계

이러한 조건을 가정하면 대부분의 작업에서 정확도 오차는 2%이내가 된다. 또한  APPROX_COUNT_DISTINCT 정확도 오차는 COUNT DISTINCT에서 20%이상 되어서는 안된다.

 

APPROX_COUNT_DISTINCT 고유 카운트를 계산할때, COUNT DISTINCT 보다 훨씬 적은 메모리를 사용한다. 이러한 이유 때문에 데이터 집합에 수십억개의 행이 있더라도 디스크를 사용하지  않고 메모리에서 계산을 수행할 가능성이 훨씬 크다. 일반적으로 COUNT DISTINCT 경우 메모리가 부족하면 tempdb 사용하므로 성능 저하를 초래(일반적으로 디스크가 가장 느리기 때문)한다.  APPROX_COUNT_DISTINCT tempdb 사용하지 않고 내부적 알고리즘을 사용하므로 결과적으로 APPROX_COUNT_DISTINCT COUNT DISTINCT보다 훨씬 빠르게 실행된다.

 

아래 그림은 일반적인 COUNT(DISTINCT ()) 사용한 것과 APPROX_COUNT_DISTINCT 사용한 경우이다.

[DISTINCT COUNT]

 


[APPROX_COUNT_DISTINCT]

 


COUNT DISTINCT 경우 정렬 연산자가 추가되어 있으므며 APPROX_COUNT_DISTINCT 경우 Hash match 대한 Stream Aggregate 대체 된다는 것을 있다. 또한 계획은 모두 동일한 클러스터된 인덱스 검색을 사용하며 COUNT DISTINCT 경우 검색에 95% 비용을 사용하지만, APPROX_COUNT_DISTINCT 경우99% 검색에 사용되는 것을 있다. 뜻은 전통적인 COUNT DISTINCT 작업은 외부의 스캔 처리 작업이 많다는 것을 의미한다.

 

APPROX_COUNT_DISTINCT 사용할 경우 실행 계획에 아래와 같은 연산자를 확인할 있다.

<ScalarOperator ScalarString=”APPROX_COUNT_DISTINCT_CONVERT([globalagg1004])”>

 

 

[참고자료]

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2018/07/16/approximate-count-distinct-enters-public-preview-in-azure-sql-database/

 

 

2018-09-10 / Sungwook Kang / http://sqlmvp.kr

 

Azure SQL,  count distinct, APPROX_COUNT_DISTINCT

Azure SQL 데이터베이스 소유권 체인

 

·         Version : Azure SQL

 

Azure SQL Database Managed Instance에서는 SQL Server 동일한 방식으로 데이터베이스간 쿼리를 실행할 있다. 또한 데이터베이스간 소유권 체인을 지원한다. 이번 포스트에서는 Managed Instance 소유권 체인에 대해서 다루어 본다.

 

데이터베이스간의 소유권 체인을 사용하면 명시적 권한이 객체에 부여되지 않더라도SQL 인스턴스의 다른 데이터베이스에 있는 객체에 액세스 있다. DB_CHAINING옵션이 데이터베이스에서 설정되어 있는 경우 동일한 소유자로 다른 데이터베이스에 액세스할 있다.  

DB_CHAINING is disabled by default on new databases because you need to be aware what it exactly do and does it violates some security policy in your system before you explicitly enable it.

 

DB_CHAINING 시스템의 일부 보안 정책을 위반하므로 기본적으로 비활성화 되어있다.

 

여러 데이터베이스의 여러 오브젝트에서 동일한 소유자가 있고 오브젝트에 액세스하는 스토어드 프로시저가 있는 경우 프로시저가 액세스해야하는 모든 오브젝트에 대한 액세스 권한을 GRANT 필요가 없다. 프로시저와 오브젝트의 소유자가 동일한 경우 프로시저에 대한 GRANT 권한을 부여할 있으며 데이터베이스 엔진은 프로시저가 동일한 소유자를 공유하는 다른 모든 개체에 액세스 있게 한다.

아래 예제에서는 데이터에 액세스하는데 사용되는 동일한 소유자 로그인이 있는 개의 데이터베이스를 생성한다. PrimaryDatabase DataTable 이라는 테이블을 가지고 있으며,  SecondaryDatabase PrimaryDatabase DataTable 에서 데이터를 읽는 저장프로시저를 갖는다. 저장 프로시저를 실행하기 위해 로그인이 부여되었지만 테이블 데이터를 읽지는 못하였다.

-- Create two databases and a login that will call procedure in one database

CREATE DATABASE PrimaryDatabase;

GO

CREATE DATABASE SecondaryDatabase;

GO

CREATE LOGIN TheLogin WITH PASSWORD = 'Very strong password!'

GO

 

-- Create one database with some data table,

-- and another database with a procedure that access the data table.

USE PrimaryDatabase;

GO

CREATE PROC dbo.AccessDataTable

AS

BEGIN

SELECT COUNT(*) FROM SecondaryDatabase.dbo.DataTable;

END;

GO

CREATE USER TheUser FOR LOGIN TheLogin;

GO

GRANT EXECUTE ON dbo.AccessDataTable TO TheUser;

GO

 

USE SecondaryDatabase;

GO

SELECT * INTO dbo.DataTable FROM sys.objects;

GO

CREATE USER TheUser FOR LOGIN TheLogin;

GO

 

저장 프로시저를 사용하여 데이터를 읽으려고 하면 로그인에 테이블에 대한 GRANT 권한이 없기 때문에 오류가 발생한다.

EXECUTE('SELECT * FROM SecondaryDatabase.dbo.DataTable') AS LOGIN = 'TheLogin' ;

GO

-- Msg 229, Level 14, State 5, Line 34

-- The SELECT permission was denied on the object 'DataTable', database 'SecondaryDatabase', schema 'dbo'.

 

저장 프로시저를 사용하여 테이블에서 데이터를 읽으려고 하는 경우에도 동일한 문제가 발생한다.

EXECUTE('EXEC PrimaryDatabase.dbo.AccessDataTable') AS LOGIN = 'TheLogin' ;

GO

--Msg 229, Level 14, State 5, Procedure dbo.AccessDataTable, Line 5 [Batch Start Line 65]

--The SELECT permission was denied on the object 'DataTable', database 'SecondaryDatabase', schema 'dbo'.

 

사용자에게 프로시저를 실행할 있는 권한은 있지만 데이터베이스 엔진은 해당 로그인에 대해 SecondaryDatabase 테이블에서 읽을 있는 액세스 권한이 없으므로 쿼리를 차단한다. 아래 스크립트는 데이터베이스 소유권 체인을 활성화 한다.

ALTER DATABASE PrimaryDatabase SET DB_CHAINING ON;

GO

ALTER DATABASE SecondaryDatabase SET DB_CHAINING ON;

GO

 

저장 프로시저를 통해 테이블에 다시 액세스하면 결과가 표시된다.

EXECUTE('EXEC PrimaryDatabase.dbo.AccessDataTable') AS LOGIN = 'TheLogin' ;

 

Managed Instance / Database Engine 저장 프로시저와 테이블이 동일한 소유자를 가지며 DB_CHAINING 켜져있기 때문에 테이블에 대한 액세스를 허용한다. 그러나 해당 로그인에 대해 직접적인 테이블에 액세스하는 권한은 없으므로 직접 테이블에 액세스 수는 없다.

EXECUTE('SELECT * FROM SecondaryDatabase.dbo.DataTable') AS LOGIN = 'TheLogin' ;

GO

--Msg 229, Level 14, State 5, Line 54

--The SELECT permission was denied on the object 'DataTable', database 'SecondaryDatabase', schema 'dbo'.

 

데이터베이스 소유권 체인이 유용할 뿐만 아니라 보안 관점에서 예기치 않은 동작이 발생할 있으므로 해당 옵션을 활성화 할때는 신중함이 필요하다.

 

[참고자료]

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2018/08/17/database-ownership-chaining-in-azure-sql-managed-instance/

 

 

 

2018-09-06 / Sungwook Kang / http://sqlmvp.kr

 

Azure SQL,  Managed Instance, SQL, Azure SQL Database, SQL Server Security, DB_CHAINING


Azure SQL 내부 디스크 할당 방법   저장 가능 용량 확인

 

·         Version : Azure SQL

 

Azure SQL Database Managed Instance 모든 데이터베이스 파일을 Azure Storage 보관하는 Azure Cloud SQL Server 서비스 이다. 이번 포스트에서는 Managed Instance 스토리지 계층에 디스크를 할당하는 방법과 최대 사용한 가능한 디스크 공간을 확인하는 방법에 대해서 살펴본다.

 

 Azure SQL Database Managed Instance 일반적인 목적의  스토리지 계층과 데이터베이스 파일이 위치하는 Azure Premium 스토리지 계층을 가지고 있다. Managed Instance 모든 파일에 대해 미리 정의된 크기의 Azure disk(128GB, 256GB, 512GB ) 사용한다. 모든 파일은 현재 파일의 크기를 포함할 있는 가장 작은 단일 디스크에 저장된다. 이는 모든 Managed Instance 최대 35TB 내부 저장장치를 가지고 있기 때문에 중요하다. Managed Instance 프로비저닝하면 가지 스토리지 제한이 있다.

1.       Managed Instance 사용자 저장소는 사용자가 포털에서 선택한 managed instance 저장소 이며 저장소 크기에 따라 비용을 지불한다.

2.       Azure Premium disk 내부에서 물리적으로 공간을 할당할 35TB 초과 없다. 결과적으로 GP 인스턴스에서  가장 작은 디스크(128GB) 위치한 280개의 파일이 35TB 한도에 도달하므로 GP인스턴스에 280 넘는 파을을 저장할 없다.

 

데이터베이스 파일을 만들때,  Azure Premium Disk 사용하는데 파일크기가 경우 파일의 내부 조각화가 발생할 있다. Azure Premium Disk 고정된 디스크 크기 집합을 제공하므로 Managed Instance 데이터베이스 파일을 일치하는 디스크에 저장하도록 구현된다. 할당된 디스크 크기의 합은 35TB 초과할 없다. 만약 한계에 도달하면 에러가 발생할 있다. 아래 스크립트는 저장용량 한도에 도달했는지 확인할 있다.  sys.master_files뷰를 래핑하고 모든 파일에 할당된 디스크 크기를 반환하는 표준 스키마뷰를 만든다.

CREATE SCHEMA mi;

GO

CREATE OR ALTER VIEW mi.master_files

AS

WITH mi_master_files AS

( SELECT *, size_gb = CAST(size * 8. / 1024 / 1024 AS decimal(12,4))

FROM sys.master_files )

SELECT *, azure_disk_size_gb = IIF(

database_id <> 2,

CASE WHEN size_gb <= 128 THEN 128

WHEN size_gb > 128 AND size_gb <= 256 THEN 256

WHEN size_gb > 256 AND size_gb <= 512 THEN 512

WHEN size_gb > 512 AND size_gb <= 1024 THEN 1024

WHEN size_gb > 1024 AND size_gb <= 2048 THEN 2048

WHEN size_gb > 2048 AND size_gb <= 4096 THEN 4096

ELSE 8192

END, NULL)

FROM mi_master_files;

GO

 

이제 Azure Premium disk 할당된 모든 데이터베이스 파일에 대한 크기를 있다.

SELECT db = db_name(database_id), name, size_gb, azure_disk_size_gb

from mi.master_files;

 

Azure disk 크기의 합은 35TB 초과해서는 안된다. 그렇지 않으면 Azure disk 오류가 발생한다. 다음 쿼리를 사용하여 할당된 저장 공간을 확인할 있다.

SELECT storage_size_tb = SUM(azure_disk_size_gb) /1024.

FROM mi.master_files

 

아래 스크립트는 새로운 파일이 128GB보다 작은것으로 가정하고 Managed Instance 추가할 있는 파일 수를 확인한다. 결과값이 0 경우 (또는 0 근사할 수록) 파일을 할당할 없으므로 새로운 계획이 필요 하다.

SELECT remaining_number_of_128gb_files =

(35 - ROUND(SUM(azure_disk_size_gb) /1024,0)) * 8

FROM mi.master_files

 

 

[참고자료]

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2018/08/20/reaching-azure-disk-storage-limit-on-general-purpose-azure-sql-database-managed-instance/

 

 

2018-09-05 / Sungwook Kang / http://sqlmvp.kr

 

Azure SQL,  Managed Instance, SQL, Azure disk, database file. Azure storage

Azure SQL에서 이메일 보내기

 

·         Version : Azure SQL

 

Azure SQL Database Managed Instance Azure 클라우드에서 대부분의 SQL Server 기능을 사용할 있다. 이번 포스트에서는 Managed Instance에서 메일 프로필을 설정하고 이메일을 보내는 방법에 대해서 알아본다.

 

먼저 이메일 서버에 액세스하는데 필요한 로그인/암호 정보와 함께 실제 이메일을 보낼 전자 메일 서버의 주소가 포함된 이메일 계정 정보를 설정한다. 아래 스크립트를 참고하여 설정을 사용자에게 맞게 변경 실행 한다.

-- Create a Database Mail account

EXECUTE msdb.dbo.sysmail_add_account_sp

    @account_name = '...',

    @description = '...',

    @email_address = '$(loginEmail)',

    @display_name = '...',

    @mailserver_name = '$(mailserver)' ,

    @username = '$(loginEmail)' , 

    @password = '$(password)'

 

-- Create a Database Mail profile

EXECUTE msdb.dbo.sysmail_add_profile_sp

    @profile_name = 'AzureManagedInstance_dbmail_profile',

    @description = '...' ;

 

-- Add the account to the profile

EXECUTE msdb.dbo.sysmail_add_profileaccount_sp

    @profile_name = 'AzureManagedInstance_dbmail_profile',

    @account_name = '...',

    @sequence_number = 1;

 

여기서 중요한것은 하나의 이메일 프로필만 만들 있으며 프로필명은 반드시 AzureManagedInstance_dbmail_Profile 이라고 해야 한다. 이렇게 하지 않으면 Managed Instance 프로필을 사용하여 이메일을 보낼 없다.

 

이메일 서버 설정이 완료되었으면 sp_configure에서 Database Mail XPs 옵션을 활성화 한다.

EXEC sp_configure 'show advanced options', 1; 

GO 

RECONFIGURE; 

GO 

EXEC sp_configure 'Database Mail XPs', 1; 

GO 

RECONFIGURE 

GO

 

이제 sp_send_dbmail sp_notify_operator 프로시저를 사용하여 전자 메일을 보내 구성을 테스트할 있다. 전자 메일을 보내는 방법은 아래 스크립트를 참고 한다.

DECLARE @body VARCHAR(4000) = 'The email is sent with msdb.dbo.sp_send_dbmail from ' + @@SERVERNAME;

EXEC msdb.dbo.sp_send_dbmail

        @profile_name = 'AzureManagedInstance_dbmail_profile',

        @recipients = '$(email)',

        @body = @body,

        @subject = 'Azure SQL Instance - test email' ;

 

운영자에게 이메일을 보내기 위해서는 운영자 정보를 추가해야 한다. 아래 스크립트를 참고하여 운영자 이메일을 설정하여 그룹을 생성한다.

EXEC msdb.dbo.sp_add_operator

          @name = N'SQL DevOp',

          @enabled = 1,

          @email_address = N'$(email)',

          @weekday_pager_start_time = 080000,

          @weekday_pager_end_time = 170000,

          @pager_days = 62 ;

 

운영자 등록이 완료되었으며 아래 스크립트를 참고하여 설정이 정상적으로 되었는지 운영자에게 이메일 알림을 전송해본다.

DECLARE @body VARCHAR(4000) = 'The email is sent using sp_notify_operator from ' + @@SERVERNAME;

EXEC msdb.dbo.sp_notify_operator

              @profile_name = N'AzureManagedInstance_dbmail_profile',

              @name = N'SQL DevOp',

              @subject = N'Azure SQL Instance - Test Notification',

              @body = @body;

 

아래 예시는 Managed Instance에서 작업이 성공하거나 실패할 이메을 통해 운영자에게 알릴 있다.

EXEC msdb.dbo.sp_update_job

              @job_name=N'My job name',

              @notify_level_email=2,

              @notify_level_page=2,

              @notify_email_operator_name=N'SQL DevOp'

 

 

[참고자료]

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2018/08/31/sending-emails-in-azure-sql-managed-instance/

 

 

2018-09-04 / Sungwook Kang / http://sqlmvp.kr

 

Azure SQL,  Managed Instance, SQL, database mail, XPs Mail, sp_send_dbmail, sp_notify_operator


Azure SQL에서 읽기 전용 복제본에 대한 접속 링크 서버 생성

 

·         Version : Azure SQL

 

Azure SQL Database Managed Instance Azure 클라우드에서 호스트 되는 완벽하게 관리되는 SQL Server 데이터베이스 엔진으로 Azure SQL Pass SQL Server 기능(: 링크된 서버 기본 제공되는 무료 보조 읽기 전용 복제본) 제공한다. 이번 포스트에서는 연결된 서버(링크드 서버) 사용하여 읽을 있는 보조 복제본을 연결하는 방법을 살펴 본다.

 

Managed Instance Business Critical Tier 모든 데이터베이스에 대해서 리포트 목적의 용도로 사용할 있는 읽기 전용 상태의 무료 읽기 전용 복제본을 제공한다. 아래 그림에는 하나의 기본 인스턴스와   개의 읽기 전용 서버로 구성된다. 서버는 읽기 전용 끝점을 이용해서 연결된다.


어플리케이션에서 보조 복제본에 접속하는 방법은 기본 인스턴스에 액세스하는 사용하는 것과 동일한 연결 문자열을 사용한다. 다만 연결 문자열에ApplicationIntent=ReadOnly 추가하면 된다.

기본 인스턴스에서 보조 인스턴스로 연결된 서버를 만드는 경우 T-SQL 사용하여 기본 인스턴스에서 보조 인스턴스로 연결할 있다. 아래 스크립트는 보조 복제본에 연결된 서버를 만든다.

EXEC sp_addlinkedserver

@server=N'SECONDARY',

@srvproduct=N'',

@provider=N'SQLNCLI',

@provstr = N'ApplicationIntent=ReadOnly',

@datasrc= @@SERVERNAME;

 

@@SERVERNAME Managed Instance 정규화된 도메인 이름을 반환하고 동일한 이름은 보조 인스턴스에 액세스하는데 사용된다. 추가해야하는 유일한 것은 공급자 문자열에서 ApplicationIntent=ReadOnly 이다

 

읽기 보조 복제본 서버로 연결된 서버를 생성하면 T-SQL 사용하여 읽기 전용 인스턴스를 쿼리 있다. 예를 들어 아래 쿼리를 사용하여 보조 데이터베이스가 읽기 전용인지 확인할 있다.

SELECT *

FROM OPENQUERY([SECONDARY],

'SELECT DATABASEPROPERTYEX (''master'', ''Updateability'' ) ')

 

또한 4 part name 구문을 사용하여 Secondary 서버의 테이블이나 뷰를 쿼리할 있다.

SELECT *

FROM SECONDARY.master.sys.databases;

 

 

[참고자료]

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2018/08/27/create-linked-server-to-readable-secondary-replica-in-managed-instance-business-critical-service-tier/

 

 

 

2018-08-30 / Sungwook Kang / http://sqlmvp.kr

 

Azure SQL,  Managed Instance, SQL, read only, High Availability, Linked server, Read-only replica

Azure SQL에서 네트워크를 구성하는 방법

 

·         Version : Azure SQL

 

Azure SQL Managed Instance Azure 클라우드에서 호스팅되고 Azure 가상 네트워크를 사용하는 완전히 관리되는 SQL Server 인스턴스이다. Managed Instance 가상 네트워크를 사용하기 때문에  Azure 관리 서비스에서 사용자가 네트워크 액세스 규칙을 정의할 있다. 잘못된 네트워크 구성은  Managed Instance  배포할때  오류가 발생하므로 아래 순서에 따라서 네트워크 환경을 구성하여 사용한다. 일반적으로 인스턴스용 가상 네트워크를 구성할 기본 5가지 단계로 정리할 있다.

1.       Managed Instance 사용할 가상 네트워크를 구성

2.       Managed Instance Azure 관리 서비스와 통신할 있도록Route 테이블 생성

3.       가상 네트워크 생성시 생성된 기본 서브넷을 사용하거나Managed Instance 전용 서브넷을생성

4.       서브넷에 Route 테이블 할당

5.       4단계 까지 올바르게 구성되었는지 확인

 

[1. 가성 네트워크 구성]

Managed Instance 사설 IP주소가 할당된 Azure Virtual network 사용한다. 가상 네트워크는 Managed Instance 만들기 전에 생성해야 한다. 가상 네트워크는 Azure Portal, PowerShell, Azure CLI등을 이용하여 생성할 있다. Azure Portal 사용하여 가상 네트워크를 생성할때 Resource Manager 배포 모델을 사용해야 한다. 클래식 가상 네트워크 모델은 지원되지 않는다. 가상 네트워크를 생성할 Virtual Network Blade Service Endpoint 옵션이 Disable 설정되어 생성 되는데 는기본 옵션이므로 변경하지 않는 것을 권장한다.

 

Managed Instance 서브넷은 16 ~ 256개의 주소를 사용할 있다. 따라서 기본 서브넷의IP범위를 정의할 서브넷 마스크 /28 - /24 범위를 사용한다. 많은 인스턴스가 있을 경우 기본 서브넷에 (인스턴스 X 2) + 5개의 주소가 있는지 확인한다.

 

[2. Route 테이블 생성]

가성 네트워크 생성이 완료 되었으면 Managed Instance Azure 관리 서비스와 통신할 있도록 Route 테이블을 구성해야한다. Route 테이블은 Managed Instance 배포된 서브넷에 연결되며 서브넷을 통해서 나가는 패킷은 연결된 Route 테이블 기반으로 처리된다. 서브넷은 단일 Route 테이블에만 연결할 있다. (Azure에서 Route 테이블을 만드는데는 비용이 부과 되지 않는다.)

Azure Portal에서 ‘Route table’이라는 리소스를 추가하고 Route 규칙에 “0.0.0.0/0 Next Hop Internet” 경로를 추가한다. 경로를 사용하면 가상 네트워크에 있는 Managed Instance 인스턴스를 관리하는 Azure 관리 서비스와 통신할 있다. 이렇게 하지 않으면 Managed Instance 배포 없다.

 

[3. 추가 서브넷 생성(선택사항)]

Managed Instance 서브넷에 배포되므로 Managed Instance 배포하기 전에 서브넷을 생성해야 한다. 기본 서브넷에 인스턴스를 배포하고 기본 경로를 사용하는경우 단계를 건너 있다. 서브넷은 Managed Instance 전용이며 다른 리소스를 포함할 없다. 서브넷에는 최소한 16개의 주소가 있어야 하고 5개의 주소는 Azure 내부 서비스용으로 예약되어 있다. 서브넷에 포함되는  모든 Managed Instance 에는 인스턴스당 2개의 주소가 필요하다. 서브넷에 Managed Instance 배치하면 서브넷의 크기를 변경할 없으므로 주의한다.

 

[4.서브넷 구성]

서브넷(기본값 1 또는 신규)에는 “0.0.0.0/0 Next Hop Internet” 정보를 포함한 User Route Table(UDR) 있어야 한다. “0.0.0.0/0 Next Hop Internet” Route 테이블을 생성하면 Managed Instance 서브넷에 할당 있다. Azure Portal에서 서브넷을 정보를 확인할 있으며 아래 항목을 확인한다.

1.       서브넷에 할당된 Managed Instance Route 테이블 있다.

2.       서브넷에는 네트워크 보안그룹이 없어야 한다.

3.       서브넷에는 서비스 엔드 포인트가 없어야 한다.

4.       서브넷에는 다른 리소스가 없다.

 

[5. 체크리스트]

4단계 까지