2010-07-29 22 views
5

Pracuję więc nad bazą danych, którą dodam do moich przyszłych projektów jako pewnego rodzaju db wspierający, ale mam trochę problem, zwłaszcza dzienniki.Sql Server 2008 Tuning z dużymi transakcjami (700k + wiersze/transakcja)

Baza danych zasadniczo musi być aktualizowana raz w miesiącu. Główna tabela musi zostać wyczyszczona, a następnie uzupełniona z pliku CSV. Problem polega na tym, że serwer Sql wygeneruje dla niego dziennik, który jest MEGA duży. Udało mi się go wypełnić raz, ale chciałem przetestować cały proces, oczyszczając go, a następnie uzupełniając.

Wtedy pojawia się błąd, że plik dziennika jest wypełniony. Przeskakuje z 88 MB (po zmniejszeniu poprzez plan konserwacji) do 248 MB, a następnie zatrzymuje cały proces i nigdy się nie kończy.

Ograniczam jego wzrost do 256 MB, zwiększając o 16 MB, dlatego nie udało się, ale w rzeczywistości nie potrzebuję go do logowania w ogóle. Czy istnieje sposób na całkowite pominięcie rejestrowania każdego zapytania w bazie danych?

Dzięki za wszelkie odpowiedzi z góry!

EDYTOWANIE: Zgodnie z sugestiami @ mattmc3 zaimplementowałem SqlBulkCopy dla całej procedury. Działa AMAZING, z tym, że moja pętla w jakiś sposób zawiesza się na ostatnim pozostałym fragmencie, który musi zostać wstawiony. Nie jestem zbyt pewny, gdzie idę źle, cholera, nawet nie wiem, czy to jest odpowiednia pętla, więc byłbym wdzięczny za pomoc.

Wiem, że jest to problem z ostatnimi wywołaniami GetDataTable lub SetSqlBulkCopy. Próbuję wstawić 788189 wiersze, 788000 wejść i pozostałe 189 są upaść ...

string[] Rows; 

using (StreamReader Reader = new StreamReader("C:/?.csv")) { 
    Rows = Reader.ReadToEnd().TrimEnd().Split(new char[1] { 
     '\n' 
    }, StringSplitOptions.RemoveEmptyEntries); 
}; 

int RowsInserted = 0; 

using (SqlConnection Connection = new SqlConnection("")) { 
    Connection.Open(); 

    DataTable Table = null; 

    while ((RowsInserted < Rows.Length) && ((Rows.Length - RowsInserted) >= 1000)) { 
     Table = GetDataTable(Rows.Skip(RowsInserted).Take(1000).ToArray()); 

     SetSqlBulkCopy(Table, Connection); 

     RowsInserted += 1000; 
    }; 

    Table = GetDataTable(Rows.Skip(RowsInserted).ToArray()); 

    SetSqlBulkCopy(Table, Connection); 

    Connection.Close(); 
}; 

static DataTable GetDataTable(
    string[] Rows) { 
    using (DataTable Table = new DataTable()) { 
     Table.Columns.Add(new DataColumn("A")); 
     Table.Columns.Add(new DataColumn("B")); 
     Table.Columns.Add(new DataColumn("C")); 
     Table.Columns.Add(new DataColumn("D")); 

     for (short a = 0, b = (short)Rows.Length; a < b; a++) { 
      string[] Columns = Rows[a].Split(new char[1] { 
       ',' 
      }, StringSplitOptions.RemoveEmptyEntries); 

      DataRow Row = Table.NewRow(); 

      Row["A"] = Columns[0]; 
      Row["B"] = Columns[1]; 
      Row["C"] = Columns[2]; 
      Row["D"] = Columns[3]; 

      Table.Rows.Add(Row); 
     }; 

     return (Table); 
    }; 
} 

static void SetSqlBulkCopy(
    DataTable Table, 
    SqlConnection Connection) { 
    using (SqlBulkCopy SqlBulkCopy = new SqlBulkCopy(Connection)) { 
     SqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("A", "A")); 
     SqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("B", "B")); 
     SqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("C", "C")); 
     SqlBulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping("D", "D")); 

     SqlBulkCopy.BatchSize = Table.Rows.Count; 
     SqlBulkCopy.DestinationTableName = "E"; 
     SqlBulkCopy.WriteToServer(Table); 
    }; 
} 

EDIT/KOD KOŃCOWA: więc aplikacja jest już gotowy i działa niesamowite, i dość szybki! @ Mattmc3, dzięki za pomoc! Oto ostateczny kod dla każdego, komu może się przydać:

List<string> Rows = new List<string>(); 

using (StreamReader Reader = new StreamReader(@"?.csv")) { 
    string Line = string.Empty; 

    while (!String.IsNullOrWhiteSpace(Line = Reader.ReadLine())) { 
     Rows.Add(Line); 
    }; 
}; 

if (Rows.Count > 0) { 
    int RowsInserted = 0; 

    DataTable Table = new DataTable(); 

    Table.Columns.Add(new DataColumn("Id")); 
    Table.Columns.Add(new DataColumn("A")); 

    while ((RowsInserted < Rows.Count) && ((Rows.Count - RowsInserted) >= 1000)) { 
     Table = GetDataTable(Rows.Skip(RowsInserted).Take(1000).ToList(), Table); 

     PerformSqlBulkCopy(Table); 

     RowsInserted += 1000; 

     Table.Clear(); 
    }; 

    Table = GetDataTable(Rows.Skip(RowsInserted).ToList(), Table); 

    PerformSqlBulkCopy(Table); 
}; 

