기준 region : Seoul

 

Aurora Mysql
인스턴스 타입 : db.r5.2xlarge , 8vcore/64GB Ram , $1024.8/month  (이중화시 $2049.6/month )
스토리지 비용 : $120/TB/month
I/O 비용 : $0.24/1백만I/O (300 IOPS 1시간 = 108만개 , 1개월 비용 = $189.73)
백업 스토리지 비용 : $23/TB/month (100GB 14일 = 1.4TB)
RI 비용 : 1년 no upfront $680.76/month , Partial upfront $581.208/month , all upfront $569.496/month

Azure SQL

인스턴스 타입 : SQL Ent 8vcore/41GB Ram , $4199/month (3중화) , Azure 하이브리드+라이선스비용 = $2245/month

스토리지 비용 : $295/TB/month 

백업 스토리지 비용 : RA-GRS $236/TB/month (100GB 14일 = 1.4TB) 

 

 

 

클라우드 벤더들의 자사 주력 PaaS RDBMS 제품은 가격을 비슷하게 맞추는듯.

부수적인 내용이 약간씩 차이 나지만 실제로 굴려보면 크게 비용차이가 안날듯 하고, 

부하 패턴이나 사용 방식에 따라 비용차이가 날 것으로 생각 됨.

 

AND

 

-- 테스트 데이터 생성

--drop table t1
create table t1 (col1 int, col2 varchar(max))

insert into t1 with(tablock)
select top 10000 ROW_NUMBER() over (order by (select 1)) col1, replicate(convert(varchar(max), 'abcdefghijklmnopqrstuvwxyz'), 1000) col2
from sysindexes a, sysindexes a1, sysindexes a2

sp_spaceused t1
t1 10000                275152 KB 275072 KB 8 KB 72 KB

create clustered index cl_t1 on t1 (col1) with(data_compression = page)
sp_spaceused t1
t1 10000                275536 KB 275072 KB 80 KB 384 KB

 

-- > 페이지 압축을 통해서는 압축이 거의 되지 않음 (data_compression = row 압축도 동일함)

 

 

drop index t1.cl_t1
sp_spaceused t1
t1 10000                275472 KB 275072 KB 72 KB 328 KB

create clustered columnstore index cci_t1 on t1
sp_spaceused t1
t1 10000                4104 KB 4032 KB 0 KB 72 KB

 

-- > CCI로는 압축이 됨

 

 

AND


openrowset 수행을 위한 옵션 변경

EXEC sp_configure 'Show Advanced', 1

RECONFIGURE

sp_configure 'Ad Hoc Distributed Queries', 1

RECONFIGURE


openrowset을 이용해 엑셀파일을 읽어보면 오류가 발생한다

SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0'

,'Excel 12.0;Database=D:\temp\MyFile.xlsx;HDR=YES'

,'select * from [Sheet1$]')


메시지 7403, 수준 16, 상태 1, 5

OLE DB 공급자 "Microsoft.ACE.OLEDB.12.0"() 등록하지 않았습니다.


엑셀파일을 읽어들이는 ACE OLEDB 12 공급자가 없어서 그렇다.

아래에서 "Microsoft Access Database Engine 2010 Redistributable"을 다운받아 설치한다. 

난 64bit SQL Server라 64bit를 설치 했다.

https://www.microsoft.com/en-us/download/details.aspx?id=13255


이제 xp_enum_oledb_providers 를 수행하면 다음과 같이 ACE OLEDB 12 공급자가 등록된 것을 확인할 수 있다.


이제 다시한번 openrowset을 이용해 엑셀파일을 읽어보면 정상적으로 읽어지는것을 확인할 수 있다.

마지막 줄의 Sheet1은 엑셀의 시트 이름이며, 뒤에 $를 꼭 붙여야만 한다.

SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0'

,'Excel 12.0;Database=D:\temp\MyFile.xlsx;HDR=YES'

,'select * from [Sheet1$]')



넥슨 게임DB팀 하만철

AND

