Кешування в MongoDB для прискорення Laravel-застосунків

Перекладено ШІ 0 Laravel News 26 червня, 2026

Навіщо додавати Redis, якщо ваша MongoDB вже вміє ефективно кешувати дані? Розповідаємо, як пришвидшити застосунок на Laravel та спростити інфраструктуру проєкту без зайвих сервісів.

Запити до бази даних — це перше, на що варто звернути увагу, коли ваш застосунок на Laravel починає працювати повільно. Кожного разу, коли користувач завантажує сторінку, система може неодноразово звертатися до бази за одними й тими самими даними. Така повторювана робота марнує ресурси сервера та збільшує час очікування.

Кешування розв'язує цю проблему, зберігаючи дані, до яких часто звертаються, у швидкому шарі доступу. Хоча Redis та Memcached є популярними рішеннями, існує альтернатива, яку часто ігнорують: сам MongoDB. Якщо ви вже використовуєте MongoDB як основну базу даних, навіщо перевантажувати інфраструктуру ще одним сервісом?

Завдяки офіційному пакету mongodb/laravel-mongodb (версія 5.5.0 станом на 2025 рік), ви можете використовувати MongoDB як сховище кешу з нативною підтримкою TTL-індексів, які автоматично видаляють застарілі записи. Це спрощує вашу інфраструктуру, забезпечуючи при цьому відмінну продуктивність кешування.

# Що знадобиться

Перед початком переконайтеся, що у вас є:

PHP 8.1 або вище зі встановленим розширенням MongoDB PHP.

Laravel 11.x або 12.x — остання версія інтеграції Laravel з MongoDB підтримує обидві версії.

MongoDB 4.4+, запущений локально, або кластер MongoDB Atlas.

Composer для керування залежностями.

Якщо ви використовуєте Laravel Herd або встановлювали PHP через php.new, розширення MongoDB вже доступне. Перевірити його наявність можна командою:

php –ri mongodb

Ви повинні побачити версію розширення MongoDB та параметри конфігурації.

# Налаштування середовища

Спершу переконайтеся, що розширення MongoDB правильно встановлене та активоване як для CLI, так і для вебсервера:

php –ri mongodb

Якщо команда не повертає інформацію про розширення, його потрібно встановити:

pecl install mongodb

Після цього додайте рядок extension=mongodb.so (Linux/Mac) або extension=mongodb.dll (Windows) у ваш файл php.ini.

# Встановлення пакету MongoDB для Laravel

Пакет mongodb/laravel-mongodb офіційно підтримується командою MongoDB. Він містить драйвер кешу, підтримку Eloquent та розширення конструктора запитів.

Встановіть його за допомогою Composer:

composer require mongodb/laravel-mongodb

Підтримка Laravel 12 була додана у версії 5.2.0 у березні 2025 року. Пакет автоматично реєструє сервіс-провайдер, тому в Laravel 11+ ручне налаштування не потрібне.

# Налаштування MongoDB як драйвера кешу

Щоб увімкнути кешування через MongoDB, потрібно налаштувати два елементи: з'єднання з базою даних та сховище кешу.

# Налаштування з'єднання з базою даних

Відкрийте config/database.php і додайте з'єднання MongoDB:

'connections' => [
    // ... існуючі з'єднання
    'mongodb' => [
        'driver' => 'mongodb',
        'dsn' => env('MONGODB_URI', 'mongodb://localhost:27017'),
        'database' => env('MONGODB_DATABASE', 'laravel'),
    ],
],

Потім оновіть файл .env актуальними даними:

MONGODB_URI=mongodb://localhost:27017
MONGODB_DATABASE=laravel

Для MongoDB Atlas рядок підключення виглядатиме інакше (відповідно до вашого кластера).

# Конфігурація сховища кешу

У файлі config/cache.php додайте налаштування для сховища mongodb:

'stores' => [
    // ... існуючі сховища
    'mongodb' => [
        'driver' => 'mongodb',
        'connection' => 'mongodb',
        'collection' => 'cache',
        'lock_connection' => 'mongodb',
        'lock_collection' => 'cache_locks',
    ],
],