static DataTable GetDataTable(
    List<string> Rows, 
    DataTable Table) { 
    for (short a = 0, b = (short)Rows.Count; a < b; a++) { 
     string[] Columns = Rows[a].Split(new char[1] { 
      ',' 
     }, StringSplitOptions.RemoveEmptyEntries); 

     DataRow Row = Table.NewRow(); 

     Row["A"] = ""; 

     Table.Rows.Add(Row); 
    }; 

    return (Table); 
} 

static void PerformSqlBulkCopy(
    DataTable Table) { 
    using (SqlBulkCopy SqlBulkCopy = new SqlBulkCopy(@"", SqlBulkCopyOptions.TableLock)) { 
     SqlBulkCopy.BatchSize = Table.Rows.Count; 
     SqlBulkCopy.DestinationTableName = ""; 
     SqlBulkCopy.WriteToServer(Table); 
    }; 
} 
+0

Kilka sugestii, jeśli chcesz przyspieszyć to jeszcze bardziej. 1.) Zamiast robienia Reader.ReadToEnd(), wykonaj pętlę i rób Reader.ReadLine() po jednej linii na raz. Zajmie to mniej pamięci. 2.) Jeśli nikt nie będzie mieć dostępu do twojego stołu podczas ładowania, użyj opcji 'SqlBulkCopyOptions.TableLock'. 3.) Aby zaoszczędzić trochę kodu, obiekt SqlBulkCopy zawiera mapowania kolumn, jeśli nazwy kolumn są takie same, jak w tabeli docelowej, a ponieważ obsługuje się fragmentację, nie ma powodu, aby ustawić .BatchSize albo. Szczęśliwe kodowanie! – mattmc3

+0

Na temat przekierowywania kolumn będzie działać, jeśli: 'DBTable = {Id (PK, IDENTITY), A, B, C, D}', ale 'DataTable = {A, B, C, D}'? Myślę, że to sprawiało mi kłopoty, dlatego je wyszczególniłem, ale znowu mogłem to jakoś skręcić ... – Gup3rSuR4c

+0

Cóż, gotowe! Zaimplementowałem wszystko, co poleciłeś i działa NIESAMOWITE! Pamięć została przecięta na pół do ~ 85 MB, a cała operacja trwa około 45 sekund. I wymyśliłem powyższe kolumny, miałem rację, ale właśnie dodałem symbol zastępczy dla "Id" i zadziałało. 'DZIĘKUJĘ ZA POMAGANIE MNIE W TEJ I NAUCZANIE MNIE O RZECZY NIGDY NIE ZNAJDOWAŁO !!!' – Gup3rSuR4c

Odpowiedz

5

Jeśli robisz luzem wstawić do tabeli w SQL Server, który jest, jak należy to robić (BCP, Bulk Insert, Insert Into...Select lub w .NET, klasa SqlBulkCopy) można użyć „bulk Zapisane” model odzyskiwania. Gorąco polecam czytanie artykułów MSDN na temat modeli odzyskiwania: http://msdn.microsoft.com/en-us/library/ms189275.aspx

+0

Myślałem, że SqlBulkCopy (które jest to, czego bym używał od operacji jest wykonywana przez aplikację konsoli) ma być używany od stołu do stołu? – Gup3rSuR4c

+0

O nie ... to od DataTable do tabeli SQL Server. Dane są ładowane do obiektu System.Data.DataTable, który pasuje do tabeli docelowej. Możesz pobrać te dane do DataTable z pliku, z zapytania, z obiektów busniess ... jakkolwiek chcesz. Polecam pobranie kawałka około 1000 rekordów, wykonanie masowej kopii za pomocą obiektu SqlBulkCopy, a następnie wyczyszczenie DataTable i wykonanie kolejnej porcji. Za kulisami obiekt SqlBulkCopy używa właśnie tych samych udogodnień, co instrukcja 'Bulk Insert'. Robiłem ETL w ten sposób od lat i jest to szybkie i proste. – mattmc3

+0

Ok. Próbuję teraz napisać kod, nigdy wcześniej nie korzystałem z DataTables i muszę pamiętać o dobrych metodach "OL Sql", ponieważ nie używałem ich od momentu wyskakiwania Linq ... :) W każdym razie, dzięki za pomoc ! – Gup3rSuR4c

1

Nie ma sposobu na ominięcie za pomocą dziennika transakcji w SQL Server.

+2

Musisz użyć dziennika - to prawda. Możesz jednak zminimalizować wpływ rejestrowania, wybierając alternatywne modele odzyskiwania i mając strategiczne podejście do wstawiania danych. – mattmc3

2

Możesz ustawić model odzyskiwania dla każdej bazy danych oddzielnie. Może model odzyskiwania prosty zadziała. Prosty model:

Automatycznie odzyskuje obszar dziennika, aby zmniejszyć wymagania dotyczące miejsca, zasadniczo eliminując konieczność zarządzania obszarem dziennika transakcji.

Przeczytaj na ten temat here.

Powiązane problemy