Сучасна диджитал-освіта для дітей — безоплатне заняття в GoITeens ×
Mazda CX 30
×

Оновлюємо схему бази даних без простоїв

Мене звати Юрій Івон, я співпрацюю з компанією EPAM як Senior Solution Architect. Хотів би поділитись досвідом та міркуваннями на тему оновлення схеми бази без простоїв (zero-downtime deployment).

Існує чимало статей про розгортання без простоїв і багато його аспектів є досить очевидними. Rolling upgrades та blue-green deployment — це все добре і може здаватися не складним, якщо розглядати лише оновлення сервісів, що не мають стану (stateless services). На жаль, одну з найскладніших частин досить часто упускають. Мова йде про те, як робити зміни в схемі даних в цьому випадку.

Незалежно від механізму зберігання, схема даних завжди існує: чи то жорстка схема в реляційній базі, чи структура на рівні програми, через яку йде взаємодія з колекцією в базі з довільною схемою. Зчитані зі сховища фрагменти інформації повинні бути зрозумілі клієнту, навіть якщо сховище не накладає жодних обмежень на їх структуру.

Припустимо, що існує робоча система, яка зберігає кілька терабайтів у своїй базі даних. У новій версії нам потрібно змінити її схему, щоб реалізувати нові функції та покращити продуктивність. Попри обсяг бази, її треба оновити без простоїв та якомога швидше. Примітивні зміни, які нам можуть знадобитися, можна класифікувати наступним чином:

  • Додавання поля або іншого об’єкту бази.
  • Видалення поля або іншого об’єкту бази.
  • Перейменування поля або іншого об’єкту бази.
  • Зміна типу поля.

Складніші зміни, такі як переміщення поля до іншої колекції через нормалізацію або денормалізацію, складаються з цих примітивів.

Додавання поля або будь-якого іншого об’єкта бази даних є зворотно сумісною зміною, яка не потребує спеціальної обробки з точки зору розгортання без простоїв (за умови, що цей новий об’єкт функціонально не залежить від уже збережених даних). Достатньо застосувати зміни структури, запустити трансформацію даних та розгорнути новий код після її завершення.

Видалення об’єкта бази даних не є зворотно сумісною зміною, але його легко реалізувати — непотрібні об’єкти можна видалити після розгортання нового коду.

Решта є складнішою з точки зору розгортання без простоїв. Загалом обидві ці зміни можна реалізувати копіюванням даних в інші об’єкти бази та видаленням вихідних після завершення міграції.

Наприклад, перейменування можна виконати копіюванням значень зі старого поля в нове з новою назвою, після чого старе можна видалити. Зміни типу поля можна виконати так само, якщо разом зі зміною типу поле отримує нову назву. Якщо є потреба зберегти оригінальний ідентифікатор, потрібні будуть додаткові кроки «перейменування», але основа залишається незмінною. Так чи інакше, деякий час база даних повинна підтримувати як старі, так і нові контракти. Є принаймні три варіанти робити такі зміни «на льоту».

Якщо база даних підтримує тригери

На діаграмі нижче показано послідовність станів системи під час розгортання. Початковий стан — зліва, а кінцевий — справа. «Old» і «New» відповідають об’єктам бази даних, які беруть участь у трансформації схеми. Найчастіше мова йде про окремі поля, але іноді потрібно мігрувати цілу колекцію.

На високому рівні цей алгоритм можна описати так:

  1. Застосувати зміни до схеми, якщо база підтримує схему.
  2. Встановити тригери, які копіюють дані зі старого об’єкта бази даних в новий перед будь-яким додаванням або оновленням запису. Залежно від необхідних перетворень можуть також знадобитися тригери видалення.
  3. Запустити утиліту перетворення даних, яка виконує те саме копіювання для всіх записів бази. Оскільки тригери вже застосовано, вона може виконати фіктивне оновлення для кожного запису (UPDATE table SET column = column ... або еквівалентний виклик API, якщо ваша база не підтримує SQL-подібні оператори). Майте на увазі, що цій утиліті не можна перетирати нові значення, записані програмою. Докладніше про забезпечення таких гарантій описано в розділі «Технічні деталі».
  4. Коли утиліта перетворення завершить свою роботу, розгорнути новий код, сумісний з оновленою схемою. На цьому етапі всі записи, які були в базі на момент запуску утиліти, а також нещодавно додані, будуть перетворені відповідно до нової структури.
  5. Видалити тригери. Старі об’єкти, які вже не потрібні, також краще видалити, але якщо база не має схеми, може знадобитися створення спеціальної додаткової утиліти.

