Новые сообщения [новые:0]
Дайджест
Горячие темы
Избранное [новые:0]
Форумы
Пользователи
Статистика
Статистика нагрузки
Мод. лог
Поиск
|
15.01.2017, 17:19
|
|||
---|---|---|---|
|
|||
BusyTimeout и многопточная запись в БД |
|||
#18+
Добрый день, Подскажите пожалуйста: В моем софте с базой работают два типа потоков: 1. Выполняющие быструю вставку/удаление записей (собственно, это обработчики клиентских кнопок Добавить, Изменить, Удалить на appserver'e). 2. Выполняющие массовую вставку/удаление большого числа (до миллионов) записей (обработчики кнопок Загрузить из файла и Найти и удалить). Массовые операции разбиваются на транзакции по 5000 операций в каждой и встает вопрос: какую паузу стоит выдерживать между массовыми транзакциями чтобы не слишком блокировать работу быстрых ? Массовые операции выполняются относительно редко, быстрые - относительно часто и не хотелось бы чтобы пока Юзер1 грузит данные в базу остальные юзеры не могли быстро добавить или удалить какую-то запись - ну 2-3-5 секунд ожидания это куда не шло, а вот больше уже не хотелось бы. Грубо говоря, есть код: Код: pascal 1. 2. 3. 4. 5. 6. 7.
Для доступа к базе используются компоненты LiteDac от DevArt, судя по исходникам они используют ф-цию sqlite3_busy_timeout sqlite, но не понятно как часто выполняется проверка на предмет того есть ли активные пишущие транзакции или нет ? Т.к. от этого зависит какую паузу нужно поставить между массовыми транзакциями чтобы остальные потоки успели увидеть что база сейчас свободна и успели сделать свои быстрые вставки/удаления ? В хэлпе к LiteDac по этому поводу не слова, там просто написано, что можно задать BusyTimeout, но как он работает ? Если BusyTimeout 15 секунд то что произойдет: ждущая транзакция попробует еще раз через 15 секунд и при не удаче вывалится с ошибкой ИЛИ она будет проверять, что база свободна каждые Х милисекунд и при первой же возможности выполнится ? Спасибо за помощь ! ... |
|||
:
Нравится:
Не нравится:
|
|||
|
17.01.2017, 18:48
|
|||
---|---|---|---|
BusyTimeout и многопточная запись в БД |
|||
#18+
авторКаким стоит сделать PauseTime чтобы остальные потоки не слишком тупили ? Поэкспериментируй. Это самое надежное. Никто не знает какая у тебя реальная нагрузка. Играйся с размером пакетных вставок и паузами. BusyTimeout это совсем другое, в твоей проблеме оно не поможет, скорее помешает. Читай здесь: https://www.sqlite.org/c3ref/busy_handler.html ... |
|||
:
Нравится:
Не нравится:
|
|||
|
17.01.2017, 22:57
|
|||
---|---|---|---|
|
|||
BusyTimeout и многопточная запись в БД |
|||
#18+
автор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.
Судя по static const u8 delays[] = { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; Это и есть интервалы через которые Sqlite проверяет, что база освободилась и можно начинать ждущую транзакцию. Но уверенности в этом нет ))) Не может же быть чтобы никто не знал как в точности BusyTimeout работает ? ) ... |
|||
:
Нравится:
Не нравится:
|
|||
|
18.01.2017, 00:37
|
|||
---|---|---|---|
BusyTimeout и многопточная запись в БД |
|||
#18+
amsdevПо логике должно быть так: 1. Поток1 сделал StartTransaction и что-то долго делает. 2. В это время Поток2 тоже пытается сделать StartTransaction Если BusyTimeout не задан - Поток2 должен сразу отвалиться с ошибкой SQLITE_BUSY. А если задан - Поток2 ждет BusyTimeout и если база не освободилась - вываливается с ошибкой SQLITE_BUSY, а если освободилась - запускает свою транзакцию. Если это работает не так - в BusyTimeout вообще нет смысла !Все так и есть. ааа.... я понял о чем ты волнуешься. Тогда, да, прошу прощения за неверный комментарий. Да, если твой Поток1 занимается вставкой 5000 строк, то при нулевом BusyTimeout, Поток2 с микро-транзакцией должен получить SQLITE_BUSY. Но с другой стороны, зачем тебе два потока? Почему вдруг "Загрузить из файла" и "Добавить" должны выполняться параллельно??? ... |
|||
:
Нравится:
Не нравится:
|
|||
|
19.01.2017, 21:58
|
|||
---|---|---|---|
|
|||
BusyTimeout и многопточная запись в БД |
|||
#18+
авторНо с другой стороны, зачем тебе два потока? Почему вдруг "Загрузить из файла" и "Добавить" должны выполняться параллельно??? Потому что это сервер и запросы от клиентов (на чтение, запись, модификацию и т.д.) выполняются в потоках. Почитал мэйлинг лист 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 ? Там мутновато в справке на эту тему.... А у меня как раз ситуация когда это надо т.к. приходят потоки от клиентов и каждый раз создается новое соединение. Пулинг по идее тут должен сильно помочь, но как он у них работает не вполне понятно.... ... |
|||
:
Нравится:
Не нравится:
|
|||
|
26.01.2017, 16:30
|
|||
---|---|---|---|
|
|||
BusyTimeout и многопточная запись в БД |
|||
#18+
автор1. SharedCache выключить 2. Для читающих соединений включить read_uncommited После тестов уточню оптимальный сеттинг, может кому будет полезно: 1. Read_uncommited должен использоваться совместно с SharedCache т.е. они оба должны быть включены. Что это даст: пишущий поток не будет блокировать читающие потоки, если эти опции не включены то пока запись не закончена из базы ничего нельзя читать. Другой вариант - это использовать режим WAL, но я его еще не пробовал и не знаю какие там подводные камни. ... |
|||
:
Нравится:
Не нравится:
|
|||
|
26.01.2017, 16:39
|
|||
---|---|---|---|
|
|||
BusyTimeout и многопточная запись в БД |
|||
#18+
авторИ еще вопрос в догонку: кто-нибудь понимает как в точности работает Connection Pooling в DevArt LiteDAC ? И отвечу на свой же вопрос: чтобы работал пулинг нужно создавать (в потоках) TLiteConnection c в точности одинаковыми настройками. Т.е. у них кэшируется подключения к самой длле, после удаления TLiteConnection соединение с дллкой остается на заданное время. При создании нового TLiteConnection с теми же настройками - ему подсовывается кэшированное соединение с длл если оно есть в пуле и его настройки такие же как у вновь созданного TLiteConnection. ... |
|||
:
Нравится:
Не нравится:
|
|||
|
|
start [/forum/topic.php?fid=54&tablet=1&tid=2008536]: |
0ms |
get settings: |
9ms |
get forum list: |
14ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
55ms |
get topic data: |
10ms |
get forum data: |
2ms |
get page messages: |
101ms |
get tp. blocked users: |
2ms |
others: | 275ms |
total: | 474ms |
0 / 0 |