SerpBase 省 credit 实战:缓存 + 去重,把重复请求砍掉一半

采集量一上来,你会发现 credit 消耗里有一部分是「白花的」:同一个词在调试时查了八遍、失败重跑把已成功的词又查了一遍、两个任务重叠查了同一批词。这些重复请求不会带来任何新信息,只会加速余额消耗。这篇用两级缓存 + 去重,把重复请求砍掉。

先定「什么能缓存、缓多久」

不是所有数据都适合缓存,先按端点分类(TTL 按你的业务容忍度定):

数据类型 建议 TTL 理由
排名(/google/search) 当天有效(按天为键) 排名日内波动小,按天缓存最符合业务语义
新闻(/google/news) 10-30 分钟 新闻时效高,长了会漏
地图商家(/google/maps/*) 7 天 商家信息变化慢
图片/视频列表 1 天 变化慢,但没必要更久

缓存键:全部参数,一个不能少

这是缓存最容易翻车的地方:键必须包含所有影响结果的参数 ------endpoint + q + gl + hl + page。少一个,就会把「美国市场的结果」返给「德国市场」的请求。

python 复制代码
import hashlib, json

def cache_key(endpoint: str, params: dict) -> str:
    raw = json.dumps({"e": endpoint, "p": params}, sort_keys=True, ensure_ascii=False)
    return hashlib.md5(raw.encode()).hexdigest()

两级缓存:内存 + 落盘

  • 内存层:同一次任务内命中,零成本
  • 落盘层(SQLite) :跨任务、跨天补跑命中------失败重跑只补没成功的词
python 复制代码
import sqlite3, time

con = sqlite3.connect("cache.db")
con.execute("""CREATE TABLE IF NOT EXISTS http_cache (
    key TEXT PRIMARY KEY, body TEXT NOT NULL, saved_at REAL NOT NULL
)""")

def get_cache(key: str, ttl: int):
    row = con.execute("SELECT body, saved_at FROM http_cache WHERE key=?", (key,)).fetchone()
    if row and (time.time() - row[1]) < ttl:
        return json.loads(row[0])
    return None

def set_cache(key: str, body: dict):
    con.execute("INSERT OR REPLACE INTO http_cache VALUES (?,?,?)",
                (key, json.dumps(body, ensure_ascii=False), time.time()))
    con.commit()

def fetch_cached(endpoint: str, params: dict, ttl: int) -> dict:
    key = cache_key(endpoint, params)
    hit = get_cache(key, ttl)
    if hit is not None:
        return hit
    resp = requests.post(f"https://api.serpbase.dev/google/{endpoint}",
                         headers=HEADERS, json=params, timeout=(5, 30))
    data = resp.json()
    if data.get("status") == 0:          # 只缓存成功响应!
        set_cache(key, data)
    return data

去重:两层

  1. 词表去重 :任务开始前 set(keywords) 去重,重复词直接删
  2. 在途合并:并发任务里同一个词只发一次请求(用一个「进行中」的字典登记,后来者等结果)------这就是 SingleFlight 的思路,能挡住并发场景的重复查询

效果(一次真实场景)

一次「修 bug 后重跑」的 500 词任务:480 个词命中缓存(前一天刚采过),只发了 20 次真实请求------credits 消耗从 500 降到 20。缓存把「重跑成本」几乎清零,这对调试和失败恢复意义很大。

踩坑记录

坑 1:缓存键缺参数。 最容易发生的事故:串市场、串页码。全参数入键。

坑 2:TTL 一刀切。 排名按天、新闻按分钟------统一 TTL 要么漏数据要么浪费。按端点分档。

坑 3:缓存了失败响应。 把 status != 0 的结果缓存下来,下次「命中」一个错误。只缓存成功响应。

坑 4:缓存表无限增长。 没清理机制的缓存迟早变成第二张日志表。按时间/条数定期清理。

坑 5:跨天误用。 排名缓存必须以「天」为键的一部分,不能拿昨天的排名当今天的。日期入键。

坑 6:把缓存当数据库。 缓存会被清理/过期,分析数据必须照常落库。缓存是加速层,不是数据层。

小结

省 credit 的组合拳:全参数缓存键 → 两级缓存(内存+落盘)→ 词表/在途去重 → 只缓存成功响应 。对采集任务来说,这套机制最大的价值不只是省钱------它让「重跑」变得几乎免费,而重跑自由,正是调试和容错能力的基础。

端点计费规则(1/2 credits)与信封字段说明见 SerpBase 官方文档。利益相关说明:文中提到的服务是我自己在用的,花自己的钱,独立体验。你们给采集做过缓存层吗?命中率多少?评论区聊聊。

相关推荐
lsc_blog1 天前
年底还在招的岗位,先确认缺口和到岗要求
git·缓存·ci/cd·职场和发展·github
xixiaoyunya1 天前
Redis缓存三大问题实战:穿透、击穿、雪崩,我用一个电商秒杀场景讲清楚
数据库·redis·缓存
我不会起名字3221 天前
Redis 从 0 到 1(二十三):一个热 key 打到 10 万 QPS——hotkey 与本地缓存的 Go 实现
redis·缓存·go·qps·hotkey
On The Road :1 天前
深入理解 JVM 垃圾回收:GC 是什么、如何判定垃圾、四大回收算法全解析
jvm·算法
Yves..1 天前
JVM 学习笔记:内存区域、类加载机制与垃圾回收
jvm
akinggw2 天前
libagentcore - 一个基于c++的AI多agent系统升级到2.0版本
开发语言·jvm·c++
真上帝的左手2 天前
30. 技术专题-‌应用框架组件-缓存-Redis
数据库·redis·分布式·缓存·中间件
yh弓长2 天前
JVM 核心机制
jvm