Існують інструменти з відкритим кодом, такі як pt-online-schema-change і Facebook OnlineSchemaChange для MySQL або pg-online-schema-change для PostgreSQL, які використовують цей підхід.

Якщо база даних підтримує потоки змін

Будь-яка база даних, що підтримує реплікацію, дозволяє підписуватися на свій журнал транзакцій. Теоретично його можна використовувати замість тригерів у описаному вище підході: поки інструмент перетворення обробляє вже існуючі дані, підписник журналу транзакцій отримує нові зміни та обробляє їх відповідно до логіки перетворення.

Проблема полягає в тому, що програмні інтерфейси журналу транзакцій є дуже низькорівневими, іноді погано задокументованими, і зрештою можуть вимагати значних зусиль для інтеграції з ними.

Деякі бази даних надають більш високорівневі та прості у використанні інтерфейси потоку змін — наприклад, Cosmos DB Change Feed або MongoDB Change Streams. І хоча їх можна використовувати замість тригерів, є кілька потенційних проблем, про які слід пам’ятати. Потоки змін зазвичай асинхронні, тому ви отримуєте сповіщення вже після того, як відповідна зміна була повністю зафіксована, і це може спричинити невідповідності:

  • Деякі оновлення можуть бути втрачені, якщо виняткові ситуації не обробляються належним чином.
  • Між змінами в старій і новій частинах схеми буде затримка. Досить часто це допустимо, але може бути проблемою у випадку канаркового розгортання (canary deployment).
  • Якщо логіка перетворення включає запити до інших записів бази даних, стан гонитви (race condition) стає цілком ймовірним.
  • Cosmos DB Change Feed не реєструє видалення, і деякі інші механізми баз даних можуть поводитися так само. Хоча і є обхідні шляхи, це все одно додає певні незручності.

Водночас «before» тригери дають більше гарантій узгодженості, оскільки вони виконуються в одній транзакції зі змінами, які їх викликали.

Наразі я знаю лише один інструмент з відкритим вихідним кодом, який використовує подібний механізм — gh-ost для MySQL.

Якщо база даних не підтримує ані тригери, ані потоки змін

У цьому випадку, окрім того, щоб створити інструмент перетворення, вам доведеться додати деяку логіку міграції даних у код програми.

  1. Розгорнути нову версію програми (проміжну версію 1 на діаграмі), яка завжди читає зі старого об’єкта бази даних, але записує як в старий, так і в новий.
  2. Запустити утиліту перетворення, яка копіює дані зі старого об’єкта бази даних у новий для всіх записів. Майте на увазі, що цій утиліті не можна перетирати нові значення, записані програмою. Докладніше про забезпечення таких гарантій описано в розділі «Технічні деталі».
  3. Коли утиліта перетворення завершить свою роботу, розгорніть нову версію програми (проміжну версію 2 на схемі), яка читає дані з нового об’єкта бази даних, але записує в обидва. Оскільки на цей момент схема вважається повністю перетвореною, усі нові функції релізу, що розгортається, можна зробити доступними для користувачів. Цей крок потрібен, якщо ваш підхід до розгортання дозволяє запускати дві версії програми одночасно, як у разі поступового оновлення (rolling upgrade) або канаркового розгортання (canary deployment). Оскільки проміжні версії 1 і 2 працюють одночасно, будь-які зміни даних, внесені одним екземпляром програми, будуть доступні іншому. Якщо ми пропустимо цей крок і розгорнемо код, який не взаємодіє зі старими об’єктами бази даних, старий екземпляр програми не буде знати про будь-які оновлення, зроблені новим кодом.
  4. Після того, як проміжна версія 1 стане непотрібною, розгорніть фінальну нову версію, яка має всі нові функції та більше не взаємодіє зі старими об’єктами бази даних. На цьому етапі краще видалити старі об’єкти, але якщо база не має схеми, може знадобитися створення спеціальної додаткової утиліти.

