如何降低每次API请求更新last_activity产生的数据库调用频次
可行实现方案
下面是3种落地性强的方案,可根据你的业务流量规模选择:
方案1:缓存时间窗口拦截(最常用)
- 依赖Redis(分布式场景)或本地内存缓存(单实例场景),给每个会话ID生成一个缓存键,比如
activity_updated:{session_id} - 每次API请求到达时,先尝试给对应缓存键设置当前时间,同时设置过期时间等于你设定的更新窗口(比如60秒),使用原子写入操作(Redis用
SETNX,本地缓存用带原子性的putIfAbsent方法) - 如果写入缓存成功,说明当前时间窗口内没有执行过更新,再触发数据库的
last_activity字段更新;如果写入失败,说明窗口内已经更新过,直接跳过数据库操作 - 优势:可以拦截99%以上的无效数据库请求,实现简单,性能损耗极低
方案2:数据库条件更新(零额外依赖)
- 直接改写你的更新SQL,添加时间判断条件,不需要引入任何外部组件:
UPDATE user_sessions SET last_activity = CURRENT_TIMESTAMP WHERE session_id = '当前会话ID' AND last_activity < CURRENT_TIMESTAMP - INTERVAL 60 SECOND;
- 数据库会自动判断如果该会话的
last_activity距离当前不足60秒,就不会执行实际的写入操作,执行效率远高于无条件更新 - 优势:不需要修改业务架构,代码改动最小;适合流量规模中等,不想额外维护缓存组件的场景
方案3:异步批量更新(超高流量场景)
- 所有API请求触发
last_activity更新时,仅将会话ID写入本地队列或消息队列,不直接操作数据库 - 后台启动一个定时任务,比如每60秒执行一次:取出队列中所有的会话ID去重后,批量执行数据库更新操作,完成后清空队列
- 优势:数据库写入次数降到最低,即使每秒数万请求也只会每分钟执行1次批量更新;适合用户量极大、并发极高的场景
注意事项
- 时间窗口长度可以根据你的活跃用户统计精度要求调整,比如统计日活的话窗口设为1小时都没问题,统计分钟级活才需要设更小的窗口
- 用缓存方案时一定要用原子写入操作,避免并发请求同时穿透缓存触发多次数据库更新
内容的提问来源于stack exchange,提问作者Saurabh Gangamwar
相关产品推荐
相关产品推荐