SQL Server의 mdf, ldf 파일에서 장애 발생시 대처할 수 있는 시나리오를 테스트 해 봤습니다.

 

  1. mdf 파일 장애 발생
    전체 복구모델을 사용하고 5분 주기로 트랜잭션 로그 백업을 수행하고 있다고 가정하겠습니다.
    mdf 파일에 장애 발생시 마지막 트랜잭션 로그 백업 이후 수행된 트랜잭션에 대한 로그백업을 수행하여 최대한 데이터를 살려내야 하며 이를 비상 로그 백업이라 합니다.
    비상 로그 백업을 위해서는 다음과 같이 BACKUP LOG 명령에 NO_TRUNCATE 또는 CONTINUE_AFTER_ERROR 옵션을 붙여서 수행하면 됩니다.
         BACKUP LOG CrashDB to disk = 'D:\ CrashDB.trn' with NO_TRUNCATE
         BACKUP LOG CrashDB to disk = 'D:\ CrashDB.trn' with CONTINUE_AFTER_ERROR
    이후 RESTORE 명령으로 (전체 백업 파일 + 트랜잭션 로그 백업 파일 + 비상 로그 백업 파일)을 이용하여 최대한 데이터를 복구해 낼 수 있습니다.
  2. ldf 파일 장애 발생
    ldf 파일 장애가 발생할 경우 트랜잭션 로그 백업을 할 수 없기 때문에 마지막 트랜잭션 로그 백업 이후에 수행된 트랜잭션에 대한 유실이 발생하게 됩니다.
    이때 mdf 파일은 데이터 정합성이 맞지 않는 상태로 DB Attach 하여 유실된 데이터를 확인하는데 참고하는 정도로 사용할 수 있습니다.

 

결국 mdf파일의 장애는 복구 가능하지만 ldf파일의 장애 발생시 일부 데이터 유실이 발생하게 됩니다.

 

== 테스트 환경

select @@version

Microsoft SQL Server 2014 - 12.0.4422.0 (X64)

         Jul 27 2015 16:56:19

         Copyright (c) Microsoft Corporation

         Developer Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: ) (Hypervisor)

 

== mdf 장애 발생

CREATE DATABASE CrashDB

ON  PRIMARY ( NAME = N'CrashDB', FILENAME = N'D:\temp\CrashDB.mdf' , SIZE = 10MB)

LOG ON ( NAME = N'CrashDB_log', FILENAME = N'D:\temp\CrashDB_log.ldf' , SIZE = 10MB)

GO

 

use CrashDB

go

create table t1 (col1 varchar(1000))

BACKUP DATABASE CrashDB to disk = 'd:\temp\CrashDB_1.bak'

insert into t1 select 'step 1'

backup log CrashDB to disk = 'd:\temp\CrashDB_Log_1.trn'

insert into t1 select 'step 2'

 

use master

go

alter database CrashDB set offline

 

-- delete mdf file 수행(장애 발생했다고 가정 함)

 

alter database CrashDB set online



 

-- NO_TRUNCATE, CONTINUE_AFTER_ERROR 사용하여 백업 수행

backup log CrashDB to disk = 'd:\temp\CrashDB_Log_2.trn' with no_truncate

--backup log CrashDB to disk = 'd:\temp\CrashDB_Log_2.trn' with CONTINUE_AFTER_ERROR

 

RESTORE DATABASE CrashDB_Restore from disk = 'd:\temp\CrashDB_1.bak'

with move 'CrashDB' to 'D:\temp\CrashDB_restore.mdf'

, move 'CrashDB_log' to 'D:\temp\CrashDB_restore_log.ldf'

, norecovery

 

RESTORE LOG CrashDB_Restore from disk = 'd:\temp\CrashDB_Log_1.trn' with norecovery

RESTORE LOG CrashDB_Restore from disk = 'd:\temp\CrashDB_Log_2.trn' with norecovery

RESTORE DATABASE CrashDB_Restore with recovery



 

AND

SQL Denali에서는 메타데이터를 이용할 있는 DMV 구문들이 추가 되었습니다. 이전 버전에서는 이런 기능들을 이용하기 위해 system view 조회 하거나 임시 테이블에 한번 넣은 뒤에 메타데이터를 조작/확인해야 했습니다. 하지만 SQL Denali에서는 이런 추가적인 작업 없이 손쉽게 메타데이터를 조회 하거나 사용할 있습니다.

 

테스트를 위해 다음과 같이 테이블 저장프로시저를 생성하도록 하겠습니다.

CREATE TABLE t1 (

       col1 int

     col2 decimal(12,2)

,      col3 varchar(max)

)

 

CREATE PROC usp_t1

AS

       SELECT * FROM t1
GO


