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 官方文档。利益相关说明:文中提到的服务是我自己在用的,花自己的钱,独立体验。你们给采集做过缓存层吗?命中率多少?评论区聊聊。

相关推荐
大侠归来2 小时前
Redis Cluster 的 16384 个槽(Slot)是什么?
数据库·redis·缓存
xixingzhe24 小时前
秒杀库存三种缓存实现方案
数据库·redis·缓存
戴西软件4 小时前
企业如何搭建自己的AI智能体?戴西智能体管理与戴西SKILL管理实战解析
运维·jvm·人工智能·自动化·rpa
lv__pf6 小时前
redis【TL redis(6)】
数据库·redis·缓存
hweiyu006 小时前
Redis命令:RPOPLPUSH
redis·缓存
霸道流氓气质7 小时前
Redisson BloomFilter:分布式位图实现、误判率调优与缓存穿透三级防护的 Spring Boot 生产级实战
spring boot·分布式·缓存
wefg121 小时前
【Redis】安装与启动
数据库·redis·缓存
大圣编蚕1 天前
常见的 Redis 面试题(高频整理)
数据库·redis·缓存
梅菲塔亚1 天前
博客_Redis入门到实战
数据库·redis·缓存