Используем несколько баз данных в Laravel

Привет, хабр!
Бывало ли у вас такое?
Основная MySQL-база разрослась до гигантских размеров, и аналитические запросы тормозят продакшен.
Нужно подключиться к старой легаси-системе, но переносить данные в основную БД слишком дорого.
Часть данных идеально ложится в документоориентированную MongoDB, а другая — в реляционную PostgreSQL.
И тут возникает вопрос: стоит ли использовать несколько баз данных в одном проекте?
Когда это действительно нужно?
Разные типы данных — если часть данных (например, логи, аналитика) плохо уживается с реляционной моделью.
Интеграция с легаси-системой — когда старая БД остаётся, но новому коду нужно с ней работать.
Горизонтальное масштабирование — шардинг или выделение отдельных БД под разные сервисы. Изоляция критичных данных — например, финансовая информация в отдельной защищённой БД.
Подключение к двум разным базам данных в Laravel
Laravel предоставляет удобные инструменты для работы с несколькими базами данных. Рассмотрим пошаговую настройку.
1. Настройка конфигурации
Добавляем подключения в config/database.php:
'connections' => [
'mysql' => [ // Основная база
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
],
'secondary' => [ // Вторая база
'driver' => 'mysql',
'host' => env('DB_SECONDARY_HOST', '127.0.0.1'),
'port' => env('DB_SECONDARY_PORT', '3306'),
'database' => env('DB_SECONDARY_DATABASE', 'forge'),
'username' => env('DB_SECONDARY_USERNAME', 'forge'),
'password' => env('DB_SECONDARY_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
],
],
2. Добавляем данные в .env
DB_SECONDARY_HOST=127.0.0.1
DB_SECONDARY_PORT=3306
DB_SECONDARY_DATABASE=secondary_db
DB_SECONDARY_USERNAME=root
DB_SECONDARY_PASSWORD=secret
Теперь, когда мы настроили подключение к двум базам данных, давайте реализуем полноценную работу с ними. Рассмотрим весь цикл разработки: от создания миграций и моделей до реализации CRUD с элементами горизонтального шардинга.
1. Создание миграций для разных баз
Основная база (пользователи)
php artisan make:migration create_users_table
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('shard_key')->comment('Ключ для шардинга');
$table->timestamps();
});
Вторичная база (заказы)
php artisan make:migration create_orders_table
Schema::connection('secondary')->create('orders', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->decimal('amount', 10, 2);
$table->string('status');
$table->timestamps();
});
2. Создание моделей с отношениями
Модель User (основная база)
class User extends Model
{
protected $connection = 'mysql';
public function orders(): HasMany
{
// Логическая связь с заказами во второй базе. В моделе Order обязательно
нужно указать $connection!
return $this->hasMany(
Order::class,
'user_id',
'id',
);
}
// Определяем шард для пользователя
public function getConnectionName()
{
if ($this->shard_key !== null) {
return $this->shard_key % 2 == 0
? 'mysql_shard1'
: 'mysql_shard2';
}
return parent::getConnectionName();
}
}
Модель Order (вторая база)
class Order extends Model
{
protected $connection = 'secondary';
public function user(): BelongsTo
{
// Обратная связь с пользователем. В моделе User обязательно
нужно указать $connection!
return $this->belongsTo(
User::class,
'user_id',
'id',
);
}
}
3. Реализация базового CRUD с шардингом
UserService (работа с пользователями)
class UserService
{
public function createUser(array $data)
{
// Автоматическое определение шарда на основе email
$shardKey = crc32($data['email']) % 2;
$data['shard_key'] = $shardKey;
return User::create($data);
}
}
public function getUserWithOrders($userId)
{
$user = User::findOrFail($userId);
$orders = $user->orders()->get();
return [
'user' => $user,
'orders' => $orders
];
}
Таким образом, запись сразу достаётся и кладётся в нужную нам базу данных. Но, к сожалению, у Laravel есть ограничения, при работе с двумя и более базами данных. Практически всегда вам придётся указывать подключения явно, используя метод connection()
Свыкнуться с этим можно, но хотелось бы видеть более удобный способ кооперации двух баз данных. Если эта тема будет интересна, я бы хотел поработать над пакетом для Laravel для реализации подобного функционала. Неравнодушные, сплочаемся!
Работа с несколькими базами данных в Laravel требует дополнительных усилий, но в определённых сценариях это единственный разумный путь. Основные правила:
Чётко разделяйте данные по функциональному признаку
Документируйте, какие модели к каким БД относятся
Реализуйте механизмы синхронизации критичных данных
Мониторьте производительность всех подключений
А вам приходилось работать с несколькими БД в одном проекте? Делитесь опытом в комментариях — обсудим лучшие практики и подводные камни!
Habrahabr.ru прочитано 5087 раз