1. 
쿼리 결과에 대한 메타데이터 사용 (sys.dm_exec_describe_first_result_set)
이전 버전에서는 메타데이터 정보를 조회하기 위해 쿼리나 저장프로시저의 결과를 임시테이블에 저장한 다음 해당 임시테이블의 정보를 system view 이용하여 조회해야만 했습니다. 하지만 SQL Denali에서 제공하는 새로운 DMV 이용하면 쿼리나 저장프로시저의 결과에 대한 메타데이터 정보를 손쉽게 조회할 있습니다.

이전 버전에서는 t1 테이블에 대한 메타데이터를 조회하기 위해서는 다음과 같은 쿼리를 이용할 있었습니다.

SELECT c.name

,     t.name

,     c.max_length

,     c.[precision]

,     c.scale

,     c.is_nullable

FROM sys.columns AS c

       INNER JOIN sys.types AS t   

              ON c.system_type_id = t.system_type_id   

              AND c.user_type_id  = t.user_type_id

WHERE c.[object_id] = OBJECT_ID('dbo.t1')

ORDER BY c.column_id;

SQL Denali에서는 sys.dm_exec_describe_first_result_set DMV 파라메터로 쿼리나 저장프로시저의 실행 구문을 넘겨주기만 하면 손쉽게 메타데이터를 조회할 있습니다.

SELECT

      name

,     system_type_name

,     is_nullable

FROM sys.dm_exec_describe_first_result_set

        (N'SELECT * FROM dbo.t1;', NULL, 0 ) AS f

ORDER BY column_ordinal;

 이전 버전에서 사용하던 system view 이용한 방식과 새로운 DMV 결과가 비슷해 보이지만 새로운 DMV 경우 join 결과에 대한 메타데이터를 생성 한다거나 저장프로시저 결과에 대한 메타데이터를 가져올 있다는 점에서 기존 방식보다 훨씬 유연하게 사용할 있습니다.
, 결과 system_type_name 부분을 보면 완전한 형태의 자료형을 보여주고 있는데 이런 부분은 CREATE TABLE 스크립트를 생성할 간편함을 제공해 주게 되며, 순수하게 CREATE TABLE스크립트 생성을 위해서는 비슷한 기능을 하는 sys.dm_exec_describe_first_result_set_for_object DMV 이용할 수도 있습니다.
필요에 따라 저장프로시저 형태의 sp_describe_first_result_set 명령을 사용할 수도 있는데, Application에서 특정 테이블이나 저장프로시저의 메타데이터를 조회할 유용할 같습니다.


2.  정의되지 않은 파라메터가 포함된 쿼리의 메타데이터 예측 (sys.sp_describe_undeclared_parameters)

만일 다음과 같은 쿼리를 수행하기 위해서는 파라메터를 선언한 다음 값을 입력해 주어야 합니다. 쿼리의 경우 단순한 쿼리이지만 쿼리를 작성하다 보면 파라메터가 10 이상 선언되는 경우는 흔하게 있습니다. 또는 여러 테이블을 join 쿼리일 경우 자료형을 맞춰가며 파라메터를 선언하는 작업은 불필요하게 많은 시간을 뺏을 있습니다. 

SELECT * FROM sys.objects

WHERE [object_id] = @ObjectID
        OR (name LIKE @ObjectName);

SQL Denali에서는 다음과 같은 저장프로시저를 이용하여 파라메터의 자료형을 예측할 있습니다. 결과값을 이용하여 선언부의 샘플을 손쉽게 생성해 있습니다.

EXEC sys.sp_describe_undeclared_parameters   

@tsql = N'SELECT * FROM sys.objects

         WHERE [object_id] = @ObjectID

        OR (name LIKE @ObjectName);';

 하지만 파라메터를 예측하는 작업이기 때문에 모든 결과가 올바르게 나올 없습니다. 자세히 보신분은 눈치 채셨겠지만 예제의 sys.objects view에서 name 컬럼의 자료형은 sysname , nvarchar(128)입니다. 이런 부분에 대해서는 필요한 경우 수정을 해서 사용해야 합니다.

다음은 몇가지 특별한 경우를 정리한 내용입니다.

== 오류 발생

SELECT * FROM t1 WHERE @p1 = @p2

SELECT * FROM t1 WHERE c1 = @p1 + @p2

SELECT * FROM t1 WHERE @p1 = SUBSTRING(@p2, 2, 3)

 

