
背景:約1年前、私たちの小さくても誇らしげなWebスタジオは、オンラインストアprinter38.ruの開発を受注しました。 また、CMS Drupalを専門としているため、Drupal Commerceをオンラインストアモジュールとして使用することにしました。
カタログの1ページを読み込むのに5分かかった理由と、それをどうやって克服したかに興味がある人は、catの下で歓迎します。
Yandex Marketでプリンターを選択したことがある場合は、同様の製品のフィールド数を表す必要があります。 データベース内のすべての製品の特性を備えた184のフィールドがあります-印刷速度からバッテリー寿命の可用性まで。

ここで、CMS Drupalの1つの機能について説明する必要があります。フィールドごとに、データベースに個別のテーブルが作成されます。 あなたが望むものは何でも、汎用性の支払い...
特にプロジェクトのもう1つの機能は、多くのフィールドがフィルターで使用されることです。これが、ディレクトリページ全体をキャッシュすることができない理由です。 したがって、ページが表示されるたびに、データベースへの要求が発生します。
すべてのフィルターフィールドを正直に表示するビューを初めて作成したとき、メインページが読み込まれるのを待つことができませんでした。 これは、サイトが非常に優れた特性を持つ別のサーバーで実行されているという事実にもかかわらずです。
古典的な「タンバリンと踊る」が始まりました-MySQL最適化、クエリキャッシング、ハードデバッグ、プロファイリング:)
この投稿では、最適化アクションのシーケンスを復元しようとします。その結果、サイトにページをロードするための許容可能な速度を達成することができました。
1. memcachedモジュールの接続
Drupalの方向への「キック」については、動作が遅いと言われますが、その伝道者は「キャッシュを使用」と応答します。 実際、これは行われました。
ご存じのように、Drupalの標準キャッシングは、同じデータベースにキャッシュを保存しますが、この場合は最初は役に立ちませんでした。
そのため、キャッシュをRAMに保持することが決定されました-幸いなことに、サーバーではこれで十分でした。 この目的のために、彼らはサーバー上のmemcachedとDrupalのmemcache_storageを使用しました( 素晴らしいモジュールを 提供してくれたEugene Spleshkaに感謝します )。
データをキャッシュに転送した後、すべてが著しく速く動き始めましたが、それでも私が望んだ方法ではありませんでした。 さらに理解します...
2. memcachedのフォームキャッシュを削除する
ある曇りの日、私たちはcache_formテーブルの非人間的なサイズに気づきました-7GB以上! テーブルはきれいになりましたが、再び急速に成長し始めました。
その理由は簡単でした。製品カタログの各[購入]ボタンは、Drupalがキャッシュする必要があると見なすと同時に、「手元にあるものはすべて」そこにドラッグするミニフォームです。 そして、AJAXボタンを使用すると(この場合のように)、キャッシュは1桁速く成長し始めます 。 さらに、一部のDrupal主導の開発者のみが、他のキャッシュテーブルの場合のように、 cache_formテーブルは自動的にクリアされません 。
問題を理解することは、それを90%解決することを意味します:)
cache_formもmemcachedでレンダリングされました(データベースに保存する推奨事項に反して)。
定期的なテーブルクリーニングには、 optimizedbモジュールを使用しました(Drupal Commerceを使用するすべてのサイトにデフォルトで配置します)。
XandeadxはAJAXボタンを使用して問題を解決しましたが、その解決策は、説明した手順の数か月後に表示されたため、その時点ではプロジェクトで使用できませんでした。 ;)
サイトのメインページは「飛び」始めました。 ただし、カタログはまだ問題を抱えています-ページが2〜3分間開きます。 :(
3.ビューのフィールドの失敗
私たちの最も経験豊富な開発者が言ったように、友人は次のように述べています。 では、なぜそれを使用しないのでしょうか。また、ビューを作成するたびに、データベースからすべてのデータを取得しますか?」
すぐに言ってやった。 ビューとページめくりのソーイングに関するチームの作業の約1〜2時間、そして見よ、カタログページの読み込みを「約」40〜50秒に減らすことができました。 ユーザーは待ちますよね? 彼らは急ぐ場所がありません...
4.別の最適化の試み-標準ページネーターの放棄、「ビュー」でのキャッシュの接続、エンティティのキャッシュ
その後、プログラマーは再びキャビネットからタンバリンを入手しました(残念なことに、タンバリンを遠くまで取り除く時間はありませんでした)。
賢い人たちは、「ブレーキ」の問題は標準的なポケットベル(ページング、別名ページング)になる可能性があることを読んでいます。 モジュールviews_litepagerをインストールすることにより硬化します。
同時に、製品オブジェクトのエンティティをキャッシュする必要があるcommerce_entitycacheモジュールをインストールしました。
ただし、これらすべての「ダンス」は速度のわずかな増加のみをもたらしました。

最も重要な結果は、ビューのキャッシュの接続でしたが、ここでもすべてがスムーズに進むわけではありませんでした。 まず、リクエストをキャッシュすると、製品フィルターが同じ結果を生成し始めたため、それを無効にする必要がありました。 次に、フィルターが選択されていない「クリーン」ページをロードする場合にのみ、加速が観察されました。 少なくとも1つのチェックボックスを選択する価値があり、ページが読み込まれるのを待っている間もコーヒーを飲むことができます。
ページ実行時間は69728.43ミリ秒でした
うーん...
6.ほぼ勝利。 手動クエリ要求の最適化
ある時点で、私たちは厳しい方法で行動する時であることに気付きました。 つまり、データベースでそのようなビューが正確に何を要求しているかを詳細に調べるには、結果を生成するのに少なくとも30秒かかります。
そして、次のようなものを見ました:
... INNER JOIN {commerce_product} commerce_product_field_data_field_product_reference ON field_data_field_product_reference.field_product_reference_product_id = commerce_product_field_data_field_product_reference.product_id INNER JOIN {field_data_commerce_price} commerce_product_field_data_field_product_reference__field_data_commerce_price ON commerce_product_field_data_field_product_reference.product_id = commerce_product_field_data_field_product_reference__field_data_commerce_price.entity_id AND ( commerce_product_field_data_field_product_reference__field_data_commerce_price.entity_type = 'commerce_product' AND commerce_product_field_data_field_product_reference__field_data_commerce_price.deleted = '0' ) INNER JOIN {field_data_field_printer_a4_speed_2} commerce_product_field_data_field_product_reference__field_data_field_printer_a4_speed_2 ON commerce_product_field_data_field_product_reference.product_id = commerce_product_field_data_field_product_reference__field_data_field_printer_a4_speed_2.entity_id AND ( commerce_product_field_data_field_product_reference__field_data_field_printer_a4_speed_2.entity_type = 'commerce_product' AND commerce_product_field_data_field_product_reference__field_data_field_printer_a4_speed_2.deleted = '0' ) ...
など-フィルタリングに関係する各フィールドに対して。
はい、多くのJOINがあります。 しかし、彼らはそんなに長くは働けません!
「待って、なぜ型チェックが必要なのですか? 製品IDが「commerce_product」でなければならないすべてのエンティティがありますか?」
IDEを使用して、モジュールに小さなフックを作成します。
/** * Implementation of hook_views_query_alter * @param type $view * @param type $query */ function mymodule_views_query_alter(&$view, &$query) { if ($view->name == 'catalog_v_2') { foreach ($query->table_queue as $key=>$item) { $query->table_queue[$key]['join']->extra=array(); } } }
つまり、JOINからすべての追加条件を単純に「破棄」します(これは、経験上、常にゼロである不可解な削除を含みます)。
少なくとも1つのJOINを完全に削除することはできましたが、すでに深夜であり、誰もが眠りたかったのです:)
比較する:
前: 34665.211ミリ秒
後: 0.13 ms
はい、「完璧に制限はありません」。したがって、最適化の実験を続けます。 私たちの経験が誰かに役立つことを願っており、Drupal Commerceの多くの便利で高速なオンラインストアが生まれます:)