Jaka jest różnica między kursorem laravel a metodą Laravel chunk?
Chciałbym wiedzieć, jaka jest różnica między metodą Laravel chunk a metodą Laravel cursor. Która metoda jest bardziej odpowiednia do zastosowania? Jakie będą przypadki użycia obu z nich? Wiem, że powinieneś użyć kursora, aby zapisać pamięć, ale jak to naprawdę działa w backendzie?
Szczegółowe wyjaśnienie z przykładem byłoby przydatne, ponieważ szukałem na stackoverflow i innych stronach, ale nie znalazłem zbyt wiele informacji.
Oto fragment kodu z dokumentacja laravel.
Chunking Results
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
Używanie Kursorów
foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
//
}
6 answers
Rzeczywiście to pytanie może przyciągnąć pewną opiniotwórczą odpowiedź, jednak prosta odpowiedź jest tutaj w Laravel Docs
Tylko dla odniesienia:
To jest chunk:
Jeśli potrzebujesz przetworzyć tysiące elokwentnych rekordów, użyj polecenia
chunk
. Metodachunk
pobierze "fragment" wymownych modeli, przekazując je do danegoClosure
w celu przetworzenia. Użycie metodychunk
pozwoli zaoszczędzić pamięć podczas pracy z dużymi zestawami wyników:
To jest Kursor:
Metoda
cursor
pozwala na iterację rekordów bazy danych za pomocą kursora, który wykona tylko jedno zapytanie. Przy przetwarzaniu dużych ilości danych można użyć metodycursor
, aby znacznie zmniejszyć zużycie pamięci:
Chunk pobiera rekordy z bazy danych i ładuje je do pamięci, ustawiając kursor na ostatnim pobranym rekordzie, aby nie doszło do kolizji.
Więc zaletą jest tutaj, jeśli chcesz sformatować large rekord zanim zostaną wysłane, lub chcesz wykonać operację na n-tej liczbie rekordów na raz, to jest to przydatne. Przykładem jest budowanie arkusza view out / excel, dzięki czemu można wziąć rekord w liczbach, dopóki nie zostaną wykonane, aby wszystkie z nich nie zostały załadowane do pamięci na raz, a tym samym osiągnięcie limitu pamięci.
Kursor używa generatorów PHP, możesz sprawdzić stronę Generatory php jednak tutaj jest ciekawy podpis:
Generator pozwala na pisanie kodu, który wykorzystuje foreach do iteracji zbioru danych bez konieczności budowania tablicy w pamięci, co może spowodować przekroczenie limitu pamięci lub wymagać znacznego czasu przetwarzania do wygenerowania. Zamiast tego, możesz napisać funkcję generatora, która jest taka sama jak normalna funkcja, z tym wyjątkiem, że zamiast zwróćING raz, generator może uzyskać tyle razy, ile musi, aby dostarczyć wartości do be iterated over.
Chociaż nie mogę zagwarantować, że w pełni rozumiem pojęcie kursora, ale w przypadku Chunk, chunk uruchamia zapytanie przy każdym rozmiarze rekordu, pobierając je i przekazując do zamknięcia w celu dalszych prac nad rekordami.
Mam nadzieję, że to się przyda.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-26 00:08:07
Mamy porównanie: chunk () vs cursor()
- cursor (): High Speed
- chunk (): stałe zużycie pamięci
10,000 rekordy :
+-------------+-----------+------------+
| | Time(sec) | Memory(MB) |
+-------------+-----------+------------+
| get() | 0.17 | 22 |
| chunk(100) | 0.38 | 10 |
| chunk(1000) | 0.17 | 12 |
| cursor() | 0.16 | 14 |
+-------------+-----------+------------+
100,000 rekordy :
+--------------+------------+------------+
| | Time(sec) | Memory(MB) |
+--------------+------------+------------+
| get() | 0.8 | 132 |
| chunk(100) | 19.9 | 10 |
| chunk(1000) | 2.3 | 12 |
| chunk(10000) | 1.1 | 34 |
| cursor() | 0.5 | 45 |
+--------------+------------+------------+
- TestData: tabela użytkowników domyślnej migracji Laravel
- Homestead 0.5.0
- PHP 7.0.12
- MySQL 5.7.16
- Laravel 5.3.22
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-12 05:39:57
Cursor()
- Tylko Pojedyncze zapytanie
- Pobierz wynik wywołania
PDOStatement::fetch()
- domyślnie używane jest buforowane zapytanie i pobiera wszystkie wyniki naraz.
- zamienił tylko bieżący wiersz w wymowny model
Plusy
- Minimalizuj wymowną pamięć modelu
- łatwy do manipulowania
Cons
- ogromny wynik prowadzi do out of memory
- buforowane lub niebuforowane jest kompromisem
Chunk()
- chunk query in to queries with limit and offset
- Pobierz wynik wywołania
PDOStatement::fetchAll
- przekształcił wyniki w wymowne modele
Plusy
- kontrolowany rozmiar używanej pamięci
Cons
- przekształcanie wyników w wymowne modele batchly może spowodować nadmiarowość pamięci
- zapytań i wykorzystania pamięci jest traid-off
TL; DR
Kiedyś myślałem, że cursor () wykona zapytanie za każdym razem i zachowa tylko jeden wynik wiersza w pamięci. Więc kiedy zobaczyłem tabelę porównawczą @ mohammad-asghari, naprawdę się pogubiłem. To musi być jakiś bufor Za kulisami.
Śledząc Kod Laravela jak poniżej
/**
* Run a select statement against the database and returns a generator.
*
* @param string $query
* @param array $bindings
* @param bool $useReadPdo
* @return \Generator
*/
public function cursor($query, $bindings = [], $useReadPdo = true)
{
$statement = $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
if ($this->pretending()) {
return [];
}
// First we will create a statement for the query. Then, we will set the fetch
// mode and prepare the bindings for the query. Once that's done we will be
// ready to execute the query against the database and return the cursor.
$statement = $this->prepared($this->getPdoForSelect($useReadPdo)
->prepare($query));
$this->bindValues(
$statement, $this->prepareBindings($bindings)
);
// Next, we'll execute the query against the database and return the statement
// so we can return the cursor. The cursor will use a PHP generator to give
// back one row at a time without using a bunch of memory to render them.
$statement->execute();
return $statement;
});
while ($record = $statement->fetch()) {
yield $record;
}
}
Zrozumiałem, że Laravel buduje tę funkcję za pomocą wrap PDOStatement:: fetch(). I przez wyszukiwanie bufor PDO fetch i MySQL, znalazłem ten dokument.
Https://www.php.net/manual/en/mysqlinfo.concepts.buffering.php
Zapytania domyślnie używają trybu buforowanego. Oznacza to, że wyniki zapytań są natychmiast przesyłane z serwera MySQL do PHP, a następnie przechowywane w pamięci procesu PHP.
Więc wykonując PDOStatement:: execute() pobieramy całe wiersze wynikowe na jedynkach i zapisane w pamięci , a nie tylko jeden rząd. Jeśli wynik jest zbyt duży, spowoduje to wyjątek out of memory.
Chociaż pokazany dokument możemy użyć $pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
, aby pozbyć się buforowanego zapytania. Ale wadą powinna być ostrożność.
Niezbuforowane zapytania MySQL wykonują zapytanie, a następnie zwracają zasób, podczas gdy dane wciąż czekają na serwerze MySQL na pobranie. To zużywa mniej pamięci po stronie PHP, ale może zwiększyć obciążenie serwera. Chyba że pełny zestaw wyników był pobierane z serwera żadne dalsze zapytania nie mogą być wysyłane przez to samo połączenie. Zapytania niebuforowane mogą być również określane jako "użyj wyniku".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-03 14:55:02
chunk
opiera się na paginacji, utrzymuje numer strony i wykonuje pętlę za Ciebie.
Na przykład, DB::table('users')->select('*')->chunk(100, function($e) {})
wykona wiele zapytań, aż zestaw wyników będzie mniejszy niż rozmiar kawałka(100
):
select * from `users` limit 100 offset 0;
select * from `users` limit 100 offset 100;
select * from `users` limit 100 offset 200;
select * from `users` limit 100 offset 300;
select * from `users` limit 100 offset 400;
...
cursor
jest oparty na PDOStatement::fetch
i generatorze.
$cursor = DB::table('users')->select('*')->cursor()
foreach ($cursor as $e) { }
Zrobi jedno zapytanie:
select * from `users`
Ale sterownik nie pobiera wyniku ustawionego na raz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-22 08:35:15
Metoda cursor używa leniwych kolekcji, ale uruchamia zapytanie tylko raz.
Https://laravel.com/docs/6.x/collections#lazy-collections
Jednak metoda cursor konstruktora zapytań zwraca instancję LazyCollection. Pozwala to nadal uruchamiać tylko jedno zapytanie z bazą danych, ale także przechowywać tylko jeden wymowny model załadowany w pamięci na raz.
Chunk uruchamia zapytanie wiele razy i ładuje każdy wynik fragmentu do wymownych modeli w jeden raz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-11 10:50:06
Zrobiłem jakiś benchmark używając kursora i gdzie
foreach (\App\Models\Category::where('type','child')->get() as $res){
}
foreach (\App\Models\Category::where('type', 'child')->cursor() as $flight) {
//
}
return view('welcome');
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-20 11:32:33