Зробіть MongoDB драйвером кешу за замовчуванням у файлі .env:

CACHE_STORE=mongodb

Або змініть значення безпосередньо у config/cache.php:

'default' => env('CACHE_STORE', 'mongodb'),

# Налаштування TTL-індексів

Індекси Time to Live (TTL) дозволяють MongoDB автоматично видаляти документи. Ми наполегливо рекомендуємо їх використовувати, оскільки вони:

  • Автоматично видаляють застарілі записи — не потрібно писати додатковий код.
  • Покращують продуктивність — MongoDB очищує дані у фоновому режимі.
  • Економлять місце — колекція кешу залишається компактною.

Фоновий процес MongoDB перевіряє застарілі документи кожні 60 секунд і видаляє їх пачками до 50,000 за один раз.

Важливі вимоги до TTL-індексів:

  • Вони працюють лише з однополевими індексами.
  • Індексоване поле має містити дату або масив дат.
  • MongoDB зберігає дати як типи BSON date, а не як рядки.

Створіть міграцію для налаштування TTL-індексів для кешу та блокувань:

php artisan make:migration create_mongodb_cache_indexes

Відкрийте створений файл і додайте наступний код:

<?php
 
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Cache;
use MongoDB\Laravel\Cache\MongoStore;
use MongoDB\Laravel\Cache\MongoLock;
 
return new class extends Migration
{
    public function up(): void
    {
        // Створення TTL-індексу для сховища кешу
        $store = Cache::store('mongodb')->getStore();
 
        if ($store instanceof MongoStore) {
            $store->createTTLIndex();
        }
 
        // Створення TTL-індексу для блокувань кешу
        $lock = Cache::store('mongodb')->lock('setup_lock');
 
        if ($lock instanceof MongoLock) {
            $lock->createTTLIndex();
        }
    }
 
    public function down(): void
    {
        // TTL-індекси можна видалити вручну за потреби:
        // db.cache.dropIndex("expires_at_1")
        // db.cache_locks.dropIndex("expires_at_1")
    }
};

Запустіть міграцію:

php artisan migrate

Метод createTTLIndex() створює індекс на полі expires_at, що дає команду MongoDB автоматично видаляти документи після завершення терміну їхньої дії.

# Базове використання кешу

Facade Cache в Laravel забезпечує єдиний API незалежно від обраного драйвера. Ось основні операції:

# Зберігання та отримання даних

Збереження значення в кеші з часом дії у секундах:

use Illuminate\Support\Facades\Cache;
 
// Зберігаємо на 60 секунд
Cache::put('user_count', 1500, 60);
 
// Зберігаємо на 1 годину за допомогою Carbon
Cache::put('dashboard_stats', $stats, now()->addHour());
 
// Отримуємо значення
$count = Cache::get('user_count');
 
// Отримуємо зі значенням за замовчуванням
$count = Cache::get('user_count', 0);
 
// Перевіряємо наявність ключа
if (Cache::has('user_count')) {
    // Ключ існує і не застарів
}

// Видаляємо конкретний елемент
Cache::forget('user_count');
 
// Повне очищення кешу
Cache::flush();

# Патерн Remember

Метод remember намагається отримати дані з кешу, а якщо їх там немає — виконує функцію, зберігає результат і повертає його:

$users = Cache::remember('active_users', 3600, function () {
    return User::where('status', 'active')
        ->with('profile')
        ->get();
});

Це ідеально підходить для важких запитів до бази даних. Перший запуск виконає запит, а наступні звернення протягом години повертатимуть дані миттєво.

Для даних, що майже не змінюються, використовуйте rememberForever:

$settings = Cache::rememberForever('app_settings', function () {
    return Setting::all()->pluck('value', 'key')->toArray();
});

# Інкремент та декремент

Кеш MongoDB підтримує атомарні операції з числами:

// Ініціалізація лічильника
Cache::put('page_views', 0, 3600);
 
// Збільшення на 1
Cache::increment('page_views');
 
// Збільшення на певне значення
Cache::increment('page_views', 10);
 
// Зменшення
Cache::decrement('api_calls_remaining');
Cache::decrement('api_calls_remaining', 5);
 
