高流量下每篇文章每次點擊都 `UPDATE hits=hits+1` 會拖垮 MySQL。本方案採用「記憶體/檔案快取累積 → 定時批次同步資料庫」,大幅降低資料庫寫入壓力。
<?php
error_reporting(E_ALL);
$ROOT_PATH = '../';
include_once($ROOT_PATH . "include/config.php");
$update_time = 1800; // 同步間隔(秒)
$article_id = (isset($_GET['article_id']) && is_numeric($_GET['article_id']) && $_GET['article_id'] > 0)
? intval($_GET['article_id']) : 0;
if ($article_id > 0) {
$filename = $ROOT_PATH . 'log/click_log.txt';
$d_ary = [];
// 讀取現有快取
if (file_exists($filename)) {
$content = file_get_contents($filename);
if ($content) {
foreach (explode("\n", $content) as $line) {
list($id, $hits) = explode('|', $line);
if (is_numeric($id) && is_numeric($hits)) $d_ary[$id] = $hits;
}
}
}
// 累加點擊
$d_ary[$article_id] = ($d_ary[$article_id] ?? 0) + 1;
// 寫回快取 (帶檔案鎖)
$s = '';
foreach ($d_ary as $k => $v) $s .= "$k|$v\n";
$fp = fopen($filename, "w");
if (flock($fp, LOCK_EX)) {
fwrite($fp, $s);
flock($fp, LOCK_UN);
}
fclose($fp);
@chmod($filename, 0777);
// 定時同步邏輯
$last_update = intval(@file_get_contents('../log/last_update.txt'));
if (($last_update + $update_time) < time()) {
require_once("adodb.inc.php");
$db = NewADOConnection($DB_TYPE);
$db->Connect($DB_HOST, $DB_USER, $DB_PASS, $DB_DATABASE);
$content = file_get_contents($filename);
if ($content) {
foreach (explode("\n", $content) as $line) {
list($id, $hits) = explode('|', $line);
if (is_numeric($id) && is_numeric($hits)) {
$db->Execute("UPDATE article SET hits=hits+$hits WHERE id=$id");
}
}
}
// 清空快取、更新同步時間
file_put_contents($filename, '');
file_put_contents('../log/last_update.txt', time());
@chmod('../log/last_update.txt', 0777);
$db->Close();
}
exit();
}
?>
此方案以極低成本解決「寫入放大」問題,是中小型站點升級前的最佳過渡架構。