Я выполняю большое количество INSERTS в базе данных SQLite. Я использую только один поток. Я загружаю записи для повышения производительности и немного защиты в случае сбоя. В основном я кэширую кучу данных в памяти, а затем, когда я считаю нужным, я просматриваю все эти данные и выполняю INSERTS. Код для этого показан ниже:
public void Commit()
{
using (SQLiteConnection conn = new SQLiteConnection(this.connString))
{
conn.Open();
using (SQLiteTransaction trans = conn.BeginTransaction())
{
using (SQLiteCommand command = conn.CreateCommand())
{
command.CommandText = "INSERT OR IGNORE INTO [MY_TABLE] (col1, col2) VALUES (?,?)";
command.Parameters.Add(this.col1Param);
command.Parameters.Add(this.col2Param);
foreach (Data o in this.dataTemp)
{
this.col1Param.Value = o.Col1Prop;
this. col2Param.Value = o.Col2Prop;
command.ExecuteNonQuery();
}
}
this.TryHandleCommit(trans);
}
conn.Close();
}
}
Теперь я использую следующий трюк, чтобы заставить вещь в конечном итоге работать:
private void TryHandleCommit(SQLiteTransaction trans)
{
try
{
trans.Commit();
}
catch (Exception e)
{
Console.WriteLine("Trying again...");
this.TryHandleCommit(trans);
}
}
Я создаю свою БД следующим образом:
public DataBase(String path)
{
//build connection string
SQLiteConnectionStringBuilder connString = new SQLiteConnectionStringBuilder();
connString.DataSource = path;
connString.Version = 3;
connString.DefaultTimeout = 5;
connString.JournalMode = SQLiteJournalModeEnum.Persist;
connString.UseUTF16Encoding = true;
using (connection = new SQLiteConnection(connString.ToString()))
{
//check for existence of db
FileInfo f = new FileInfo(path);
if (!f.Exists) //build new blank db
{
SQLiteConnection.CreateFile(path);
connection.Open();
using (SQLiteTransaction trans = connection.BeginTransaction())
{
using (SQLiteCommand command = connection.CreateCommand())
{
command.CommandText = DataBase.CREATE_MATCHES;
command.ExecuteNonQuery();
command.CommandText = DataBase.CREATE_STRING_DATA;
command.ExecuteNonQuery();
//TODO add logging
}
trans.Commit();
}
connection.Close();
}
}
}
Затем я экспортирую строку подключения и использую ее для получения новых подключений в разных частях программы.
По-видимому, случайные интервалы, хотя и слишком велики, чтобы игнорировать или иным образом обходить эту проблему, я получаю необработанное SQLiteException: файл базы данных заблокирован. Это происходит, когда я пытаюсь совершить транзакцию. Кажется, что ошибки до этого не возникли. Это не всегда. Иногда все работает без заминки.
- Чтение не выполняется над этими файлами до завершения коммитов.
- У меня есть самый последний SQLite файл.
- Я компилирую для .NET 2.0.
- Я использую VS 2008.
- db - локальный файл.
- Все это действие инкапсулируется в один поток/процесс.
- Защита от вирусов отключена (хотя я думаю, что это было актуально только в том случае, если вы подключались по сети?).
- По сообщению Scotsman я внедрил следующие изменения:
- Режим журнала установлен на "Постоянно"
- Файлы DB, хранящиеся в C:\Docs + Settings\ApplicationData через
System.Windows.Forms.Application.AppData
вызов Windows - Внутреннее исключение
- Проверено на двух разных машинах (хотя очень похожее аппаратное и программное обеспечение)
- Был запущен Process Monitor - никакие посторонние процессы не привязаны к файлам DB - проблема определенно в моем коде...
Кто-нибудь знает, что происходит здесь?
Я знаю, что я просто сбросил весь беспорядок кода, но я пытался понять это слишком долго. Спасибо всем, кто дойдет до конца этого вопроса!
Брайен
ОБНОВЛЕНИЕ:
Спасибо за предложения! Я реализовал многие из предлагаемых изменений. Я чувствую, что мы приближаемся к ответу... однако...
Приведенный выше технический код работает, однако он не детерминирован! Не гарантируется что-либо сделать, кроме спина в нейтральной навсегда. На практике это работает где-то между 1-й и 10-й итерациями. Если я улажу свои коммиты с разумным интервалом, урон будет уменьшен, но я действительно не хочу оставлять вещи в этом состоянии...
Дополнительные предложения приветствуются!