== c1 컬럼이 char(30) 경우 >, <비교를 하게 되면 varchar(8000) 반환하게 . (= 비교의 경우 정상적으로 반환)

SELECT * FROM t WHERE c1 > @p


3.
결과에서 데이터 타입 컬럼명 변경 (WITH RESULT SETS)
이전 버전에서 저장프로시저 수행시 컬럼명이나 데이터타입을 변경할 있는 방법은 없었습니다. 하지만 SQL Denali에서는 다음과 같이 WITH RESULT SETS 구문을 이용하여 결과값의 메타데이터를 재정의 있습니다.

exec sp_readerrorlog

 

exec sp_readerrorlog

WITH RESULT SETS (

       (LogDateTime datetime

       ,ProcessInfo varchar(50)

       ,LogText varchar(max)

       )

)

예를 들어 다음과 같은 시나리오들에서 유용하게 이용할 있을것 같습니다.

1.  2가지 시스템이 하나의 저장프로시저를 통해 로그 데이터를 조회하는 경우 한쪽에서는 날짜 정보를 char 타입으로 원하고, 다른 한쪽에서는 datetime으로 원하는 경우

2.  Text라는 컬럼의 이름을 명확히 하기 위해 LogText라는 컬럼 이름을 사용하고 싶은 경우

3.  interface용으로 특정 저장프로시저를 호출해주는 저장 프로시저를 만드는 경우

 
[참고]

SQL Server v.Next (Denali) : Metadata enhancements

http://sqlblog.com/blogs/aaron_bertrand/archive/2010/12/20/sql-server-v-next-denali-metadata-discovery.aspx

 

sys.dm_exec_describe_first_result_set (Transact-SQL)

http://msdn.microsoft.com/en-us/library/ff878258(v=sql.110).aspx

 

sys.dm_exec_describe_first_result_set_for_object (Transact-SQL)

http://msdn.microsoft.com/en-us/library/ff878236(v=SQL.110).aspx

 

sp_describe_first_result_set (Transact-SQL)

http://msdn.microsoft.com/en-us/library/ff878602(v=SQL.110).aspx

 

sp_describe_undeclared_parameters (Transact-SQL)

http://msdn.microsoft.com/en-us/library/ff878260(v=SQL.110).aspx

 

EXECUTE (Transact-SQL)

http://msdn.microsoft.com/en-us/library/ms188332(SQL.110).aspx

 

 

하만철 / Ha ManCheol

AND

Denali - THROW

SQL Server 2011. 9. 3. 17:24

이전 버전에서 오류 메시지를 발생 시키기 위해서는 다음과 같이 오류코드를 등록한 다음 RAISERROR 명령을 이용할 있었습니다. 하지만 RAISERROR 명령의 경우 오류 메시지를 정의해 주어야만 사용할 있었기 때문에 사용하는데 불편함이 있었습니다.

EXEC sys.sp_addmessage

     @msgnum   = 60000

,    @severity = 16

,    @msgtext  = N'This is my error %s.';

 

RAISERROR(60000, 16, 1, N'manha');


SQL Denali
에서는 다음과 같이 2가지 방식을 통해 THROW명령을 사용할 있으며, 파라메터가 없는 경우 오류 메시지 정의 없이 사용할 있습니다.

-- 파라메터가 없는 경우

THROW 60000, N'my error message', 1;

-- 파라메터가 있는 경우

-- THROW 60000, N'my error message', 1, N'manha' 같은 형태로 사용할 있었으면 훨씬 훌륭했을것 같다는 생각입니다.

DECLARE @msg NVARCHAR(2048) = FORMATMESSAGE(60000, N'manha');

THROW 60000, @msg, 1;

 

== RAISERROR THROW 차이

RAISERROR statement

THROW statement

If a msg_id is passed to RAISERROR, the ID must be defined in sys.messages.

The error_number parameter does not have to be defined in sys.messages.

The msg_str parameter can contain printf formatting styles.

The message parameter does not accept printf style formatting.

The severity parameter specifies the severity of the exception.

There is no severity parameter. The exception severity is always set to 16.


[
참고]

SQL Server v.Next (Denali) : Exploring THROW

http://sqlblog.com/blogs/aaron_bertrand/archive/2010/11/22/sql-server-v-next-denali-using-throw-instead-of-raiserror.aspx

 

THROW (Transact-SQL)

