原子性更新短链接计数须用INCR单命令,在同一Redis连接中与跳转逻辑绑定;禁用GET+SET,避免竞态;区分真实点击需校验Method=GET、过滤UA并做指纹去重;计数key设30天TTL,配合每日归档;查总量用GET,查24小时增量依赖TS.MADD或daily key,UV统计用PFADD/PFCOUNT。

短链接跳转时如何原子性更新 Redis 计数
直接用 INCR 就行,但必须和跳转逻辑绑定在同一个 Redis 连接上下文里,否则可能漏计或重复计。常见错误是先查再加:先 GET 再 SET,中间有竞态窗口——并发请求会覆盖彼此的计数。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 跳转响应前,只调一次
INCR,返回值即最新计数值(可选存入响应头或日志) - 用
redis.Conn或redis.UniversalClient的 pipeline 包裹跳转重定向和计数,避免网络往返引入延迟间隙 - 如果用
redis-go/radix/v4,优先走Do+cmd("INCR", key);若用go-redis/redis/v9,用client.Incr(ctx, key).Val() - 注意 key 命名:建议用
short:count:<code>xxx格式,和跳转 key(如short:url:<code>xxx)分离,方便 TTL 独立控制
如何区分真实点击和爬虫/健康检查请求
不做过滤的话,curl -I、HEAD 请求、监控探针、CDN 预热都会刷高计数,导致统计失真。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 在 Go HTTP handler 里检查
r.Method,只对GET和HEAD做跳转,但仅对GET执行INCR - 看
r.Header.Get("User-Agent"),屏蔽已知爬虫 UA(如Googlebot、bingbot),但别依赖 UA 字段做唯一判断——它可伪造 - 加简单指纹:组合
RemoteAddr+User-Agent+Referer做哈希,10 分钟内同一指纹只计一次(用SET key "1" EX 600 NX实现) - 别在中间件里统一拦截——短链接路由通常绕过常规中间件,得在具体 handler 里做
Redis 计数键该不该设 TTL?怎么设才不影响分析
不设 TTL,key 永久存在,内存涨得慢但不可控;设太短(比如 1 天),历史数据就丢了,无法做周/月趋势对比。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 跳转计数 key 必须设 TTL,推荐
EX 2592000(30 天),足够覆盖常规分析周期,又不会长期驻留冷数据 - 不要用
EXPIRE单独补 TTL——INCR不会刷新过期时间,得用INCR+EXPIREpipeline,或改用INCR后立刻EXPIREAT到固定时间点 - 如果要用更长周期(如半年),别全靠 Redis 存——每天凌晨用
GET抓取当日累计值,写进 PostgreSQL 或 Parquet 文件归档,Redis 只留最近 30 天热数据 - 注意:用
go-redis时,client.Incr(ctx, key).Val()返回后,得立刻跟一个client.Expire(ctx, key, 30*24*time.Hour),且要检查第二个命令是否成功
怎么查某条短链的总跳转量和最近 24 小时增量
单纯 GET 只能拿到当前值,没法回溯变化。想查“今天比昨天多了多少”,得额外存时间序列。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用 Redis 的
TS.MADD(需开启 RedisTimeSeries 模块)最省事:每次INCR后,同步写一条时间戳记录,如TS.MADD short:ts:<code>xxx* 1 - 没装模块的话,退而求其次:每天一个 key,如
short:daily:<code>xxx:20240520,用INCR累加当天次数,查询时MGET多个日期 key - 避免用
HASH存每日数据——字段名是日期字符串,遍历时没法范围扫描,还得自己拼 key - Go 里查总量直接
GET short:count:<code>xxx;查 24 小时增量,要么靠 TS 模块的TS.RANGE,要么查两个 daily key 做减法(注意兜底:缺失 key 当 0)
真正麻烦的是“去重 UV”——Redis 基础类型搞不定,得上 PFADD + PFCOUNT,但要注意 HyperLogLog 有 0.81% 误差,且不支持删除。这事容易被忽略,一上来就想统计独立访客,结果发现没法精确去重。










