Гость
Форумы / SQLite [игнор отключен] [закрыт для гостей] / BusyTimeout и многопточная запись в БД / 7 сообщений из 7, страница 1 из 1
15.01.2017, 17:19
    #39385039
amsdev
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
BusyTimeout и многопточная запись в БД
Добрый день,

Подскажите пожалуйста:

В моем софте с базой работают два типа потоков:

1. Выполняющие быструю вставку/удаление записей (собственно, это обработчики клиентских кнопок Добавить, Изменить, Удалить на appserver'e).

2. Выполняющие массовую вставку/удаление большого числа (до миллионов) записей (обработчики кнопок Загрузить из файла и Найти и удалить).

Массовые операции разбиваются на транзакции по 5000 операций в каждой и встает вопрос: какую паузу стоит выдерживать между массовыми транзакциями чтобы не слишком блокировать работу быстрых ? Массовые операции выполняются относительно редко, быстрые - относительно часто и не хотелось бы чтобы пока Юзер1 грузит данные в базу остальные юзеры не могли быстро добавить или удалить какую-то запись - ну 2-3-5 секунд ожидания это куда не шло, а вот больше уже не хотелось бы.

Грубо говоря, есть код:

Код: pascal
1.
2.
3.
4.
5.
6.
7.
while not done do
   begin
   StartTransaction;
   Make5000Inserts;
   Commit;
   Sleep(PauseTime); // Каким стоит сделать PauseTime чтобы остальные потоки не слишком тупили ?
   end;



Для доступа к базе используются компоненты LiteDac от DevArt, судя по исходникам они используют ф-цию sqlite3_busy_timeout sqlite, но не понятно как часто выполняется проверка на предмет того есть ли активные пишущие транзакции или нет ? Т.к. от этого зависит какую паузу нужно поставить между массовыми транзакциями чтобы остальные потоки успели увидеть что база сейчас свободна и успели сделать свои быстрые вставки/удаления ?

В хэлпе к LiteDac по этому поводу не слова, там просто написано, что можно задать BusyTimeout, но как он работает ? Если BusyTimeout 15 секунд то что произойдет: ждущая транзакция попробует еще раз через 15 секунд и при не удаче вывалится с ошибкой ИЛИ она будет проверять, что база свободна каждые Х милисекунд и при первой же возможности выполнится ?

Спасибо за помощь !
...
Рейтинг: 0 / 0
17.01.2017, 18:48
    #39386326
White Owl
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
BusyTimeout и многопточная запись в БД
авторКаким стоит сделать PauseTime чтобы остальные потоки не слишком тупили ?
Поэкспериментируй.
Это самое надежное. Никто не знает какая у тебя реальная нагрузка. Играйся с размером пакетных вставок и паузами.

BusyTimeout это совсем другое, в твоей проблеме оно не поможет, скорее помешает. Читай здесь: https://www.sqlite.org/c3ref/busy_handler.html
...
Рейтинг: 0 / 0
17.01.2017, 22:57
    #39386471
amsdev
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
BusyTimeout и многопточная запись в БД
авторBusyTimeout это совсем другое, в твоей проблеме оно не поможет, скорее помешает.

Тогда не совсем понятно в чем его смысл ?

По логике должно быть так:

1. Поток1 сделал StartTransaction и что-то долго делает.
2. В это время Поток2 тоже пытается сделать StartTransaction

Если BusyTimeout не задан - Поток2 должен сразу отвалиться с ошибкой SQLITE_BUSY. А если задан - Поток2 ждет BusyTimeout и если база не освободилась - вываливается с ошибкой SQLITE_BUSY, а если освободилась - запускает свою транзакцию. Если это работает не так - в BusyTimeout вообще нет смысла !

В исходниках sqlite нашел функцию:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
/*
** This routine implements a busy callback that sleeps and tries
** again until a timeout value is reached.  The timeout value is
** an integer number of milliseconds passed in as the first
** argument.
*/
static int sqliteDefaultBusyCallback(
 void *ptr,               /* Database connection */
 int count                /* Number of times table has been busy */
){
#if SQLITE_OS_WIN || HAVE_USLEEP
  static const u8 delays[] =
     { 1, 2, 5, 10, 15, 20, 25, 25,  25,  50,  50, 100 };
  static const u8 totals[] =
     { 0, 1, 3,  8, 18, 33, 53, 78, 103, 128, 178, 228 };
# define NDELAY ArraySize(delays)
  sqlite3 *db = (sqlite3 *)ptr;
  int timeout = db->busyTimeout;
  int delay, prior;

  assert( count>=0 );
  if( count < NDELAY ){
    delay = delays[count];
    prior = totals[count];
  }else{
    delay = delays[NDELAY-1];
    prior = totals[NDELAY-1] + delay*(count-(NDELAY-1));
  }
  if( prior + delay > timeout ){
    delay = timeout - prior;
    if( delay<=0 ) return 0;
  }
  sqlite3OsSleep(db->pVfs, delay*1000);
  return 1;
#else
  sqlite3 *db = (sqlite3 *)ptr;
  int timeout = ((sqlite3 *)ptr)->busyTimeout;
  if( (count+1)*1000 > timeout ){
    return 0;
  }
  sqlite3OsSleep(db->pVfs, 1000000);
  return 1;
#endif
} 



Судя по

static const u8 delays[] = { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 };

Это и есть интервалы через которые Sqlite проверяет, что база освободилась и можно начинать ждущую транзакцию.

Но уверенности в этом нет )))

