Коли розмір бази даних перевищує мільйони рядків, доцільно почати масштабування додатку і розбити базу даних на кілька фізичних серверів.
Найбільшою проблемою поділу бази даних на декілька частин є її подальша синхронізація у разі запиту користувачем конкретних даних.
Припустимо, у вас є таблиця "статей", але через те, що у вас величезний сайт, вище розташовані десятки мільйонів статей, і вам доводиться фізично розподіляти їх між декількома машинами.
Якби ми використовували звичайне ціле число в якості "id" (первинного ключа) з налаштуванням автоінкременту, то дуже швидко виявили б, що при створенні записів на різних машинах децентралізовано, а потім їх синхронізації, виникають колізії ідентифікаторів і доводиться складним чином перенумеровувати записи. Крім того, якщо ми вирішуємо багато сесій за іншими столами, це може бути дуже складною накладною роботою, в якій легко припуститися помилок.
Тому замість числового ідентифікатора ми можемо генерувати UUID
, який являє собою текстовий рядок, що генерується за складним алгоритмом, який гарантує, що він буде унікальним, навіть якщо буде згенерований незалежно на декількох машинах.
Переваги:
19010018
, то неважко здогадатися, що існує також користувач 19010017
та інші. Атака називається векторною.UUID можна отримати або простим SQL запитом SELECT UUID();
, але при цьому збільшується кількість запитів до бази даних і ми втрачаємо можливість підготувати дані спочатку масово в логіці програми, а потім записати їх відразу.
Тому мені подобається користуватися пакетом ramsey/uuid, отриманим за допомогою Composer, як хорошим рішенням. Сам UUID має кілька версій, і пакет може грайливо генерувати всі види за потреби.
Це робить його простим у використанні:
require 'vendor/autoload.php';use Ramsey\Uuid\Uuid;// Створює об'єкт UUID версії 1 (на основі часу)$uuid1 = Uuid::uuid1();echo $uuid1->toString() . "\n"; // e4eaaaf2-d142-11e1-b3e4-080027620cdd// Генерує об'єкт UUID версії 3 (на основі імені та хешований як MD5)$uuid3 = Uuid::uuid3(Uuid::NAMESPACE_DNS, 'php.net');echo $uuid3->toString() . "\n"; // 11a38b9a-b3da-360f-9353-a5a725514269// Формує об'єкт UUID версії 4 (випадковий)$uuid4 = Uuid::uuid4();echo $uuid4->toString() . "\n"; // 25769c6c-d34d-4bfe-ba98-e0ee856f3e7a// Генерує об'єкт UUID версії 5 (на основі імені та хешований як SHA1)$uuid5 = Uuid::uuid5(Uuid::NAMESPACE_DNS, 'php.net');echo $uuid5->toString() . "\n"; // c4a760a8-dbcf-5254-a0d9-6a4474bd1b62
Якщо ви використовуєте Doctrine, існує розширення ramsey/uuid-doctrine, яке генерує ідентифікатор безпосередньо як тип даних.
У своїх перших спробах я використовував varchar(36)
як первинний ключ (ID), але це зовсім не найкраща ідея.
Пояснення внутрішньої логіки:**.
Бази даних MySql (і багато інших) не можуть ефективно використовувати
varchar
,char
або інші типи даних, що виражають рядок як первинний ключ. У деяких базах даних існує тип данихGUID
, який призначений для безпосереднього зберігання UUID. Якщо ви не можете використовувати цей тип, є відповідний замінник у виглядіbinary(16)
.
При фізичному дослідженні бази даних ідентифікатор після цього представляється в HEX форматі (оскільки двійковий формат не відображається), замість красивого ідентифікатора 726c67c4-e5eb-4a4c-8fcc-031da5d6f3c6
ви побачите просто 726C67C4E5EB4A4C8FCC031DA5D6F3C6
, який в запиті INSERT виглядає як '?kYߟKg2c;'
.
varchar(36)
в `binary(16)Я припускаю, що ви представляєте (або плануєте представляти) новий встановлений ідентифікатор в базі даних як:
`id` binary(16) NOT NULL
Однак, просто змінити тип даних не вийде, тому щось на кшталт:
SET FOREIGN_KEY_CHECKS=0;ALTER TABLE article CHANGE id id BINARY(16) NOT NULLSET FOREIGN_KEY_CHECKS=1;
Причин, в основному, дві:
Тому єдине правильне рішення - зробити резервну копію даних (але це все одно потрібно робити перед кожною міграцією), підготувати порожню базу даних з функціональними зв'язками і знову перенести туди дані за допомогою міграції.
Якщо ви раніше генерували UUID дивним чином, краще вибрати якийсь послідовний метод отримання UUID і перенумерувати всі записи. Причина полягає в тому, що послідовне розміщення дозволяє краще впорядкувати значення і створити btree
, що робить продуктивність практично ідентичною bigint
.
Якщо ви знаєте кращий спосіб конвертації існуючої бази даних з UUID, що зберігається у вигляді varchar, у двійковий формат без необхідності розробки складних міграцій та зі збереженням зовнішніх ключів, буду дуже вдячний за зворотній зв'язок.
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | uk