http://msdn.microsoft.com/en-us/library/ee677615(v=sql.110).aspx


하만철 / Ha ManCheol

AND

SQL Denali에서는 Server Role 생성하여 계층적인 관리를 있게 되었습니다.

Server Role에서 마우스 오른쪽 버튼을 클릭해 주시면 간단히 Server Role 생성할 있는 메뉴를 있습니다.
 

예를 들어 우리의 서버에 생성된 Login 모니터링 부서에서는 사용하는 Login에게는 모니터링용 DMV 호출하기 위해 “VIEW SERVER STATE”권한과 bulkadmin Server Role 가져야 한다고 가정해 보겠습니다.
기존에는 새로운 Login 생성될 마다 이런 권한을 매번 추가해 밖에 없었습니다. 또는 기존의 권한에 setupadmin 권한을 추가해야 한다면 모든 모니터링용 Login에게 setupadmin 하나하나 추가해 주어야 했었습니다.
하지만 SQL Denali 새로운 기능을 이용하면 자신만의 Server Role 생성한 다음 필요한 권한을 추가해 Login 맵핑할 있습니다. 이제 새로운 Login 생성된다거나 기존의 권한을 변경해야 경우 Login 권한을 수정할 필요 없이 새로 생성한 Server Role에서 권한을 변경하면 됩니다. 

시나리오대로 Login Server Role 생성해 보겠습니다.

USE [master]

GO

 

-- Login 생성

CREATE LOGIN [user1] WITH PASSWORD=N'1234', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF

CREATE LOGIN [user2] WITH PASSWORD=N'1234', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF

 

-- Server Role 생성

CREATE SERVER ROLE myRole

 

-- Server Role 필요한 권한 추가

ALTER SERVER ROLE bulkadmin ADD MEMBER myRole

 

-- ServerRole Login 맵핑

ALTER SERVER ROLE myRole ADD MEMBER user1

ALTER SERVER ROLE myRole ADD MEMBER user2

GO

 

상태에서 user1 로그인 다음 쿼리를 수행해 보면 VIEW SERVER STATE권한이 없어 오류와 함께 쿼리 수행에 실패하게 됩니다.

SELECT * FROM sys.dm_exec_query_stats

 

Msg 297, Level 16, State 1, Line 1

The user does not have permission to perform this action.

 

이제 다음과 같이 myRole VIEW SERVER STATE 권한을 추가 합니다.

GRANT VIEW SERVER STATE TO myRole

 

다시 한번 user1 로그인한 세션에서 sys.dm_exec_query_stats DMV 조회해 보면 오류 없이 수행 되는것을 확인할 있습니다.

기능을 통해 복잡한 권한 관리를 계층적으로 처리하여 권한 변경을 손쉽게 있게 되었습니다.

[참고]

Create Your Own SQL Server Server Roles

http://www.straightpathsql.com/archives/2010/11/create-your-own-sql-server-server-roles/

 

CREATE SERVER ROLE (Transact-SQL)

http://msdn.microsoft.com/en-us/library/ee677610(v=sql.110).aspx

하만철 / Ha ManCheol

AND

오랜만의 블로깅인데 SQL 이야기가 아니네요. ㅎㅎ
이번에 잠시 XpressEngine이 필요해서 잠시 설치를 해봤는데 설명과 다른 부분이 좀 보여서 기록차 글을 남깁니다.
저같은 사람이 찾아보고 도움이 될 수 있도록. ^^;;

0. 환경
windows 7 x64
MSSQL 2008 Dev x64
최대한 Windows 환경에서 진행하기 위해 PHP 모듈만을 가져오고 웹서버와 DB는 IIS 7.5와 MSSQL 2008을 사용하도록 합니다.

1. IIS 설치
[Control Panel\All Control Panel Items\Programs and Features - Turn Windows features on or off] 으로 들어간 다음 [Internet Information Services] 을 선택 합니다.
그리고 [Internet Information Services - World Wide Web Services - Application Development Features - CGI] 를 체크해 줍니다.
(간단한 부분이니 귀찮아서 그림은 패스 합니다. ㅎㅎ)
이제 [확인] 버튼을 누르면 IIS 7.5 준비는 완료됩니다. 참 쉽죠? ㅎㅎ

