Was ist der Unterschied zwischen Laravel-Cursor und Laravel-Chunk-Methode?
Lesezeit: 10 Minuten
Suraj
Ich würde gerne wissen, was der Unterschied zwischen der Laravel-Chunk- und der Laravel-Cursor-Methode ist. Welche Methode ist besser geeignet? Was werden die Anwendungsfälle für beide sein? Ich weiß, dass Sie den Cursor verwenden sollten, um Speicher zu sparen, aber wie funktioniert es eigentlich im Backend?
Eine detaillierte Erklärung mit Beispiel wäre nützlich, da ich auf Stackoverflow und anderen Websites gesucht habe, aber nicht viele Informationen gefunden habe.
Hier ist das Code-Snippet aus der Laravel-Dokumentation.
Chunking-Ergebnisse
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
Verwenden von Cursorn
foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
//
}
von dem API-Dokumente: Stück: Chunk der Ergebnisse der Abfrage. Mauszeiger: Holen Sie sich einen Generator für die angegebene Abfrage.
TestData: Benutzertabelle der Laravel-Standardmigration
Gehöft 0.5.0
PHP 7.0.12
MySQL 5.7.16
Laravel 5.3.22
Haben Sie eine Ahnung, warum Chunks weniger Speicher verbrauchen als Cursor? Das kommt mir etwas seltsam vor.
– Antti Pihlaja
21. Februar 2019 um 11:52 Uhr
@AnttiPihlaja Ich denke, das liegt daran cursor() behält die Ergebnismenge (100.000 Datensätze) weiterhin im Speicher und ruft die Zeilen bei Bedarf als Objekte ab (mit PDOStatement::fetch. chunk() Verwendet LIMIT und OFFSET um die Größe der Ergebnismenge zu begrenzen und die gesamte Ergebnismenge für jeden Block/jede Abfrage (10.000 Zeilen) in den Speicher zu laden PDOStatement::fetchAll.
– Ion Bazan
31. Juli 2019 um 7:49 Uhr
@IonBazan Ja. Aber das ist ein sehr unerwartetes Verhalten für den DB-Cursor. Der Grund dafür ist, dass Laravel die zugrunde liegende PDO-Verbindung so konfiguriert, dass sie sich so verhält.
– Antti Pihlaja
2. August 2019 um 3:38 Uhr
Es scheint, dass die Verwendung von Cursor immer besser ist als get(), aber das stimmt nicht. Die Cursor-Leistung ist bei größeren Datensätzen langsamer als die von get(), da der Cursor mit fetch Datensätze einzeln aus dem Puffer holt, während get mit fetchAll alles zurückgibt. fetchAll hat sich als schneller als das Schleifen durch fetch erwiesen.
– Bernhard Wiesner
18. November 2021 um 5:45 Uhr
@BernardWiesner Sie können Ihre Szenarien testen und die Antwort aktualisieren.
– Seed Mohammad Asghari
19. November 2021 um 7:48 Uhr
Oluwatobi Samuel Omisakin
In der Tat könnte diese Frage eine rechthaberische Antwort hervorrufen, aber die einfache Antwort ist hier drin Laravel-Docs
Nur als Referenz:
Das ist Brocken:
Wenn Sie Tausende von Eloquent-Datensätzen verarbeiten müssen, verwenden Sie die chunk Befehl. Das chunk -Methode ruft einen “Bruch” von Eloquent-Modellen ab und füttert sie mit einem gegebenen Closure zum Bearbeiten. Verwendung der chunk -Methode spart Speicherplatz, wenn mit großen Ergebnismengen gearbeitet wird:
Das ist Cursor:
Das cursor -Methode können Sie Ihre Datenbankeinträge mit einem Cursor durchlaufen, der nur eine einzige Abfrage ausführt. Bei der Verarbeitung großer Datenmengen ist die cursor Methode kann verwendet werden, um Ihre Speichernutzung stark zu reduzieren:
Chunk ruft die Datensätze aus der Datenbank ab und lädt sie in den Arbeitsspeicher, während ein Cursor auf den letzten abgerufenen Datensatz gesetzt wird, damit es keine Konflikte gibt.
Hier ist also der Vorteil, wenn Sie die neu formatieren möchten groß aufnehmen, bevor sie versendet werden, oder wenn Sie eine Operation mit einer n-ten Anzahl von Datensätzen pro Mal durchführen möchten, dann ist dies nützlich. Ein Beispiel ist, wenn Sie ein Ansichts-/Excel-Blatt erstellen, sodass Sie den Datensatz zählen können, bis sie fertig sind, damit nicht alle auf einmal in den Speicher geladen werden und dadurch die Speichergrenze erreicht wird.
Cursor verwendet PHP-Generatoren, die Sie überprüfen können PHP-Generatoren Seite, aber hier ist eine interessante Bildunterschrift:
Mit einem Generator können Sie Code schreiben, der verwendet für jeden um einen Datensatz zu durchlaufen, ohne ein Array im Speicher erstellen zu müssen, was dazu führen kann, dass Sie eine Speichergrenze überschreiten oder eine beträchtliche Menge an Verarbeitungszeit zum Generieren benötigen. Stattdessen können Sie eine Generatorfunktion schreiben, die mit einer normalen identisch ist Funktionaußer dass statt Rückkehring einmal, kann ein Generator Ertrag so oft wie nötig, um die Werte bereitzustellen, über die iteriert werden soll.
Ich kann zwar nicht garantieren, dass ich das Konzept von Cursor vollständig verstehe, aber für Chunk führt Chunk die Abfrage bei jeder Datensatzgröße aus, ruft sie ab und übergibt sie an die Closure für weitere Arbeiten an den Datensätzen.
Hoffe, das ist nützlich.
Danke für die ehrliche Antwort. Obwohl ich das Konzept des Cursors immer noch nicht vollständig verstehe. Aber deine Antwort erklärt einiges.
– Suraj
2. August 2017 um 16:41 Uhr
Wenn es dir helfen kann, es besser zu verstehen, Laravels select verwendet PHPs fetchAll während Laravels cursor verwendet PHPs fetch. Beide führen die gleiche Menge an SQL aus, aber ersteres erstellt sofort ein Array mit den gesamten Daten, während letzteres die Daten zeilenweise abruft, sodass nur diese Zeile im Speicher gehalten werden kann, nicht die vorherige oder die folgenden.
– Grasdoppelt
5. Dezember 2018 um 5:28 Uhr
劉恒溫
Cursor()
nur Einzelabfrage
Ergebnis per Anruf abrufen PDOStatement::fetch()
standardmäßig wird eine gepufferte Abfrage verwendet und alle Ergebnisse auf einmal abgerufen.
verwandelte nur aktuelle Reihe in beredtes Modell
Vorteile
Minimieren Sie den Overhead des eloquenten Modellspeichers
leicht zu manipulieren
Nachteile
riesige Ergebnis führt aus dem Speicher
gepuffert oder ungepuffert ist ein Kompromiss
Chunk()
Chunk-Abfrage in Abfragen mit Limit und Offset
Ergebnis per Anruf abrufen PDOStatement::fetchAll
Ergebnisse stapelweise in beredte Modelle verwandelt
Vorteile
steuerbare verwendete Speichergröße
Nachteile
Wenn Ergebnisse stapelweise in eloquente Modelle umgewandelt werden, kann dies zu einem gewissen Speicheraufwand führen
Abfragen und Speicherverbrauch ist ein Kompromiss
TL;DR
Ich dachte immer Mauszeiger() führt jedes Mal eine Abfrage durch und behält nur ein Zeilenergebnis im Speicher. Als ich also die Vergleichstabelle von @mohammad-asghari sah, war ich wirklich verwirrt. Es müssen einige sein Puffer hinter den Kulissen.
Durch Verfolgen des Laravel-Codes wie unten beschrieben
/**
* 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;
}
}
Ich habe verstanden, dass Laravel dieses Feature per Wrap erstellt PDOStatement::fetch(). Und per Suche Puffer-PDO-Abruf und MySQLich habe dieses Dokument gefunden.
Abfragen verwenden standardmäßig den gepufferten Modus. Das bedeutet, dass Abfrageergebnisse sofort vom MySQL-Server an PHP übertragen und dann im Speicher des PHP-Prozesses gehalten werden.
Indem wir also PDOStatement::execute() ausführen, holen wir tatsächlich ganze Ergebniszeilen bei Einsen und im Speicher abgelegt, nicht nur eine Reihe. Wenn das Ergebnis also zu groß ist, wird dies der Fall sein zu Gedächtnislücken führen Ausnahme.
Obwohl das gezeigte Dokument wir verwenden könnten $pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); um die gepufferte Abfrage loszuwerden. Aber der Nachteil sollte Vorsicht sein.
Ungepufferte MySQL-Abfragen führen die Abfrage aus und geben dann eine Ressource zurück, während die Daten noch auf dem MySQL-Server darauf warten, abgerufen zu werden. Dies verbraucht weniger Speicher auf der PHP-Seite, kann aber die Last auf dem Server erhöhen. Wenn nicht die vollständige Ergebnismenge vom Server abgerufen wurde, können keine weiteren Abfragen über dieselbe Verbindung gesendet werden. Ungepufferte Abfragen können auch als “Ergebnis verwenden” bezeichnet werden.
sehr schöne erklärung. Ich war verwirrt darüber, wie der Cursor bei einem großen Datensatz zu einem Problem mit nicht genügend Arbeitsspeicher führen würde. Deine Antwort hat mir wirklich weitergeholfen.
– Anuj Shrestha
18. April 2020 um 7:17 Uhr
chunk basiert auf Paginierung, behält eine Seitennummer bei und erledigt die Schleifen für Sie.
Zum Beispiel, DB::table('users')->select('*')->chunk(100, function($e) {}) führt mehrere Abfragen durch, bis die Ergebnismenge kleiner als die Blockgröße ist (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 basiert auf PDOStatement::fetch und Generator.
$cursor = DB::table('users')->select('*')->cursor()
foreach ($cursor as $e) { }
wird eine einzige Abfrage ausgeben:
select * from `users`
Aber der Treiber ruft die Ergebnismenge nicht sofort ab.
Die Cursormethode verwendet Lazy Collections, führt die Abfrage jedoch nur einmal aus.
Die Cursormethode des Abfragegenerators gibt jedoch eine LazyCollection-Instanz zurück. Auf diese Weise können Sie immer noch nur eine einzige Abfrage für die Datenbank ausführen, aber auch jeweils nur ein Eloquent-Modell im Speicher laden.
Chunk führt die Abfrage mehrmals aus und lädt jedes Ergebnis des Chunks gleichzeitig in Eloquent-Modelle.
oguz463
Angenommen, Sie haben einen Millionen-Datensatz in db. Wahrscheinlich würde dies das beste Ergebnis liefern. So etwas kann man verwenden. Damit verwenden Sie Chunked LazyCollections.
while ($record = $statement->fetch()) {
yield $record;
}
Es verwendet bringen, lädt es jeweils nur 1 Datensatz aus dem Puffer in den Speicher. Beachten Sie jedoch, dass nur eine Abfrage ausgeführt wird. Weniger Speicher, aber langsamer, da es nacheinander iteriert. (Beachten Sie, dass der Puffer je nach Ihrer PHP-Konfiguration entweder auf der PHP-Seite oder in MySQL gespeichert werden kann. Lesen Sie mehr hier)
public function chunk($count, callable $callback)
{
$this->enforceOrderBy();
$page = 1;
do {
$results = $this->forPage($page, $count)->get();
$countResults = $results->count();
if ($countResults == 0) {
break;
}
if ($callback($results, $page) === false) {
return false;
}
unset($results);
$page++;
} while ($countResults == $count);
return true;
}
Verwendet viele kleinere Aufrufe von fetchAll (durch Verwendung von get()) und versucht, den Arbeitsspeicher gering zu halten, indem ein großes Abfrageergebnis in kleinere Abfragen aufgeteilt wird Grenze abhängig von der angegebenen Chunk-Größe. In gewisser Weise wird versucht, den Vorteil von get() und cursor() zu nutzen.
Als Faustregel würde ich sagen, gehen Sie mit Chunk oder noch besser chunkById, wenn Sie können. (Chunk hat eine schlechte Leistung an großen Tischen, da es verwendet wird versetztchunkBy id verwendet Grenze).
faul()
In Laravel 8 gibt es auch lazy(), es ähnelt Chunk, aber die Syntax ist sauberer (verwendet Generatoren)
In macht dasselbe wie chunk(), nur braucht man keinen Callback, da es den PHP-Generator verwendet. Sie können auch lazyById() ähnlich wie chunk verwenden.
12980400cookie-checkWas ist der Unterschied zwischen Laravel-Cursor und Laravel-Chunk-Methode?yes
von dem API-Dokumente: Stück: Chunk der Ergebnisse der Abfrage. Mauszeiger: Holen Sie sich einen Generator für die angegebene Abfrage.
– Online-Thomas
2. August 2017 um 15:16 Uhr
Schau mal hier ist gut erklärt 🙂
– Maraboc
2. August 2017 um 15:19 Uhr