Angepasstes Kundenraster, das MySQL-Tabellenscans und Dateisortierungen verursacht, auch bekannt als langsame Leistung

8

BEARBEITEN: Ich hatte diese Frage ursprünglich auf Stackoverflow gepostet , die nie vollständig beantwortet wurde. Ich poste sie hier in der Hoffnung, mehr Licht ins Dunkel zu bringen, warum die massiven Tabellenscans und alle Mittel, um dies mit (hoffentlich) minimalen Kernänderungen zu verhindern.


Magento Enterprise. 1.10.1.1. Der Datensatz von Kunden und Adressen ist halbgroß (125.000 +). CSRs befinden sich häufig in diesem Raster (manchmal mehr als 25 gleichzeitige Benutzer gleichzeitig).

Hier ist das Code-Snippet, in dem die Sammlung in der Kundendatei generiert wird Grid.php Block. Nichts Besonderes oder Außergewöhnliches, nur das Hinzufügen von Attributen zur Sammlung.

$collection = Mage::getResourceModel('customer/customer_collection')
    ->addNameToSelect()
    ->addAttributeToSelect('email')
    ->addAttributeToSelect('group_id')
    ->addAttributeToSelect('prod_codes')
    ->addAttributeToSelect('last_called_date')
    ->addAttributeToSelect('time_zone')
    ->addAttributeToSelect('salesrep')
    ->addAttributeToSelect('do_not_call')
    ->addAttributeToSelect('club_member')
    ->addAttributeToSelect('call_back_date')
    ->addAttributeToSelect('marketing_code_outcome')
    ->joinAttribute('billing_postcode', 'customer_address/postcode', 'default_billing', null, 'left')
    ->joinAttribute('billing_city', 'customer_address/city', 'default_billing', null, 'left')
    ->joinAttribute('billing_telephone', 'customer_address/telephone', 'default_billing', null, 'left')
    ->joinAttribute('billing_region', 'customer_address/region', 'default_billing', null, 'left');

$this->setCollection($collection);

Generiert diese Abfrage, die sich schlecht verhält und sehr lange Ladezeiten im Kundenraster verursacht:

SELECT 
    e . *,
    _table_prefix.value AS prefix,
    _table_firstname.value AS firstname,
    _table_middlename.value AS middlename,
    _table_lastname.value AS lastname,
    _table_suffix.value AS suffix,
    CONCAT(IF(_table_prefix.value IS NOT NULL AND _table_prefix.value != '',
                CONCAT(TRIM(_table_prefix.value), ' '),
                ''),
            TRIM(_table_firstname.value),
            IF(_table_middlename.value IS NOT NULL AND _table_middlename.value != '',
                CONCAT(' ', TRIM(_table_middlename.value)),
                ''),
            ' ',
            TRIM(_table_lastname.value),
            IF(_table_suffix.value IS NOT NULL AND _table_suffix.value != '',
                CONCAT(' ', TRIM(_table_suffix.value)),
                '')) AS name,
    _table_default_billing.value AS default_billing,
    _table_billing_postcode.value AS billing_postcode,
    _table_billing_city.value AS billing_city,
    _table_billing_telephone.value AS billing_telephone,
    _table_billing_region.value AS billing_region
FROM
    customer_entity AS e
        LEFT JOIN
    customer_entity_varchar AS _table_prefix ON (_table_prefix.entity_id = e.entity_id) AND (_table_prefix.attribute_id = '4')
        LEFT JOIN
    customer_entity_varchar AS _table_firstname ON (_table_firstname.entity_id = e.entity_id) AND (_table_firstname.attribute_id = '5')
        LEFT JOIN
    customer_entity_varchar AS _table_middlename ON (_table_middlename.entity_id = e.entity_id) AND (_table_middlename.attribute_id = '6')
        LEFT JOIN
    customer_entity_varchar AS _table_lastname ON (_table_lastname.entity_id = e.entity_id) AND (_table_lastname.attribute_id = '7')
        LEFT JOIN
    customer_entity_varchar AS _table_suffix ON (_table_suffix.entity_id = e.entity_id) AND (_table_suffix.attribute_id = '8')
        LEFT JOIN
    customer_entity_int AS _table_default_billing ON (_table_default_billing.entity_id = e.entity_id) AND (_table_default_billing.attribute_id = '13')
        LEFT JOIN
    customer_address_entity_varchar AS _table_billing_postcode ON (_table_billing_postcode.entity_id = _table_default_billing.value) AND (_table_billing_postcode.attribute_id = '29')
        LEFT JOIN
    customer_address_entity_varchar AS _table_billing_city ON (_table_billing_city.entity_id = _table_default_billing.value) AND (_table_billing_city.attribute_id = '25')
        LEFT JOIN
    customer_address_entity_varchar AS _table_billing_telephone ON (_table_billing_telephone.entity_id = _table_default_billing.value) AND (_table_billing_telephone.attribute_id = '30')
        LEFT JOIN
    customer_address_entity_varchar AS _table_billing_region ON (_table_billing_region.entity_id = _table_default_billing.value) AND (_table_billing_region.attribute_id = '27')
