พื้นที่ดิสก์เต็มในระหว่างการแทรกเกิดอะไรขึ้น


17

วันนี้ฉันค้นพบฮาร์ดไดรฟ์ที่เก็บฐานข้อมูลของฉันเต็ม สิ่งนี้เคยเกิดขึ้นมาก่อนโดยปกติสาเหตุจะชัดเจนมาก มักจะมีแบบสอบถามที่ไม่ดีซึ่งทำให้เกิดการรั่วไหลขนาดใหญ่เพื่อ tempdb ซึ่งเติบโตขึ้นจนดิสก์เต็ม คราวนี้มันเห็นได้ชัดน้อยลงว่าเกิดอะไรขึ้นเนื่องจาก tempdb ไม่ได้เป็นสาเหตุของไดรฟ์เต็มรูปแบบมันเป็นฐานข้อมูลของตัวเอง

ข้อเท็จจริง:

  • ขนาดฐานข้อมูลปกติประมาณ 55 GB เพิ่มขึ้นเป็น 605 GB
  • ไฟล์บันทึกมีขนาดปกติและมีขนาดใหญ่มาก
  • Datafile มีพื้นที่ว่าง 85% (ฉันตีความว่าเป็น 'อากาศ': พื้นที่ที่ใช้ แต่ได้รับการปล่อยให้ว่างแล้ว SQL Server จะสำรองพื้นที่ทั้งหมดเมื่อจัดสรรแล้ว)
  • ขนาด Tempdb เป็นปกติ

ฉันได้พบสาเหตุที่น่าจะเป็น; มีหนึ่งแบบสอบถามที่เลือกแถวมากเกินไป (การรวมที่ไม่ดีทำให้เกิดการเลือก 11 พันล้านแถวซึ่งคาดว่าสองแสนคน) นี่คือ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และให้แน่ใจว่า log มีเพียงไม่กี่แถว (3 ในกรณี 2016) 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 แถวแสดงให้เห็นว่าคุณมี transactions2

สมมติฐานที่ฉันร่างที่นี่ถูกต้อง? นี่เป็นสถานการณ์ที่น่าจะเกิดขึ้นหรือไม่

ใช่มันถูกต้อง

insertส่วนหนึ่งของการselect intoเป็นrolled backแต่มันไม่ได้ปล่อยพื้นที่ข้อมูลใด ๆ คุณสามารถตรวจสอบได้โดยการดำเนินการsp_spaceused; unallocated spaceคุณจะเห็นความอุดมสมบูรณ์ของ

หากคุณต้องการให้ฐานข้อมูลปล่อยพื้นที่ที่ไม่ได้ปันส่วนนี้คุณควรใช้shrinkไฟล์ข้อมูลของคุณ


15

คุณถูกต้องSELECT...INTOคำสั่งไม่ใช่อะตอม สิ่งนี้ไม่ได้รับการบันทึกไว้ในขณะที่โพสต์ต้นฉบับ แต่ตอนนี้ถูกเรียกออกมาโดยเฉพาะในหน้าSELECT - INTO Clause (Transact-SQL)บน MS Docs (yay open source!):

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'

แต่มีตารางกระทู้ว่างในฐานข้อมูลใหม่ของฉัน:

สกรีนช็อตของผลลัพธ์เป็นศูนย์จากตารางที่สร้างขึ้นใหม่

ดังนั้นในขณะที่คุณสงสัยว่าCREATE TABLEประสบความสำเร็จ แต่INSERTส่วนที่ถูกย้อนกลับทั้งหมด วิธีแก้ปัญหาคือใช้ธุรกรรมที่ชัดเจน (ซึ่งคุณได้ระบุไว้ในคำถามของคุณแล้ว)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.