2. PHP windows binary 다운로드
예전에는 다음 URL에서 PHP 모듈을 다운받았었습니다.
http://www.php.net/downloads.php
하지만 들어가보면 windows binary는 다음에서 다운로드를 받으라고 합니다.
http://windows.php.net/download/
여기로 들어가서 PHP 5.3의 "VC9 x86 Non Thread Safe"을 다운로드 받습니다.
   VC6 = Apache용 , VC9 = IIS용
   Non Thread 방식은 IIS에서 안정성 문제와 성능적인 문제로 선택하는듯 합니다.
이제 다운받은 파일을 특정 폴더에 넣고 압축을 풉니다.

3. IIS에 PHP 모듈 세팅
압축을 푼PHP 폴더에서 "php.ini-development" 파일을 "php.ini"로 복사한 다음 사용합니다. production이라는 확장사는 운영서버용입니다.
그리고 php.ini파일에서 다음 부분을 수정합니다. (대부분 주석을 제거해 주게 됩니다.)
   fastcgi.impersonate = 1
   cgi.fix_pathinfo=1
   cgi.force_redirect = 0
   extension_dir = "./ext"
   extension=php_gd2.dll
다음을 순서대로 수행합니다.
[IIS - 서버명 click - 오른쪽 화면의 Handler Mappings 실행 - 목록에서 마우스 우클릭 - Add module mapping]
다음 정보를 입력합니다. (php-cgi.exe 경로는 압축을 푼 경로를 입력합니다.)
   Request path : *.php
   Module : FastCgiModule
   Executable(optional) : "C:\feisia\XE\PHP\php-cgi.exe"
   Name : PHP FastCGI
Request Restrictions 클릭 후
   Mapping 탭
      Invoke handler only if request is mapped to: 체크
      File or folder 선택

4. XE 다운로드 및 설치 1
다음 사이트에서 XE Core를 다운로드 받습니다. 저는 최신 버전인 1.4.4.4를 다운 받았습니다.
http://www.xpressengine.com/?mid=download&category_srl=18322907
적당한 폴더에 압축을 풀어 둡니다. (저는 c:\feisia\WWW_TEST에 압축을 풀었습니다. 이안에 XE라는 폴더가 생성됩니다.)
IIS에서 새로운 사이트를 생성하고, 위치는 방금 압축을 푼 폴더를 지정합니다. 포트는 8080으로 하였습니다. (저는 c:\feisia\WWW_TEST 를 지정했습니다.)

이 폴더에 test.php파일을 생성하여 내용은 다음과 같이 입력하여 저장합니다.
<?php
phpinfo();
?>
다음과 같이 test.php파일에 접근해 보면 무슨 timezone이 어쩌고 하면서 오류가 발생합니다.
http://localhost:8070/test.php
timezone이 지정되지 않아서 발생하는 오류입니다. php.ini파일을 열어 timezone 설정 부분에 다음과 같이 추가해 줍니다.
date.timezone=Asia/Seoul
설정 변경 후에는 IIS 서비스를 재시작 해줍니다. 이제 다시한번 test.php 파일을 열어 보면 php 설치 정보가 정상적으로 나타나는것을 볼 수 있습니다.

이제 다음 URL로 접근하여 XE 설치 초기화면이 잘 나타나는지 확인합니다.
http://localhost:8070/xe/
다시 오류가 발생할텐데 기본 파일명에 XE의 기본 파일인 index.php가 추가되지 않아서 발생한 오류입니다.
생성된 사이트 이름을 클릭한 다음 "Default Document"에 "index.php"를 추가해 줍니다.
자. 이제 XE를 설치할 준비가 되었습니다.

4. XE 다운로드 및 설치 2
다음 URL을 통해 XE 설치를 진행합니다.
http://localhost:8070/xe/
1) 처음에 언어 선택은 당근 한국어. ㅎㅎ
2) 필수 설치 조건도 당연히 모두 Pass
3) 다음 단계에서 오류가 발생합니다.
Call-time pass-by-reference 어쩌고 저쩌고 나오는데 다시 php.ini파일을 열어 다음 설정을 수정해 줍니다.
allow_call_time_pass_reference = On
물론 IIS 서비스 재시작은 잊지 말아 주시구요. 자 페이지가 정상적으로 떳습니다.
DB를 선택하는 페이지인데.. mssql이 선택되지 않습니다.

