Suche und Ajax-Suche sehr langsam

Hallo Forum,

ich arbeite gerade an meinem ersten Shop und stoße dabei auf einige Probleme. Das größte ist derzeit die Performance der Suche. Unser Testshop (Reifenhandel) ist eigentlich recht performant (Speedindex 90/100 Mobil und 93/100 Desktop). Die Suche allerdings ist unterirdisch von der Performance (21/100 und 23/100). Grund sind die langen Antwortzeiten des Severs (Time-To-First-Byte).

Der Shop hat derzeit 34.000 Artikel in 4 Kategorien. Sucht man nun z.B. nach “conti”, so dauert die Ajax-Suche für die Vorschläge 20sec, die Suche selbst zwischen 28sec (schnellste Zeit) und 49sec (langsamste Zeit). Ab dem 2. Aufruf der selben Suche geht es ratzfatz. Sucht man dann nach “Conti” (mit großem “C”), dauert es wieder Ewigkeiten.

Testshop befindet sich im Produktivmodus, Caching ist an. Suchindex wurde unzählige Male aufgebaut. Plugin “Intelligente Suche” ist installiert und konfiguriert. Im Backend in den Einstellungen --> Intelligente Suche --> Vorschau dauert die Suche nach “conti” nichtmal eine Sekunde.

Der Sever wird von Mutavi gehostet, hat 16GB RAM, 250GB Speicher. Und es ist ein NGINX-Webserver.

Shopware-Version 5.3.4, PHP 7.0.26, php-cache:300MB. ZendOPCache ist aktiv, APCu nicht aktiv. MySQL 2.5.5, query_cache aktiv mit 256MB, innodb_buffer_size liegt bei 2GB.

Vielleicht könnt ihr mir ein wenig auf die Sprünge helfen.

 

Dankeschön.

Hallo,

serverseitig kann man natürlich einiges machen. Shopwareseitig kann das an der Konfiguration der Suche liegen.
Was und wie viel wird durchsucht und indexiert z.B.?

Desto größer der Suchcache wird, desto größer ist auch die Auswirkung auf die Geschwindigkeit selbst.
Zusätzlich hat auch der Suchalgorithmus Einfluss auf die Performance. Da kann man schon diverse Einstellungen einmal durchgehen.

Die intelligente Suche macht auch Vorschläge zu verwandten Suchbegriffen. Da kann man in der Datenbank (beispielsweise: s_plugin_swag_fuzzy_statistics) nachsehen, wie groß diese Tabellen mittlerweile sind. Die können u.U. auch groß werden und dann einen negativen Effekt auf Suchanfragen haben.

Das mal so als erste Anhaltspunkte

Sebastian

Da ich diesbezüglich noch nicht allzuviel Erfahrung habe, hier mal die Einstellungen zur Suche und die Statistik-Tabelle:

 

 

MaSQL 2.5.5 ?

CPU? Wird auch eine SSD verwendet?

Gehe auch das mal durch: http://community.shopware.com/Performance-Tipps-Shopware_detail_1258.html

Sorry, MySQL 5.5.58, CPU: Intel QuadCore, SSD wird verwendet.

zu den Performanc-Tipps:

Alles, was ich im Backend optimieren kann, wurde auch gemacht. Plugins deaktiviert. Aktualisierungen auf Cronjob, HTTP-Cache aktiv und aufgewärmt. An der MySQL-Konfiguration kann ich nix machen, da fehlen die root-Rechte. 

Habe zu Test- und Analysezwecken den Shyim-Profiler laufen, in dem ist zu erkennen, das je mehr Ergebnisse die Suche bringt, desdo länger haben die SQL-Abfragen gedauert.

Dann weiß ich auch nicht weiter. Die Shopware Mitarbeiter hier im Forum kommen vielleicht noch auf ein paar Ideen.

@Shopware Mitarbeiter: mit bis zu wievielen Artikel/Varianten habt ihr mal Shopware laufen lassen und da mal Performance Tests durchgeführt (Shopware Suche, Intelligente Suche, Variantenwechsel usw.) Gibt’s da von eurer Seite auch noch ein paar Performance Bremsen zu lösen?

Also wir haben auch einen Kunden der enorme Performance-Probleme mit der Intelligenten Suche hatte. Wir haben sie nun deaktiviert. Es gibt auch andere Lösungen (Agolia, Findologic,… ) die ähnliche Features anbieten.
Teste doch mal was es bringt, das Plugin zu deaktivieren. Falls das eh nichts ändert, dann kann man ja weiter nach dem Problem suchen. Wenn doch, kannst du dich ja mal nach einer Alternative umsehen oder den Plugin-Support beanspruchen, der dir ja zusteht.

