Множественный доступ к одному файлу базы данных SQLite через System.Data.SQLite и c #

Как я могу прочитать из FAQ по SQLite, он поддерживает чтение нескольких процессов (SELECT) и только один процесс, записывающий (INSERT, UPDATE, DELETE) базу данных в любой момент времени:

SQLite использует блокировки чтения / записи для управления доступом к базе данных. Когда какой-либо процесс хочет выполнить запись, он должен заблокировать весь файл базы данных на время его обновления. Но обычно это занимает всего несколько миллисекунд. Другие процессы просто ждут, пока писатель закончит, а затем продолжают свои дела.

Я использую System.Data.SQLite < / strong> через c #.

Может ли кто-нибудь объяснить мне, пожалуйста, как именно происходит этот процесс?

Будет ли этот процесс работать автоматически, и запись SQLiteCommand будет просто ждать, если в той же базе данных уже выполняется другая запись SQLiteCommand?

Или, может быть, это вызовет исключение? Что это за?

Извините, но я не нашел информации об этой механике :)

Спасибо.

ОБНОВЛЕНИЕ:

Я нашел сообщение о том, что возникнет исключение с конкретным кодом ошибки

Это утверждение правильно?


person bairog    schedule 13.03.2013    source источник
comment
Пока нет :) Я предпочитаю читать руководства перед кодированием - чтобы получить оптимизированные и правильные решения ...   -  person bairog    schedule 13.03.2013
comment
Если вы предпочитаете optimized and correct solutions, не используйте SQLite для чего-то, для чего он не был оптимизирован; это отличная база данных, но это не отличная многопользовательская база данных.   -  person Tim    schedule 13.03.2013
comment
Мне нужна бесплатная и легкая (я имею в виду нулевую конфигурацию и бессерверную) базу данных для моего проекта. Так что большинство многопользовательских баз данных мне не подходят :)   -  person bairog    schedule 14.03.2013


Ответы (1)


Я исследовал это сам:

Я создал образец базы данных SQLite c:\123.db с одной таблицей Categories, содержащей два поля: ID (uniqueidentifier) ​​и Name (nvarchar).

Затем я написал многопоточный код для имитации множественного доступа на запись к базе данных (не забудьте добавить ссылку System.Data.SQLite на свой проект, если вы используете этот код):

using System;
using System.Data.SQLite;
using System.Threading.Tasks;

namespace SQLiteTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var tasks = new Task[100];

            for (int i = 0; i < 100; i++)
            {
                tasks[i] = new Task(new Program().WriteToDB);
                tasks[i].Start();
            }

            foreach (var task in tasks)
                task.Wait();
        }

        public void WriteToDB()
        {
            try
            {
                using (SQLiteConnection myconnection = new SQLiteConnection(@"Data Source=c:\123.db"))
                {
                    myconnection.Open();
                    using (SQLiteTransaction mytransaction = myconnection.BeginTransaction())
                    {
                        using (SQLiteCommand mycommand = new SQLiteCommand(myconnection))
                        {
                            Guid id = Guid.NewGuid();

                            mycommand.CommandText = "INSERT INTO Categories(ID, Name) VALUES ('" + id.ToString() + "', '111')";
                            mycommand.ExecuteNonQuery();

                            mycommand.CommandText = "UPDATE Categories SET Name='222' WHERE ID='" + id.ToString() + "'";
                            mycommand.ExecuteNonQuery();

                            mycommand.CommandText = "DELETE FROM Categories WHERE ID='" + id.ToString() + "'";
                            mycommand.ExecuteNonQuery();
                        }
                        mytransaction.Commit();
                    }
                }
            }
            catch (SQLiteException ex)
            {
                if (ex.ReturnCode == SQLiteErrorCode.Busy)
                    Console.WriteLine("Database is locked by another process!");
            }
        }
    }
}

В результате на моем Core2Duo E7500 исключение никогда не возникает!

Похоже, SQLite достаточно оптимизирован для моих нужд (блокировка / разблокировка происходит очень быстро и обычно занимает всего несколько миллисекунд, так как SQLite FAQ сообщает нам) - Отлично!

Обратите внимание, что нет необходимости получать целочисленный ErrorCode для SQLiteException - вместо этого вы можете использовать специальное поле enum ReturnCode. Все коды описаны здесь.

Надеюсь, эта информация кому-нибудь поможет.

person bairog    schedule 14.03.2013
comment
Ваш код доказывает, что несколько потоков в рамках одного процесса могут беспрепятственно обращаться к базе данных SQLite. Удалось ли вам проверить, могут ли несколько разных процессов получить доступ к одной и той же базе данных? - person pmont; 10.07.2013
comment
Что ж, первое, что мне приходит в голову, это компиляция этого кода в Test1.exe, а затем добавление Process.Start (Test1.exe) в первую строку Main и перекомпилируйте его в Test2.exe. Вроде работает, но никогда не тестировал ... - person bairog; 11.07.2013
comment
Отличная идея! Я только что протестировал этот подход, и он работает. Многопроцессорный доступ отлично работает с SQLite. - person pmont; 12.07.2013
comment
Просто хочу это дополнить. Я пишу службу ведения журнала гарантированной доставки на основе NLog / SignalR, которая использует System.Data.SQLite для записи в локальную базу данных для фонового потока для отправки этих данных в LogStash, и у меня было 40 потоков, отправляющих сообщения журнала в локальный концентратор SignalR . Это определенно вызывало ошибку SQLite (5): база данных заблокирована исключения, когда этот единственный процесс обрабатывал сообщения из других потоков других процессов. - person James Eby; 04.03.2017
comment
ИСПРАВЛЕНИЕ Я думал, что ошибка SQLite (5): база данных заблокирована - это моя служба Windows, работающая в режиме консоли, улавливающая ошибка и отображение ее ... НО это похоже на внутренний код SQLite, потому что у меня нет ошибок, и я отслеживаю количество вставок, которые я должен вставлять в базу данных, и я не теряю никаких вставок ... Итак, команда BRAVO SQLITE! - person James Eby; 04.03.2017
comment
Итак, для моего теста 6 процессов, по 10 потоков каждый, отправляющие 100 сообщений журнала каждый в центральный узел SignaR Hub. Все 6000 записей журнала были написаны без специального кода, компенсирующего блокировки БД. - person James Eby; 04.03.2017
comment
Звучит действительно здорово! - person bairog; 04.03.2017
comment
@JamesEby SQlite использует внутреннее ведение журнала, поэтому, когда что-то выходит из строя из-за состояния блока или чего-то другого, он пытается повторить попытку, пока не будет выполнено. Итак, это не внутреннее уплотнение, и, конечно же, ваши вставки не теряются. Фактически, если вы видите это сообщение, это означает, что произошла ошибка и позже произойдет повторная попытка. Ошибка блокировки такого рода часто встречается при использовании потоков для вставки. Кроме того, если у вас ОГРОМНОЕ чтение из потоков, следует использовать модель журнала WAL, так что записи и чтения выполняются в отдельных потоках, поэтому запись из потоков не влияет на возможные тысячи одновременных чтений. - person m3nda; 12.08.2018
comment
Я связал это, и у меня была ошибка блокировки базы данных, но я решил ее, используя асинхронные версии этих методов, и он отлично работает. Для запроса SELECT не обязательно использовать метод Async. - person Banana Cake; 16.08.2018