먼저 장비에 SQL 2008 Native Client가 설치되어 있지 않다면 다음 URL에서 다운받아 설치합니다. (SQL 2008 client가 설치되어 있다면 아마 같이 깔렸을겁니다.)
http://www.microsoft.com/downloads/ko-kr/details.aspx?familyid=C6C3E9EF-BA29-4A43-8D69-A2BED18FE73C&displaylang=ko
그리고 다음 URL에서 "SQL Server Driver for PHP"를 다운받아 설치합니다.
단지 압축을 풀면 php.ini에서 추가할 수 있는 dll파일들이 생성됩니다. 이 dll파일들을 PHP 폴더의 하위폴더인 ext 폴더에 복사합니다.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=CCDF728B-1EA0-48A8-A84A-5052214CAAD9&displayLang=en
php.ini에서 다음 부분을 추가해 줍니다.(PHP 5.3 , non thread safe , IIS라는 의미입니다.)
extension=php_sqlsrv_53_nts_vc9.dll
다시 IIS 서비스 재시작 후 XE 설치 페이지를 리프레시 해보면 mssql이 선택 가능하게 됩니다.

4) 드디어 마지막 단계 입니다. 계정 정보를 입력하는 페이지 입니다.
DB의 호스트명과 port를 입력하는란이 나누어져 있는데 "DB 호스트네임" 부분에 DB 접속 포트를 함께 입력해야 합니다.
저는 5433 포트를 사용했는데 이때 다음과 같이 입력해야 합니다.
   MANHA-PC,5433
port 입력칸은 비워 둡니다.
가장 아래의 "등록" 버튼을 누르면 이제 설치는 종료됩니다.
이 단계에서 오류가 나면 어떤 오류라는 말이 안나오고 그냥 "null"이라는 메세지와 함께 설치 첫단계로 넘어가 버리게 됩니다. (젠장!!)


[참고자료]
[IIS 7 초급 1-2] PHP 어플리케이션을 위한 IIS 7, PHP 설치 및 구성
http://laigo.kr/565
Non Thread Safe 와 Thread Safe의 차이
http://cafe.naver.com/phpcafe.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=7753
MSSQL - PHP 환경 설정
http://blog.naver.com/resious?Redirect=Log&logNo=30085454668


제 전문 분야도 아니고 해서 귀찮음이 적잖게 섞여서 그림은 한장도 넣지 못했습니다.
그래도 각 단계별로 고생한 부분을 정리 했으니 분명 도움이 되는 분들이 있을거라 믿습니다. ㅎㅎ
혹시나 다른 오류를 만나게 되실 경우 말씀해 주세요~! ^^

하만철 / Ha Man-cheol

AND

테스트 환경 SQL 2008 R2 ENT x64 10.50.1600
DB는 미러링에서 사용하는것으로 1개만 생성 하였습니다.

미러링 구성한 DB에서 주서버와 미러서버의 write를 확인해 보면 log write 량은 동일하지만 data write는 미러서버쪽 DB가 더 많이 발생합니다.
주서버의 DB는 checkpoint를 이용해 data를 디스크로 기록하는 반면 미러서버의 DB는 트랜잭션 처리마다 즉시 디스크에 기록합니다.
미러서버의 DB는 로그를 쓴다는 것이 트랜잭션 처리를 위해 SQL Engine에 의해 로그를 쓰는게 아니라 단지 데이터 동기화를 위해 미러링에 의해 로그 파일에 데이터를 기록하는 것으로 생각 됩니다.

@ 정리
- 미러서버의 DB에서는 checkpoint가 없지만 page writes/sec가 발생하고 있는 것이 확인 가능합니다. 
  그리고 checkpoint 없이 디스크로 동기화 하다 보니 주서버의 DB보다 많은 데이터를 디스크로 쓸 수 밖에 없습니다.
- 주서버의 DB에서는 Log를 쓰는걸 Log Bytes Flushed/sec를 보면 알 수 있지만 미러서버에서는 이 카운터에 값이 보이지 않습니다.
   실제 미러서버 DB의 ldf가 위치한 디스크의 logical 또는 physical disk write bytes를 보면 주서버의 DB와 동일한 양의로그를 씁니다.
- 동기 모드 / 비동기 모드 모두 동일한 결과가 나타난다.

주서버(왼쪽) , 미러서버(오른쪽)



관련자료
Database Mirroring Best Practices and Performance Considerations
http://technet.microsoft.com/en-us/library/cc917681.aspx

하만철 / Ha Man-cheol

AND


