I have an integer MySQL column that grows every time a page is viewed. The SQL query looks something like this:
UPDATE page SET views = views + 1 WHERE id = $id
We began to encounter scaling problems when the same page (the same identifier) was scanned many times per second (writing is blocked in MySQL), and the query will stop MySQL. To combat this, we used the following strategy:
Each time the page loads, we increase the counter in Memcache and put the task in the queue (Gearman), which updates the counter in MySQL in the background (among 3 working machines). The simplified code is as follows:
In page view:
$memcache->increment("page_view:$id"); $gearman->doBackground('page_view', json_encode(array('id' => $id)));
In the background, the worker:
$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}"); }
It worked out well. This allows us to reduce database queries (since we collect representations in memcache before writing to the database), and the database is written in the background, not supporting page loading.
Unfortunately, we are starting to notice MySQL again. It seems that very active pages still start almost simultaneously, causing MySQL to block again. Castles slow down records and often kill our workers. This causes the lineup to grow very large, often having 70k + jobs that are “behind”
My question is: what should we do next to scale this?