#c# #sqlite #.net-core
#c# #sqlite #.net-ядро
Вопрос:
Мне нужно заполнить таблицу Sqlite предварительно вычисленными значениями. Только одна таблица с текущими ~ 20 строками.
После того, как я повозился день или два, мне удалось получить подход 1, работающий с массовыми вставками, который отлично работает для «меньшего количества записей» (5_000_000 записей), и это довольно быстро. К сожалению, подход 1 зависает где-то на 139_000_000 записей, и к настоящему времени итератор доставит ~ 200_000_000 записей. Консольное приложение не останавливается и не генерирует исключение, которым я мог бы поделиться здесь.
И мне интересно, куда идут данные, потому что объем памяти значительно не увеличивается и файл базы данных не записывается (это происходит при фиксации в конце).
Мой подход 1:
public static void SqliteBulkInsert()
{
CustomIterator citer = new CostumIterator();
using (var connection = new SqliteConnection("Filename=Iterations.db"))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
var command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = @"INSERT INTO iterations (V1, V2) VALUES ($V1, $V2)";
var p1 = command.CreateParameter();
p1.ParameterName = "$V1";
command.Parameters.Add(p1);
var p2 = command.CreateParameter();
p2.ParameterName = "$V2";
command.Parameters.Add(p2);
long i = 0;
while (citer.Next(out byte v1, out byte v2))
{
p1.Value = v1;
p2.Value = v2;
command.ExecuteNonQuery();
if (i % 10_000 == 0) Console.WriteLine(i);
}
transaction.Commit();
}
Console.WriteLine("done");
connection.Close();
}
}
Во втором подходе я попытался создать новую транзакцию для каждого пакета 1_000_000. Но как бы я ни старался, я получал разные исключения, такие как «транзакция команды не принадлежит текущему соединению» и так далее.
Когда я просто добавляю коммит в пакет, как в
if (i % 10_000 == 0)
{
transaction.Commit();
Console.WriteLine(i);
}
Я получаю:
System.InvalidOperationException
HResult=0x80131509
Message=The transaction object is not associated with the same connection object as this command.
Source=Microsoft.Data.Sqlite
StackTrace:
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteNonQuery()
at DevTryOuts.Program.PopulateSqliteCombinationsBulkInsert2() in A:DevTryOutsProgram.cs:line 568
at DevTryOuts.Program.Main(String[] args) in A:DevTryOutsProgram.cs:line 115
Любой намек на то, как я мог бы правильно реализовать новую транзакцию для каждого пакета?
Были некоторые ошибки:
- Итератор не генерирует 200 миллионов записей: он генерирует только около 140 миллионов
- Поэтому он достигает фиксации транзакции (по этой причине последняя запись журнала показывает индекс 139 миллионов
- Он «зависает» (включая полное замораживание ПК) во время записи записей на диск
- Он зависал из-за нехватки места на диске (~ 50 ГБ базы данных) и работал при выборе другого диска с большим количеством свободного места на диске.
Комментарии:
1. В SQLite нет массовых операций. Это встроенная база данных, в ней нет отдельного сервера и нет необходимости в операциях массовой вставки. Единственное, что вы можете исключить, это отдельные записи журнала — для этого и предназначена явная транзакция. Какие фактические исключения вы получаете? Если что-то работает для 10 строк, оно работает для 10 строк.
2. Кроме того, если данные поступают в формате CSV, вы можете открыть их непосредственно через SQLite, как если бы это была база данных SQLite, или получить к ней доступ как к виртуальной таблице и скопировать данные в свою собственную базу данных. Вы могли бы использовать
sqlite3
и использовать, например.import c:/sqlite/city.csv cities
, для импорта данных в новую таблицу.3. Данные поступают не из CSV. Он вычисляется с использованием итератора. Если что-то работает для 10 строк, оно работает для 10 строк. Ну, может быть. В настоящее время я сталкиваюсь с доказательством, что это не обязательно работает с 200 миллионами строк. Мои ожидания — это просто заполнение таблицы. Я начал с EF Core, но это было слишком медленно и заняло слишком много времени. Итак, я работал над подходом 1, чтобы повысить скорость. Я начал с этого фрагмента документа: docs.microsoft.com/en-us/dotnet/standard/data/sqlite /…
4.
When I just add a commit to the batch as in
в этом проблема. На этом транзакция завершается . Он предназначен только для использования в самом конце. Если вы попытаетесь снова использовать ту же транзакцию, вы получите сообщение об ошибке. Если вы хотите совершать пакетные транзакции, вам следует запускать новую транзакцию для каждого пакета5. в любом случае вы не опубликовали ни исходное, ни текущее исключение. Полный текст исключения содержит все внутренние исключения и трассировку стека с вызовами, которые привели к ошибке. Вполне возможно, что исключение уже сообщает вам, что транзакция больше не существует