Така інтерпретація оновлення бази даних без простоїв змушує розробляти та розгортати три версії програми, що є виснажливим і громіздким. Хороша новина полягає в тому, що це можна спростити за допомогою feature toggle. При такому підході логіка обробки даних усіх трьох версій буде вбудована в єдину, а поточний режим буде керуватися через параметр конфігурації, який можна змінювати на льоту. Таким чином, ви можете розгорнути одну версію, а потім змінювати параметр для переходу між етапами розгортання, описаними вище.

Ті, хто досліджував проблему розгортання без простоїв, можуть здивуватися, чому я нічого не сказав про версіонування схеми. Хоча це гарна ідея та може легко вирішити деякі сценарії, вона не є комплексним вирішенням проблеми. Є принаймні два питання, які необхідно розглянути перед прийняттям рішення:

  • Якщо розроблена вами нова функціональність потребує нового поля, залученого в умовах запитів, базу даних потрібно повністю перенести на нову схему перш ніж цю функціональність можна буде робити доступною. Це означає, що вам все одно доведеться пройти два етапи розгортання: запустити утиліту перетворення разом з проміжною версією, яка змінює схему під час запису, і лише потім — нову повнофункціональну версію програми.
  • Якщо ваш підхід до розгортання передбачає одночасний запуск двох версій програми, як, наприклад, у разі поступового оновлення (rolling upgrade) або канаркового розгортання (canary deployment), оновлення схеми під час запису має бути зворотно сумісним. Усі поля з попередньої версії мають бути синхронізовані з новими аж до останнього етапу.

Таким чином, у найзагальнішому випадку версіонування схеми може лише допомогти об’єднати проміжні версії 1 і 2. Фактично, це означає, що ви не можете позбутися проміжної версії, утиліти перетворення та кроку очищення. Але якщо описані вище проблеми не стосуються вашої системи, версіонування схеми може спростити розгортання.

Технічні деталі

Оптимізація використання ресурсів утилітою перетворення

Оскільки утиліта перетворення працюватиме поверх звичайного робочого навантаження, вона має розумно споживати ресурси, щоб уникнути зниження продуктивності та проблем зі стабільністю. Немає сенсу намагатися оновити все в одній транзакції, оскільки конверсія за своєю природою не «все або нічого», не кажучи вже про технічні проблеми, які може спричинити така спроба.

Замість цього краще виконувати оновлення порціями, де кожен наступний фрагмент легко знайти та легко повторити спробу у разі потреби. Якщо колекція даних має унікальне ключове поле, що підтримує запити за діапазоном значень, і кожне нове значення цього ключа завжди більше попереднього, алгоритм перетворення може виглядати так:

  1. Прочитати ключ останнього перетвореного запису (LastProcessedKey). Це значення використовуватиметься лише утилітою перетворення та має зберігатися в постійному сховищі, щоб не втратити його у разі «падіння» утиліти з будь-якої причини.
  2. Прочитати ключі (припустимо, що поле називається Key) для наступної порції записів, упорядкованих за Key, де Key більший за LastProcessedKey. Якщо LastProcessedKey порожній, остання умова не потрібна.
  3. Якщо на попередньому кроці нічого не знайдено, вийти з процедури.
  4. Перетворити записи, що відповідають ключам з кроку № 2, згідно з новою схемою. Наприклад, із SQL-сумісною базою даних і підходом на основі тригерів цей крок може виглядати як UPDATE table SET someColumn = someColumn WHERE KeyColumn IN (@keys).
  5. Повторити крок № 4 у випадку помилки.
  6. Зберегти ключ останнього перетвореного запису в LastProcessedKey.
  7. Перейти до кроку № 1.

Пошук наступної порції за допомогою індексованого поля дуже важливий з точки зору оптимізації ресурсів. Повному скануванню слід запобігти за будь-яку ціну.

Перетворення має бути ідемпотентним, щоб спростити логіку повторів. Таким чином, навіть якщо утиліта перетворення не використовує транзакції та випадково завершує роботу в середині порції, вся порція буде повторена знову після перезапуску утиліти без жодної шкоди для вже трансформованих записів.

Якщо логіка перетворення звертається до інших колекцій для обчислення нових значень, скоріш за все, її потрібно буде запустити в транзакції з рівнем ізоляції, вищим за «read committed». Який саме вибрати залежить від того, які феномени читання можуть виникнути у вашому конкретному сценарії.