// Працює навіть із числами з рухомою комою (float)
Cache::increment('total_revenue', 99.99);

Ці операції атомарні, тому їх безпечно використовувати у високонавантажених системах, де кілька процесів можуть оновлювати лічильник одночасно.

# Практичний приклад: Каталог товарів

Використаємо кешування у ProductController для прискорення роботи каталогу:

<?php
 
namespace App\Http\Controllers;
 
use App\Models\Product;
use Illuminate\Support\Facades\Cache;
 
class ProductController extends Controller
{
    public function index() {
        // Кешуємо каталог на 30 хвилин
        $products = Cache::remember('products:catalog', 1800, function () {
            return Product::with(['category', 'images'])
                ->where('status', 'active')
                ->orderBy('featured', 'desc')
                ->orderBy('created_at', 'desc')
                ->limit(100)
                ->get();
        });
 
        return view('products.index', compact('products'));
    }
 
    public function show(string $slug) {
        // Кешуємо окремий товар на 1 годину
        $product = Cache::remember("product:{$slug}", 3600, function () use ($slug) {
            return Product::with(['category', 'images', 'variants', 'reviews'])
                ->where('slug', $slug)
                ->firstOrFail();
        });
 
        return view('products.show', compact('product'));
    }
 
    public function update(Request $request, string $id) {
        $product = Product::findOrFail($id);
        $product->update($request->validated());
 
        // Інвалідація кешу після оновлення
        Cache::forget("product:{$product->slug}");
        Cache::forget('products:catalog');
 
        return redirect()->back()->with('success', 'Товар оновлено');
    }
}

Цей контролер кешує і список товарів, і сторінки окремих продуктів. При оновленні товару ми очищуємо відповідні ключі, щоб користувачі бачили актуальну інформацію.

# Обмеження кешування в MongoDB

Варто знати про одне важливе обмеження: драйвер кешу MongoDB не підтримує теги кешу (Cache tags).

Теги працюють із драйверами, що зберігають дані в оперативній пам'яті (Redis, Memcached), але не з базами даних на кшталт MongoDB чи DynamoDB. Це зумовлено специфікою структур даних.

Якщо вам потрібна функціональність тегів, є два виходи:

Варіант 1: Використовуйте префікси ключів для організації записів:

// Групуємо через префікси замість тегів
Cache::put('products:featured:list', $products, 3600);
Cache::put('products:category:electronics', $electronics, 3600);
 
// Видаляємо за списком ключів
Cache::forget('products:featured:list');
Cache::forget('products:category:electronics');

Варіант 2: Використовуйте Redis для тегів паралельно з MongoDB:

// У config/cache.php додайте обидва сховища
// MongoDB для загального кешу
Cache::store('mongodb')->put('user_profile', $profile, 3600);
 
// Redis, коли потрібні теги
Cache::store('redis')->tags(['products', 'featured'])
    ->put('featured_products', $products, 3600);

Для більшості проєктів підхід із префіксами є цілком достатнім і дозволяє не ускладнювати інфраструктуру.

# Розподілені блокування (Distributed locks)

Щоб уникнути конфліктів, коли кілька процесів одночасно намагаються оновити ті самі дані, використовуйте блокування:

use Illuminate\Support\Facades\Cache;
 
public function processOrder(string $orderId)
{
    // Намагаємося отримати блокування на 10 секунд
    $lock = Cache::lock("order:processing:{$orderId}", 10);
 
    if ($lock->get()) {
        try {
            // Обробка замовлення...
            $this->processPayment($orderId);
        } finally {
            $lock->release();
        }
    } else {
        throw new \Exception('Замовлення вже обробляється іншим процесом');
    }
}

Для очікування вивільнення блокування використовуйте метод block:

// Чекаємо до 5 секунд
$lock = Cache::lock('inventory:update', 10);
 
$lock->block(5, function () {
    // Код виконається, коли блокування буде отримано
    $this->updateInventory();
});

MongoDB зберігає блокування у колекції cache_locks. Завдяки TTL-індексам вони автоматично зникають, якщо процес раптово завершився помилкою.

# MongoDB проти Redis для кешування

Вибір залежить від потреб вашого застосунку:

