Управління пам'яттю
Вступ
Вбудований драйвер MySQL (mysqlnd) управляє пам'яттю по-іншому, на відміну клієнтської бібліотеки MySQL (libmysql). Бібліотеки розрізняються способом виділення та звільнення пам'яті, тим, як пам'ять виділяється по шматках під час читання результатів з MySQL, існуючими опціями для налагодження та розробки, і тим, як результати, раховані з MySQL, пов'язані з змінними користувачами PHP.
Наступна інформація призначена як запровадження та узагальнення для користувачів, зацікавлених у розумінні mysqlnd
на уровне C-кода.
Функції, що використовуються для керування пам'яттю
Всі операції виділення та звільнення пам'яті відбуваються за допомогою PHP-функцій, призначених для управління пам'яттю. Тому споживання пам'яті вбудованого драйвера MySQL може бути відстежене за допомогою викликів PHP API, таких як memory_get_usage(). Через те, що пам'ять виділяється та звільняється за допомогою системи керування пам'яттю PHP, зміни на рівні операційної системи можуть бути помітні не миттєво. Система керування пам'яттю PHP поводиться як проксі, яка може викликати затримку у звільненні пам'яті. З огляду на це порівняння використання пам'яті вбудованого драйвера MySQL і клієнтської бібліотеки MySQL (libmysql) досить складно. Клієнтська бібліотека MySQL (libmysql) використовує систему управління пам'яттю операційної системи безпосередньо, отже, ефект лише на рівні операційної системи може бути негайно.
Будь-яке обмеження пам'яті, встановлене PHP, також впливає на вбудований драйвер MySQL. Це може викликати помилки переповнення пам'яті при вилученні великих масивів даних, які перевищують розмір пам'яті, що залишилася, наданих РНР. Через те, що клієнтська бібліотека MySQL не використовує функції управління пам'яті PHP, вона не підкоряється обмеженню пам'яті, встановленому в PHP. При використанні libmysql, залежно від моделі розгортання, обсяг пам'яті, який займає PHP-процес, може зрости за межі обмежень, встановлених в PHP. У той же час, PHP-скрипти можуть обробляти більший обсяг масивів даних, оскільки області пам'яті, виділені для зберігання даних, не знаходяться під керуванням РНР.
Функції системи керування пам'яттю PHP викликаються вбудованим драйвером MySQL через легковажну обгортку. Серед іншого, обгортка робить налагодження легшим.
Обробка масивів отриманих даних
Різні MySQL-сервера та різні клієнтські API розрізняють буферизовані та небуферизовані результати. Небуферизовані результати передаються рядок за рядком від MySQL до клієнта та клієнт читає їх по порядку. Буферизовані результати забираються клієнтської бібліотекою до передачі їх клієнту.
Вбудований драйвер MySQL використовує PHP-потоки для мережного спілкування із сервером MySQL. Результати, надіслані MySQL-сервером, вибираються з мережевих буферів PHP-потоків в результуючий буфер mysqlnd. Результатуючий буфер складається з zvals. На другому етапі результати стають доступними PHP-скрипту. Остання передача з результуючого буфера PHP-змінні викликає споживання пам'яті і в більшості випадків воно помітне при використанні буферизованих результатів.
За промовчанням вбудований драйвер MySQL намагається уникнути подвійного зберігання буферного результату в пам'яті. Результати зберігаються лише один раз у внутрішніх результуючих буферах та їх зvals. Коли результати забираються до РНР-змінних PHP-скриптів, змінні будуть посилатися на внутрішні результати буферів. Результати запитів до баз даних не копіюються і зберігаються лише один раз. Достатньо користувачеві змінити вміст змінної, що містить результати роботи бази даних, як буде виконано механізм копіювання під час запису (Copy-On-Write), щоб уникнути зміни посилаючого внутрішнього буфера результату. Вміст буфера не повинен бути змінений, тому що користувач може ухвалити рішення прочитати результат вдруге. Механізм копіювання під час запису реалізується за допомогою додаткового керування списком посилань та використання стандартних zval лічильників посилань. Копіювання при записі також має бути зроблено, якщо користувач читає дані результату в PHP змінних і звільняє дані результату, перш ніж змінні будуть знищені.
Загалом, цей шаблон працює добре для скриптів, які читають набори даних один раз і не змінюють змінних, що містять результати. Його головний недолік у накладних витратах пам'яті, викликаних додатковим управлінням посиланнями, причина якого в першу чергу пов'язана з тим, що змінні користувача, що утримують результати, не можуть бути повністю звільнені до того, як система управління посиланнями mysqlnd містить посилання на них. Вбудований драйвер MySQL видаляє посилання на змінні користувача коли масив отриманих даних звільняється або виконується механізм копіювання при записі. Спостерігач побачить зростання загального споживання пам'яті, поки масив отриманих даних не звільниться. Використовуйте статистику, щоб перевірити, скрипт явно справив звільнення даних результату або драйвер зробив це неявно і тому пам'ять використовується протягом більш тривалого часу, ніж це необхідно. Статистика також допомагає побачити кількість операцій копіювання під час запису.
PHP-скрипт, що читає безліч невеликих рядків у буферизованому масиві даних, використовує код, подібний while ($row = $res->fetch_assoc()) { ... }
, може оптимізувати споживання пам'яті, запросивши копії замість посилань. Хоча і запит копій означає збереження тих же результатів у пам'яті двічі, це дозволяє PHP знищити копію, що містить $row
як ітерований масив даних і перед знищенням результат встановлює сам себе. На завантаженому сервері оптимізація використання пам'яті може допомогти покращити загальну продуктивність системи, хоча для окремого скрипту підхід з копією замість посилань може бути повільнішим у зв'язку з додатковим виділенням пам'яті та додатковими операціями копіювання в пам'яті.
Контроль та налагодження
Існує кілька методів відстеження використання пам'яті у вбудованому драйвері MySQL "mysqlnd". Якщо мета - отримати швидкий високорівневий огляд або перевірити ефективність PHP-скриптів під час роботи з пам'яттю, перевірте статистику, зібрану бібліотекою. Статистика дозволить вам, наприклад, упіймати SQL-запит, який генерує більше результатів, ніж обробляються PHP-скриптом.
Журнал налагодження може бути налаштований для запису викликів системи керування пам'яттю. Це допомагає побачити коли пам'ять виділяється та звільняється. Однак, розмір запрошених шматків пам'яті може бути в списку.
У деяких останніх версіях вбудованого драйвера MySQL "mysqlnd" є можливість емуляції випадкових ситуацій нестачі пам'яті. Ця можливість була задумана для використання лише C-розробниками бібліотеки чи авторами плагіна mysqlnd. Будь ласка, використовуйте пошук за вихідним кодом для відповідного налаштування PHP та для подальшої інформації. Ця можливість є недокументованою і може бути змінена будь-коли без додаткового повідомлення.