Ще одним способом контролювати споживання ресурсів є додавання затримки між порціями. Вона навіть може бути змінною залежно від розкладу пікових навантажень: в час, коли системою активно користуються — велика затримка, в час меншого навантаження — маленька або зовсім відсутня.

Запобігання втраті оновлень

Як вже було зазначено, утиліті перетворення не можна перезаписувати нові значення, записані програмою. За допомогою «before» тригерів цього легко досягти, оскільки вони виконуються як частина операції запису, яка ексклюзивно блокує все, що пише. Але пам’ятайте, що окрім «before» тригерів, ваша база даних повинна підтримувати операції часткового оновлення, щоб ідея спрацювала. Часткове оновлення означає, що не потрібно явно вказувати всі поля запису, і все, що не вказано — залишиться незмінним, але тригер буде викликано.

Якщо у вашому розпорядженні немає «before» тригерів або операцій часткового оновлення, процедура перетворення для окремого запису бази, швидше за все, буде складатися з трьох кроків:

  1. Прочитати запис.
  2. Застосувати логіку перетворення схеми.
  3. Зберегти запис.

Якщо програма пише нові дані в той час, як утиліта перетворює той самий запис, ці дані скоріш за все буде перезаписано. Щоб цього не сталося, потрібно використовувати або песимістичне, або оптимістичне блокування.

Песимістичне блокування рідко є вибором, оскільки воно шкодить продуктивності. До того ж багато баз даних просто не підтримують його явно. Таким чином, оптимістичне блокування часто є єдиним життєздатним варіантом. Типові приклади вбудованої підтримки такого механізму можна знайти в Cosmos DB та Elasticsearch. Я також хотів би виділити кілька зауважень щодо реалізації:

  • Трансформаційна утиліта може просто пропустити запис, якщо перевірка оптимістичного блокування завершилася помилкою. Така помилка буде означати, що запис вже був трансформований програмою при внесенні нових даних, тому утиліті вже немає чого робити з цим записом.
  • Якщо програма використовує оптимістичне блокування, але автоматично не об’єднує нові дані зі збереженими в разі порушення паралелізму, будьте готові, що кінцеві користувачі можуть випадково отримувати помилки блокування під час повної трансформації схеми. У найгіршому випадку їм доведеться перезавантажити те, що вони редагували, і знову внести зміни.

Зміна схеми бази «на льоту»

Хоча різні бази даних можуть дотримуватися різної термінології, у цьому розділі я буду використовувати терміни «таблиця» та «стовпець».

Зміна структури таблиці в базі даних, що підтримує схему, у більшості випадків ексклюзивно блокує всю таблицю. Якщо зміна впливає лише на каталог схем, вона буде внесена відносно швидко й не повинна спричиняти жодних проблем через надто тривалу затримку інших запитів до цієї таблиці. Але якщо вона передбачає сканування або навіть оновлення всієї таблиці, у вас можуть виникнути проблеми.

Найпростіший підхід, щоб уникнути небажаних побічних ефектів, — це підтримувати тіньову копію таблиці: створіть нову таблицю відповідно до нової схеми та заповніть її будь-яким зі способів, наведених у статті. Коли утиліта перетворення завершить свою роботу, або поміняйте місцями імена таблиць, якщо ваша база даних може це зробити атомарно, або в новій версії програми посилайтеся на нову таблицю замість оригінальної.

Тут недоліком є те, що необхідно копіювати всю таблицю, навіть якщо ви вносите незначні зміни в структуру. З великими таблицями це може значно вплинути на час трансформації і на використання ресурсів.

