Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

Пости з тегом #AmazonRedshift

26.09.2022

Ключові моменти розробки схеми для AWS Redshift

❄️📦🪣 Сьогодні вдало попрацював з AWS Redshift.

Redshift - це аналітична (OLAP) база даних, що виросла з PostgreSQL, виглядає як PostgreSQL, але працює фундаментально по-іншому. Вона призначена для обробки великого обсягу даних, про що свідчить і цінник, що починається з $0.25 на годину.

Але просто завантажити дані у Redshift та отримати швидкий результат не вийде. (Перевірено.) Треба знати деякі ключові моменти.


31.10.2022

Перші враження від Obsidian. Тести з Redshift.

🪨📓💫 Що ж, переніс нотатки в Obsidian. Перші враження дуже приємні, як вже писав, працює швидко, інтеграція з Git вбудована. Знайшов ще важливу властивість — при перейменуванні чи пересуванні нотатки всі посилання на неї будуть виправлені автоматично. Що стане до нагоди, коли я буду впорядковувати базу, для чого і шукав більш зручну програму.

Дуже зручно мати глобальну комбінацію клавіш для пошуку та додавання нотаток. Таку комбінацію можна створити програмою BetterTouchTool - для цього дія на комбінацію має бути “відправити ⌘+O програмі Obsidian, перед чим вивести її на передній план”.

По роботі писав та тестував SQL-скрипти, що мають працювати на AWS Redshift. Великий недолік Redshift - у неї немає локального емулятора. При цьому мова помітно відрізняється від Postgres - настільки, що тести для Postgres недостатні. Багато разів було таке, що навіть після ретельного тестування код не працював, і дізнавались про це тільки на стейджингу. Тому у нас на CI тести (RSpec) запускаються також і з Redshift. Це надзвичайно повільно — ця база не розрахована на транзакційні операції. Маю ідеї переробити тести на щось більш сумісне — наприклад, замість створення тестових даних на кожний тест окремо, підготувати базу для всіх тестів один раз.


09.11.2022

Як поєднати Ruby on Rails та AWS Redshift

☁️🗄️☑️ Коротенький чекліст, як зробити, щоб Rails і Redshift запрацювали гармонічно:

… Тож насправді чекліст виходить не такий і короткий.


12.12.2022

Висновки від розгортування величезної переробки для нашого Redshift

🗄️🛫🌧️ Побачила світло продакшна велике перероблення схеми Redshift, яка тривала понад два місяці. Кілька думок:


30.12.2022

AWS Redshift, його видатне кешування, та як їм користуватись

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

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

Раніше я думав, що головна перевага Redshift - це можливість швидко обробляти великі обсяги даних. Але ні, це лише визначальна властивість OLAP баз.

Зараз мені здається, що у Redshift найкрутіше — це кешування. Навіть складні запити (основну роботу OLAP бази) можна зробити швидкими, якщо розробити таку структуру запитів, яка буде здатна до часткового кешування. Ось знайшов сьогодні гарну статтю від AWS.

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

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


13.02.2023

Трохи уроків з Event Sourcing на Redshift

Трохи уроків з так званого “event sourcing”, що для мене значить, архітектури, де ми, замість того, щоб редагувати записи в базі, зберігаємо кожну зміну стану як маленький, незмінний запис - “подію”. А потім з сукупності подій генеруємо поточний стан даних. (Наочний приклад — це реєстр операцій в магазині, підсумувавши які, можна отримати кількість грошей в касі.)

Мене на event sourcing підштовхнув сервіс AWS Kinesis Firehose. Бо він пропонує ну дуже спокусливу властивість — практично необмежений вхідний обсяг. Скільки не пиши, все візьме, складе в пачки, та відправить далі в базу, наприклад, в Redshift. А вже у Redshift доведеться виконати перетворення, щоб зібрати події в загальну картину. Можливо, це будуть матеріалізовані розрізи.

Так от, нарешті, до уроку. Події краще комбінувати в найбільші можливі. Тобто якщо за одну операцію генерується послідовно три події, що стосуються одна одної — краще зробити одну велику подію. Причина в тому, що кожна операція зʼєднання вам коштуватимете. Якщо зʼєднань в запиті багато, наприклад, десять — планувальник Redshift починає заплутуватись. Особливо, якщо ти знаєш, що треба поєднати події, які мають відповідність “одна до одної”. Ми-то знаємо про це, а Redshift - ні. Тому, якщо є можливість, краще відразу єднати в одну подію, а відповідно — й таблицю.

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

