surga Lab

開発したい!!

アクセスカウンタのデータベース設計

アクセスカウンタを作るときのデータベースについて考えてみます。

RDB

例えばCloud SQL/MySQLならこんな感じのテーブルでしょうか。

PVカラムをインクリメントしていけばカウントできそうです。

f:id:hisurga:20190505023753p:plain

問題点

MySQLでは更新時にロックをかけます。

複数人が同時にアクセスすると1人1人処理する必要があるため、パフォーマンスが悪いです。

対策

1つのページに対するレコードの数を増やし、増やした行の中からランダムな行でインクリメントするようにします。

アクセス数を出すときは、それぞれのレコードを集計して出します。

f:id:hisurga:20190505024817p:plain

レコードを増やした分だけ同時アクセスに対するパフォーマンスの向上が期待できます。

ただし、あらかじめどの程度アクセスがあるかを予測した上でレコードの数を考える必要があります。

NoSQL

例えばGCPのCloud Datastoreとしたら、こんな感じでしょうか。

pvをインクリメントすればアクセス数をカウントできそうです。

class Pages(ndb.Model):
    page = ndb.StringProperty(indexed=True)
    pv = ndb.IntegerProperty(indexed=False)

問題点

Cloud Datastoreは1つのエンティティに対しての変更が1回/1secに制限されています。

そのため、上記実装だと1秒に1アクセスしか捌くことができません。

対策

高パフォーマンスなkey-value NoSQLを利用する。
(自信ない対策案です...)

GCPだとBigtableとかでしょうか。Bittableは大規模データ用なので微妙かもしれませんけど。

Firesttoreでは分散カウンタを紹介しています。

先述したMySQLの対策と同じコンセプトですかね。

firebase.google.com

ユニークユーザーを数える場合

上記データベースではPVは計測できてもUUはわかりません。

UUも数える場合は、来訪者が来るたびにIPでuserを判別し、重複不可で追加していけばいいと思います。
最後にまとめてSUMでUUがわかります。

これならロックの心配もありません。

f:id:hisurga:20190505032410p:plain

あるいはページごとにテーブルを作成するのもいいかもしれません。

f:id:hisurga:20190505032531p:plain

その他

メルカリのPV管理では一度プールしてからまとめてSQLを更新しているみたいです。

tech.mercari.com

参考にさせていただいたサイト

MySQLでハイパフォーマンスなアクセスカウンター - まるまるこふこふ

MySQL - カウントアップするだけのAPIのつくりかた|teratail