カスタマイズされたCustomer Gridにより、MySQLテーブルスキャンとファイルソート、つまりパフォーマンスが低下する


8

編集:私は元々この質問をStackoverflowに投稿しましたが、完全に回答されることはありませんでした。私はここに投稿しています。これは、大規模なテーブルスキャンの理由と、(できれば)最小限のコア変更でこれを防ぐためのあらゆる手段を明らかにすることを期待しています。


Magento Enterprise。1.10.1.1。顧客と住所のデータセットはセミラージ(125k +)であり、CSRはこのグリッドにあることがよくあります(一度に25+以上の同時ユーザー)。

これは、コレクションがCustomer Grid.php Blockファイルで生成されているコードスニペットです。主にコレクションに属性を追加するだけで、特別なことや異常なことは何もありません。

$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);

このクエリを生成します。これは、顧客グリッド内での動作が非常に長くなり、読み込み時間が非常に長くなります。

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

EXPLAINクエリのan は、表eのExtraに注意してください

*************************** 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)

1.10.1のMagento自体からのデフォルトのインデックス以外に変更されたインデックスはありませんここに1.5.1(CE)の構造を参照してください:http : //www.magereverse.com/index/magento-sql-structure/version/1- 5-1-0

以下は、AS eと呼ばれるエイリアステーブルです。スキャン時:

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
)

したがって、問題は、このクエリのパフォーマンスを向上させ、一時テーブルが作成およびスキャンされないようにする方法です。

このクエリのパフォーマンスを向上させるために何をインデックスに登録できるのか正確にはわかりません。MagentoのORMを変更することにあまり深く掘り下げたくありません。

回答:


2

あなたの窮状への答えはカスタマイズにありません、それらは結構です。クエリの問題のある部分はORDER BYであり、グリッドが管理者の[名前]列で並べ替えられている場合にのみ、醜い頭を再配置します。

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

Name列は顧客の接頭辞、名、ミドルネーム、姓の連結であるため、このORDER BY句はName列でソートするために必要です。情報を組み合わせて順序付けに使用するには、連結されるデータがクエリのJOINから生成されているため、tmpテーブルを使用する必要があります。これはMagentoコアがこの並べ替えを処理する方法です。これは、並べ替えを適用するためにWHERE句で使用されているのと同じコードを適用するだけだからです。

クエリ全体を副選択としてラップすると、一時テーブルが使用されなくなるためクエリ時間を少し短縮できますが、filesortは引き続き使用されます。

したがって、ここで確認できる2つの「解決策」があります。

  1. 保存時に入力される顧客エンティティーにfull_name属性を作成します。この事前入力されたfull_name属性は、どのような形式の連結もなしにグリッドで使用でき、一時テーブルとファイルソートの両方の必要性を排除します。

  2. 時間が問題になる場合は、単に[名前]列で並べ替えないでください。これは、名前でソートする必要がある場合には明らかに役立ちませんが、上記の#1の変更を構築する機会があるまでは、覚えておく必要があります。:)


ORDER BY条項についての回答と説明、および最初の姓の接頭辞の追加に感謝します。私の当初の計画は、顧客を単純化し、テーブルに対応し、ロジックの一部を書き直すことでした。これらの他の2つのオプションは、実装がより迅速で簡単になるようです。再度、感謝します。
B00MER 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.