Та ще до речі — якщо вже треба єднати в одну десять таблиць, може статись, що UNION ALL ... GROUP BY працюватиме значно ефективніше, ніж купа JOIN. Варто погратись.


14.02.2023

Інкрементальні розрізи подій у Redshift

Нарешті сьогодні розкусив дві проблеми, що мене турбували по Redshift.

Перша — як я вчора писав, події краще комбінувати… логічною вершиною такого підходу буде взагалі позбавитись від різних потоків для типів подій та звалювати все в одну велику таблицю. Далі це дозволить нам згрупувати таблицю по спільному ключу, та агрегувати результат. Головне, щоб агрегація підпадала під інкрементальне оновлення. Бо тоді ми звільняємося від необхідності сканувати всю таблицю, а будемо завжди працювати тільки з новими подіями — це виводить здатність масштабування на новий рівень. Обмеження для інкрементального оновлення досить суворі, але коли всі події в одній таблиці, то можна робити цікаві речі, наприклад: COUNT(NULLIF(event_type = 'purchase', false)) AS purchase_count.

Друга — була така задача, щоб в межах GROUP BY взяти деяке поле з найсвіжішої події. Визначити час цієї події легко - MAX(created_at)… але ж агрегаційні функції працюють в межах стовпчика, тому інші стовпчики за таким запитом не знайдеш. Винайшов трохи хитрий підхід. Якщо інше поле — то рядок, то можна додати до нього час події як префікс. Час має бути в форматі з лексикографічним порядком — наприклад, ISO8601. Тепер можемо знайти останнє за часом значення будь-якого стовпчика - SUBSTRING(MAX(prefixed_item_id), 24). Як бачите, до результатів агрегаційних функцій можна застосовувати інші функції, та це не зашкодить інкрементальному оновленню.


17.02.2023

Розрізи з інкрементальним оновленням в Redshift

Після нещодавніх відкриттів та досліджувань по Redshift перейшов до політики “інкрементальне оновлення, або геть!” З одного боку, набув впевненості, що це можливо, а з іншого — що без інкрементального оновлення та повністю матеріалізованих розрізів нічого хорошого не буде. Автоматичне оновлення менш важливо; запустити його вручну нескладно. Але критично важливо, щоб оновлення відбувалось швидко та без повної перебудови.

На жаль, здогадатись, що розріз не здатний до інкрементального оновлення, важко. Тож додав автоматичний тест, який перевіряє це для всіх розрізів, якось так: SELECT name, state=1 AS is_incremental FROM stv_mv_info. Без цього в майбутньому ми приречені наробити собі проблем.

Про механізм інкрементального оновлення написано дуже мало. Як я кажу, хотілося б щоб можна було глянути на розріз та відразу сказати, чи він інкрементальний, але ж ні. Особливо коли заходить за розрізи, залежні від інших розрізів. Декілька спостережень:

  1. Виявив, що деколи розріз з вкладеним запитом не буде інкрементальним, проте якщо винести вкладений запит в окремий розріз, то обидва стануть інкрементальними (хоча тоді зовнішній розріз втрачає автоматичне оновлення — бо воно можливе тільки для розрізів, що залежать від таблиць.)

  2. На агрегаційні функції можна накладати інші функції, або навпаки, вкладати, та це не порушить інкрементальність. Наприклад, можна зробити date(min(created_at)).

  3. АЛЕ! Коли один розріз залежить від одного, то інкрементальним він стане тільки якщо не залежить від стовпчиків з “загорнутою” агрегаційною функцією. (Це, напевно, псує відстеження змін.) Тож, щоб виправити, можна внести функцію під агрегацію: min(date(created_at)). (Якщо це дозволяє логіка.)

  4. Ще можу рекомендувати редактор запитів Query Editor v2, що вбудований в AWS Console. В ньому є дуже корисна функція “записничок” (notebook) - мені було зручно готувати експерименти з матеріалізованими розрізами. Бо експериментувати доведеться багато, це вже зрозуміло. Перед тим, як впроваджувати розріз в додаток, раджу випробувати його вручну та наживу, та перевірити, чи є він інкрементальним.


21.02.2023

Виправляв помилки з матеріалізованими розрізами в Redshift

