ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从零构建PHP网站访问统计系统:核心架构、代码实现与优化实践

从零构建PHP网站访问统计系统:核心架构、代码实现与优化实践 简介这是一套开箱即用的PHP网站访问统计系统源码面向Web开发初学者与中小型站点运维人员解决无第三方服务依赖的轻量级流量数据采集、可视化与用户行为分析需求。资源包共2000个文件以1785个JSON配置与数据存储文件为核心辅以117个JavaScript前端交互脚本、41个Markdown文档说明、40个HTML页面模板及9个CSS样式表涵盖统计面板、地理热力图、设备/浏览器识别、页面停留时长等完整功能模块压缩包体积为13.66MB结构清晰支持快速部署与二次定制。已有87人学习下载提供完整的前后端分离架构、响应式管理界面及标准化安装引导流程包含多主题CSS样式如jqplot、bootstrap、map等与组件化HTML结构如paged-users-list便于理解统计系统数据流设计与前端渲染逻辑。1. 项目概述从零构建一个属于自己的网站访问统计系统最近在整理服务器上的老项目翻到了一个尘封已久的压缩包名字就叫“PHP开源网站访问统计系统源码.zip”。这让我想起了十多年前那时候Google Analytics还没像现在这样普及CNZZ、51la等国内统计服务也方兴未艾很多站长尤其是个人站长和技术爱好者都热衷于自己搭建一套访问统计系统。这不仅仅是为了数据自主可控更是一种技术上的“炫技”和深度学习的实践。今天我就以这个典型的PHP统计系统源码为蓝本带大家彻底拆解一遍从设计思路到代码实现再到部署运维手把手教你如何理解并构建一个功能完备的访问统计后台。这个项目本质上是一个轻量级的Web数据采集与分析平台。它的核心工作流程是在你的网站页面中嵌入一段由PHP生成的JavaScript追踪代码通常称为“埋点”。当访客浏览页面时这段JS代码会执行收集访客的IP、浏览器信息、访问页面URL、来源Referer等数据然后通过一个异步请求如图片请求或Ajax将这些数据发送到你的统计服务器。服务器端的PHP脚本接收这些数据经过清洗、去重防刷、归类后存入数据库通常是MySQL。最后通过另一个PHP管理后台从数据库中查询数据并以图表、列表等形式展示出PV页面浏览量、UV独立访客、IP、地域分布、来路分析、关键词等核心指标。为什么今天还要聊这个“古老”的话题首先对于开发者而言理解数据从采集到展示的全链路是构建任何数据驱动型应用的基础。其次自己搭建的系统在数据隐私和安全方面有绝对优势数据完全掌握在自己手中。再者你可以根据自身业务需求高度定制统计维度比如统计某个特定按钮的点击量、某个API接口的调用情况这是通用统计工具难以做到的。最后对于学习PHP、MySQL、JavaScript乃至前端图表库的全栈开发来说这是一个绝佳的练手项目涵盖了Web开发的绝大多数核心环节。2. 核心架构与设计思路拆解一个健壮的访问统计系统远不止是“插入代码-记录数据-展示结果”这么简单。我们需要在架构设计之初就考虑好扩展性、性能、准确性和安全性。下面我们来拆解几个关键的设计考量点。2.1 数据采集方案为什么选择“JS Beacon 1x1 GIF”绝大多数开源PHP统计系统都采用了一种经典且高效的数据采集方案在页面中嵌入一个动态生成的JavaScript脚本该脚本收集环境数据后向一个特定的PHP接口发起一个请求这个请求通常指向一个1像素 x 1像素的透明GIF图片即stat.gif或pixel.gif。为什么是GIF图片而不是直接的Ajax请求跨域与兼容性在早期Ajax的跨域请求CORS处理起来比较麻烦。而图片标签img src...的src属性天生支持跨域兼容所有浏览器包括非常古老的版本。异步与非阻塞图片加载是异步的不会阻塞页面渲染和后续脚本执行对用户体验影响最小。简单可靠服务器只需要返回一个极小的GIF图片文件内容通常硬编码在PHP脚本里响应速度极快消耗资源极少。这个GIF图片本身没有显示意义它的唯一作用就是触发一次HTTP请求将附在URL查询字符串Query String里的统计参数带到服务器。规避某些限制在一些严格的网络环境或浏览器插件中对脚本请求的拦截可能比对图片资源的拦截更严格。采集的数据维度通常包括页面信息当前页面URL (u)、页面标题 (t)。访客标识通过Cookie或LocalStorage生成的唯一访客ID (vid)用于计算UV。来源信息前一个页面的URL即HTTP Referer (r)用于分析流量来源。客户端信息用户代理字符串 (ua)用于解析浏览器、操作系统、设备类型。网络信息访客IP地址通常从$_SERVER[‘REMOTE_ADDR’]获取但需注意代理情况。屏幕信息屏幕分辨率 (sr)。时间戳访问发生的时间 (ts)。这些参数会以?uxxxtxxxvidxxx...的形式附加在请求stat.gif的URL后面。2.2 数据存储设计MySQL表结构规划数据库设计直接决定了统计功能的丰富性和查询效率。一个基础的统计系统至少需要以下几张核心表1. 访问记录表 (stat_logs)这是最核心的流水表记录每一次页面浏览的原始数据。CREATE TABLE stat_logs ( id int(11) unsigned NOT NULL AUTO_INCREMENT, visit_time datetime NOT NULL COMMENT 访问时间, ip_address varchar(45) DEFAULT NULL COMMENT IP地址(支持IPv6), user_agent text COMMENT 用户代理字符串, page_url varchar(2048) NOT NULL COMMENT 页面URL, page_title varchar(512) DEFAULT NULL COMMENT 页面标题, referer_url varchar(2048) DEFAULT NULL COMMENT 来源URL, visitor_id varchar(64) DEFAULT NULL COMMENT 访客唯一ID, screen_resolution varchar(20) DEFAULT NULL COMMENT 屏幕分辨率, created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_visit_time (visit_time), KEY idx_ip (ip_address(15)), KEY idx_visitor (visitor_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT原始访问日志表;注意page_url和referer_url字段长度要给足现代URL可能很长。索引的创建要谨慎虽然加速查询但会影响写入速度。通常对时间(visit_time)和访客ID(visitor_id)建索引是必要的。2. 每日/每小时汇总表 (stat_daily_summary)直接对巨大的流水表进行聚合查询如“查询本月每天的PV”在数据量大时性能极差。因此需要设计预聚合的汇总表定时任务如每天凌晨将前一天的流水数据聚合后存入此表。CREATE TABLE stat_daily_summary ( id int(11) unsigned NOT NULL AUTO_INCREMENT, stat_date date NOT NULL COMMENT 统计日期, pv int(11) NOT NULL DEFAULT 0 COMMENT 页面浏览量, uv int(11) NOT NULL DEFAULT 0 COMMENT 独立访客数, ip_count int(11) NOT NULL DEFAULT 0 COMMENT 独立IP数, avg_visit_time int(11) DEFAULT NULL COMMENT 平均访问时长(秒), bounce_rate decimal(5,2) DEFAULT NULL COMMENT 跳出率, PRIMARY KEY (id), UNIQUE KEY udx_date (stat_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT每日数据汇总表;使用汇总表后前台展示“昨日数据”或“本月趋势图”时直接查询这张小表速度极快。3. 访客会话表 (stat_sessions)用于更精确地追踪用户的一次访问会话Session计算访问深度、停留时长等。CREATE TABLE stat_sessions ( session_id varchar(64) NOT NULL, visitor_id varchar(64) NOT NULL, start_time datetime NOT NULL, end_time datetime DEFAULT NULL, entry_page varchar(2048) DEFAULT NULL, exit_page varchar(2048) DEFAULT NULL, page_count int(11) DEFAULT 1, ip_address varchar(45) DEFAULT NULL, PRIMARY KEY (session_id), KEY idx_visitor_time (visitor_id, start_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT访客会话表;会话通常通过一定时间间隔如30分钟内无新页面浏览来判定结束。2.3 性能与扩展性考量日志表分区对于stat_logs这类只增不删的流水表当数据量达到千万级时查询性能会显著下降。可以使用MySQL的表分区功能按时间如按月进行分区可以极大提升按时间范围查询的效率也便于历史数据归档。读写分离统计系统的写操作记录日志非常频繁而读操作管理员查看报表相对较少。如果流量很大可以考虑数据库读写分离将写操作指向主库复杂的统计查询指向从库。引入缓存对于变化不频繁的汇总数据如“今日总PV”可以将其存储在Redis或Memcached中定时更新避免频繁查询数据库。前端异步加载管理后台的图表数据渲染应使用Ajax异步加载避免一个页面初始化时执行大量SQL查询导致加载缓慢。3. 核心模块代码实现与解析接下来我们深入到代码层面看看各个核心模块是如何实现的。我会用更现代、更安全的编码方式来重述经典实现。3.1 数据接收端 (track.php或pixel.gif.php)这个文件是数据采集的入口它伪装成一个图片实际是PHP脚本。?php // track.php // 关闭PHP错误输出避免污染GIF图片数据 ini_set(display_errors, 0); // 1. 获取并过滤所有传入参数 $page_url filter_input(INPUT_GET, u, FILTER_SANITIZE_URL); $page_title filter_input(INPUT_GET, t, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW); $referer filter_input(INPUT_GET, r, FILTER_SANITIZE_URL); $visitor_id filter_input(INPUT_GET, vid, FILTER_SANITIZE_STRING); $screen_res filter_input(INPUT_GET, sr, FILTER_SANITIZE_STRING); // 用户代理从header获取更可靠 $user_agent $_SERVER[HTTP_USER_AGENT] ?? ; // 2. 获取客户端IP (处理代理情况) function getClientIP() { $ip_keys [HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR, HTTP_X_FORWARDED, HTTP_X_CLUSTER_CLIENT_IP, HTTP_FORWARDED_FOR, HTTP_FORWARDED, REMOTE_ADDR]; foreach ($ip_keys as $key) { if (array_key_exists($key, $_SERVER) true) { foreach (explode(,, $_SERVER[$key]) as $ip) { $ip trim($ip); // 验证IP格式 if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) ! false) { return $ip; } } } } return $_SERVER[REMOTE_ADDR] ?? 0.0.0.0; } $ip_address getClientIP(); // 3. 简单的防刷逻辑基于IP和URL的短期访问频率限制 $cache_key rate_limit: . md5($ip_address . $page_url); $redis new Redis(); // 假设已连接Redis $current_count $redis-incr($cache_key); $redis-expire($cache_key, 60); // 设置60秒过期 if ($current_count 30) { // 60秒内同一IP对同一页面访问超过30次视为刷量 // 可以记录到黑名单或直接忽略本次请求 logSuspiciousActivity($ip_address, $page_url); servePixel(); exit; } // 4. 数据入库 (使用PDO防止SQL注入) try { $pdo new PDO(mysql:hostlocalhost;dbnamestat_db;charsetutf8mb4, username, password); $pdo-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $sql INSERT INTO stat_logs (visit_time, ip_address, user_agent, page_url, page_title, referer_url, visitor_id, screen_resolution) VALUES (NOW(), :ip, :ua, :url, :title, :ref, :vid, :sr); $stmt $pdo-prepare($sql); $stmt-execute([ :ip $ip_address, :ua $user_agent, :url $page_url, :title $page_title, :ref $referer, :vid $visitor_id, :sr $screen_res ]); } catch (PDOException $e) { // 错误处理记录到日志文件不要输出到页面 error_log(Stat insert failed: . $e-getMessage()); } // 5. 返回1x1像素的GIF图片 function servePixel() { header(Content-Type: image/gif); header(Cache-Control: private, no-cache, no-store, must-revalidate); header(Expires: Sat, 01 Jan 2000 00:00:00 GMT); // 这是一个1x1像素透明GIF的二进制数据 echo base64_decode(R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); } servePixel(); ?实操心得filter_input函数是过滤用户输入的第一道安全防线。IP获取逻辑要复杂一些因为用户可能通过CDN或代理访问。生产环境中防刷逻辑必不可少否则容易被恶意脚本刷高统计数字。数据库操作一定要用预处理语句PDO或mysqli_prepare这是避免SQL注入的黄金法则。3.2 前端追踪脚本 (stat.js)这个脚本由PHP动态生成以便注入一些服务器端配置如track.php的路径。?php // stat.js.php header(Content-Type: application/javascript); // 设置较长的缓存时间因为这个脚本内容基本不变 header(Cache-Control: public, max-age86400); ? (function() { var _trackUrl ?php echo htmlspecialchars($config[‘track_url‘], ENT_QUOTES, ‘UTF-8‘); ?; var _siteId ?php echo $site_id; ?; // 生成或获取访客唯一ID (优先使用LocalStorage其次Cookie) function getVisitorId() { var key _stat_visitor_id; var cid; try { if (window.localStorage) { cid localStorage.getItem(key); if (!cid) { cid generateUUID(); localStorage.setItem(key, cid); } } } catch(e) { // 隐私模式可能禁用localStorage } if (!cid) { // 回退到Cookie cid getCookie(key); if (!cid) { cid generateUUID(); setCookie(key, cid, 365 * 2); // 保存两年 } } return cid; } // 简单的UUID生成 function generateUUID() { return xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx.replace(/[xy]/g, function(c) { var r Math.random() * 16 | 0, v c x ? r : (r 0x3 | 0x8); return v.toString(16); }); } // 收集页面数据 var params { u: encodeURIComponent(window.location.href), t: encodeURIComponent(document.title || ), r: encodeURIComponent(document.referrer || ), sr: screen.width x screen.height, vid: getVisitorId(), sid: _siteId, // 添加一个随机数防止缓存 _: new Date().getTime() }; // 构建请求URL var queryString Object.keys(params).map(function(key) { return encodeURIComponent(key) params[key]; }).join(); // 使用Image对象发送请求兼容性好无需等待加载 var img new Image(1, 1); img.src _trackUrl ? queryString; // 将图片节点插入DOM最底部确保请求发出可选 document.body.appendChild(img); })();注意事项使用Image对象发送请求是“发后即忘”的我们通常不关心其是否成功加载。将访客ID存储在LocalStorage比Cookie更持久且不会随每个HTTP请求发送更节省带宽。encodeURIComponent对参数进行编码是必须的防止URL中出现特殊字符导致请求错误。3.3 数据展示后台 (admin.php)后台的核心是复杂的SQL查询和前端图表渲染。这里以展示“今日概况”和“最近7天访问趋势”为例。后端数据接口 (api/overview.php):?php // api/overview.php require_once ../config.php; // 包含数据库连接等配置 header(Content-Type: application/json); $response [code 0, msg success, data []]; try { $pdo getPDOConnection(); // 获取数据库连接的函数 // 1. 今日实时数据 (从流水表查询数据量大时可考虑用汇总表实时增量) $today_start date(Y-m-d 00:00:00); $sql_today SELECT COUNT(*) as pv, COUNT(DISTINCT visitor_id) as uv, COUNT(DISTINCT ip_address) as ip FROM stat_logs WHERE visit_time :today_start; $stmt $pdo-prepare($sql_today); $stmt-execute([:today_start $today_start]); $today_data $stmt-fetch(PDO::FETCH_ASSOC); // 2. 最近7天趋势数据 (从每日汇总表查询性能极佳) $sql_trend SELECT stat_date as date, pv, uv, ip_count as ip FROM stat_daily_summary WHERE stat_date DATE_SUB(CURDATE(), INTERVAL 6 DAY) ORDER BY stat_date ASC; $stmt $pdo-prepare($sql_trend); $stmt-execute(); $trend_data $stmt-fetchAll(PDO::FETCH_ASSOC); $response[data] [ today $today_data, trend $trend_data ]; } catch (Exception $e) { $response[code] 500; $response[msg] Server Error; error_log($e-getMessage()); } echo json_encode($response); ?前端使用Chart.js渲染图表:!-- admin_overview.html 部分 -- div classrow div classcol-md-8 canvas idtrendChart/canvas /div /div script srchttps://cdn.jsdelivr.net/npm/chart.js/script script $(document).ready(function() { $.get(/api/overview.php, function(resp) { if (resp.code 0) { // 更新今日数据卡片 $(#todayPV).text(resp.data.today.pv); $(#todayUV).text(resp.data.today.uv); // ... 更新其他卡片 // 绘制趋势图 var trendCtx document.getElementById(trendChart).getContext(2d); var dates resp.data.trend.map(item item.date.substr(5)); // 取月-日 var pvData resp.data.trend.map(item item.pv); var uvData resp.data.trend.map(item item.uv); new Chart(trendCtx, { type: line, data: { labels: dates, datasets: [{ label: PV, data: pvData, borderColor: rgb(54, 162, 235), tension: 0.1 }, { label: UV, data: uvData, borderColor: rgb(255, 99, 132), tension: 0.1 }] }, options: { responsive: true, plugins: { title: { display: true, text: 最近7天访问趋势 } }, scales: { y: { beginAtZero: true } } } }); } }); }); /script经验之谈后台的数据查询一定要做好索引优化。对于“今日实时数据”这种查询如果stat_logs表数据量巨大比如上亿条即使有时间索引在访问高峰时也可能对数据库造成压力。一个优化方案是使用stat_daily_summary表查询昨日及之前的数据再加上一个Redis计数器实时累加今日的PV/UV最后在查询时两者相加。这需要更复杂的架构但能保证高性能。4. 高级功能实现与优化一个基础的统计系统完成后我们可以在此基础上添加更多有价值的功能。4.1 用户行为分析页面点击热图Heatmap热图能直观展示用户在页面上的点击分布。实现原理是在前端追踪点击事件并将坐标数据发回服务器。前端点击追踪:// 在 stat.js 中追加 document.addEventListener(click, function(e) { // 只记录对特定元素如链接、按钮的点击避免噪音 var tagName e.target.tagName.toLowerCase(); if ([a, button, input[typesubmit], div[rolebutton]].some(tag e.target.matches(tag))) { var rect e.target.getBoundingClientRect(); var clickData { x: e.clientX - rect.left, y: e.clientY - rect.top, x_percent: ((e.clientX - rect.left) / rect.width * 100).toFixed(2), y_percent: ((e.clientY - rect.top) / rect.height * 100).toFixed(2), pageX: e.pageX, pageY: e.pageY, element: e.target.tagName (e.target.id ? # e.target.id : ) (e.target.className ? . e.target.className.split( )[0] : ), text: e.target.textContent?.substring(0, 50) || // 截取部分文本 }; // 使用navigator.sendBeacon或Image对象发送数据 var beaconData new FormData(); beaconData.append(type, click); beaconData.append(data, JSON.stringify(clickData)); beaconData.append(page, window.location.href); navigator.sendBeacon(/api/track_behavior.php, beaconData); } }, {capture: true}); // 使用捕获阶段确保能监听到所有点击navigator.sendBeacon方法非常适合在页面卸载如跳转、关闭前发送数据它异步且可靠不会阻塞页面跳转。后端存储与渲染:后端将接收到的点击坐标、元素信息和页面URL存入数据库。在热图查看页面后端查询指定页面的所有点击数据前端使用Canvas或SVG根据坐标密度通过高斯模糊算法渲染出颜色从冷蓝色点击少到热红色点击多的热力图。4.2 搜索引擎与关键词分析分析流量来源是统计系统的核心功能之一。我们需要从referer_url中解析出搜索引擎和搜索关键词。function parseSearchEngine($referer) { if (empty($referer)) return [engine direct, keyword ]; $host parse_url($referer, PHP_URL_HOST); $query parse_url($referer, PHP_URL_QUERY); parse_str($query, $params); $engines [ www.google. [name Google, param q], www.bing. [name Bing, param q], search.yahoo. [name Yahoo, param p], www.baidu. [name Baidu, param wd], www.so.com [name 360搜索, param q], www.sogou.com [name 搜狗, param query], // ... 更多搜索引擎规则 ]; foreach ($engines as $domainPart $engineInfo) { if (stripos($host, $domainPart) ! false) { $keyword $params[$engineInfo[param]] ?? ; // 百度等搜索引擎的关键词可能是GBK编码需要转换 if ($engineInfo[name] Baidu $keyword) { $keyword mb_convert_encoding(urldecode($keyword), UTF-8, GBK); } else { $keyword urldecode($keyword); } return [engine $engineInfo[name], keyword trim($keyword)]; } } // 如果不是已知搜索引擎则归为外部链接 return [engine external, keyword , domain $host]; }在数据入库时调用此函数解析来源并将搜索引擎名称和关键词存入单独的字段便于后续统计分析。4.3 数据清理与归档策略stat_logs表会无限增长必须制定数据清理策略。实时数据保留最近3-6个月的详细日志用于实时查询和深度分析。归档数据超过3个月的原始数据可以按月导出为CSV或Parquet文件压缩后存储到对象存储如AWS S3、阿里云OSS或冷备份硬盘中。然后从主数据库表中删除。汇总数据stat_daily_summary这类高度聚合的数据可以永久保留用于展示长期趋势。实现上可以编写一个PHP CLI脚本由Crontab定时执行。# 每天凌晨3点执行数据归档任务 0 3 * * * /usr/bin/php /path/to/your_project/scripts/archive_logs.phparchive_logs.php脚本负责查询3个月前的数据。生成压缩文件并上传到云存储。确认上传成功后删除数据库中的过期记录。记录归档日志。5. 部署、运维与常见问题排查5.1 服务器环境部署要点Web服务器配置确保Nginx/Apache正确配置能解析PHP。对于track.php这类接口可以配置更宽松的client_max_body_size和超时时间。PHP配置max_execution_time数据处理脚本可能需要更长时间。memory_limit处理大数据量查询时可能需要增加内存。date.timezone设置为正确的时区保证时间统计准确。MySQL配置优化innodb_buffer_pool_size设置为可用物理内存的70-80%这是InnoDB最重要的性能参数。为stat_logs表设置合适的innodb_file_per_table和innodb_page_size。考虑启用innodb_online_alter_log_max_size以便在线修改大表结构。计划任务Crontab这是系统的“心脏”用于执行每日数据汇总、数据归档、发送日报等任务。务必确保PHP CLI路径正确并正确设置脚本的执行权限和日志输出。5.2 常见问题与排查技巧问题1统计数据明显偏低或为零。排查检查前端stat.js是否被正确嵌入到网站页面中。查看网页源代码确认脚本标签存在且src地址正确。打开浏览器开发者工具的“网络(Network)”面板刷新页面查看是否有对track.php或stat.gif的请求。检查该请求的状态码是否为200请求参数是否正常。检查服务器端track.php脚本是否有语法错误或权限问题。查看PHP错误日志/var/log/php-fpm/error.log或Apache错误日志。检查数据库连接是否成功插入语句是否有错误。可以在track.php中临时添加错误日志记录功能。问题2UV独立访客数据异常偏高。排查Cookie/LocalStorage失效检查getVisitorId函数逻辑。如果用户禁用Cookie且浏览器不支持LocalStorage或者隐私模式浏览每次都会生成新ID。这是技术局限可以接受。爬虫和机器人大量网络爬虫没有JavaScript执行环境但可能会直接请求页面资源触发统计。需要在服务器端通过user_agent进行过滤。维护一个常见的爬虫UA列表进行匹配过滤。IP频繁变动例如移动网络用户、大型公司出口IP池会导致同一用户被计为多个UV。这很难完全避免是行业通病。问题3管理后台查询速度非常慢。排查与优化检查SQL慢查询日志在MySQL中启用慢查询日志(slow_query_log)找出执行时间过长的语句。使用EXPLAIN分析对慢查询语句使用EXPLAIN命令查看其执行计划是否使用了正确的索引。常见瓶颈stat_logs表缺少对visit_time或visitor_id的索引。查询条件中使用了函数如DATE(visit_time) ‘2023-10-27’这会导致索引失效。应改为visit_time ‘2023-10-27 00:00:00’ AND visit_time ‘2023-10-28 00:00:00’。频繁进行COUNT(DISTINCT ...)全表扫描。务必依赖stat_daily_summary等汇总表。引入查询缓存对于变化不频繁的聚合数据如“本月TOP10页面”将查询结果缓存到Redis中设置5-10分钟的过期时间。问题4遭遇恶意刷量CC攻击。防御措施频率限制如前面代码所示在track.php入口处基于IP和页面进行短时间内的请求次数限制。验证码对于疑似刷量的IP在统计代码返回前先返回一个简单的JavaScript验证码挑战如图形点选、算术题只有通过挑战才发送真正的统计请求。这能有效阻挡简单的自动化脚本。UA和IP黑名单建立动态黑名单将短时间内触发频率限制的IP加入黑名单一段时间内拒绝其所有统计请求。关联分析正常用户的访问行为是有规律的有Referer浏览多个页面停留一定时间。刷量脚本往往行为单一固定URL、无Referer、请求间隔极其规律。可以在后台分析阶段标记并过滤这类异常数据。从头到尾实现一个网站访问统计系统是一个将Web开发全栈知识串联起来的绝佳实践。它涉及前端JavaScript、后端PHP、数据库MySQL、服务器运维、数据分析乃至简单的产品设计。虽然现在有众多优秀的SaaS统计产品但自己动手搭建一遍你对数据流动的理解、对性能瓶颈的把握、对异常情况的处理能力都会得到质的提升。这个“PHP开源网站访问统计系统源码.zip”不仅仅是一堆代码更是一个等待你去探索、优化和赋予新生命的技术骨架。本文还有配套的精品资源点击获取
返回列表