Якщо «тіньова таблиця» не підходить, структурні зміни можна застосувати безпосередньо до наявної таблиці, але з урахуванням додаткових заходів для запобігання довготривалим блокуванням:

  • Додавання стовпця, який дозволяє пусті значення: зазвичай це безпечно робити на льоту, якщо стовпець не має обмежень (constraints) або значення за замовчуванням (default value). Хоча і тут можуть бути деякі винятки. Я пам’ятаю випадок з MySQL, коли таблиця, створена в попередній версії, вимагала повної перебудови після додавання нового стовпця, оскільки версія сервера була нещодавно оновлена.
  • Додавання стовпця зі значенням за замовчуванням: якщо значення за замовчуванням константне, більшість сучасних реляційних баз даних не роблять повне оновлення таблиці. Однак, якщо ваша база недостатньо розумна або вираз за замовчуванням включає функцію, то знадобиться обхідний шлях, щоб уникнути проблем: додайте стовпець і відповідний вираз за замовчуванням окремими командами, а потім заповніть стовпець значеннями за замовчуванням за допомогою утиліти перетворення.
  • Додавання стовпця з обмеженням або нового обмеження до існуючого стовпця: будь-яке обмеження за замовчуванням перевірятиме всі рядки таблиці. Щоб уникнути затримок, спричинених блокуванням, нові обмеження повинні бути додані зі спеціальним прапорцем, який вказує, що вони будуть застосовані тільки до нових змін, пропускаючи перевірку вже існуючих даних. Цей прапорець може відрізнятися між базами: у SQL Server це NOCHECK, у PostgreSQL — NOT VALID, у MySQL — NOT ENFORCED тощо. Після завершення конвертації даних обмеження треба повернути до нормального стану встановленням прапорця в початкове значення. Ця операція пройде всі існуючі рядки, але блокування, яке вона здійснює, дозволить одночасне читання та запис. Існує один неочевидний нюанс з обмеженнями «NOT NULL»: щоб використовувати цей підхід, традиційні декларації «NOT NULL» потрібно замінити еквівалентними обмеженнями типу «CHECK», оскільки «NOT NULL» є частиною визначення стовпця і не може додаватися окремо.
  • Видалення стовпця: зазвичай оновлює лише каталог схем і не переписує таблицю. Можуть бути винятки, але мені про них не відомо.
  • Додавання індексу: щоб запобігти значним затримкам, нові індекси потрібно додавати в режимі «онлайн», що дозволяє іншим запитам отримувати доступ до таблиці під час створення індексу. Цей режим зазвичай не застосовується за замовчуванням, а відповідний прапорець може відрізнятися між базами: у SQL Server і Oracle — ONLINE, у PostgreSQL — CONCURRENTLY тощо.
  • Видалення індексу: ідея така ж, як і зі створенням індексу. І хоча це може виглядати неінтуїтивно, видалення індексу може зайняти значний проміжок часу, тому безпечніше застосувати режим «онлайн».
  • Додавання таблиці: цілком безпечно, оскільки створюється новий об’єкт, про який програма ще не знає.
  • Видалення таблиці: цілком безпечно, тому що має виконуватися, лише якщо програма більше її не використовує.

Як бачите, у більшості випадків є спосіб застосувати зміни схеми на льоту без помітного впливу на нормальне робоче навантаження. Хоча наведений вище список не є вичерпним, він окреслює основні ідеї. Щоб узагальнити підхід і зробити процедуру розгортання максимально надійною, я б дотримувався ще кількох рекомендацій:

  • Автоматизуйте створення скриптів схеми — щоб уникнути людського фактору у написанні скриптів перетворення схеми згідно з наведеними ідеями, варто створити інструмент, який аналізує список змін, отриманих з утиліти порівняння, та створює два скрипти для етапів до та після конвертації.
  • Завжди перевіряйте свої скрипти розгортання на великій тестовій базі даних.
  • Якщо ви зіткнулися з ситуацією, коли жоден з обхідних шляхів не дозволяє уникнути тривалого ексклюзивного блокування таблиці, поверніться до підходу «тіньової таблиці».

Очищення схеми в базах даних з гнучкою схемою

Якщо ціла колекція в базі даних стає непотрібною через оновлення схеми, процедура очищення є простою — просто видаліть її. Але якщо ви хочете видалити лише деякі поля з кожного запису колекції, частіше за все доведеться писати спеціальну утиліту, яка сканує всю колекцію і явно видаляє застарілі поля з кожного запису. Аналогічно як з інструментом конвертації даних, вона повинна використовувати оптимістичне блокування, щоб запобігти втраті оновлень.

Підтримка двох версій схеми з підходом на основі тригерів

Хоча описаний раніше загальний підхід зберігає застарілі об’єкти бази даних до кроку очищення, може бути незрозуміло, як їх синхронізувати з новими, якщо тригери копіюють дані лише в одному напрямку. Відповідь залежить від того, про який об’єкт бази даних йдеться:

  • Якщо ви створюєте нову колекцію для нової версії системи, вона може мати власний тригер, який копіює дані назад.
  • Якщо і старе, і нове поля належать до однієї колекції, порівняйте старі та нові набори значень, щоб зрозуміти, яке саме з них було змінено. Якщо змінили старе — заповніть нові поля відповідними значеннями, якщо нове — виконайте зворотну синхронізацію.