WHERE
    (e.entity_type_id = '1')
ORDER BY CONCAT(IF(_table_prefix.value IS NOT NULL AND _table_prefix.value != '',
            CONCAT(TRIM(_table_prefix.value), ' '),
            ''),
        TRIM(_table_firstname.value),
        IF(_table_middlename.value IS NOT NULL AND _table_middlename.value != '',
            CONCAT(' ', TRIM(_table_middlename.value)),
            ''),
        ' ',
        TRIM(_table_lastname.value),
        IF(_table_suffix.value IS NOT NULL AND _table_suffix.value != '',
            CONCAT(' ', TRIM(_table_suffix.value)),
            '')) desc
LIMIT 20 OFFSET 60

In EXPLAINder Abfrage wird Folgendes angezeigt : HINWEIS : Extra in Tabelle e, Verwenden von temporärem und Verwenden von Dateisortierung :

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: e
         type: ref
possible_keys: IDX_ENTITY_TYPE
          key: IDX_ENTITY_TYPE
      key_len: 2
          ref: const
         rows: 55556
        Extra: Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_prefix
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_VARCHAR_ATTRIBUTE,FK_CUSTOMER_VARCHAR_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod.e.entity_id,const
         rows: 1
        Extra:
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_firstname
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_VARCHAR_ATTRIBUTE,FK_CUSTOMER_VARCHAR_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod.e.entity_id,const
         rows: 1
        Extra:
*************************** 4. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_middlename
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_VARCHAR_ATTRIBUTE,FK_CUSTOMER_VARCHAR_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod.e.entity_id,const
         rows: 1
        Extra:
*************************** 5. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_lastname
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_VARCHAR_ATTRIBUTE,FK_CUSTOMER_VARCHAR_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod.e.entity_id,const
         rows: 1
        Extra:
*************************** 6. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_suffix
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_VARCHAR_ATTRIBUTE,FK_CUSTOMER_VARCHAR_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod.e.entity_id,const
         rows: 1
        Extra:
*************************** 7. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_default_billing
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_INT_ATTRIBUTE,FK_CUSTOMER_INT_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod.e.entity_id,const
         rows: 1
        Extra:
*************************** 8. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_billing_postcode
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_ADDRESS_VARCHAR_ATTRIBUTE,FK_CUSTOMER_ADDRESS_VARCHAR_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod._table_default_billing.value,const
         rows: 1
        Extra:
*************************** 9. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_billing_city
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_ADDRESS_VARCHAR_ATTRIBUTE,FK_CUSTOMER_ADDRESS_VARCHAR_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod._table_default_billing.value,const
         rows: 1
        Extra:
*************************** 10. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_billing_telephone
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_ADDRESS_VARCHAR_ATTRIBUTE,FK_CUSTOMER_ADDRESS_VARCHAR_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod._table_default_billing.value,const
         rows: 1
        Extra:
*************************** 11. row ***************************
           id: 1
  select_type: SIMPLE
        table: _table_billing_region
         type: eq_ref
possible_keys: IDX_ATTRIBUTE_VALUE,FK_CUSTOMER_ADDRESS_VARCHAR_ATTRIBUTE,FK_CUSTOMER_ADDRESS_VARCHAR_ENTITY,IDX_VALUE
          key: IDX_ATTRIBUTE_VALUE
      key_len: 6
          ref: prod._table_default_billing.value,const
         rows: 1
        Extra:
11 rows in set (0.00 sec)

Es wurden keine anderen Indizes als die Standardindizes von Magento selbst für 1.10.1 geändert. Siehe Struktur von 1.5.1 (CE) hier: http://www.magereverse.com/index/magento-sql-structure/version/1- 5-1-0

Hier ist die Alias-Tabelle, auf die sich AS e bezieht. auf dem Scan:

