Кешування даних =============== Кешування даних — це зберігання деякої змінної PHP в кеші та її отримання звідти. Для цієї мети базовий клас [CCache] компонента кешу має два найбільш використовуваних методи: [set()|CCache::set] та [get()|CCache::get]. Для кешування змінної `$value` ми вибираємо унікальний ідентифікатор (ID) і викликаємо метод [set()|CCache::set] для її збереження в кеші: ~~~ [php] Yii::app()->cache->set($id, $value); ~~~ Дані будуть залишатися в кеші до тих пір, поки не будуть видалені із-за деяких умов функціонування кеша (наприклад, не залишилося місця для кешування, тоді більш старі дані видаляються). Для зміни такої поведінки ми можемо встановити значення терміну дії кешу при виклику методу [set()|CCache::set]. Тоді дані будуть видалені з кешу після певного періоду часу: ~~~ [php] // зберігаємо значення змінної в кеші не більше 30 секунд Yii::app()->cache->set($id, $value, 30); ~~~ Пізніше, коли нам знадобиться доступ до цієї змінної (у цьому ж або іншому запиті), ми викликаємо метод [get()|CCache::get] з ідентифікатором змінної. Якщо отримане значення - false, то змінна не доступна в кеші і ми повинні регенерувати її (оновити в кеші). ~~~ [php] $value=Yii::app()->cache->get($id); if($value===false) { // оновлюємо $value, тому що змінна не знайдена у кеші, // і зберігаємо в кеш для подальшого використання: Yii::app()->cache->set($id,$value); } ~~~ При виборі ідентифікатора для кешованої змінної, враховуйте, що він повинен бути унікальним для кожної змінної з тих, що можуть бути кешованими у додатку. *НЕ* потрібно, щоб ідентифікатор був унікальним між різними додатками, тому що компонент кешу досить розумний для розрізнення ідентифікаторів різних додатків. Деякі кеш-сховища, такі як MemCache, APC, підтримують завантаження декількох кешованих значень у пакетному режимі, що може зменшити накладні витрати на отримання даних, збережених в кеші. Метод [mget()|CCache::mget] дозволяє використовувати цю особливість. У випадку, коли кеш-сховище не підтримує цю функцію, [mget()|CCache::mget] буде як і раніше імітувати її. Для видалення кешованого значення з кешу потрібно викликати метод [delete()|CCache::delete], а для очищення всього кешу - викликати метод [flush()|CCache::flush]. Потрібно бути обережним при виклику метода [flush()|CCache::flush], так як він також видаляє кешовані дані інших додатків. > Tip|Підказка: клас [CCache] реалізує `ArrayAccess`, > тому компонент кешу може використовуватися як масив. Нижче наведено приклади: > ~~~ > [php] > $cache=Yii::app()->cache; > $cache['var1']=$value1; // еквівалентно $cache->set('var1',$value1); > $value2=$cache['var2']; // еквівалентно $value2=$cache->get('var2'); > ~~~ Залежність кешу --------------- Крім встановлення терміну дії, кешовані дані також можуть стати недійсними відповідно з деякими змінами залежності (dependency). Наприклад, якщо ми кешуємо вміст деякого файлу, і файл змінився, ми повинні прийняти кешовану копію як недійсну і зчитати свіжий вміст із файлу, а не з кешу. Ми представляємо залежність як екземпляр класу [CCacheDependency] або класів, які його успадковують. Ми передаємо екземпляр залежності разом з кешованими даними, коли викликаємо метод [set()|CCache::set]. ~~~ [php] // значення дійсно не більше 30 секунд // також, значення може стати недійсним раніше, якщо залежний файл змінено Yii::app()->cache->set($id, $value, 30, new CFileCacheDependency('FileName')); ~~~ Тепер, якщо ми спробуємо одержати значення `$value` з кешу, викликавши метод [get()|CCache::get], залежність буде перевірена і, якщо вона змінилася, ми отримаємо значення false, що вказує, що дані потребують оновлення. Нижче наведено перелік доступних залежностей кешу: - [CFileCacheDependency]: залежність змінюється, якщо час модифікації файлу змінено; - [CDirectoryCacheDependency]: залежність змінюється, якщо будь-який файл в каталозі або в підкаталогах змінений; - [CDbCacheDependency]: залежність змінюється, якщо результат запиту деякого певного SQL виразу змінено; - [CGlobalStateCacheDependency]: залежність змінюється, якщо значення певного глобального стану змінено. Глобальне стан — це змінна, яка є постійною в багаторазових запитах і сесіях програми. встановлюється методом [CApplication::setGlobalState()]; - [CChainedCacheDependency]: залежність змінюється, якщо будь-яка залежність ланцюжка змінена; - [CExpressionDependency]: залежність змінюється, якщо результат певного виразу PHP змінено. Кешування запитів ----------------- У версію 1.1.7 Yii додана підтримка кешування запитів. Побудоване на кешуванні даних, кешування запитів зберігає результат запиту до бази даних в кеші і, тим самим, заощаджує час, що витрачається на одні й ті ж запити. > Info|Інформація: Деякі СУБД, такі як [MySQL](http://dev.mysql.com/doc/refman/5.1/en/query-cache.html), підтримують кешування на стороні сервера бази даних. Підтримка кеша в Yii більш гнучка і потенційно більш ефективна. ### Увімкнення кешування запитів Для того, щоб увімкнути кешування запитів, переконайтеся, що в [CDbConnection::queryCacheID] знаходиться ID підключеного компонента, що реалізує кешування. За замовчуванням це компонент `cache`. ### Використання кешування запитів з DAO Для того, щоб використовувати кешування запитів необхідно викликати метод [CDbConnection::cache()]. Приклад: ~~~ [php] $sql = 'SELECT * FROM tbl_post LIMIT 20'; $dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post'); $rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll(); ~~~ При виконанні наведеного коду, Yii спочатку перевірить, чи є в кеші актуальний результат, відповідний SQL-запиту, який ми збираємося виконати. При цьому перевіряється: - що в кеші є дані із запитом у якості індексу. - що ці дані не застаріли (минуло менше 1000 секунд з останнього запису в кеш). - що залежність не змінилася (максимальне значення `update_time` те саме, що було при збереженні результату запиту в кеш). Якщо всі три умови виконуються, то результат береться з кешу. Інакше виконується SQL запит, його результат записується в кеш і повертається. ### Використання кешування запитів з ActiveRecord Кешування запитів також можна використовувати з [Active Record](/doc/guide/database.ar). Для цього ми використовуємо метод [CActiveRecord::cache()]: ~~~ [php] $dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post'); $posts = Post::model()->cache(1000, $dependency)->findAll(); // реляційний запит $posts = Post::model()->cache(1000, $dependency)->with('author')->findAll(); ~~~ Метод `cache()` є коротким записом виклику [CDbConnection::cache()]. При виконанні SQL запиту, згенерованого ActiveRecord, Yii спробує використовувати кешування так само, як це було описано в попередньому підрозділі. ### Кешування кількох запитів За замовчуванням, кожний раз, коли ми викликаємо метод `cache()` (як [CDbConnection], так і [CActiveRecord]), він кешує наступний за його викликом запит. Всі інші запити НЕ кешуються доки ми не викличемо `cache()` ще раз. Наприклад: ~~~ [php] $sql = 'SELECT * FROM tbl_post LIMIT 20'; $dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post'); $rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll(); // кешування запиту НЕ використовується $rows = Yii::app()->db->createCommand($sql)->queryAll(); ~~~ Передаючи методу `cache()` додатковий параметр `$queryCount`, ми можемо закешувати кілька запитів, які виконуються підряд. У наступному прикладі ми кешуємо два запити: ~~~ [php] // ... $rows = Yii::app()->db->cache(1000, $dependency, 2)->createCommand($sql)->queryAll(); // буде використовуватися кешування запитів $rows = Yii::app()->db->createCommand($sql)->queryAll(); ~~~ Як відомо, при виконанні реляційного AR-запиту, можуть використовуватися кілька SQL запитів (це можна дізнатися, перевіривши [журнал повідомлень](/doc/guide/topics.logging)). Приміром, якщо відношення між `Post` і `Comment` `HAS_MANY`, то код, наведений нижче, виконає два запити: - спочатку будуть обрані 20 записів; - після цього будуть обрані коментарі для цих записів. ~~~ [php] $posts = Post::model()->with('comments')->findAll(array( 'limit'=>20, )); ~~~ Якщо використовувати кешування запитів, як показано нижче, закешований буде тільки перший запит до БД: ~~~ [php] $posts = Post::model()->cache(1000, $dependency)->with('comments')->findAll(array( 'limit'=>20, )); ~~~ Для того, щоб в кеш потрапили обидва запити, необхідно передати додатковий параметр, що задає кількість кешованих запитів: ~~~ [php] $posts = Post::model()->cache(1000, $dependency, 2)->with('comments')->findAll(array( 'limit'=>20, )); ~~~ ### Обмеження Кешування запитів не працює з результатами, які містять посилання на ресурс. Приміром, вона повертається в деяких СУБД при використанні типу `BLOB`. У деяких сховищах кешу є обмеження на розмір збережених даних. Приміром, в memcache максимальний розмір однієї одиниці даних дорівнює одному мегабайту. Тому, якщо розмір результату запита перевищить дане обмеження, то кешування не спрацює.