2

SqlBulkCopy를 사용하여 데이터를 테이블 집합으로 이동시키는 응용 프로그램이 있습니다. 최근 SQL2016을 사용하는 사용자는 하드 드라이브가 매우 큰 데이터베이스로 채워지는 문제를보고하고 있습니다. SQL2014에서는이 문제가 발생하지 않습니다. 검사 결과 TableDataSizes.sql (스크립트가 연결됨)을 실행하면 UnusedSpaceKB에 많은 양의 공간이 있음을 알 수 있습니다.SQLBulkCopy 사용 - SQL Server 2016에서보다 SQL Server 2016에서 훨씬 큰 테이블 2014

나는 a) SQLServer 2016에 버그가 있거나 SQLBulkCopy를 사용하여 새로운 기능이 "충돌"했는지 알고 싶습니다. SQL Server 2016에서 페이지 할당에 몇 가지 변경 사항이 있습니다. 일반적으로이 문제의 원인은 무엇입니까?

재현 방법 주 - 아래에서는 필 요하지 않은 정보가 제거 된 상황을 설명합니다. 나는 실제로 데이터베이스 테이블에 수천 개의 타임 스탬프를 저장하지 않고있다. (다른 컬럼은 제거되었다.)

  1. (광산를 TestDB를 불렀다)
  2. 가 (아래 스크립트를 사용하여) 그 DB에서 테이블을 작성

    USE [TestDB] 
    GO 
    
    /****** Object: Table [dbo].[2017_11_03_DM_AggregatedPressure_Data] Script Date: 07/11/2017 10:30:36 ******/ 
    SET ANSI_NULLS ON 
    GO 
    
    SET QUOTED_IDENTIFIER ON 
    GO 
    
    CREATE TABLE [dbo].[TestTable](
        [TimeStamp] [datetime] NOT NULL 
    ) ON [PRIMARY] 
    
    GO 
    
  3. 하는 스크립트를 사용하여 해당 테이블에 인덱스를 (만들기 SQL의 데이터베이스를 만듭니다 아래와 같이)

    USE [TestDB] 
    GO 
    
    /****** Object: Index [2017_11_03_DM_AggregatedPressure_Data_Index] Script Date: 07/11/2017 10:32:44 ******/ 
    CREATE CLUSTERED INDEX [TestTable_Index] ON [dbo].[TestTable] 
    (
        [TimeStamp] ASC 
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
    GO 
    
  4. 아래 제공된 코드를 사용하여 테이블에 레코드를 실행하기 시작하십시오. (이 코드는 윈도우가 단순히에 btnGo라는 버튼을 nupRecordsToInsert라는 numericUpDown을 가지고 형성을위한 뒤에이다. (500)의 주위에 어떤 시점에서

    Public Class Form1 
    
    Private conStr As String = "Integrated Security=true;Persist Security Info=true;Server=.;Database=TestDB;Pooling=True" 
    Dim tableName As String = "TestTable" 
    
    Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click 
    
        Dim table as DataTable = GetData(nupRecordsToInsert.Value) 
    
        Using conn As SqlConnection = New SqlConnection(conStr) 
         conn.Open() 
         Using sbc As SqlBulkCopy = New SqlBulkCopy(conStr, SqlBulkCopyOptions.UseInternalTransaction Or SqlBulkCopyOptions.KeepIdentity) 
    
          sbc.DestinationTableName = "[" & tableName & "]" 
          sbc.BatchSize = 1000 
          sbc.WriteToServer(table) 
    
         End Using 
        End Using 
    
        MessageBox.Show($"Records Inserted = {nupRecordsToInsert.Value} into Database - TestDB. Table - {tableName}") 
    End Sub 
    
    Private Function GetData(numOfRecordsNeeded As Integer) As DataTable 
        Dim table As DataTable = New DataTable() 
        table.Columns.Add("TimeStamp", GetType(DateTime)) 
    
        Dim dtDateTimeToInsert as DateTime = DateTime.Now 
    
        For index As Integer = 1 To numOfRecordsNeeded 
         dtDateTimeToInsert = dtDateTimeToInsert.AddSeconds(2) 
         table.Rows.Add(dtDateTimeToInsert) 
        Next 
    
        Return table 
    End Function 
    

    최종 클래스

  5. 항목의 수를 기록 데이터베이스 테이블에 새 레코드가 새 페이지에 기록 될 필요가 있음을 의미합니다. 실제 결과에 설명 된대로 흥미있는이 시점에서 이런 일이.

실제 결과 SQL2016의 데이터베이스가 매우 큽니다 (첫 번째 페이지가 채워지고 두 번째 페이지가 시작된 후에 발생 함). tablesizes의 아이디어를 얻기 위해 아래의 SQL을 실행할 때

  1. 더 자세히 볼 수 있습니다. 데이터베이스로 실행하는 레코드가 많을수록 UnusedSpaceKB 열에 더 많은 수를 볼 수 있습니다. 에 많은 수를 보여주는

    use [TestDB] 
    
    SELECT 
        t.NAME AS TableName, 
        s.Name AS SchemaName, 
        p.rows AS RowCounts, 
        SUM(a.total_pages) * 8 AS TotalSpaceKB, 
        SUM(a.used_pages) * 8 AS UsedSpaceKB, 
        (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB 
    FROM 
        sys.tables t 
    INNER JOIN  
        sys.indexes i ON t.OBJECT_ID = i.object_id 
    INNER JOIN 
        sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id 
    INNER JOIN 
        sys.allocation_units a ON p.partition_id = a.container_id 
    LEFT OUTER JOIN 
        sys.schemas s ON t.schema_id = s.schema_id 
    WHERE 
        t.NAME = 'TestTable' 
        AND t.is_ms_shipped = 0 
        AND i.OBJECT_ID > 255 
    GROUP BY 
        t.Name, s.Name, p.Rows 
    ORDER BY 
        RowCounts desc 
    

출력 UnusedSpaceKB의

enter image description here

    아래 쿼리를 실행
  1. 가 많은 페이지가 할당 된 것을 알 수 있지만, 단지 그 모든 '8 세트'중 첫 번째가 사용됩니다.이로 인해 8 페이지 중 마지막 7 페이지가 사용되지 않아 낭비되는 공간이 많이 생깁니다.

    select * from sys.dm_db_database_page_allocations 
    (DB_id() , object_id('[dbo].[TestTable]') , NULL , NULL , 'DETAILED') 
    

아래는 페이지 할당이 지속적으로 실행하지 않는 결과의 일부를 보여줍니다. (위와 같이) 해당 쿼리를 실행하는 경우 SQL2014에서
Showing spaces in database_page_allocations results

데이터베이스는 우리가 UnusedSpaceKB 열에 큰 값이 표시되지 않는이 문제 1. 표시되지 않습니다.

  1. 다른 쿼리 (queries - dm_db_database_page_allocations)를 실행하면 많은 페이지가 할당되었지만 각 페이지가 순서대로 사용 중임을 알 수 있습니다. 공백이 없습니다. 7 개의 사용되지 않는 페이지가 없습니다. 내가 SQL2016를 기대

Contiguous page allocations

예상 결과는 SQL2014처럼 행동하고 매우 큰 테이블을 생성 할 수 있습니다. 특히 나는 페이지가 연속적으로 할당되고 할당에서 7 페이지의 간격을 가지지 않을 것으로 기대합니다.

누구나 내가이 차이점을 보았을 때 어떤 생각을 가지고 있다면 대단히 도움이 될 것입니다.

+2

서버 FillFactor가 두 서버에서 모두 동일한 지 확인 했습니까? CREATE INDEX는이를 명시 적으로 지정하지 않으므로 server default가 사용됩니다. 추신 BulkCopy 이후에 색인을 만들면 어떻습니까? 이제는 최소한의 로깅을 사용하지 않을 것입니다. – sepupic

+0

가장 효율적인 방법으로 대량 복사를 사용하고 있습니다. 테이블에 클러스터 된 인덱스가 있으며 배치 크기가 1000이고 행 잠금이 아닌 테이블 잠금 장치. 스트리밍 된 데이터는 계속 가져 오지만 작업 자체는 완전히 기록됩니다. 그러나 그 자체는 SQL Server 2014에서 변경하면 안됩니다. 두 경우 모두 복구 모델이 동일합니까? 사용자 지정 추적 플래그가 적용 되었습니까? (클러스터 된 인덱스가있는 테이블에서 대량 삽입에 대한 최소 로깅을 가능하게하는 추적 플래그 610과 유사합니까?) –

+0

데이터베이스의 자동 증가 설정은 무엇입니까? 얼마나 많은 데이터가 이동됩니까? '너무 큽니다'는 무엇을 의미합니까? 이 질문의 단계는 너무 모호하여 문제를 재현 할 수 없습니다. –

답변

-1

당신은 use trace flag 692해야합니다

어떤 이유로 경우, BATCHSIZE을 변경할 수 없습니다 또는 기본 최소 로깅 동작과 향상된 데이터로드 성능을보고하지 않는 경우, 당신은 SQL에서 빠른 삽입 동작을 비활성화 할 수 있습니다 추적 플래그 (692) (...)를 사용하는 서버 (2016). 정상적인 상황에서는 고객에게이 추적 플래그가 필요할 것으로 예상하지 않습니다.

+0

이 링크는 질문에 대답 할 수 있지만 답변의 핵심 부분을 여기에 포함시키고 참조 용 링크를 제공하는 것이 좋습니다. 링크 된 페이지가 변경되면 링크 전용 답변이 유효하지 않게 될 수 있습니다. - [리뷰에서] (리뷰/저품절 게시물/18625000) –