Drupalコマースをどのように加速したか

写真の著者:コリー... Disclamer:以下に書かれているすべてが「ベビートーク」や非常に明白なものを好むようであれば、私たちはあなたと一緒に仕事をさせていただきます。



背景:約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の多くの便利で高速なオンラインストアが生まれます:)



All Articles