get_option() против get_theme_mod(): Почему одна функция медленнее?
Я некоторое время использовал get_theme_mod()
в различных своих проектах. Я решил воспользоваться преимуществами API настройки тем в WordPress v3.4, как только он стал доступен, поскольку считал его незаменимым инструментом для моих клиентов.
Спустя некоторое время я начал замечать, что мои сайты стали работать немного медленнее обычного, а настройщик тем (Customizer) в частности загружался довольно долго. После множества проб и ошибок во время моего исследования, я решил попробовать изменить type
при регистрации настроек (т.е. $wp_customize->add_setting()
) с theme_mod
на option
.
Как только я сделал это и заменил все вызовы get_theme_mod()
на get_option()
, я заметил очень значительное увеличение скорости при использовании последнего варианта по сравнению с первым как во фронтенде, так и особенно в настройщике тем в административной части. Я просматривал ядро WordPress в попытке найти ответ, почему это происходит, но не смог определить, в чем конкретно заключается проблема в этом сценарии.
Буду признателен за любые идеи от сообщества относительно того, почему get_option()
работает значительно быстрее, чем get_theme_mod()
.
Ответ заключается в том, что да, функции theme_mod будут работать медленнее, но незначительно, и преимущества перевешивают различия.
Настройки темы хранятся как опции. По сути, функции theme_mod являются обертками вокруг функций для работы с опциями.
Сначала нужно понять, что настройки theme_mod хранятся в виде массива в одной опции, привязанной к конкретному имени темы. Например, если я сделаю так:
set_theme_mod('aaa',123);
set_theme_mod('bbb',456);
То в базе данных появится одна строка опции с именем theme_mods_themename, содержащая сериализованный массив с данными ('aaa'=>123, 'bbb'=>456).
Теперь, функция get_theme_mod
будет работать медленнее, потому что на самом деле она делает два вызова get_option
. Сначала она получает имя темы, затем — опцию theme_mods_themename
. Это уже приводит к потере 50% скорости. Остальная работа в основном связана с фильтрами, где есть дополнительный вызов фильтра, но если у вас нет обработчиков для этого фильтра, это несущественно.
Важно отметить, что система опций сохраняет полученные данные в кеше объекта, поэтому здесь не происходит множественных запросов к базе данных. Только первое использование приводит к обращению к базе.
Функция set_theme_mod
будет немного медленнее, потому что она делает те же два вызова get_option
, затем еще один вызов get_option
для повторного получения имени темы, после чего выполняет update_option
с полным набором измененных опций. Это приводит к обновлению базы данных, и тот факт, что отправляется гораздо больше данных, действительно может вызвать заметное замедление. Обновление нескольких байт происходит быстрее, чем обновление большой строки. Но разница обычно незначительна. Разве что у вас очень много настроек...
Функции theme_mod, безусловно, требуют оптимизации, но тем не менее их следует использовать вместо get_option
и подобных функций из-за дочерних тем.
Проблема с прямым использованием опций заключается в том, что вы работаете с ними напрямую, используя конкретные имена ключей для настроек.
Если у меня есть тема "AAA" и я создаю дочернюю тему "BBB" для другого сайта, то моя тема "AAA" может использовать опцию с именем "example". Когда я обновляю один сайт, и он обновляет мою опцию, то та же опция теперь применится и к моей дочерней теме. А что, если я не хотел этого? Что, если я хочу, чтобы дочерняя тема использовала другой набор настроек?
Theme_mod, включая фактическое имя темы (а не жестко заданное значение) в ключ, гарантирует, что каждая "тема" на сайте использует свой собственный набор настроек. Я могу переключаться между ними, и настройки не переносятся, оставаясь такими, какими я их задал. Это проще, понятнее и интуитивнее.
И если в будущем изменения в ядре или плагины изменят работу theme_mod, вы автоматически получите преимущества без каких-либо изменений. Обертки всегда будут медленнее — это неизбежно, такова их природа. Тем не менее, вы все равно пишете код на PHP, а не на машинном языке. Мы используем такие обертки для упрощения и разделения функциональности. Темы не должны знать или заботиться о том, как их настройки хранятся в базе данных или как работает именование. Функции theme_mod предоставляют более простое и чистое решение.

get_theme_mod
- это всего лишь обёртка над get_option
. Теоретически, из-за дополнительного уровня абстракции, она должна работать медленнее, но на практике разница будет слишком незначительной, чтобы её можно было заметить.
Реальные различия в скорости могут возникать, если у вас есть медленный код, подключённый к хукам theme_mod.

Не обёртка. get_theme_mod сохраняет значение в сериализованном массиве вместе со всеми настройками темы в строке таблицы wp_options с предопределённым ключом "theme_mods_<имя темы>". get_option сохраняет значение в поле option_value строки таблицы wp_options с ключом, который вы указываете. Другими словами, get_theme_mod помещает значение в сериализованный массив со всеми остальными настройками темы; get_option этого не делает.

Может быть что-то происходит в Customizer? Я наблюдаю ту же проблему, что и автор оригинального поста.
Могу подтвердить, что при использовании около 30 опций время загрузки Customizer сократилось с примерно 3 секунд до примерно 0.5 секунды при переходе с get_theme_mod
на get_option
.
При прямом вызове методов разница составляет 2 мс.
(https://gist.github.com/anonymous/d98a46d00d52d40e7dec)
Разница может быть незаметна при прямом сравнении API, но должно быть что-то в том, как они используются в Customizer.

Вы можете ПРОТЕСТИРОВАТЬ ВРЕМЯ выполнения get_option
(100 итераций) с помощью этого кода (поместите его в functions.php
или в другое место):
add_action('wp','My_Test');
function My_Test(){
var_dump(microtime(true));
for ($i=1; $i<100; $i++) { get_option('blogdescription'); }
var_dump(microtime(true));
for ($i=1; $i<100; $i++) { get_theme_mod('blogdescription'); }
var_dump(microtime(true));
exit;
}
Дополнительные размышления
Я не уверен, есть ли разница (возможно, разработчики WordPress знают лучше), но я подумал, что если сайт имеет ВЫСОКИЙ трафик, и при каждой загрузке страницы ему нужно получать сотни опций, то что если я объединю многие опции в одну с помощью get_option
? Например, так:
update_option('my_extra_optss', array(
'myNAME' => 'Джордж',
'myAGE' => 43 ));
Затем:
$x = get_option('my_extra_optss');
$x['myNAME'];
$x['myAGE'];
................
Будет ли это немного ускорять работу сайта?

Кратко: Если вы разработчик тем, вам следует использовать get_theme_mod
Подробный ответ:
Если у вас 100 вызовов get_option, это приводит к 100 запросам к вашей базе данных.
Если у вас 100 вызовов get_theme_mod, это приводит всего к 1 запросу к базе данных.
Почему? Потому что все настройки темы хранятся в одной строке базы данных и вызываются только один раз, в то время как каждая опция — это отдельная строка, и 100 вызовов get_option приведут к 100 запросам к базе данных, что, конечно же, замедлит работу вашего сайта.
Если ваша тема имеет много настроек, использование get_theme_mod значительно сократит количество запросов к базе данных.
Вы можете проверить производительность и количество запросов с помощью плагина Query Monitor

Это немного неверно. Функция get_theme_mod использует get_theme_mods - https://developer.wordpress.org/reference/functions/get_theme_mods/ - которая фактически выполняет 2 функции get_option. Таким образом, 100 вызовов get_theme_mod делают всего 2 запроса к базе данных ;)
