삽입하는 동안 디스크 공간이 가득 찼습니다. 어떻게됩니까?


17

오늘 저는 데이터베이스를 저장하는 하드 드라이브가 가득 찼음을 발견했습니다. 이것은 전에 일어 났으며, 일반적으로 원인이 분명합니다. 일반적으로 쿼리가 잘못되어 tempdb에 대량 유출이 발생하여 디스크가 가득 찰 때까지 커집니다. 이번에는 tempdb가 전체 드라이브의 원인이 아니기 때문에 데이터베이스 자체였습니다.

사실 :

  • 일반적인 데이터베이스 크기는 약 55GB이며 605GB로 증가했습니다.
  • 로그 파일의 크기가 정상이고 데이터 파일이 큽니다.
  • 데이터 파일의 사용 가능한 공간은 85 %입니다 (이를 '공기': 해석되었지만 사용 된 공간으로 해석합니다. SQL Server는 할당 된 모든 공간을 예약합니다).
  • Tempdb 크기는 정상입니다.

가능한 원인을 찾았습니다. 너무 많은 행을 선택하는 쿼리가 하나 있습니다 (잘못된 조인은 수십만이 예상되는 110 억 개의 행을 선택합니다). 이것은 SELECT INTO쿼리이므로 다음 시나리오가 발생할 수 있는지 궁금합니다.

  • SELECT INTO가 실행됩니다
  • 대상 테이블이 생성됩니다
  • 선택된 데이터가 삽입됩니다
  • 디스크가 가득 차서 삽입 실패
  • SELECT INTO가 중단되고 롤백됩니다.
  • 롤백은 여유 공간을 비우고 (이미 삽입 된 데이터는 제거됨) SQL Server는 빈 공간을 해제하지 않습니다.

그러나이 상황에서는에 의해 생성 된 테이블 SELECT INTO이 여전히 존재할 것으로 기대하지 않았으므로 롤백으로 삭제해야합니다. 나는 이것을 테스트했다 :

BEGIN TRANSACTION 
SELECT  T.x
INTO    TMP.test
FROM    (VALUES(1))T(x)

ROLLBACK

SELECT  * 
FROM    TMP.test

결과 :

(1 row affected)
Msg 208, Level 16, State 1, Line 8
Invalid object name 'TMP.test'.

그러나 목표 테이블이 존재합니다. 실제 쿼리는 명시 적 트랜잭션에서 실행되지 않았지만 대상 테이블의 존재를 설명 할 수 있습니까?

여기에 스케치 한 가정이 정확합니까? 이 시나리오가 발생했을 가능성이 있습니까?

답변:


17

실제 쿼리는 명시 적 트랜잭션에서 실행되지 않았지만 대상 테이블의 존재를 설명 할 수 있습니까?

그렇습니다.

select into외부 에서 간단한 작업을 수행하는 경우 자동 커밋 모드 explicit transaction에는 두 가지 transactions가 있습니다. 첫 번째는 작성 table하고 두 번째는 작성합니다 .

이런 식으로 스스로 증명할 수 있습니다.

database의 테스트 서버 전용 에서 simple recovery model먼저를 만들고 checkpoint로그에와 관련된 몇 개의 행 (2016의 경우 3) 만 포함되어 있는지 확인하십시오 checkpoint. 그런 다음 실행 select into한 행의를하고를 확인 log찾는, 다시 begin tran와 관련된 select into:

checkpoint;

select *
from sys.fn_dblog(null, null);

select 'a' as col
into dbo.t3;  

select *
from sys.fn_dblog(null, null)
where Operation = 'LOP_BEGIN_XACT'
      and [Transaction Name] = 'SELECT INTO';

2 행이 표시되어 2가 있음을 나타 transactions냅니다.

여기에 스케치 한 가정이 정확합니까? 이 시나리오가 발생했을 가능성이 있습니까?

예, 맞습니다.

insert의 일부 select into였다 rolled back,하지만 데이터 공간을 해제하지 않습니다. 이를 실행하여이를 확인할 수 있습니다 sp_spaceused. 당신은 많이 볼 수 unallocated space있습니다.

데이터베이스가이 할당되지 않은 공간을 해제하도록하려면 shrink데이터 파일 이 있어야 합니다.


15

당신은 맞습니다, SELECT...INTO명령은 원자 적이 아닙니다. 이것은 원래 게시물 당시에 문서화되지 않았지만 이제는 MS Docs (yay open source!) 의 SELECT-INTO Clause (Transact-SQL) 페이지에서 구체적으로 설명됩니다 .

SELECT...INTO새 테이블을 생성하고 행 삽입 - 문은 두 부분으로 운영하고 있습니다. 이는 삽입이 실패하면 모두 롤백되지만 새 (빈) 테이블은 그대로 남아 있음을 의미합니다. 전체 작업이 전체적으로 성공 또는 실패해야하는 경우 명시 적 트랜잭션을 사용하십시오 .

전체 복구 모델을 사용하는 데이터베이스를 생성하겠습니다. 상당히 작은 로그 파일을 제공 한 다음 로그 파일이 자동 증가 할 수 없다고 알려줍니다.

CREATE DATABASE [SelectIntoTestDB]
ON PRIMARY 
( 
    NAME = N'SelectIntoTestDB', 
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\SelectIntoTestDB.mdf', 
    SIZE = 8192KB, 
    FILEGROWTH = 65536KB
)
LOG ON 
( 
    NAME = N'SelectIntoTestDB_log', 
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\SelectIntoTestDB_log.ldf', 
    SIZE = 8192KB, 
    FILEGROWTH = 0
)

그런 다음 StackOverflow2010 데이터베이스 복사본에서 모든 게시물을 삽입하려고 시도합니다. 이것은 로그 파일에 많은 것들을 기록해야합니다.

USE [SelectIntoTestDB];
GO

SELECT *
INTO dbo.Posts
FROM StackOverflow2010.dbo.Posts;

4 초 동안 실행 한 후 다음 오류가 발생했습니다.

메시지 9002, 수준 17, 상태 4, 줄 1
데이터베이스 'SelectIntoTestDB'에 대한 트랜잭션 로그가 'ACTIVE_TRANSACTION'으로 인해 가득 찼습니다.

그러나 새 데이터베이스에 빈 Posts 테이블이 있습니다.

새로 생성 된 테이블의 결과가없는 스크린 샷

의심 한대로 CREATE TABLE성공했지만 그 INSERT부분은 모두 롤백되었습니다. 해결 방법은 명시 적 트랜잭션 (이미 질문에 언급 한)을 사용하는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.