РЕДАКТИРОВАТЬ: я поставил полные 2 сабвуфера и 1 функцию, которые используются.
Я также разместил комментарии вокруг раздела, который со временем замедляется.
Прежде чем кто-либо упоминает FileHelpers, я не хочу использовать сторонний компонент.
Итак, у меня есть файл CSV, 3,5 миллиона строк, и я анализирую каждую строку CSV и вставляю ее в SQLite (неиндексную таблицу), чтобы ускорить работу. Я также буферирую 100 000 строк за раз из CSV - все хорошо с миром, но в моем цикле (после того, как я буферизовал 100 000 строк в список строк, следующий код проходит и разбивает каждую строку, соответственно, наращивается вставка строки и все готово - около 1000 строк обрабатывается в секунду - я могу с этим смириться, но после пары сотен тысяч строк она начинает замедляться до примерно 200 строк в секунду. В конце концов, после примерно 600 000 строк, это ползет до 25 строк и далее (не знаю, в какой момент), после того, как более 2 миллионов строк замедляются до 10-15 строк. Мне бы очень хотелось сохранять 1000 строк в секунду (или даже улучшать их, если это возможно).
Я пропустил некоторый код, подпрограмму fixsquote, которая вызывается для сортировки любых кавычек, а также оператор IF THEN, чтобы определить, есть ли кавычки, и разделить его по-разному - для этой конкретной строки с 3,5 миллионами CSV, есть кавычек нет, поэтому я просто хотел уменьшить количество кода, который я публикую здесь, чтобы было удобнее читать.
Первоначально я использовал древний массив для управления разбиением, и это было то же самое, замедление после X тысяч строк, но, глядя в Интернете, казалось, что список строк будет лучше, поэтому я преобразовал несколько строк кода для использования списка строк в моем цикле, и это не имеет значения. У меня есть чувство, хотя я, кажется, не использую массивы, я сбит с толку возможным замедлением. Что-то или не используется повторно должным образом и может быть проблема стека или кучи, о которых я не знаю?
У меня нет замедления во время массовой вставки - я могу полностью удалить вставку и выполнение в SQLITE, поэтому SQLITE не является проблемой. Это как-то связано со списком или строкой. Я собираюсь попробовать предложение о повышении версии .net - у меня было это на 4.6, так что подниму до 4.7.1 ******
Вот код, может кто-то заметит что-то очевидное.
Private Sub CSVImport
Dim SQLStr As New Text.StringBuilder
Dim BigSQLStr As New Text.StringBuilder
Dim Comma As String = ""
Dim FirstInsertStr As New Text.StringBuilder
Dim BufferT As Integer = 0
Try
If IO.File.Exists(FileName) = True Then
Dim C As Integer
Dim line As String
FirstInsertStr.Clear()
FirstInsertStr.Capacity = 0
FirstInsertStr.Append("INSERT INTO " & Chr(34) & DestinationTableName & Chr(34))
FirstInsertStr.Append(" (" & Chr(34) & "LON" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "LAT" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "NUMBER" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "STREET" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "UNIT" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "CITY" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "DISTRICT" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "REGION" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "POSTCODE" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "ADDRESSIO_ID" & Chr(34))
FirstInsertStr.Append(", " & Chr(34) & "HASH" & Chr(34))
FirstInsertStr.Append(") VALUES (")
BufferT = 0
BigSQLStr.Clear()
BigSQLStr.Capacity = 0
' Create new StreamReader instance with Using block.
Using reader As IO.StreamReader = New IO.StreamReader(FileName)
' Read one line from file
line = reader.ReadLine ' first line in headers so ignore
Do Until line = Nothing
' Stop
Dim BufferRead As New List(Of String)
Dim BufferLoad As Integer = 0
Do Until BufferLoad = 100000
line = reader.ReadLine
If line = Nothing Then
Exit Do
End If
BufferRead.Add(line)
BufferLoad += 1
Loop
Dim Z As Integer = 0
For Z = 0 To BufferLoad - 1
BufferT += 1
Comma = ""
' ************** I BELIEVE THE SLOWDOWN IS WITHIN HERE, FAST AT FIRST, THEN SLOWS DOWN AFTER 30k RECORDS OR SO **********
Dim objFields2 As New List(Of String)
If BufferRead(Z).Contains(Chr(34)) = True Then
BufferRead(Z) = FixsQuote(BufferRead(Z))
objFields2.AddRange(Split(BufferRead(Z), ",", Chr(34), True))
Else
objFields2.AddRange(BufferRead(Z).Split(","))
End If
With SQLStr
.Clear()
.Capacity = 0
For C = 0 To objFields2.Count - 1
If C > 11 Then
Exit For ' we only ever want the first 11 fields.
End If
If C > 0 Then
Comma = ","
End If
If IsDBNull(objFields2(C)) = False Then
If objFields2(C).Contains(Chr(34)) = True Then
If objFields2(C).Replace(Chr(34), "").Length > 0 Then
.Append(Comma & "'" & FixsQuote(objFields2(C).Replace(Chr(34), "")) & "'")
Else
.Append(Comma & "Null")
End If
Else
If objFields2(C).Length > 0 Then
.Append(Comma & "'" & FixsQuote(objFields2(C)) & "'")
Else
.Append(Comma & "Null")
End If
End If
Else
.Append(Comma & "Null")
End If
Next
BigSQLStr.Append(FirstInsertStr.ToString)
BigSQLStr.Append(.ToString)
BigSQLStr.Append(");")
' Now Insert what we have
If BufferT = 1000 Then
Using OleCMD As New SQLite.SQLiteCommand(BigSQLStr.ToString, AddressesIOSQLDB)
OleCMD.CommandTimeout = 0
OleCMD.ExecuteNonQuery()
End Using
BigSQLStr.Clear()
BigSQLStr.Capacity = 0
BufferT = 0
End If
End With
objFields2.Clear()
objFields2 = Nothing
Next
BufferRead.Clear()
' ****************** end of what i believe is the slow down ****************
Loop
If BufferT > 0 Then
Try
Using OleCMD As New SQLite.SQLiteCommand(BigSQLStr.ToString, AddressesIOSQLDB)
OleCMD.CommandTimeout = 0
OleCMD.ExecuteNonQuery()
End Using
Catch ex As Exception
Stop
End Try
BufferT = 0
End If
End Using
End If
Catch ex As Exception
Stop
End Try
BigSQLStr.Clear()
BigSQLStr.Capacity = 0
FirstInsertStr.Clear()
FirstInsertStr.Capacity = 0
SQLStr.Clear()
SQLStr.Capacity = 0
End Sub
Private Function FixsQuote(ByVal s As String) As String
Return s.Replace("'", "''")
End Function
Private Function Split(
ByVal expression As String,
ByVal delimiter As String,
ByVal qualifier As String,
ByVal ignoreCase As Boolean) As List(Of String)
Dim _Statement As String = String.Format("{0}(?=(?:[^{1}]*{1}[^{1}]*{1})*(?![^{1}]*{1}))", Regex.Escape(delimiter), Regex.Escape(qualifier))
Dim _Options As RegexOptions = RegexOptions.Compiled Or RegexOptions.Multiline
If ignoreCase Then _Options = _Options Or RegexOptions.IgnoreCase
Dim _Expression As Regex = New Regex(_Statement, _Options)
Return _Expression.Split(expression).ToList
End Function