Висновки

Легко помітити, що розгортання без простоїв — це громіздка і крихка процедура, тому вона має сенс тільки якщо у вас є відповідні вимоги з боку бізнесу. Але майте на увазі, що навіть якщо вимога до доступності низька (наприклад, 99% на рік з 24-годинним періодом обслуговування), необхідне для нової версії програми перетворення даних може тривати довше дозволеного. Таким чином, якщо ваше рішення має зберігати великі обсяги даних і його схема може розвиватися з часом, краще бути готовим до застосування деяких з описаних підходів.

Найпростіший спосіб додати можливість оновлення схеми без простоїв — це готові інструменти, такі як pt-online-schema-change, Facebook OnlineSchemaChange, pg-online-schema-change або gh-ost, згадані раніше.

На жаль, такі інструменти можуть бути недоступні для вашої бази даних, а деякі з них можуть мати неприйнятні технічні обмеження. Загальною проблемою всіх цих утиліт є те, що вони використовують підхід «тіньової таблиці», який передбачає копіювання всієї таблиці. Якщо ви маєте справу з наборами даних розміром у сотні гігабайт або більше, він може бути неоптимальним з точки зору використання ресурсів і тривалості оновлення.

Єдиною альтернативою «тіньовій таблиці» є пряма зміна існуючої структури без тривалих блокувань, як описано в розділі «Зміна схеми бази даних у запущеній системі». На перший погляд, вона може здатися складною через кількість випадків, які потрібно розглянути, але насправді це невелика проблема, оскільки підготовку скриптів схеми можна автоматизувати.

На мій погляд, оновлення схеми без простоїв зазвичай важче та крихкіше з NoSQL базами, за винятком випадків, коли шаблон версіонування схеми є достатнім для вашої системи, попри його обмеження.

Як завжди, чим більше етапів процесу автоматизовано — тим краще. Логіку трансформації даних доведеться писати вручну, але загальну процедуру розгортання та підготовку скриптів схеми слід максимально автоматизувати.

Примітка: це переклад моєї статті з blog.devgenius.io

👍ПодобаєтьсяСподобалось18
До обраногоВ обраному10
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Чи має хтось досвід використання Oracle Edition-Based Redefinition?

Дякую за статтю.

Зміна структури таблиці в базі даних, що підтримує схему, у більшості випадків ексклюзивно блокує всю таблицю.

Працюю багато з MySQL, і більшість змін схеми не лочать таблиці. Не скажу за всі типи баз, але здається більшість реляційних баз вже вміють не лочити таблиці, наприклад, при додаванні або видаленні колонок та індексів. А це мабуть 90% всіх змін, що трапляються.
Раніше часто використовував

pt-online-schema-change

, зараз рідко. Але іноді без неї ніяк. Дуже гарна утіліта.

Не скажу за всі типи баз, але здається більшість реляційних баз вже вміють не лочити таблиці, наприклад, при додаванні або видаленні колонок та індексів.

MySQL дійсно багато зробив у цьому напрямку (з версії 5.7, якщо я не помиляюсь) — dev.mysql.com/...​nline-ddl-operations.html. Кількість операцій, які потребують тривалого локу та/або перебудови таблиці зведена до мінімуму. Але в інших базах, нажаль, це не так.

Спробуйте додати до дійсно великої таблиці поле з NOT NULL

Недостатньо деталей :) Який енджин бази даних, якої версії і яке значення за замовченням в цього поля?

Хм, та навiть int default 0

та база з 10 000 000 записiв, досить невеличка

Є в мене під рукою невеличка таблиця в 5М записів, міг би і більше знайти, але на то знадобиться більше часу і насправді немає потреби. Я так розумію, ви мали на увазі просту NOT NULL колонку з примітивним значенням за замовченням — alter table SalesTest add sampleint int not null default 10. Якщо так — то і MariaDB 10.8, і SQL Server 2019 виконують її миттєво, тому що насправді їм для цієї операції не треба переписувати рядки. Нові бази вміють застосовувати значення за замовченням під час виборки даних. PostgreSQL навіть не перевіряв, тому що і так знаю, що він саме так працює.

Дякую за тест

Підписатись на коментарі