Чи безпечно SQLite3 обробляє одночасний доступ за допомогою декількох процесів читання / запису з однієї БД? Чи є з цього винятки з платформи?
Чи безпечно SQLite3 обробляє одночасний доступ за допомогою декількох процесів читання / запису з однієї БД? Чи є з цього винятки з платформи?
Відповіді:
Якщо більшість цих одночасних доходів є читаннями (наприклад, SELECT), SQLite може дуже добре впоратися з ними. Але якщо ви почнете писати одночасно, суперечка щодо блокування може стати проблемою. Тоді багато чого залежатиме від того, наскільки швидко працює ваша файлова система, оскільки сам двигун SQLite надзвичайно швидкий і має багато розумних оптимізацій, щоб мінімізувати суперечки. Особливо SQLite 3.
У більшості програм для настільних ПК / ноутбуків / планшетів / телефонів SQLite досить швидкий, оскільки не вистачає одночасності. (Firefox широко використовує SQLite для закладок, історії тощо)
Для серверних додатків хтось деякий час тому сказав, що що-небудь менше 100 000 переглядів сторінок на день може ідеально оброблятися базою даних SQLite в типових сценаріях (наприклад, блоги, форуми), і я ще не бачив жодних доказів протилежного. Насправді, із сучасними дисками та процесорами, 95% веб-сайтів та веб-сервісів добре працювали б із SQLite.
Якщо ви хочете по-справжньому швидкого доступу для читання / запису, використовуйте базу даних SQLite в пам'яті . Оперативна пам’ять на кілька порядків швидша, ніж диск.
Так, SQLite добре обробляє паралельність, але це не найкраще з точки зору продуктивності. З того, що я можу сказати, винятків із цього немає. Деталі розміщені на сайті SQLite: https://www.sqlite.org/lockingv3.html
Ця заява викликає інтерес: "Модуль пейджера гарантує, що зміни відбуваються відразу, що або відбуваються всі зміни, або жодна з них не відбувається, що два або більше процесів не намагаються одночасно отримати доступ до бази даних несумісними способами".
Так. Давайте розберемося, чому
Всі зміни в межах однієї транзакції в SQLite або відбуваються повністю, або взагалі не відбуваються
Така підтримка ACID, а також одночасне читання / запис забезпечується 2 - ма способами - з допомогою так званого журналирования (назвемо його « старим ») або каротаж записи вперед (дозволяє називати його « новий шлях »)
У цьому режимі SQLite використовує блокування DATABASE-LEVEL . Це важливий момент для розуміння.
Це означає, що коли йому потрібно щось прочитати / записати, він спочатку набуває блокування у файлі бази даних ENTIRE . Кілька читачів можуть співіснувати і паралельно щось читати
Під час написання він гарантує придбання ексклюзивного блокування, і жоден інший процес не читає / записує одночасно, а значить, записи є безпечними.
Ось чому тут вони кажуть, що SQlite здійснює серіалізаційні транзакції
Оскільки для цього потрібно заблокувати всю базу даних кожного разу, і всі чекають, поки процес страждання надходить одночасно, а такі паралельні записи / читання мають досить низьку продуктивність
Перш ніж написати щось у файл бази даних, SQLite спочатку збереже фрагмент, який потрібно змінити у тимчасовий файл. Якщо щось трапиться в середині запису у файл бази даних, він підніме цей тимчасовий файл і відновить зміни з нього
У цьому випадку всі записи додаються до тимчасового файлу ( журнал попереднього запису ), і цей файл періодично об'єднується з вихідною базою даних. Коли SQLite щось шукає, спершу слід перевірити цей тимчасовий файл, а якщо нічого не знайдено, перейти до основного файлу бази даних.
Як результат, читачі не конкурують із письменниками, а продуктивність значно краща порівняно зі Старим Шляхом.
SQlite сильно залежить від основної функції блокування файлової системи, тому її слід використовувати з обережністю, детальніше тут
Ви також можете зіткнутися з помилкою заблокованої бази даних , особливо в режимі проїзду, тому ваша програма повинна бути розроблена з урахуванням цієї помилки
Здається, ніхто не згадував режим WAL (Write Ahead Log). Переконайтесь, що транзакції належним чином організовані та з увімкненим режимом WAL, немає необхідності тримати базу даних заблокованою, коли люди читають речі під час оновлення.
Єдине питання полягає в тому, що в якийсь момент WAL необхідно повторно включити в основну базу даних, і це робиться, коли завершується останнє підключення до бази даних. На дуже зайнятому сайті вам може знадобитися кілька секунд, щоб усі з'єднання були близькими, але 100 Кт звернень на день не повинні бути проблемою.
database is locked
помилка буде підвищена письменником
У 2019 році є два нові параметри запису, які ще не випущені, але доступні в окремих галузях.
Перевага цього режиму журналу перед звичайним режимом «wal» полягає в тому, що автори можуть продовжувати писати в один Wal-файл, а інший перевіряється.
ПОЧАТИ СУЧАСНИЙ - посилання на детальний док
Розширення BEGIN CONCURRENT дозволяє багатьом авторам одночасно обробляти транзакції запису, якщо база даних перебуває в режимі "wal" або "wal2", хоча система все ще послідовно виконує команди COMMIT.
Коли транзакція запису відкривається за допомогою "BEGIN CONCURRENT", фактично блокування бази даних відкладається, поки не буде виконано COMMIT. Це означає, що будь-яка кількість транзакцій, розпочатих з BEGIN CONCURRENT, може відбуватися одночасно. Система використовує оптимістичне блокування рівня сторінки, щоб запобігти здійсненню конфліктних одночасних транзакцій.
Разом вони присутні в початковому-одночасно-wal2 або в кожному окремому власному відділенні .
begin concurrent
матеріал.
SQLite має блокування читачів-авторів на рівні бази даних. Кілька з'єднань (можливо, належать різним процесам) можуть одночасно зчитувати дані з однієї бази даних, але лише один може записувати в базу даних.
SQLite підтримує необмежену кількість одночасних читачів, але це дозволить лише одному автору в будь-який момент. Для багатьох ситуацій це не проблема. Черга письменника вгору. Кожна програма працює, що її база даних працює швидко і продовжується, і жодне блокування не триває більше кількох десятків мілісекунд. Але є деякі програми, які потребують більшої сумісності, і ці програми, можливо, повинні шукати інше рішення. - Відповідне використання для SQLite @ SQLite.org
Блокування читачів-записувачів дозволяє незалежну обробку транзакцій, і вона реалізується за допомогою ексклюзивних та спільних блокувань на рівні бази даних.
Перед тим, як з'єднання виконує операцію запису в базі даних, слід отримати ексклюзивний замок. Після отримання ексклюзивного блокування операції зчитування та запису з інших з'єднань блокуються до повторного звільнення блокування.
SQLite має таблицю блокування, яка допомагає блокувати базу даних якомога пізніше під час операції запису, щоб забезпечити максимальну одночасність.
Початковий стан НЕ ЗАБЕЗПЕЧЕНО, і в цьому стані з'єднання ще не отримало доступу до бази даних. Коли до бази даних підключено процес і навіть розпочато транзакцію з BEGIN, з'єднання все ще знаходиться в режимі НЕ ЗАБЕЗПЕЧЕНО.
Після НЕЗАЄМОГО стану наступним станом є стан СЕКРАНІ. Для того, щоб мати можливість читати (не записувати) дані з бази даних, з'єднання спочатку повинно перейти в стан SHARED, отримавши БІЛЬШЕ Блокування. Кілька з’єднань можуть одночасно отримувати та підтримувати блоки SHARED, тому багато з’єднань можуть читати дані з однієї бази даних одночасно. Але поки навіть лише один блок SHARED залишається невипущеним, жодне з'єднання не може успішно завершити запис у базу даних.
Якщо з'єднання хоче записати в базу даних, воно спочатку має заблокувати заблокований.
Одночасно може бути активним лише один заблокований замок, хоча кілька блоків SHARED можуть співіснувати з одним блоком RESERVED. RESERVED відрізняється від PENDING тим, що нові блоки SHARED можна придбати, поки є заблокований замок. - Блокування файлів та сумісність у версії 3 SQLite @ SQLite.org
Після того, як з'єднання отримає RESERVED-замок, воно може почати обробку операцій з модифікації бази даних, хоча ці зміни можна проводити лише в буфері, а не насправді записати на диск. Зміни, внесені до вмісту зчитування, зберігаються в буфері пам'яті. Коли з'єднання хоче надіслати модифікацію (або транзакцію), необхідно оновити замок RESERVED до ЕКСКЛЮЗИВНОГО блокування. Щоб дістати замок, спершу потрібно підняти замок до ВІДКЛЮЧЕНОГО замка.
Блокування PENDING означає, що процес, що тримає замок, хоче якомога швидше записати в базу даних і просто чекає, коли всі поточні блоки SHARED будуть очищені, щоб він міг отримати ЕКСКЛЮЗИВНИЙ блокування. Ніякі нові блоки SHARED проти бази даних заборонені, якщо блокування PENDING активне, хоча існуючі блоки SHARED дозволено продовжувати.
ЕКСКЛЮЗИВНИЙ замок потрібен для запису у файл бази даних. У файлі дозволено лише одне ЕКСКЛЮЗИВНЕ блокування, і жоден інший замок будь-якого типу не може співіснувати із ЕКСКЛЮЗИВНИМ замком. Для того, щоб збільшити одночасність, SQLite працює над тим, щоб мінімізувати кількість часу, коли зберігаються ЕКСКЛЮЗИВНІ блокування. - Блокування файлів та сумісність у версії 3 SQLite @ SQLite.org
Отже, ви можете сказати, що SQLite безпечно обробляє одночасний доступ за допомогою декількох процесів, що записуються в один і той же БД, просто тому, що він не підтримує його! Ви отримаєте SQLITE_BUSY
або SQLITE_LOCKED
для другого автора, коли він потрапить на обмеження повторного спроби.
Цей потік старий, але я думаю, що було б добре поділитися результатами моїх тестів, зроблених на sqlite: я запустив 2 екземпляри програми python (різні процеси, та сама програма), виконуючи операції SELECT та UPDATE sql команди в рамках транзакції з ЕКСКЛЮЗИВНІм блокуванням та таймаутом, встановленим на 10 секунд, щоб отримати замок, і результат був розчарувальним. Кожен екземпляр робив у кроці 10000 кроків:
Навіть якщо sqlite отримав ексклюзивний замок на транзакцію, загальна кількість дійсно виконаних циклів не дорівнювала 20 000, але менше (загальна кількість ітерацій за один лічильник, що рахується для обох процесів). Програма Python майже не кинула жодного винятку (лише один раз під час вибору за 20 виконання). Ревізія sqlite в момент тестування становила 3,66, а python v3.3 CentOS 6.5. На мою думку, краще знайти більш надійний продукт для подібної роботи або обмежитися записом на sqlite до єдиного унікального процесу / потоку.
with con
цього достатньо.