Viele Grüße

Erstmal Danke für eure Hilfe. Intelligente Suche war auch nicht der Übeltäter.

Ich hab nun über den Shyim-Profiler herausbekommen, welche SQL-Queries die Suche langsam machen und konnte feststellen, das immer dann, wenn Preise involviert sind, die Suche lange dauert.

Filter:

Preisfilter mit anzeigen lassen: Suche langsam, Preisfilter raus: Suche schnell 

Sortierung:

Sortierung nach niedrigstem Preis als Standardsortierung: Suche langsam, andere Standardsortierung ohne Preisbezug: Suche schnell. Wenn man in der Sortierung irgendeine preisbezogene Sortierung nachträglich auswählt, dann dauert es auch wieder ewig.

Schuld daran ist eine INNER JOIN - Verknüpfung ind der SQL, welche aus einem Subselect besteht. Dieser Subselect liefert alle Preise einer Preisgruppe für jeden Artikel (bei mir derzeit 34.000). Nehme ich diesen Subselect mal raus, geht die SQL ratzfatz. Hier mal das Ende der SQL mit besagtem INNER JOIN (listing_price)

...
...
INNER JOIN (
    SELECT 
      prices.*, 
      MIN(
        ROUND(
          prices.price * (
            (
              100 - IFNULL(priceGroup.discount, 0)
            ) / 100
          ) * (
            (
              (
                CASE tax.id WHEN 1 THEN 19 WHEN 4 THEN 7 END
              ) + 100
            ) / 100
          ) * 1, 
          2
        )
      ) as cheapest_price 
    FROM 
      s_articles product 
      INNER JOIN s_core_tax tax ON tax.id = product.taxID 
      INNER JOIN s_articles_prices prices ON product.id = prices.articleID 
      INNER JOIN s_articles_details availableVariant ON availableVariant.articleID = product.id 
      AND availableVariant.active = 1 
      AND (
        product.laststock * availableVariant.instock
      ) >= (
        product.laststock * availableVariant.minpurchase
      ) 
      LEFT JOIN s_core_pricegroups_discounts priceGroup ON priceGroup.groupID = product.pricegroupID 
      AND priceGroup.discountstart = 1 
      AND priceGroup.customergroupID = 1 
      AND product.pricegroupActive = 1 
    WHERE 
      (
        prices.articledetailsID = availableVariant.id
      ) 
      AND (prices.from = 1) and (prices.pricegroup = 'EK')
    GROUP BY 
      product.id
  ) listing_price ON listing_price.articleID = product.id 
  INNER JOIN s_articles_attributes productAttribute ON productAttribute.articledetailsID = variant.id 
WHERE 
  avoidCustomerGroup.articleID IS NULL 
GROUP BY 
  product.id 
ORDER BY 
  listing_price.cheapest_price ASC, 
  product.id ASC 
LIMIT 
  12

Nun gilt es für mich noch herauszufinden, warum das so ist. Das erstmal als Zwischenstand.

 

Das ist jetzt aber ein Widerspruch zu deiner vorherigen Aussage:
> , so dauert die Ajax-Suche für die Vorschläge 20sec,

Wenn es am Preis liegt, wie kann die Ajax-Suche dann so lange brauchen? Da wird doch noch gar nicht nach Preisen gefiltert?
VG

@simkli schrieb:

Das ist jetzt aber ein Widerspruch zu deiner vorherigen Aussage:
> , so dauert die Ajax-Suche für die Vorschläge 20sec,

Wenn es am Preis liegt, wie kann die Ajax-Suche dann so lange brauchen? Da wird doch noch gar nicht nach Preisen gefiltert?
VG

Wenn eine Standardsortierung mit Preisbezug (Niedrigster oder Höchster) aktiv ist, dann dauert die Ajaxsuche so lange, weil auch diese Suche die Standardsuche benutzt.Bei Niedrigstem Preis steht dann in der Tat der Suchtreffer mit dem niedrigsten Preis ganz oben in der Vorschlagliste.

Hallo,

gibt es zu diesem Problem etwas neues?

