Laravel to jeden z najpopularniejszych frameworków PHP, a jego ORM – Eloquent – znacznie upraszcza operacje na bazie danych. Jednak przy dużych zbiorach danych (miliony rekordów) zapytania stają się coraz wolniejsze.
Rozwiązanie? Partycjonowanie danych!
Partycjonowanie pozwala na podział dużej tabeli na mniejsze sekcje (partycje), co zwiększa wydajność i ułatwia zarządzanie danymi. Laravel nie wspiera partycjonowania natywnie, ale dzięki pakietowi brokenice/laravel-mysql-partition możemy łatwo wdrożyć tę technikę.
Jak zainstalować pakiet?
Aby dodać pakiet do projektu, wykonaj poniższe polecenie
composer require brokenice/laravel-mysql-partition
Po instalacji warto opublikować plik konfiguracyjny, który pozwoli na dostosowanie ustawień:
php artisan vendor:publish --tag=laravel-mysql-partition-config
Partycjonowanie na poziomie bazy danych (MySQL)
Załóżmy, że mamy tabelę orders, którą chcemy podzielić na partycje według roku zamówienia (order_date).
Definiowanie modelu z partycjonowaniem
W modelu wykorzystamy trait udostępniany przez pakiet, który umożliwia integrację partycjonowania w Eloquent:
use Brokenice\LaravelMysqlPartition\Traits\Partitionable;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
use Partitionable;
protected $table = 'orders';
protected $fillable = ['customer_id', 'order_date', 'total_amount'];
/**
* Kolumna, na podstawie której odbywa się partycjonowanie.
*/
public static function partitionColumn(): string
{
return 'order_date';
}
/**
* Strategia partycjonowania – w tym przypadku partycjonowanie według zakresu.
*/
public static function partitionStrategy(): string
{
return 'range';
}
/**
* Zakresy dla poszczególnych partycji.
*/
public static function partitionRanges(): array
{
return [
'p2022' => ['min' => '2022-01-01', 'max' => '2022-12-31'],
'p2023' => ['min' => '2023-01-01', 'max' => '2023-12-31'],
'p2024' => ['min' => '2024-01-01', 'max' => '2024-12-31'],
];
}
}
Analiza kodu
use Partitionable– włącza funkcjonalność partycjonowania w Eloquent.partitionColumn()– określa kolumnę, na której będzie opierać się partycjonowanie (w naszym przypadkuorder_date).partitionStrategy()– definiuje strategię partycjonowania, tu używamy metodyrange.partitionRanges()– ustala zakresy dla poszczególnych partycji (np. lata 2022, 2023, 2024).
Tworzenie migracji dla partycji w MySQL
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateOrdersTable extends Migration
{
public function up()
{
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('customer_id');
$table->date('order_date');
$table->decimal('total_amount', 10, 2);
$table->timestamps();
})->partitionByRange('order_date', [
'p2022' => ['min' => '2022-01-01', 'max' => '2022-12-31'],
'p2023' => ['min' => '2023-01-01', 'max' => '2023-12-31'],
]);
}
public function down()
{
Schema::dropIfExists('orders');
}
}
Analiza kodu
- Metoda
partitionByRange()automatycznie dodaje partycjonowanie w MySQL na podstawie wartości w kolumnieorder_date. - Dzięki temu dane trafiają do odpowiednich partycji, co optymalizuje wykonywanie zapytań.
Pobieranie danych z wykorzystaniem partycji w Laravel
Wykorzystując standardowe zapytania Eloquent, MySQL automatycznie wybierze odpowiednią partycję na podstawie warunków:
$orders = Order::whereYear('order_date', 2023)->get();
Jeśli chcesz wymusić odpytywanie konkretnej partycji, możesz użyć surowego zapytania SQL:
$orders = DB::select(DB::raw("SELECT * FROM orders PARTITION (p2023) WHERE order_date >= '2023-01-01'"));
Partycjonowanie na poziomie aplikacji (Dynamiczne Tabele w Laravel)
Alternatywnie, zamiast partycjonowania na poziomie bazy danych, można tworzyć osobne tabele (partycje) dla poszczególnych zakresów danych, np. według roku.
Dynamiczne zmienianie tabeli w Eloquent
class OrderPartition extends Model
{
protected $table;
public function __construct($year)
{
$this->table = 'orders_' . $year;
}
}
Teraz model dynamicznie przełącza tabelę w zależności od przekazanego roku.
Tworzenie nowej tabeli (partycji) w Laravel
DB::statement("CREATE TABLE orders_2025 LIKE orders_2024");
Tworzy nową tabelę orders_2025, kopiując strukturę istniejącej partycji.
Pobieranie danych z określonej partycji
$orders = (new OrderPartition(2023))->where('customer_id', 1)->get();
Dane pobierane są tylko z tabeli orders_2023, co znacząco przyspiesza operacje przy dużych zbiorach danych.
Kiedy warto używać partycjonowania?
Warto stosować partycjonowanie, jeśli:
- Tabela zawiera miliony rekordów, co powoduje spadek wydajności.
- Większość zapytań dotyczy określonego zakresu danych (np. roku).
- Często usuwasz stare dane – można łatwo usunąć całą partycję zamiast pojedynczych rekordów.
Nie warto, jeśli:
- Dane są małe (poniżej kilku milionów rekordów).
- Zapytania wymagają częstego łączenia partycji (
JOIN), co może być wolne.
Podsumowanie
Laravel nie wspiera partycjonowania natywnie, jednak pakiet brokenice/laravel-mysql-partition umożliwia jego łatwą implementację. Możesz korzystać z partycjonowania na poziomie bazy (MySQL) lub zaimplementować partycjonowanie na poziomie aplikacji, tworząc dynamiczne tabele. Automatyczne zarządzanie partycjami przez pakiet upraszcza cały proces i może znacząco przyspieszyć działanie aplikacji obsługującej miliony rekordów.
Czy Twoja aplikacja obsługuje duże zbiory danych? Jeśli tak, zastosowanie partycjonowania może znacznie zmniejszyć obciążenie bazy i poprawić wydajność operacji.