Ekleme sırasında disk alanı dolu, ne olur?


17

Bugün veritabanlarımı depolayan sabit diski dolu buldum. Bu daha önce olmuştu, genellikle nedeni oldukça belirgindir. Genellikle büyük bir dökülme tempdb disk dolu olana kadar büyür neden kötü bir sorgu vardır. Bu sefer ne olduğu biraz daha az belirgindi, tempdb tam sürücünün nedeni olmadığından, veritabanının kendisiydi.

Gerçekler:

  • Genel veritabanı boyutu yaklaşık 55 GB, 605 GB'a çıktı.
  • Günlük dosyasının boyutu normal, veri dosyası çok büyük.
  • Datafile% 85 kullanılabilir alana sahiptir (Bunu 'air' olarak yorumluyorum: kullanılan, ancak serbest bırakılan alan. SQL Server ayrıldıktan sonra tüm alanı ayırır).
  • Tempdb boyutu normal.

Muhtemel nedeni buldum; çok fazla satır seçen bir sorgu var (kötü birleşme birkaç yüz binin beklendiği 11 milyar sıra seçimine neden oluyor). Bu, SELECT INTOaşağıdaki senaryoların gerçekleşip gerçekleşmediğini merak etmemi sağlayan bir sorgudur:

  • SELECT INTO yürütülür
  • Hedef tablo oluşturuldu
  • Veri seçildiği gibi eklenir
  • Disk dolar ve eklemenin başarısız olmasına neden olur
  • SELECT INTO iptal edildi ve geri alındı
  • Geri alma alanı boşaltır (zaten eklenmiş veriler kaldırılır), ancak SQL Server serbest alanı serbest bırakmaz.

Ancak bu durumda, tarafından oluşturulan tablonun SELECT INTOhala var olmasını beklemezdim, geri alma tarafından düşürülmelidir. Bunu test ettim:

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

ROLLBACK

SELECT  * 
FROM    TMP.test

Bunun sonucu:

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

Ancak hedef tablo var. Ancak gerçek sorgu açık bir işlemde yürütülmedi, bu hedef tablonun varlığını açıklayabilir mi?

Burada çizdiğim varsayımlar doğru mu? Bu gerçekleşmesi muhtemel bir senaryo mu?

Yanıtlar:


17

Ancak gerçek sorgu açık bir işlemde yürütülmedi, bu hedef tablonun varlığını açıklayabilir mi?

Evet, aynen öyle.

Birinin select intodışında basit bir işlem yaparsanız , otomatik tamamlama modunda explicit transactioniki tane vardır transactions: ilki oluşturur tableve ikincisi doldurur.

Bunu kendinize şu şekilde kanıtlayabilirsiniz:

İçinde bir databasetest sunucusunda adanmış bir simple recovery model, ilk yapmak checkpointve günlük sadece ilgili birkaç satır (2016 durumunda 3) içerdiğinden emin olun checkpoint. Ardından select into, bir satırdan birini çalıştırın ve aşağıdakilerle ilişkili logbir şey aramak için tekrar kontrol edin :begin transelect 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 satır olduğunu, 2 olduğunu gösterir transactions.

Burada çizdiğim varsayımlar doğru mu? Bu gerçekleşmesi muhtemel bir senaryo mu?

Evet, haklılar.

insertParçası select intooldu rolled back, ancak herhangi bir veri alanı bırakmaz. Bunu yürüterek doğrulayabilirsiniz sp_spaceused; bolca göreceksiniz unallocated space.

Veritabanının bu ayrılmamış alanı serbest bırakmasını istiyorsanız shrink, veri dosyalarınız olmalıdır .


15

Haklısın, SELECT...INTOkomut atomik değil. Bu, orijinal gönderi sırasında belgelenmemiştir, ancak şimdi özellikle MS Docs (yay açık kaynak!) Üzerindeki SELECT - INTO Yan tümcesi (Transact-SQL) sayfasında çağrılmaktadır :

SELECT...INTOYeni tablo oluşturulur ve daha sonra satır eklenir - deyim iki bölümden faaliyet göstermektedir. Bu, kesici uçların başarısız olması durumunda hepsinin geri alınacağı, ancak yeni (boş) tablonun kalacağı anlamına gelir. Bir bütün olarak başarılı olmak veya başarısız olmak için işlemin tamamına ihtiyacınız varsa, açık bir işlem kullanın .

Tam kurtarma modelini kullanan bir veritabanı oluşturacağım. Oldukça küçük bir günlük dosyası vereceğim ve sonra günlük dosyasının otomatik olarak büyüyemeyeceğini söyleyeceğim:

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
)

Ve sonra tüm mesajları StackOverflow2010 veritabanının kopyasından eklemeye çalışacağım. Bu , günlük dosyasına bir sürü şey yazmalıdır .

USE [SelectIntoTestDB];
GO

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

Bu, 4 saniye çalıştırıldıktan sonra aşağıdaki hatayla sonuçlandı:

Msg 9002, Seviye 17, Durum 4, Satır 1
'ACTIVE_TRANSACTION' nedeniyle 'SelectIntoTestDB' veritabanı için işlem günlüğü dolu.

Ancak yeni veritabanımda boş bir Mesajlar tablosu var:

yeni oluşturulan tablodaki sıfır sonuçların ekran görüntüsü

Yani, şüphelendiğiniz gibi, CREATE TABLEbaşarılı oldu, ama INSERTkısım geri alındı. Geçici bir çözüm, (sorunuzda daha önce not ettiğiniz) açık bir işlem kullanmak olacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.