应采用按天分表、精简字段、合理索引及近似统计策略:建access_log_YYYYMMDD表,存ip/url/status/ua_hash/created_at,ua_hash和created_at建索引;UV用HyperLogLog,PV用5分钟汇总表;查TOP路径需清洗URL参数并建前缀索引。

怎么设计访问统计表结构才不容易卡死
直接用 INSERT INTO log_table 每次请求都写一行,短期没问题,但流量上来后 log_table 会迅速膨胀,SELECT COUNT(*) 变慢,主从延迟拉高,甚至导致写入阻塞。
- 按天分表:建表名如
access_log_20240401,用DATE(NOW())动态路由,避免单表超千万行 - 只存必要字段:至少保留
ip、url、status、ua_hash(MD5(user_agent)截取前 16 位)、created_at(DATETIME,非TIMESTAMP,避免时区干扰) - 不加外键、不用
TEXT存 UA,ua_hash加INDEX,created_at单独建INDEX
实时统计 UV/PV 怎么避免 count(distinct ip) 慢得像爬
COUNT(DISTINCT ip) 在百万级数据上执行一次可能要 3–5 秒,不能直接放报表接口里跑。得换思路:
- 用 HyperLogLog:MySQL 8.0+ 支持
HLL_INIT()/HLL_ADD()/HLL_CARDINALITY(),内存占用固定,误差率约 0.8%,适合 UV 近似统计 - PV 用汇总表:每 5 分钟跑一次
INSERT ... SELECT COUNT(*) FROM access_log_xxx WHERE created_at >= ? AND created_at < ?,写入summary_pv_hour表,查报表直接读汇总表 - 如果必须精确 UV 且量不大(日活 < 10 万),可建
UNIQUE KEY(ip, date)去重表,每天凌晨用INSERT IGNORE批量导入当天新 IP
如何快速查出“昨天 TOP 10 访问路径”
别直接 GROUP BY url ORDER BY COUNT(*) DESC LIMIT 10 —— 没索引时全表扫描,几十秒起步。关键在预处理和索引配合:
- 给
url字段加前缀索引:ALTER TABLE access_log_20240401 ADD INDEX idx_url_128 (url(128))(URL 一般不会超 128 字符) - 限定时间范围再聚合:
WHERE created_at BETWEEN '2024-04-01 00:00:00' AND '2024-04-01 23:59:59',确保能命中created_at索引 - 如果 URL 参数太多(如带
?id=123&t=abc),入库前用正则或应用层剥离参数:REGEXP_REPLACE(url, '\?.*', ''),再存进path_clean字段并单独建索引
SELECT path_clean AS path, COUNT(*) AS pv FROM access_log_20240401 WHERE created_at BETWEEN '2024-04-01 00:00:00' AND '2024-04-01 23:59:59' GROUP BY path_clean ORDER BY pv DESC LIMIT 10;
为什么用 show processlist 看到一堆 Waiting for table metadata lock
这是统计任务和线上写入撞上了。常见于:凌晨跑汇总 SQL 时没加 LOW_PRIORITY,或对大表 ALTER TABLE 加索引没选业务低峰期。
- 所有后台统计查询加
SET SESSION wait_timeout = 60和SET SESSION max_execution_time = 30000(毫秒),防长查询拖垮连接池 - 加索引务必用
ALGORITHM=INPLACE, LOCK=NONE(MySQL 5.6+),否则 DML 会被锁住 - 避免在高峰期执行
ANALYZE TABLE,它会锁表;改用定期SELECT COUNT(*)+EXPLAIN观察执行计划是否走索引