Фактор MongoDB Redis
Інфраструктура Використовує існуючий екземпляр Потребує окремого сервера
Продуктивність Відмінна для більшості задач Трохи швидша (субмілісекунди)
Масштабованість Краще горизонтальне масштабування Обмежена обсягом RAM
Гнучкість запитів Повноцінна мова запитів MongoDB Лише пошук за ключем
Надійність Повна персистентність за замовчуванням Можлива втрата даних при збої

Обирайте MongoDB, якщо:

  • Це ваша основна база даних.
  • Ви хочете мінімізувати кількість сервісів у стеку.
  • Потрібне надійне зберігання кешованих даних.

Обирайте Redis, якщо:

  • Потрібна мінімально можлива затримка (latency).
  • Ви використовуєте складні структури (sorted sets, pub/sub).
  • Ви будуєте real-time сервіси (чати, лідерборди).

# Кращі практики

# Оптимізація індексів

Для швидкого пошуку створіть індекс на полі key:

$collection = DB::connection('mongodb')->getCollection('cache');
$collection->createIndex(['key' => 1]);

Це суттєво прискорить роботу, коли обсяг кешу почне зростати.

# Прогрівання кешу (Cache warming)

Замість того, щоб чекати, поки перший користувач "спровокує" створення кешу, наповнюйте його заздалегідь під час деплою:

// Створіть консольну команду Artisan
public function handle()
{
    $this->info('Прогріваємо кеш...');
    Cache::put('products:featured', Product::featured()->limit(20)->get(), 3600);
    $this->info('Готово!');
}

Використовуйте php artisan cache:warm у скриптах автоматичного деплою.

# Вирішення типових проблем

Проблема: Помилка дубліката ключа в cache_locks.
Рішення: Це нормальна поведінка MongoDB для забезпечення унікальності блокування. Переконайтеся, що пакет mongodb/laravel-mongodb оновлено до версії 5.2.0+, де ця ситуація коректно обробляється.

Проблема: TTL-індекс не видаляє записи.
Рішення: Перевірте наявність індексу через MongoDB shell (db.cache.getIndexes()). Пам'ятайте, що видалення відбувається не миттєво, а протягом хвилини.

Проблема: Помилки серіалізації Eloquent-моделей.
Рішення: Замість цілих моделей кешуйте масиви: Cache::put('product', $product->toArray(), 3600). Це надійніше і займає менше місця.

# Висновок

Кешування через MongoDB — це практичне рішення для Laravel-проєктів, які вже використовують цю базу даних. Ви отримуєте потужний інструмент з автоматичним керуванням TTL без необхідності підключати Redis.

Почніть із кешування найважчих запитів, стежте за показниками Hit Rate у Laravel Pulse і використовуйте прогрівання кешу під час деплою. Це дозволить значно прискорити ваш застосунок, зберігаючи архітектуру простою та зрозумілою.

Популярні

Інше, що варто прочитати

13 Оновлено 26 червня, 2026

Удосконалюйте свої проєкти Laravel за допомогою справжнього штучного інтелекту для кодування з Laravel Boost!

Готові підняти свій робочий процес у Laravel на новий рівень? У цій статті я розгляну Laravel Boost, інноваційний AI-допомічник для програмування, який зробить вашу розробку швидшою та продуктивнішою

26 Оновлено 26 червня, 2026

"SQLSTATE[HY000] [2002] Connection refused" у Laravel в GitHub Actions

Чи стикалися ви з помилкою «SQLSTATE[HY000] [2002] Connection refused» під час налаштування GitHub Actions для вашого додатку на Laravel? У нашій статті ми розглянемо три поширені причини цієї помилки та надамо рішення для їх усунення. Читайте далі, щоб дізнатися, як ваш CI/CD потік може працювати бездоганно!

57 Оновлено 26 червня, 2026

Усе, що нам відомо про Livewire 4

Нова версія Livewire 4, представленої Келебом Порзіо на Laracon US 2025, обіцяє значні покращення у швидкості та організації компонентів. Які з інноваційних функцій підкорять ваше серце? Читайте далі, щоб дізнатися більше про те, як Livewire 4 полегшить вашу роботу