Redis GEO数据不支持单点过期,EXPIRE只能删除整个zset key,无法按写入时间清理个别轨迹点;需业务层用HSET存时间戳,再扫描meta键批量删除失效点。

Redis 的 GEO 数据本身不支持 per-member 过期,所谓“GEO 过期”其实是业务层定义的失效逻辑(比如设备离线超 30 分钟、轨迹点写入时间 > 2 小时),不能靠 EXPIRE 整个 key 来解决——否则所有点一起丢,不是“清理过期”,而是“清空全部”。
为什么不能用 EXPIRE + volatile-lru 清理单个轨迹点
Geo 数据底层是 ZSET,EXPIRE 只能作用于整个 key,无法标记某个 member(如 device:8827)已过期。即使你给 geo:track:20261020 设置了 2 小时 TTL,一旦到期,整个 key 被删,连最新有效的点也没了。而 volatile-lru 策略只淘汰带 TTL 的 key,对没设 TTL 的 key 完全无效;就算设了,LRU 是按 key 最后访问时间淘汰,不是按轨迹点写入时间淘汰——设备静默期间 key 不被访问,但旧点仍在内存里。
如何基于写入时间字段识别并批量删除失效轨迹点
必须在业务写入时,把时间戳作为辅助信息存下来,例如用 HSET 维护每个 device ID 的最后上报时间:
GEOADD geo:track:20261020 116.35 39.98 "device:8827" HSET track_meta:device:8827 last_seen 1729390620 # Unix timestamp
清理脚本逻辑如下:
- 用
SCAN或HKEYS track_meta:*拉取所有活跃设备 ID 列表 - 对每个
device:xxx,HGET track_meta:device:xxx last_seen,判断是否 - 若失效,调用
ZREM geo:track:20261020 device:xxx删除对应点 - 所有要删的 member 攒成一批(≤ 100 个),统一发
ZREM,避免网络往返和慢日志
用 GEORADIUS + 时间过滤做二次校验(防误删)
单纯依赖 last_seen 可能不准(比如设备时间不同步)。更稳的做法是:先用 GEORADIUS 找出物理上“密集但陈旧”的区域,再结合时间字段过滤。例如:
GEORADIUS geo:track:20261020 116.35 39.98 500 m WITHDIST WITHCOORD COUNT 200 ASC
返回结果里,对每个 member 做 HGET track_meta:{member} last_seen,只删那些距离近 且 时间旧的点。这能避开“设备刚重启但坐标没变”的误判场景。
集群模式下必须注意的路由与性能陷阱
如果 Redis 是集群,geo:track:20261020 和 track_meta:device:8827 必须落在同一 slot,否则 ZREM + HGET 会跨节点失败。解决方案只有两个:
- 强制哈希标签:
geo:track:{20261020}和track_meta:{device:8827},确保 {} 内内容一致 - 把元数据也塞进 ZSET 的 score 里(比如用
timestamp ),避免跨 key 查询,但需客户端解码
另外,别在 Lua 脚本里循环执行 GEORADIUS + ZREM ——一次脚本运行超 5 秒就会卡住主节点。分页拉取、客户端控制节奏才是生产环境唯一可行路径。


