Wir haben aktuell bei einem Kunden das gleich Verhalten bzw. Problem. Sobald in der Suche Preise involviert sind dauert die Suche extrem lang (teilweise > 50 Sekunden). Die Anzahl an Artikel beträgt ca. 70.000 bei 90.000 Varianten.

Wir sind sehr an einer Lösung des Problems interessiert, denn so ist das kein Zustand.

 

VG

Oh sorry, ich wollte nochwas posten dazu. Ich hab erstmal eine temporäre Lösung dazu.

Weiter oben habe ich ja geschrieben, das ein Subselect schuld ist an der langsamen Suche, Ich habe nun ersteinmal in den Suchcontrollern den Subselect durch ein einfaches INNER JOIN auf die s_articles_prices ersetzt. Das macht die Suche wieder perform, ist aber noch nicht updatesicher.

Wenn ich morgen wieder auf Arbeit bin, poste ich noch bissel was Genaueres.

1 „Gefällt mir“

Am besten, wenn es reproduzierbar ist, auch ein Ticket aufmachen :wink:

ElasticSearch sollte aber auch Abhilfe schaffen.

@Moritz Naczenski schrieb:

Am besten, wenn es reproduzierbar ist, auch ein Ticket aufmachen ;)

issues.shopware.com

ElasticSearch sollte aber auch Abhilfe schaffen.

ElasticSearch zu nutzen war von anfang an auch unser Plan bzw. die Hardware dazu ist bereits im Clusterverbund vorhanden. Allerdings mussten wir im Projektverlauf leider feststellen dass viele Plugins aus dem Store mit ES nicht kompatibel sind, unter anderem Power Ajax Filter (Entwickler arbeitet daran) sowie sogar die Shopware-eigenen Premium Plugins wie z.B. Live-Shopping (Shopware Issuetracker)

@Kampfzwerg76:

Wäre super wenn Sie ein paar Details dazu posten würden. Vielen Dank schonmal.

 

Hier nun die (nicht updatesicheren) Änderungen in folgenden Dateien:

 

\engine\Shopware\Bundle\SearchBundleDBAL\ConditionHandler\PriceConditionHandler.php

public function generateCondition(...) {
        
...

/*$query->innerJoin('product', '(' . $table->getSQL() . ')', 'listing_price', 'listing_price.articleID = product.id');*/
$query->innerJoin('product', 's_articles_prices', 'listing_price', 'listing_price.articleID = product.id');

/** @var PriceCondition $condition */
if ($condition->getMaxPrice() > 0 && $condition->getMinPrice() > 0) {
  /*$query->andWhere('listing_price.cheapest_price BETWEEN ' . $minKey . ' AND ' . $maxKey);*/
  $query->andWhere('listing_price.price BETWEEN ' . $minKey . ' AND ' . $maxKey);
  ...}

if ($condition->getMaxPrice() > 0) {
  /*$query->andWhere('listing_price.cheapest_price <= ' . $maxKey);*/
  $query->andWhere('listing_price.price <= ' . $maxKey);
  $query->setParameter($maxKey, $condition->getMaxPrice());
  ...}

if ($condition->getMinPrice() > 0) {
  /*$query->andWhere('listing_price.cheapest_price >= ' . $minKey);*/
  $query->andWhere('listing_price.price >= ' . $minKey);
  $query->setParameter($minKey, $condition->getMinPrice());
  ...}
}

\engine\Shopware\Bundle\SearchBundleDBAL\ConditionHandler\HasPseudoPriceConditionHandler.php

public function generateCondition(...) {
...
/*$query->innerJoin('product', '(' . $table->getSQL() . ')', 'listing_price', 
 listing_price.articleID = product.id');*/            
$query->innerJoin('product', 's_articles_prices', 'listing_price', 'listing_price.articleID = product.id'); 
...
/*$query->andWhere('listing_price.pseudoprice > 0');*/
$query->andWhere('listing_price.price > 0');
}

\engine\Shopware\Bundle\SearchBundleDBAL\FacetHandler\PriceFacetHandler.php

public function generatePartialFacet(...) {

...

/* $query->innerJoin('product', '(' . $table->getSQL() . ')', 'listing_price', 'listing_price.articleID = product.id'); */
$query->innerJoin('product', 's_articles_prices', 'listing_price', 'listing_price.articleID = product.id');

...

/* $query->select('MIN(listing_price.cheapest_price)'); */
$query->select('MIN(listing_price.price)');

/** @var $statement \Doctrine\DBAL\Driver\ResultStatement */
$statement = $query->execute();
$min = $statement->fetch(\PDO::FETCH_COLUMN);

$query->groupBy('product.id')
/* ->orderBy('listing_price.cheapest_price', 'DESC') */
->orderBy('listing_price.price', 'DESC')

...}