Сьогодні день видався виснажливий. З ранку до ночі шукав, чому мої розрізи, які так чудово працювали в локальному Postgres та в редакторі запитів Redshift, відмовились створюватись на CI. Встиг навіть створити та розвʼязати запитання на StackOverflow. Нарешті, знайшов — методом спроб та помилок. Це те, що мені найбільше не подобається в Redshift - деякі особливості ніде не пояснені, при цьому текст помилок веде на зовсім невірний хід думок. Мабуть, вже треба писати свій гайд.

Що трапилось. Спочатку була помилка, що у користувача не вистачає прав на якусь системну таблицю. Раніше я робив експерименти від суперкористувача, тому вирішив, що у CI користувача немає прав. Як дати права? Команда GRANT працює тільки після факту. Є ALTER DEFAULT PRIVILEGES - не працює. Придумав створити процедуру, яка робить GRANT: якщо у процедури буде атрибут SECURITY DEFINER, то вона надасть CI користувача право на цю команду (щоб дати собі права.) Задоволений таким кмітливим рішенням, запустив тести на CI та… виявилось, що хоч створити розрізи вдалося, але тепер їх неможливо оновити. Чудово.

Виходить, що залежність розрізу від системної таблиці — це в принципі неправильно, та Redshift мав би так і сказати, а не водити за ніс. Тоді чого не вистачає моєму розрізу-основі, щоб цієї залежності не було? Щоб визначити це, зробив копію коду, що створює розрізи, та почав викидати з нього все підряд, запускаючи, щоб перевірити, чи не зникла проблема. Та нарешті виявилось, що треба не викидати, а додати — а саме, всі стовпчики з GROUP BY мали бути присутніми в SELECT. Напевно, інакше Redshift не може відстежувати зміни до розрізу, тому створює системну таблицю. Якби це був тільки один розріз, то я б й не помітив, а з залежностями маємо такі незрозумілі помилки.

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


20.03.2023

Складнощі з міграціями в Redshift

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

В Redshift обмежені можливі зміни стовпчиків. Навіть така незавадна зміна, як з NOT NULL на NULL, не дозволена. Можна тільки додати новий стовпчик з потрібним типом та перенести туди дані. На щастя, стовпчики можна перейменувати, тому принаймні можна замінити старий стовпчик на новий. Поки в нас не було розрізів, все було більш-менш прийнятне.

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

Але так теж не вийде, якщо розріз вже активно використовується. Хоча б тому, що перше створення розрізу може зайняти багато часу — часу простою. Тоді доведеться створити новий розріз, може, з версією - my_stats_v2_mv, наповнити його даними, та потім плавно перевести код зі старого на новий розріз. Перейменувати розріз не дозволено, тож він назавжди залишиться з версією в назві. Тобто з часом буде і v3_mv, і так далі, на кожну необхідну зміну.

Добре, що всі ці складнощі можна частково приховати за красивим кодом з боку додатка. Є навіть гем Scenic, який вміє версіонувати розрізи. Тільки Redshift він не підтримує.


11.09.2023

Інтуїція про бази даних. Чому OpenSearch агрегує дані швидше, ніж Redshift?

Отримав сьогодні таке питання — напівжартома — бо дійсно, те, що в Redshift займає десять секунд, в OpenSearch може бути виконано миттєво. Йдеться про агрегаційний запит на кшталт “підрахувати статистику суми поля по часовій шкалі.” Думаю, дуже важливо інтуїтивно розуміти, в чому саме різниця. Я вже писав про архітектуру OpenSearch/ElasticSearch, але що по цьому конкретному випадку?

Моя перша здогадка була — що OpenSearch тримає ці дані в памʼяті, а Redshift ні. Проте при перевірці виявилось, що це не зовсім так. Насправді OpenSearch не завантажує всі індекси в памʼять. Зате індекси на диску зберігаються в такому форматі, який готовий для прямого читання в структури памʼяті. Для того є механізм mmap, який уможливлює швидкий доступ до файлу в памʼяті без зайвого копіювання даних. Про це можна почитати в документації до модуля Store.

Проте mmap використовується, напевно, для читання індексів в усіх базах даних. Справжня причина швидкості OpenSearch не в тому, що індекси розташовані в памʼяті.

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

В той час як OpenSearch, хоч теж розрахований на великі обсяги даних та на масивну паралелізацію, але обмежений простими запитами, які працюють на рівні індивідуальних документів. Тож моя остаточна відповідь - OpenSearch швидше, бо простіше ніж Redshift. Такий тут компроміс.