laravel будет ли лагать такой запрос, и как это исправить
Есть такие таблицы users, eggs, birds, sellers, certificates
Каждый час или даже лучше каждые 10 минут мне нужно изменять и добавлять записи в таблицу eggs.
Для этого я сделал scheduler, запись cron и command, в которой такой запрос
public function handle() {
// Метод возвращает массив данных из таблиц birds и seller->certificates
$birds_users = User::get_all_users_birds_with_certificate();
// перебираем все это
foreach($birds_users as $key => $birds_user) {
// получаем пользователя по key (key - это user id)
$user = User::with('my_eggs') -> find($key);
// теперь перебираем каждую птицу пользователя по отдельности
foreach($birds_user[0] as $bird) {
// проверяем есть ли уже такая запись
if (
$user -> my_eggs -> contains('user_id', '===', $key) &&
$user -> my_eggs -> contains('bird_seller_id', '===', $bird["bird_seller_id"])
) {
// берем эту запись
$birdRow = Egg::where('user_id', $key) - > where('bird_seller_id', $bird["bird_seller_id"]) - > first();
// и обновляем нужные поля
$birdRow - > demand = $bird["demand"]; // if the characteristics of the bird will change
$birdRow - > count += $bird["count"] * $bird["fertility"]; // increase eggs
$birdRow - > update();
} else {
// такой записи еще нет, нужно ее создать
Egg::create([
'user_id' => $key,
'bird_seller_id' => $bird["bird_seller_id"],
'price' => $bird["egg_price"],
'demand' => $bird["demand"],
'count' => $bird["count"] * $bird["fertility"],
]);
}
}
}
}
ТО есть получается, что каждый час (или 10 минут) получаем каждого пользователя по отдельности, для каждой его птицы так же выполняем запрос. Это может вызывать большую нагрузку? Если да, то как это можно исправить
Ответы (2 шт):
Я думаю так делать не следует. Гонять данные между веб-сервером и сервером базы данных для массовых обновлений - не лучшее решение. Нужно писать сырой запрос и отсылать его серверу базы данных, пусть он там у себя все быстренько сделает.
В качестве отправной точки для написания запроса:
Да, посылать такие запросы через laravel - не лучшая идея. Нужно написать один сырой sql запрос. Сначала было страшно приступать к этому, так как в sql я писал только SELECT, иногда UPDATE. А тут куча связей. Но потом оказалось, что это не так и страшно, даже интересно)
INSERT INTO eggs (id, name, birds_count, price, demand, count, litter, collected, user_id, bird_seller_id)
SELECT
CONCAT(u.id, b_s.id), -- create UID from user_id and bird_seller_id
birds.name, -- bird name
count, -- count of birds from bird_seller_user table
birds.egg_price * (1 + IFNULL(price_bonus, 0) / 100), -- egg price with certificate
birds.demand * (1 + IFNULL(demand_bonus, 0) / 100), -- demand with certificate bonus
count * birds.fertility * (1 + IFNULL(fertility_bonus, 0) / 100), -- get count of eggs from birds_count * fertility with certificate bonus
count * birds.litter * (1 + IFNULL(litter_bonus, 0) / 100),
0, -- change collected
user_id,
bird_seller_id
FROM users AS u -- get all users
LEFT JOIN bird_seller_user AS b_s_u ON (b_s_u.user_id = u.id) -- join bird_seller_user table to get sold_bird
JOIN bird_seller AS b_s ON (b_s.id = b_s_u.bird_seller_id) -- join bird_seller table (this id sol_bird)
JOIN birds ON (b_s.bird_id = birds.id) -- join birds table
LEFT JOIN certificates AS certs ON ((SELECT certificate_id FROM sellers WHERE id = b_s.seller_id) = certs.id) -- and join certificate table
ON DUPLICATE KEY UPDATE
name = birds.name, -- update bird name (just in case)
birds_count = b_s_u.count,
price = birds.egg_price * (1 + IFNULL(price_bonus, 0) / 100),
demand = birds.demand,
count = eggs.count + b_s_u.count * birds.fertility * (1 + IFNULL(fertility_bonus, 0) / 100), -- increase eggs count
litter = b_s_u.count * birds.litter * (1 + IFNULL(litter_bonus, 0) / 100),
collected = 0
через join связываем все таблицы, аналог belongsTo в ларе,
Вставляем эти данные через INSERT INTO.
А через ON DUPLICATE KEY UPDATE обновляем запись, если она уже есть (Он работает, если совпадает id)