CREATE TABLE `customer_entity` (
    `entity_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `entity_type_id` SMALLINT(8) UNSIGNED NOT NULL DEFAULT '0',
    `attribute_set_id` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
    `website_id` SMALLINT(5) UNSIGNED NULL DEFAULT NULL,
    `email` VARCHAR(255) NOT NULL DEFAULT '',
    `group_id` SMALLINT(3) UNSIGNED NOT NULL DEFAULT '0',
    `increment_id` VARCHAR(50) NOT NULL DEFAULT '',
    `store_id` SMALLINT(5) UNSIGNED NULL DEFAULT '0',
    `created_at` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
    `updated_at` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
    `is_active` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1',
    PRIMARY KEY (`entity_id`),
    INDEX `FK_CUSTOMER_ENTITY_STORE` (`store_id`),
    INDEX `IDX_ENTITY_TYPE` (`entity_type_id`),
    INDEX `IDX_AUTH` (`email`, `website_id`),
    INDEX `FK_CUSTOMER_WEBSITE` (`website_id`),
    CONSTRAINT `FK_CUSTOMER_ENTITY_STORE` FOREIGN KEY (`store_id`) REFERENCES `core_store` (`store_id`) ON UPDATE CASCADE ON DELETE SET NULL,
    CONSTRAINT `FK_CUSTOMER_WEBSITE` FOREIGN KEY (`website_id`) REFERENCES `core_website` (`website_id`) ON UPDATE CASCADE ON DELETE SET NULL
)

Die Frage ist also, wie ich diese Abfrage zu einer besseren Leistung bringen kann, ohne dass die temporären Tabellen erstellt und gescannt werden.

Ich bin mir nicht ganz sicher, was ich indizieren kann, um die Leistung dieser Abfragen zu verbessern, und ich möchte mich nicht zu sehr mit der Änderung von Magentos ORM befassen.

B00MER
quelle

Antworten:

2

Die Antwort auf Ihre Notlage liegt nicht in den Anpassungen, diese sind in Ordnung. Der problematische Teil der Abfrage ist ORDER BY und wird den hässlichen Kopf nur dann aufrichten, wenn das Raster nach der Spalte Name im Administrator sortiert wurde:

ORDER BY CONCAT(IF(_table_prefix.value IS NOT NULL AND _table_prefix.value != '',
        CONCAT(TRIM(_table_prefix.value), ' '),
        ''),
    TRIM(_table_firstname.value),
    IF(_table_middlename.value IS NOT NULL AND _table_middlename.value != '',
        CONCAT(' ', TRIM(_table_middlename.value)),
        ''),
    ' ',
    TRIM(_table_lastname.value),
    IF(_table_suffix.value IS NOT NULL AND _table_suffix.value != '',
        CONCAT(' ', TRIM(_table_suffix.value)),
        '')) DESC

Diese ORDER BY-Klausel wird zum Sortieren nach der Spalte Name benötigt, da die Spalte Name eine Verkettung des Präfixes, des Vor-, Mittel- und Nachnamens des Kunden darstellt. Um die Informationen zu kombinieren und für die Bestellung zu verwenden, muss eine tmp-Tabelle verwendet werden, da die miteinander verketteten Daten aus den JOINs in der Abfrage stammen. Auf diese Weise behandelt der Magento-Kern diese Sortierung, da lediglich derselbe Code angewendet wird, der in der WHERE-Klausel zum Anwenden der Sortierungen verwendet wird.

Das Umschließen der gesamten Abfrage als Unterauswahl würde die Abfragezeit etwas verkürzen, da keine temporäre Tabelle mehr verwendet wird, aber weiterhin Dateisortierung verwendet wird.

Es gibt also zwei "Lösungen", die ich hier sehen kann:

  1. Erstellen Sie ein Attribut full_name für die Kundenentität, das beim Speichern ausgefüllt wird. Dieses vorab ausgefüllte Attribut full_name könnte dann ohne jegliche Verkettung im Raster verwendet werden und würde die Notwendigkeit sowohl für die temporären Tabellen als auch für die Dateisortierung verringern.

  2. Sortieren Sie einfach nicht nach der Spalte Name, wenn die Zeit ein Problem darstellt. Dies hilft natürlich nicht, wenn Sie nach Namen sortieren müssen, aber etwas, das Sie beachten sollten, bis Sie die Möglichkeit haben, die Änderungen in # 1 oben auszubauen. :) :)

Davidalger
quelle
Vielen Dank für die Antwort und Klarstellung der ORDER BYKlausel und die Verknüpfung von Vorname, Nachname und Präfix. Mein ursprünglicher Plan war es, den Kunden einfach zu verflachen, Tabellen zu adressieren und einen Teil der Logik neu zu schreiben. Diese beiden anderen Optionen klingen schneller und einfacher zu implementieren. Danke noch einmal.
B00MER