\engine\Shopware\Bundle\SearchBundleDBAL\SortingHandler\PriceSortingHandler.php

public function generateSorting(...) {
if (!$query->hasState(PriceConditionHandler::LISTING_PRICE_JOINED)) {
  $table = $this->listingPriceTable->get($context);
  /*$query->innerJoin('product', '(' . $table->getSQL() . ')', 'listing_price', 'listing_price.articleID = product.id');*/
  foreach ($table->getParameters() as $key => $value) {
    $query->setParameter($key, $value);}

  query->innerJoin('product', 's_articles_prices', 'listing_price', 'listing_price.articleID = product.id');
  $query->addState(PriceConditionHandler::LISTING_PRICE_JOINED);}

...
}

 

Nächste Aufgabe ist nun, das Ganze updatesicher zu gestalten.

 

 

1 „Gefällt mir“

Updatesicherheit hergestellt. Die 4 Handler müssen in ein eigenes Plugin und die Orginalhandler müssen durch diese erweitert werden. (https://developers.shopware.com/developers-guide/shopware-5-core-service-extensions)

 

Dazu legt man folgende Dateien an (ich orientiere mich an der Struktur der Orginalhandler)

\custom\plugins\MyPlugin\Bundle\SearchBundleDBAL\SortingHandler\My_PriceSortingHandler.php
\custom\plugins\MyPlugin\Bundle\SearchBundleDBAL\FacetHandler\My_PriceFacetHandler.php
\custom\plugins\MyPlugin\Bundle\SearchBundleDBAL\ConditionHandler\MyPriceConditionHandler.php
\custom\plugins\MyPlugin\Bundle\SearchBundleDBAL\ConditionHandler\My_HasPseudoPriceConditionHandler.php
\custom\plugins\MyPlugin\Resources\services.xml
\custom\plugins\MyPlugin\MyPlugin.php
\custom\plugins\MyPlugin\plugin.xml

MyPlugin.php

namespace MyPlugin;

use Shopware\Components\Plugin;

class MyPlugin extends Plugin
{

}

plugin.xml

    MyPlugin
    MyPlugin
    1.0.0
    (c) by 
    MIT
     
    

    
        Erstveröffentlichung
        First release

services.xml

 

Jetzt aus den Orginalhandlern den Quelltext kopieren in die My… -Handler, die Funktionen, die ich weiter oben beschrieben habe ersetzen.

Plugin installieren, Cache leeren und schon sollte es funktionieren.

 

Ich danke dir sehr, monatelang haben wir nach der Ursache für das problem gesucht. Super jetzt rennts.

Ich habe mich erstmal zum testen für die nicht updatefähige Variante entschieden.

Zur letzten Variante habe ich noch eine Frage.

 

Plugin installieren, Cache leeren und schon sollte es funktionieren.

Welches Plugin muss ich installieren?

 

@mandymue78 schrieb:

Plugin installieren, Cache leeren und schon sollte es funktionieren.

Welches Plugin muss ich installieren?

 

 

Du musst ein eigenes Plugin erstellen (Tutorials gibts genug zu finden dazu). Und dann wie im Post weiter oben beschrieben die Dateien anlegen und befüllen. Wenn du alles richtig gemacht hast, dann finsest du dein eigenes Plugin im Pluginmanager und kannst es installieren.

@Kampfzwerg76 schrieb:

@mandymue78 schrieb:

Plugin installieren, Cache leeren und schon sollte es funktionieren.

Welches Plugin muss ich installieren?

 

 

Du musst ein eigenes Plugin erstellen (Tutorials gibts genug zu finden dazu). Und dann wie im Post weiter oben beschrieben die Dateien anlegen und befüllen. Wenn du alles richtig gemacht hast, dann finsest du dein eigenes Plugin im Pluginmanager und kannst es installieren.

 

Wir bekommen beim Filtern nach Preisen leider Produkte angezeigt die nicht in den Filter passen. Hattest du deinen Code im nachhinein noch angepasst? 

Nein, der Code ist nicht verändert. Hab das mal bei mir im Shop getestet.

Suche --> Filtern nach Preis --> andere Filter noch ausgewählt --> Ergebnisse passen.