一个长网址放倒了计数器
2026 年 8 月 9 日,有二十张计数器图片没能画出来。不是画错了,也不是画得慢——访客在本该有计数器的位置什么也没拿到。
原因是一个超过 255 个字符的页面地址。
这条链
记录一次访问落在哪个页面上的那个字段是 varchar(255)。地址来的时候比这更长。插入以 SQLSTATE 22001 失败,数据对该列过长,而这在本项目的代码里会抛出异常。
而统计的写入发生在画图片之前。所以这个异常丢掉的不只是一行统计。它中断了整个请求,而这个请求正是产出计数器图片的那一个。一个地址很长的页面,会悄无声息地弄坏自己的计数器,对每一位访客都如此。
长地址一点也不稀奇。一个路径层层嵌套的图库、一家被广告平台挂上十几个跟踪参数的商店、一个搜索结果页——随便哪一种,都轻轻松松越过 255 个字符。
采取的修法,和没被采纳的那个
地址现在会在插入前被截到字段宽度。一个极长的地址在页面列表里会少掉二十个字符;除此之外什么都不变。
另一个选择是把字段加宽。它被否掉了:不存在无法被超过的宽度,而挑一个更大的数字只是把同一个故障推得更远,等它到来时反而更难辨认。故障模式该做的是不再致命,而不是变得更罕见。
把插入包进 try/catch 同样能治好看得见的症状,而且会更糟:计数器照画,那一行悄悄消失,页面列表里从此永远缺的恰好是地址最复杂的那些页面,而且哪儿都不留记录。
两点带得走的东西
外来输入要按字段宽度截断,而不是指望它塞得下。凡是从外面来的东西——一个地址、一个来源、一串浏览器标识、一个标题——长度由别人决定,而数据库对此有自己的意见,它用拒绝来表达。
要清楚你的故障模式下游还挂着什么。这是一个统计服务里的统计缺陷,可看得见的损害却是一张缺失的图片。写入和绘制共用一个请求;代码里没有任何地方说它们是耦合的,也没有任何东西提醒过:一次失败的插入会把图片一起带走。
一天二十张坏图片,量很小。它是在为完全另一件事读日志时发现的,而这正是这类问题被发现的惯常方式,也是继续读日志的理由。