FILESTREAM 애플리케이션에서 데이터베이스 작업과의 충돌 방지

적용 대상:SQL Server

SqlOpenFilestream()을 사용하여 FILESTREAM BLOB 데이터를 읽거나 쓰기 위해 Win32 파일 핸들을 여는 애플리케이션은 일반적인 트랜잭션에서 관리되는 Transact-SQL 문과 충돌 오류가 발생할 수 있습니다. 여기에는 실행을 완료하는 데 시간이 오래 걸리는 Transact-SQL 또는 MARS 쿼리가 포함됩니다. 이러한 유형의 충돌을 방지하기 위해 애플리케이션을 신중하게 설계해야 합니다.

SQL Server 데이터베이스 엔진 또는 애플리케이션이 FILESTREAM BLOB을 열려고 하면 데이터베이스 엔진은 연결된 트랜잭션 컨텍스트를 확인합니다. 데이터베이스 엔진은 열린 작업이 DDL 문, DML 문, 데이터 검색 또는 트랜잭션 관리와 함께 작동하는지 여부에 따라 요청을 허용하거나 거부합니다. 다음 표에서는 트랜잭션에 열려 있는 파일의 형식에 따라 데이터베이스 엔진이 Transact-SQL 문을 허용할지 아니면 거부할지를 결정하는 방법을 보여 줍니다.

TRANSACT-SQL 문 읽기 위해 열렸습니다. 쓰기 위해 열렸습니다.
CREATE TABLE, CREATE INDEX, DROP TABLE 및 ALTER TABLE과 같은 데이터베이스 메타데이터와 함께 작동하는 DDL 문입니다. 허용됨 차단되고 시간 초과 오류가 발생합니다.
데이터베이스에 저장된 데이터(예: UPDATE, DELETE 및 INSERT)와 함께 작동하는 DML 문입니다. 허용됨 거부됨
선택 허용됨 허용됨
COMMIT TRANSACTION 거부* 거부*.
SAVE TRANSACTION 거부* 거부*
롤백 허용됨* 허용됨*

* 트랜잭션이 취소되고 트랜잭션 컨텍스트에 대한 열린 핸들이 무효화됩니다. 애플리케이션은 열려 있는 모든 핸들을 닫아야 합니다.

다음 예제에서는 Transact-SQL 문 및 FILESTREAM Win32 액세스로 인해 충돌이 발생할 수 있는 방법을 보여 줍니다.

A. 쓰기 액세스를 위한 FILESTREAM BLOB 열기

다음 예제에서는 쓰기 액세스에 대해서만 파일을 여는 효과를 보여 있습니다.

dstHandle =  OpenSqlFilestream(dstFilePath, Write, 0,  
    transactionToken, cbTransactionToken, 0);  
  
//Write some date to the FILESTREAM BLOB.  
WriteFile(dstHandle, updateData, ...);  
  
//DDL statements will be denied.  
//DML statements will be denied.  
//SELECT statements will be allowed. The FILESTREAM BLOB is  
//returned without the modifications that are made by  
//WriteFile(dstHandle, updateData, ...).  
CloseHandle(dstHandle);  
  
//DDL statements will be allowed.  
//DML statements will be allowed.  
//SELECT statements will be allowed. The FILESTREAM BLOB  
//is returned with the updateData applied.  

B. 읽기 액세스를 위한 FILESTREAM BLOB 열기

다음 예제에서는 읽기 전용으로 파일을 여는 효과를 보여 있습니다.

dstHandle =  OpenSqlFilestream(dstFilePath, Read, 0,  
    transactionToken, cbTransactionToken, 0);  
//DDL statements will be denied.  
//DML statements will be allowed. Any changes that are  
//made to the FILESTREAM BLOB will not be returned until  
//the dstHandle is closed.  
//SELECT statements will be allowed.  
CloseHandle(dstHandle);  
  
//DDL statements will be allowed.  
//DML statements will be allowed.  
//SELECT statements will be allowed.  

C. 여러 FILESTREAM BLOB 파일 열기 및 닫기

여러 파일이 열린 경우 가장 제한적인 규칙이 사용됩니다. 다음 예에서는 두 파일을 엽니다. 첫 번째 파일은 읽기 위해 열리고 두 번째 파일은 쓰기용으로 열립니다. DML 문은 두 번째 파일이 열릴 때까지 거부됩니다.

dstHandle =  OpenSqlFilestream(dstFilePath, Read, 0,  
    transactionToken, cbTransactionToken, 0);  
//DDL statements will be denied.  
//DML statements will be allowed.  
//SELECT statements will be allowed.  
  
dstHandle1 =  OpenSqlFilestream(dstFilePath1, Write, 0,  
    transactionToken, cbTransactionToken, 0);  
  
//DDL statements will be denied.  
//DML statements will be denied.  
//SELECT statements will be allowed.  
  
//Close the read handle. The write handle is still open.  
CloseHandle(dstHandle);  
//DML statements are still denied because the write handle is open.  
  
//DDL statements will be denied.  
//DML statements will be denied.  
//SELECT statements will be allowed.  
  
CloseHandle(dstHandle1);  
//DDL statements will be allowed.  
//DML statements will be allowed.  
//SELECT statements will be allowed.  

D. 커서를 닫지 못했습니다.

다음 예제에서는 닫혀 있지 않은 문 커서가 쓰기 액세스를 위해 BLOB을 열지 못하게 하는 OpenSqlFilestream() 방법을 보여 줍니다.

TCHAR *sqlDBQuery =  
TEXT("SELECT GET_FILESTREAM_TRANSACTION_CONTEXT(),")  
TEXT("Chart.PathName() FROM Archive.dbo.Records");  
  
//Execute a long-running Transact-SQL statement. Do not allow  
//the statement to complete before trying to  
//open the file.  
  
SQLExecDirect(hstmt, sqlDBQuery, SQL_NTS);  
  
//Before you call OpenSqlFilestream() any open files  
//that the Cursor the Transact-SQL statement is using  
// must be closed. In this example,  
//SQLCloseCursor(hstmt) is not called so that  
//the transaction will indicate that there is a file  
//open for reading. This will cause the call to  
//OpenSqlFilestream() to fail because the file is  
//still open.  
  
HANDLE srcHandle =  OpenSqlFilestream(srcFilePath,  
     Write, 0,  transactionToken,  cbTransactionToken,  0);  
  
//srcHandle will == INVALID_HANDLE_VALUE because the  
//cursor is still open.  

참고 항목

OpenSqlFilestream을 사용하여 FILESTREAM 데이터 액세스
MARS(다중 활성 결과 집합) 사용