Оптимизация Active Record в Code Igniter

31st Август 2011 | Категории: Code Igniter, PHP | Метки: , , ,

Что же такое 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? А смысл?

Subscribe without commenting


  1. 1st Сентябрь 2011 в 16:16

    AR(актив рекрд) сильный шаблон. в CI он также служит для абстрагирования языка запросов от текущего типа СУБД(mysql/postress/mssql/etc). Исключение его из использования из-за расхода нескольких килобайт, не принесёт существенного выигрыша.
    Подгрузка класса работы с СУБД тоже занимает не много времени, в реалиях XCashe.
    Более существенная в CI проблема — автоподгрузка моделей, которую всё ещё не реализовали. В больших проектах это подталкивает разработчиков загружать все модели при инициализации, что скажется на скорости работы приложения.

  2. Тарлюн Максим
    1st Сентябрь 2011 в 16:36

    @IAD
    Для каждого задания нужно выбирать свой инструмент. Если пишется средней сложности проект — обычно известна среда выполнения, то есть происходит заточка под определенную БД.
    В более крупных проектах — оправданно использование ORM + MemCache.

    Но я не считаю, что CI хорошо подходит для крупных проектов.

    Вышеизложенная оптимизация помогла разгрузить один из моих проектов на шаред хостинге, где невозможно использовать кэширование. При посещаемости в 2000-3000 уников в сутки приходится выжимать все соки.

    ps. В следующий раз будет оправдательная статья и обзор некоторых плюшек для AR.