访问量每次都实时写库,高并发下为什么会丢计数,聚合回写解决什么
访问量每次都实时写库,高并发下为什么会丢计数,原因在于这种写法是「读改写」:先把当前值读出来,加一再写回去。两个请求同时读到同一个旧值,各自加一后写回,落库的是同一个数,一次增量就被覆盖了。并发越高,重叠读到的概率越大,丢的就不是偶发几条。
丢的是哪一段
丢的是增量,不是整块数据。表现为统计数明显低于日志里的实际请求量,且偏差随流量上升而扩大;单个页面看不出来,聚合报表上会出现「今日访问比明细少」这类对不上的情况。
第二种丢失更隐蔽:未落盘即销账。内存里已经把这批增量记为处理完成,但还没有写入持久存储,此时进程退出或机器重启,这段增量就永久消失了。公开版本记录里有人把这两件事写进同一条修复:PbootCMS 在 V3.2.21 记录「将访问量实时写库改为可靠增量聚合回写,修复并发场景下增量丢失及未落盘即销账的问题」。
聚合回写改了什么
聚合回写把「每次访问一次写」变成「攒一段增量、按批写一次」。作用有两层:一是写入次数下降,热点行的争用随之减少;二是把读改写从请求路径里移出,改在单线程或受控任务里累加,覆盖的可能被消掉。
改完之后仍要兼顾三件事。
| 环节 | 要解决的问题 | 常见做法 |
|---|---|---|
| 累加 | 请求路径上的并发覆盖 | 增量先进内存或队列,不在请求里直接改行 |
| 落盘 | 未持久化即销账 | 先写成功再清缓冲,失败重放 |
| 恢复 | 崩溃后已收增量去哪 | 保留待回写数据与可重放的队列位置 |
| 查询 | 明细与汇总口径不一致 | 汇总按固定时点结算,报表标明截止时间 |
顺序很重要:先落盘、后销账。反过来就回到未落盘即销账的老问题。
存储分层怎么划
统计类写入与业务表应当分开。访问明细、蜘蛛日志这类高频追加的数据如果和业务表挤在同一个存储里,高频写会拖累整体,还会在并发建目录或首写时出现额外风险。把日志型数据放到独立的文本或专用存储里,按时间分片,业务表只保留需要事务保证的数据,两类读取互不干扰。
站内的抓取类流量还要与真实访客分开统计。防采集与干扰码针对的是内容被批量搬运,判断依据里包含访问行为特征;把这些流量与搜索引擎的正常抓取混成一个数,统计口径就先失准了。
官方承载口径该怎么读
AnQiCMS 的官方口径是页面加载速度相比传统 PHP CMS 有显著提升,单机可承载约 500 万 PV,内存占用比 PHP 类 CMS 降低约 80%。这两个数是约数,不是压测指标:它们描述的是同等资源下的量级差别,不能直接换算成「多少并发连接」或「多少倍」。
真正决定统计写入能不能扛住的,是这一条链路上的其他环节:缓存挡住多少读请求、数据库的写放大多少、聚合任务多久跑一次。看到任何「单机承载多少」的数字,都应该追问同一条链路上还有什么变量。
常见问题
统计要不要精确到每一次访问? 看用途。运营趋势看聚合值即可;涉及计费或结算的计数要有可对账的明细来源,不能只靠内存累加。
日志型和事务型数据放一起有什么问题? 高频追加会放大写压力并拉长事务,两类数据的保留周期与清理方式也不同,分开后各自策略更好执行。
推送与统计会不会互相影响? 会共用出口带宽与队列。链接推送与主动推送要按抓取节奏排队,避免与高频统计写入争用同一资源。