7

ページが表示されるたびに増分される整数の MySQL 列があります。SQL クエリは次のようになります。

UPDATE page SET views = views + 1 WHERE id = $id

同じページ (同じ ID) が 1 秒間に何度も表示され (MySQL でレコードがロックされる)、クエリが MySQL を停止させると、スケーリングの問題が発生し始めました。これに対抗するために、次の戦略を使用しています。

ページが読み込まれるたびに、Memcache のカウンターをインクリメントし、バックグラウンドで MySQL のカウンターを更新するジョブ (Gearman) を (3 台のワーカー マシン間で) キューに入れます。簡略化されたコードは次のようになります。

ページ ビュー:

$memcache->increment("page_view:$id");
$gearman->doBackground('page_view', json_encode(array('id' => $id)));

バックグラウンド ワーカーで:

$payload = json_decode($payload);
$views = $memcache->get("page_view:{$payload->id}");
if (!empty($views)) {
    $mysql->query("UPDATE page SET views = views + $views WHERE id = {$payload->id}");
    $memcache->delete("page_view:{$payload->id}");
}

これはうまくいきました。これにより、DB へのクエリを削減でき (DB に書き込む前に memcache にビューを集約するため)、DB の書き込みはバックグラウンドで行われ、ページの読み込みが滞ることはありません。

残念ながら、MySQL のロックが再び発生し始めています。非常にアクティブなページがほぼ同時に実行され、MySQL が再びロックされるようです。ロックは書き込みを遅くし、多くの場合、ワーカーを殺します。これにより、キューが非常に大きくなり、多くの場合、「遅れている」ジョブが 70k 以上あります。

私の質問: これを拡大するには、次に何をすべきですか?

4

3 に答える 3

3

ギアマンはよくわからないので間違っているかもしれません。

カウンターをインクリメントするたびに、ギアマン タスクをキューに入れています。の結果$memcache->incrementが 1 の場合にのみ、タスクをキューに入れる方がよいと思います。私の理論的根拠は、ギアマン タスクがクリアされた後に次の更新が到着するときに、page_view:$iこの新しいタスクを更新しようとするギアマン タスクの長いキューを持たないということです。 DB の値。これにより、コードは更新レートから独立し、ギアマンが新しいタスクを選択する速度に上限が設けられます (うまくいけば、十分に遅くなります)。完璧な世界では、ギアマンにこのタスクを最大 1 秒遅らせるように依頼できます。これにより、このカウンターを 1 qps のレートでのみ更新することが保証されます。

ギアマンとは別に、低速の READ を受け入れることができ、InnoDB を使用していると仮定すると、このカウンターを分割できます。

これを行うには、シャード列を追加して、それを主キーの一部にします。

CREATE TABLE page (
     id INTEGER,
     shard INTEGER,
     views INTEGER,
     PRIMARY KEY (id, shard)
)

idこのカウンターを更新するときは、1 ~ 10 の間でランダムにシャードを選択します。それを読み取るときは、読み取りたいすべてのシャードを合計します。これにより、読み取りは 10 倍遅くなりますが、書き込みは 10 倍にスケーリングできます。(もちろん、10 である必要はありません。任意の数を選択できます。)

于 2013-01-31T14:08:31.260 に答える
1

ページ数を何に使用しているのか、すべてのページ数を記録することがどれほど重要かはわかりません。おそらく、各サーバーのメモリにカウントをキャッシュしてから、一定のスケジュールでのみそれらを永続化することができます。そうすれば、データベースへのアクセス数を制御できます。

これは明らかに、サーバーが何らかの理由でダウンした場合にカウントが持続することを保証するものではありません。したがって、重要な監査ログや、ページビューの一部が失われることが問題になるような場合は、これは機能しません。

于 2013-01-31T02:21:25.060 に答える
0

MySQL のINSERT DELAYED....挿入ステートメントを使用します。ロックせず、可能なときに書き込みます。

于 2013-01-31T02:11:41.287 に答える