Зміст
Коротко
У шардованому Postgres від PlanetScale (Neki) рядки однієї таблиці роз’їжджаються кількома кластерами — шардами.
Швидкі запити й транзакції з повною семантикою Postgres здебільшого ті, що лишаються на одному шарді.
Одиниця спільного розміщення в Neki — група шардів: таблиці з однаковим способом хешування ключа кладуть пов’язані рядки поруч.
Групу варто проєктувати від патернів запитів, а не від списку таблиць у схемі.
Що сталося
У попередніх матеріалах PlanetScale роутер сам збирав join customers і orders, бо кожну таблицю шардували за власним id.
Виправлення просте на словах: шардувати orders за customer_id тим самим хешем, що й клієнтів. У Neki цей прийом оформлено як групу шардів.
Уявіть склад, де замовлення клієнта збирають з однієї полиці: не бігати всім ангаром за кожною позицією.
Група шардів — це правило «які рядки жити поруч», щоб join і транзакція не роз’їжджалися кластерами.
У топології даних група задає індекс шардування (наприклад by_customer на колонці customer_id з типом xxhash) і діапазони ключів по фізичних шардах.
Таблиці orders, items, addresses успадковують цей індекс; у customers колонка ключа — id, але хеш той самий. Замовлення, позиції й адреси одного клієнта опиняються на одному шарді.
Довідники на кшталт countries чи currencies до клієнта не прив’язані. Якщо покласти їх на один шард, багато join стануть міжшардовими.
Neki оголошує такі таблиці довідковими: повна копія на кожному шарді групи. Початкове завантаження — сценарій MoveTables, далі роутер розмножує запис на всі копії.
Каталог товарів живе іншим життям: природний ключ — SKU. Додають другу групу (catalog із by_sku), часто на тих самих фізичних шардах, але з іншим розрізом простору ключів.
Окрема група main тримає метадані всієї бази на одному шарді — послідовності, відомості про схему та інше «спільне» для топології.
Індекс шардування може брати не «сиру» колонку, а SQL-вираз: lower(sku) чи left(order_id, 4).
Так змішують регістр артикулів від різних постачальників або витягують код магазину зі складеного ідентифікатора замовлення.
Алгоритм за замовчуванням — xxhash: рівномірний розподіл по 64-бітному простору ключів.
Є ще range (цілі діапазони на шарди — зручно для «старого» географічного розрізу) і modulo (класика підручників: value % N).
Модуль передбачуваний, але зміна модуля майже завжди рухає майже всі рядки. Для зростання в проді його зазвичай не радять.
Запит «замовлення за id», коли таблиця шардована за customer_id, без підказки розлітається всіма шардами.
Глобальний вторинний індекс (GSI) — окрема таблиця відповідностей, яка пов’язує id замовлення з customer_id.
Перше одношардове читання дістає ключ шарда; друге — сам рядок у orders. Поки таблицю відповідностей не заповнено, індекс не вмикають.
Розміщення не назавжди. Сценарій Reshard змінює ключ або число шардів, коли патерни запитів виростуть інакше, ніж схема під час міграції.
Той самий контур використовують, щоб уперше розрізати нешардовану таблицю або розвести групи по різних фізичних кластерах, коли навантаження перестане бути симетричним.
Чому це важливо
Шардування часто малюють як «розрізати велику таблицю».
Насправді вузьке місце — не розмір таблиці, а частка запитів, що чіпають кілька шардів.
Кожен такий розліт — збір результатів роутером.
Гірше того: транзакція без спільної атомарної фіксації і без спільного знімка даних. Neki фіксує кожен шард окремо.
Поки join клієнта із замовленнями локальний, ви отримуєте звичну семантику Postgres.
Щойно пов’язані рядки роз’їхалися — швидкість і коректність змінюють ціну водночас.
Групи шардів фіксують компроміс явно: який ключ «тримає» гарячий шлях, які таблиці поруч, що розмножується як довідник, що живе в окремій групі.
Це рішення архітектури даних, а не косметика файлу топології.
Reshard знімає страх «назавжди обрати неправильний ключ», але не скасовує вартість переїзду.
Краще почати із запитів, які реально платять за затримку, ніж із красивого списку сутностей з ER-діаграми.
На практиці
Спочатку випишіть гарячі шляхи — замовлення клієнта, кошик, оформлення.
Лише потім розкладайте таблиці по групах: інакше схема гарно ділиться на сутності, а прод платить за міжшардові join.
- Знайдіть ключ, який ділять найчастіші запити й транзакції; усі таблиці з цим ключем — кандидати в одну групу.
- Для кореневої сутності (
customers.id) задайте успадкування індексу з іншою колонкою, щоб хеш збігався зcustomer_idу дочірніх таблицях. - Довідники без «власника» оголосіть довідковими, інакше join із ними розмаже навантаження по шардах.
- Окремий домен (каталог за SKU) — окрема група; фізичні шарди можна ділити або розвести пізніше.
- Віддавайте перевагу
xxhash;rangeіmodulo— якщо застосунок уже так живе або потрібен явний географічний розріз; модуль для масштабування майже завжди пастка. - Для пошуку не за ключем шарда заведіть
GSIі не вмикайте його, доки не заповните таблицю відповідностей. - Закладіть
Reshardу план зростання: зміна ключа чи числа шардів — штатний сценарій, не аварія.
Підсумок
У Neki група шардів — одиниця спільного розміщення: однаковий хеш ключа кладе пов’язані рядки на один кластер Postgres.
Проєктування починається із запитів і транзакцій, які мають лишитися локальними, а не зі списку таблиць.
Довідники, окремі групи, вирази в індексі, GSI і Reshard — інструменти підігнати схему під життя продукту.
Якщо join уже «гуляє» по шардах — спочатку шукайте, який ключ має тримати гарячий шлях поруч, а не додавайте ще один кластер «на всяк випадок».



Коментарі