まず第一に:これは私がクリックカウンターを書く方法ではありません。
とは言うものの、100人のユーザーが同時に(最初のクリックが0で)サーバーにアクセスすると、記録された数は1..100になり、低い(=間違った)値が目立つようになる可能性があります。
- テキストファイルでカウントしたい場合は、@ lanzzの回答のようにロックして、パフォーマンスの大幅な低下に備えてください。これにより、リクエストが効果的にシリアル化されます。
- ファイルをカウントしたい場合は、SQliteを検討し、管理可能なパフォーマンスヒットに備えてください
- カウントしたいだけの場合は、パフォーマンスへの影響が非常に小さい実際のDBを検討してください。
編集:実装
テキストファイル、SQLite、MySQLのファイルカウンター用に以下の実装を作成しました
関数ファミリーを使用することで私を怒らせないでくださいmysql_*()
-常にコードは生産的ではなく、有益であるように意図されています:周囲の層ではなく、目前の問題に集中するという意味で有益です。
counter-file.php:
<?php
//acquire file handle
$fd=fopen('counter.txt','c+b');
if (!$fd) die("Can't acquire file handle");
//lock the file - we must do this BEFORE reading, as not to read an outdated value
if (!flock($fd,LOCK_EX)) die("Can't lock file");
//read and sanitize the counter value
$counter=fgets($fd,10);
if ($counter===false) die("Can't read file");
if (!is_numeric($counter)) {
flock($fd,LOCK_UN);
die("Value in file '$counter' is not numeric");
}
//increase counter and reconvert to string
$counter++;
$counter="$counter";
//Write to file
if (!rewind($fd)) {
flock($fd,LOCK_UN);
die("Can't rewind file handle");
}
$num=fwrite($fd,$counter);
if ($num!=strlen($counter)) {
flock($fd,LOCK_UN);
die("Error writing file");
}
//Unlock the file and close file handle
flock($fd,LOCK_UN);
fclose($fd);
printf ("Counter is now %05d",$counter);
?>
counter-sqlite.php:
<?php
//counter.sqlite3 was created with
//CREATE TABLE counter (counter NUMERIC)
//INSERT INTO counter VALUES (0)
//Open database
$dsn='sqlite:'.dirname(__FILE__).'/counter.sqlite3';
$db=new PDO($dsn);
if (!$db) die("Can't open SQlite database via DBO");
//Make exclusive
$sql="BEGIN EXCLUSIVE TRANSACTION";
if ($db->exec($sql)===false) die("Error starting exclusive transaction");
//Update counter
$sql="UPDATE counter SET counter=counter+1";
if (!$db->exec($sql)) die("Error inserting into database");
//Read value
$sql="SELECT counter FROM counter";
$result=$db->query($sql);
if (!$result) die("Error querying database");
foreach ($result as $row) $counter=$row['counter'];
//Commit
$sql="COMMIT TRANSACTION";
if (!$db->exec($sql)) die("Error committing to database");
//Print result
printf("Counter is now %05d",$counter);
?>
カウンター-mysql.php:
<?php
//mysql database was created with
//CREATE TABLE counter (counter INT NOT NULL)
//INSERT INTO counter VALUES (0)
//Open database connection and select database
$db=mysql_pconnect('127.0.0.1','redacted','redacted');
if (!$db) die("Can't open database");
if (!mysql_select_db('redacted', $db)) die("Can't select database");
//Update counter
$sql="UPDATE counter SET counter=counter+1";
$qry=mysql_query($sql,$db);
if (!$qry) die("Error updating database");
//Read value
$sql="SELECT counter FROM counter";
$qry=mysql_query($sql,$db);
if (!$qry) die("Error reading from database");
$counter=mysql_fetch_array($qry,MYSQL_ASSOC);
if (!$counter) die("Error reading result");
//Print result
printf("Counter is now %05d",$counter['counter']);
?>
パフォーマンスに関しては:私は修正されたままです。SQLiteの実装は他の2つよりも100倍遅くなります。これは、1000回のクリックをカウントしSTART EXCLUSIVE TRANSACTION
てテストを終了する以外に何も受け入れなければならなかったためです。ab -n 1000 -c 50 http://127.0.0.1/stackoverflow/counter/counter-sqlite.php
OPに対する私の推奨事項は、MySQLバージョンを使用することです。これは高速で、OSのクラッシュに対して確実にカウンターを保存します。ファイルバージョンのパフォーマンス特性はほぼ同じですが、OSのクラッシュによって非常に簡単に破壊される可能性があります。