2

views訪問者の IP、投稿 ID (これらのフィールドの主キーを取得しました)、およびタイムスタンプを含むエントリをテーブル ( ) に挿入することにより、誰がいつ、どの投稿を閲覧したかを追跡するブログを取得しました。

次に、このテーブルを使用して、過去の日/週/月/年および全期間の各カテゴリ (4 つを取得) の上位 5 件の投稿を表示します。つまり、合計 20 個のクエリが実行され、それぞれのクエリに 0.2 ~ 0.7 秒かかります... 私のページの読み込みには 7 秒強かかりますが、これはひどいことです。

ここに私のデータベース構造に関するいくつかの有用な情報があります:

+---------------------+        +----------------------+
|   posts (82 rows)   |        |   views (50k rows)   |
+=====================+        +======================+
|    id (primary)     |        |     ip (primary)     |
+---------------------+        +----------------------+
|        type         |        | article_id (primary) |
+---------------------+        +----------------------+
|     thumbnail       |        |     date (index)     |
+---------------------+        +----------------------+
|    title (index)    |       
+---------------------+
|         url         |
+---------------------+
| description (index) |
+---------------------+
|       content       | 
+---------------------+
|        date         |
+---------------------+
|       lastmod       |
+---------------------+
|       sources       |
+---------------------+
|        tags         |
+---------------------+
|      published      |
+---------------------+
|         ...         |
+---------------------+

...、投稿の英語版の追加フィールド ( url_entitle_endescription_entags_enおよびcontent_en) を表します。

これが私の巨大なクエリの1つです(基本的にはすべて同じです):

SELECT p.title, p.id, p.url, tmp.cnt AS views
FROM posts AS p 
LEFT JOIN (SELECT COUNT(*) AS cnt, article_id -- 0.34s
           FROM views
           WHERE article_id IN (SELECT id
                                FROM posts
                                WHERE id <> 12 AND type = 'Tutoriel') AND 
                 date BETWEEN 01-01-2013 AND NOW() -- the 01-01-2013 is normally a variable but for testing purposes I've replaced it with a fixed date here
           GROUP BY article_id
           ORDER BY cnt DESC LIMIT 5) AS tmp 
       ON p.id = tmp.article_id
WHERE p.id IN (SELECT article_id
               FROM (SELECT COUNT(*) AS cnt, article_id -- 0.34s
                     FROM views
                     WHERE article_id IN (SELECT id
                                          FROM posts
                                          WHERE id <> 12 AND type = 'Tutoriel')
                       AND date BETWEEN 01-01-2013 AND NOW()
                     GROUP BY article_id
                     ORDER BY cnt DESC LIMIT 5) AS tmp2 
              )
ORDER BY views DESC

BETWEENすべての投稿の全期間の統計情報を取得したため (つまり、カテゴリや日付に依存しない)、実行に 0.03 秒しかかからないという正確なクエリを取得したため、ほとんどの時間を費やしているのはこの句であることがわかりました。

私はこのクエリをあらゆる方法で調べましたが、よりシンプルで最適化された方法でそれを記述する方法を見つけることができませんでした...それでも、方法が必要だと感じています。たぶん、ここで明らかな何かが欠けているだけです。

私を悩ませていることの 1 つは、サブクエリの重複です。投稿データと関連ビュー数の両方を取得する他の方法は見つかりませんでした。

私が考えているのは、ユーザーがその期間のタブをクリックしたときに、その期間ごとにAJAXリクエストを実行することです(タブビューです)。ただし、それで問題が実際に解決されるわけではなく、汚い回避策のように感じられます。

posts次のいずれかの方法でテーブルを分割できます。

  • フランス語版用の表と英語版用の表
  • よくアクセスされるフィールド ( titledescriptionurl) 用の 1 つのテーブルと、残りのフィールド用の別のテーブル
  • 上記の組み合わせ

私が間違っていなければ、これは少しスピードアップする可能性があります。

これについて誰かアドバイスをいただけますか?ところで、ここまで付き合ってくれてありがとう:)

4

2 に答える 2

1

in古いバージョンの MySQL は、サブクエリを使用した最適化には特に適していません。join代わりに使用してみてください:

SELECT p.title, p.id, p.url, tmp.cnt AS views
FROM posts AS p 
LEFT JOIN (SELECT COUNT(*) AS cnt, article_id -- 0.34s
           FROM views
           WHERE article_id IN (SELECT id
                                FROM posts
                                WHERE id <> 12 AND type = 'Tutoriel') AND 
                 date BETWEEN 01-01-2013 AND NOW() -- the 01-01-2013 is normally a variable but for testing purposes I've replaced it with a fixed date here
           GROUP BY article_id
           ORDER BY cnt DESC LIMIT 5) AS tmp 
       ON p.id = tmp.article_id join
          (SELECT COUNT(*) AS cnt, article_id -- 0.34s
           FROM views v join
                (SELECT id
                 FROM posts p
                 WHERE p.id <> 12 AND p.type = 'Tutoriel'
                ) p
                on v.article_id = p.id
            WHERE v.date BETWEEN 01-01-2013 AND NOW()
            GROUP BY v.article_id
            ORDER BY cnt DESC
            LIMIT 5
           ) a
       on p.id = a.article_id
ORDER BY views DESC

編集:

クエリを正しく理解していれば、left outer joina に変更して句を完全にjoin削除できます。where

SELECT p.title, p.id, p.url, tmp.cnt AS views
FROM posts Ap JOIN
     (SELECT COUNT(*) AS cnt, article_id -- 0.34s
      FROM views
      WHERE article_id IN (SELECT id
                           FROM posts
                           WHERE id <> 12 AND type = 'Tutoriel') AND 
            date BETWEEN 01-01-2013 AND NOW() -- the 01-01-2013 is normally a variable but for testing purposes I've replaced it with a fixed date here
     GROUP BY article_id
     ORDER BY cnt DESC
     LIMIT 5
    ) tmp 
    ON p.id = tmp.article_id;

次にin、サブクエリの を結合に変更します。

SELECT p.title, p.id, p.url, tmp.cnt AS views
FROM posts Ap JOIN
     (SELECT COUNT(*) AS cnt, article_id -- 0.34s
      FROM views v join
           (SELECT distinct p.id  -- distinct may not be necessary
            FROM posts p
            WHERE p.id <> 12 AND p.type = 'Tutoriel'
           ) p
           on v.rticle_id = p.id
      WHERE date BETWEEN 01-01-2013 AND NOW() -- the 01-01-2013 is normally a variable but for testing purposes I've replaced it with a fixed date here
     GROUP BY article_id
     ORDER BY cnt DESC
     LIMIT 5
    ) tmp 
    ON p.id = tmp.article_id;
于 2013-07-27T15:47:18.877 に答える