Оптимизация Active Record в Code Igniter
Что же такое Active Record?
Active Record — это популярный паттерн доступа к данным реляционных баз данных в объектно-ориентированном программировании. Но, в отличие от других фреймворков, в Code Igniter добавлен урезанный функционал, так как в нем нет ни отношений, ни поведений, ни валидации. По своей сути, это не знаменитый паттерн, а простой построитель SQL запросов, названный популярным термином.
Как сильно влияет Active Record на производительность?
Рассмотрим простейший пример:
Простой контроллер, простая модель (в которой есть 3 метода). Из библиотек загружается только database
. Есть таблица на 5000 записей, из которой мы будем извлекать данные, после чего — замерять пиковое потребление памяти функцией memory_get_peak_usage()
. На сервере активен eAccelerator
0. Начальный вид запроса:
$this->db->select('id, name'); $this->db->from('clan'); $this->db->where('total >', 19); $this->db->where('is_bk', 1); $this->db->where('gold >', 0); $this->db->order_by("id", "asc"); $this->db->limit($count); $query = $this->db->get(); // echo memory_get_peak_usage(); // 959328 bytes |
1. Используйте метод сцепления данных:
Начиная с PHP 5.0 можно и нужно использовать метод сцепления данных.
$query = $this->db->select('id, name')->from('clan')->where('total >', 19)->where('is_bk', 1)->where('gold >', 0)->order_by("id", "asc")->limit($count)->get(); // 958168 bytes |
2. Группируйте одинаковые элементы:
Некоторые типы операций можно объединить в одну запись. В нашем случае — select()
и where()
записаны только один раз.
$query = $this->db->select('id, name')->from('clan')->where(array('total >' => 1, 'is_bk' => 1, 'gold >' => 0))->order_by("id", "asc")->limit($count)->get(); // 957432 bytes |
3. Используйте сокращенную запись там, где это возможно:
К примеру нет смысла писать ->from('clan')->limit($count)->get()
, ведь всю эту запись можно заменить одним методом ->get('clan', $count)
$query = $this->db->select('id, name')->where(array('total >' => 1, 'is_bk' => 1, 'gold >' => 0))->order_by("id", "asc")->get('clan', $count); // 956856 bytes |
Не стоит объединять записи в метод get_where()
, это отрицательно сказывается на производительности:
$query = $this->db->select('id, name')->order_by("id", "asc")->get_where('clan', array('total >' => 1, 'is_bk' => 1, 'gold >' => 0), $count); // 957288 bytes |
Итоговая оптимизация 2473 байта. Кто-то скажет мало? А если у нас десятки запросов на странице и пользователи генерируют 50000 просмотров в сутки?
Рассчитаем относительное уменьшение памяти. Загрузим пустой метод контроллера, без подключения модели:
// 898240 bytes |
Подключим модель (в модели 3 метода):
$this->load->model('tests_ar'); // 941928 bytes |
Выполнение запроса из пункта 0 расходует 17400 байт памяти, тогда как запрос из пункта 3 расходует 14928 байт. Оптимизация составила почти 15% памяти (правда в абсолютных цифрах оптимизация составила менее 1%). Мы не оптимизировали SQL запросы, не отключали кэширование. В принципе — мы не делали ровным счетом ничего. А выиграли 1% памяти. Кто то скажет, что это борьба с ветряными мельницами. Быть может и так, но неплохо бы приучить себя писать более оптимальный код.
4. Возвращайте данные в виде массива:
для пункта номер 3, limit 100
return $query->result(); //1022664 bytes return $query->result_array(); //1007752 bytes |
для пункта номер 3, limit 1000
return $query->result(); //1734984 bytes return $query->result_array(); //1598424 bytes |
Здесь можно сэкономить еще один-два процента. Во всех последних проектах на Code Igniter я использую только result_array()
;
5. Не используйтие Active Record
Да да, вы не ослышались. Зачем использовать Active Record, когда можно писать запросы на чистом SQL? Для начала выключим поддержку Active Record:
/* Location: ./system/application/config/database.php */ $active_record = FALSE; |
Перепишем метод нашей модели на чистый SQL:
$query = $this->db->query('SELECT id, name FROM (clan) WHERE total > 1 AND is_bk = 1 AND gold > 0 ORDER BY id asc LIMIT '.$count.';'); //811032 bytes |
По сравнению с пунктом 3 прирост скорости составил 145Kb (-15%). Вы все еще используете построитель запросов Active Record? А смысл?
AR(актив рекрд) сильный шаблон. в CI он также служит для абстрагирования языка запросов от текущего типа СУБД(mysql/postress/mssql/etc). Исключение его из использования из-за расхода нескольких килобайт, не принесёт существенного выигрыша.
Подгрузка класса работы с СУБД тоже занимает не много времени, в реалиях XCashe.
Более существенная в CI проблема — автоподгрузка моделей, которую всё ещё не реализовали. В больших проектах это подталкивает разработчиков загружать все модели при инициализации, что скажется на скорости работы приложения.
@IAD
Для каждого задания нужно выбирать свой инструмент. Если пишется средней сложности проект — обычно известна среда выполнения, то есть происходит заточка под определенную БД.
В более крупных проектах — оправданно использование ORM + MemCache.
Но я не считаю, что CI хорошо подходит для крупных проектов.
Вышеизложенная оптимизация помогла разгрузить один из моих проектов на шаред хостинге, где невозможно использовать кэширование. При посещаемости в 2000-3000 уников в сутки приходится выжимать все соки.
ps. В следующий раз будет оправдательная статья и обзор некоторых плюшек для AR.