보고있는 책에서 SQL 2008에서는 복구모델 Full인 경우에 TABLOCK을 주고 insert하면 로깅 최적화가 된다고 이야기를 하고 있어서 한번 테스트를 해 보았습니다.

 

-- 테스트 DB 생성

CREATE DATABASE DBFull

CREATE DATABASE DBFull_TABLock

GO

 

ALTER DATABASE DBFull SET RECOVERY FULL

ALTER DATABASE DBFull_TABLock SET RECOVERY FULL

GO

 

-- Full backup

BACKUP DATABASE DBFull to disk = 'd:\temp\DBFull.BAK' with init

BACKUP DATABASE DBFull_TABLock to disk = 'd:\temp\DBFull_TABLock.BAK' with init

go

 

-- 테스트 Table 생성

USE DBFull

GO

create table t1 (col1 bigint)

USE DBFull_TABLock

GO

create table t1 (col1 bigint)

 

-- log 파일 사이즈 확인

select name, size

from master..sysaltfiles

where name like '%log'

             and dbid >= db_id('DBFull') 이건 방금 생성한 DB만 나오게 필터

order by name

 

name                                             size

--------------------------

DBFull_log                                     72

DBFull_TABLock_log         72

모두 72page(576KB) 입니다.

 

일단 테스트 데이터를 tempdb 생성한 다음 DBFull DB 복사해 보겠습니다.

 

-- 테스트 데이터 생성(100만건, 17MB)

select top 1000000

 row_number() over (order by (select 1)) as col1

into tempdb.dbo.t1

from sysindexes A

, sysindexes B

, sysindexes C

, sysindexes D

 

insert into DBFull.dbo.t1

select * from tempdb.dbo.t1

insert into DBFull_TABLock.dbo.t1 WITH(TABLOCK)

select * from tempdb.dbo.t1

 

-- log 파일 사이즈 확인

select name, size

from master..sysaltfiles

where name like '%log'

             and dbid >= db_id('DBFull') 이건 방금 생성한 DB만 나오게 필터

order by name

 

name                                             size

--------------------------

DBFull_log                                     40840 -- 40840*8KB = 320MB

DBFull_TABLock_log         2552   -- 2552*8KB = 20MB

 

DBFull DB는 로그 사이즈가 320MB인데 DBFull_TABLock DB 20MB정도 밖에 되지 않는다.

전체 복구 모델인데 이거참;;

도대체 어떻게 쌓는 차이가 있을까???

로그에 쌓인 모양을 한번 뒤져보자.

 

-- log 확인

-- 위에서 로그를 열라 많이 적었기 때문에 시간이 좀 걸린다.

-- 너무 오래 걸리면 위의 테스트의 건수를 좀 줄여서 하자.

use dbfull

go

select * from ::fn_dblog(null, null)

use dbfull_tablock

go

select * from ::fn_dblog(null, null)

 

@ DBFull DB

전체 복구 모델에 충실하게 1row씩 열심히 로그에 기록하고 있습니다.


@ DBFull DB with TABLOCK

전체 복구 모델이지만 PAGE단위로 할당하고 데이터를 집어 넣는 것을 로그에 쓰고 있습니다.

오호~ 페이지 단위로만 기록하니 로그 사이즈가 그렇게 작았구나~!

이친구 전체 복구모델인데 꽤 쓸만 하네?

 


--
테스트 DB 정리

use master

go

drop database dbfull

drop database dbfull_tablock

 

전체 복구 모델 이더라도 TABLOCK 걸고 데이터를 넣으면 로깅을 최적화 해서 이전 버전에 비해 적은량의 로그를 쓰게 됩니다. 때문에 대량 데이터 작업을 할때는 전체 복구 모델이더라도 TABLOCK을 주어야 겠습니다.

  

아래는 보너스~ 인덱스 상황별 로그 사이즈~ ㅎㅎ

 

DBFull

DBFull_TABLock

HEAP

320 MB

20 MB

Clustered index

220 MB

20 MB

Non-clustered index

510 MB

510 MB

Clustered index

+ Non-clustered index

430 MB

380 MB

 

 

PS : 최소로깅이라는 단어와는 맞지 않아 약간 용어를 수정 했습니다.

나중에 좀 더 고민을 해보고 비슷한 내용으로 한번 더 아티클을 작성해 보도록 하겠습니다~ ^^

하만철 / Ha Man cheol

AND