Не может же быть чтобы никто не знал как в точности BusyTimeout работает ? )
...
Рейтинг: 0 / 0
18.01.2017, 00:37
    #39386516
White Owl
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
BusyTimeout и многопточная запись в БД
amsdevПо логике должно быть так:

1. Поток1 сделал StartTransaction и что-то долго делает.
2. В это время Поток2 тоже пытается сделать StartTransaction

Если BusyTimeout не задан - Поток2 должен сразу отвалиться с ошибкой SQLITE_BUSY. А если задан - Поток2 ждет BusyTimeout и если база не освободилась - вываливается с ошибкой SQLITE_BUSY, а если освободилась - запускает свою транзакцию. Если это работает не так - в BusyTimeout вообще нет смысла !Все так и есть.

ааа.... я понял о чем ты волнуешься. Тогда, да, прошу прощения за неверный комментарий.
Да, если твой Поток1 занимается вставкой 5000 строк, то при нулевом BusyTimeout, Поток2 с микро-транзакцией должен получить SQLITE_BUSY.
Но с другой стороны, зачем тебе два потока? Почему вдруг "Загрузить из файла" и "Добавить" должны выполняться параллельно???
...
Рейтинг: 0 / 0
19.01.2017, 21:58
    #39387969
amsdev
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
BusyTimeout и многопточная запись в БД
авторНо с другой стороны, зачем тебе два потока? Почему вдруг "Загрузить из файла" и "Добавить" должны выполняться параллельно???

Потому что это сервер и запросы от клиентов (на чтение, запись, модификацию и т.д.) выполняются в потоках.

Почитал мэйлинг лист sqlite и насколько я понял, для лучшей работы в условиях сервера оптимальные настройки такие:

1. SharedCache выключить
2. Для читающих соединений включить read_uncommited
3. Для пишущих соединений делать BEGIN IMMEDIATE при запуске транзакций.
4. Для пишущих соединений сделать BusyTimeout в районе 20-30 секунд.
5. Запуск BEGIN IMMEDIATE делать в try-catch и если ошибка SQLITE_BUSY или SQLITE_LOCKED - ждать сколько-то милисекунд и пробовать еще. Фишка там в том, что даже при заданном BusyTimeout возможны варианты когда все равно будет ошибка Busy или Locked (там всякие нюансы с shared, reserved, exclusive locks и их переходами от одного вида в другой. BEGIN IMMEDIATE должен свести эти проблемы к минимуму).
6. Batch операции делать для 5-7 тысяч строк за транзакцию и разделять их sleep'ом в 100-150 милисекунд.

В сумме это должно дать не плохую производительность и конкурентность. Буду пробовать вобщем.

И еще вопрос в догонку: кто-нибудь понимает как в точности работает Connection Pooling в DevArt LiteDAC ? Там мутновато в справке на эту тему.... А у меня как раз ситуация когда это надо т.к. приходят потоки от клиентов и каждый раз создается новое соединение. Пулинг по идее тут должен сильно помочь, но как он у них работает не вполне понятно....
...
Рейтинг: 0 / 0
26.01.2017, 16:30
    #39392266
amsdev
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
BusyTimeout и многопточная запись в БД
автор1. SharedCache выключить
2. Для читающих соединений включить read_uncommited

После тестов уточню оптимальный сеттинг, может кому будет полезно:

1. Read_uncommited должен использоваться совместно с SharedCache т.е. они оба должны быть включены. Что это даст: пишущий поток не будет блокировать читающие потоки, если эти опции не включены то пока запись не закончена из базы ничего нельзя читать. Другой вариант - это использовать режим WAL, но я его еще не пробовал и не знаю какие там подводные камни.
...
Рейтинг: 0 / 0
26.01.2017, 16:39
    #39392284
amsdev
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
BusyTimeout и многопточная запись в БД
авторИ еще вопрос в догонку: кто-нибудь понимает как в точности работает Connection Pooling в DevArt LiteDAC ?

И отвечу на свой же вопрос: чтобы работал пулинг нужно создавать (в потоках) TLiteConnection c в точности одинаковыми настройками. Т.е. у них кэшируется подключения к самой длле, после удаления TLiteConnection соединение с дллкой остается на заданное время. При создании нового TLiteConnection с теми же настройками - ему подсовывается кэшированное соединение с длл если оно есть в пуле и его настройки такие же как у вновь созданного TLiteConnection.
...
Рейтинг: 0 / 0
Форумы / SQLite [игнор отключен] [закрыт для гостей] / BusyTimeout и многопточная запись в БД / 7 сообщений из 7, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]