免费接口也要缓存:行政区划查询低频更新下的缓存设计
# 免费接口也要缓存:行政区划查询低频更新下的缓存设计
> 接口 / 接入点:行政区划查询(apiCode 1149)· 区域查询 1149-1 / 子区域查询 1149-2 · 免费服务
> 请求方式:POST / GET · 返回格式:JSON · 适用人群:中高级开发者、架构师 · 阅读时间:约 7 分钟
## 核心要点
- 行政区划数据**每月才同步一次民政部**,变更极低频 → 缓存命中率天然很高。
- 免费虽无按次成本,但缓存能降延迟、扛峰值、减少对外依赖,提升体验。
- 推荐 Redis:key 用 `level:areaName`(区域查询)和 `parentId`(子区域查询),TTL 30 天,并在每月 1-3 号预留刷新。
## Why:免费为什么还要缓存
很多人觉得「免费接口随便调」,于是不加任何缓存,结果高峰期把外部依赖打满、自己延迟飙升。行政区划查询的数据一个月才变一次,几乎静态——这是缓存性价比最高的场景。花一点缓存成本,换来稳定可控的本地响应。
## What:缓存设计要点
| 维度 | 建议 |
|------|------|
| 更新频率 | 每月 1-3 号检查民政部数据并更新 |
| 推荐介质 | Redis(或本地内存,单机量小也可) |
| key 规则 | 区域查询:`region:{level}:{areaName}`;子区域查询:`sub:{parentId}:{page}` |
| TTL | 30 天(覆盖一个更新周期),或监听更新后主动失效 |
| 失效策略 | 每月更新窗口(1-3 号)后批量刷新 / 设置较长 TTL 自然过期 |
## How:Redis 缓存实现(Python)
```python
import redis, requests, json
r = redis.Redis(host="127.0.0.1", port=6379, db=0)
APPKEY = "YOUR_APPKEY"
TTL = 30 * 24 * 3600 # 30 天
def region_query(area_name, level="2", page="1"):
key = f"region:{level}:{area_name}:{page}"
cached = r.get(key)
if cached:
return json.loads(cached) # 命中缓存
resp = requests.get("https://route.showapi.com/1149-1",
params={"appKey": APPKEY, "areaName": area_name, "level": level, "page": page},
timeout=10)
body = resp.json()["showapi_res_body"]
r.setex(key, TTL, json.dumps(body)) # 写入缓存
return body
def sub_region(parent_id, page="1"):
key = f"sub:{parent_id}:{page}"
cached = r.get(key)
if cached:
return json.loads(cached)
resp = requests.get("https://route.showapi.com/1149-2",
params={"appKey": APPKEY, "parentId": parent_id, "page": page},
timeout=10)
body = resp.json()["showapi_res_body"]
r.setex(key, TTL, json.dumps(body))
return body
```
### 缓存刷新(每月更新后主动失效)
```python
def refresh_after_update():
# 在每月 1-3 号民政数据更新完成后调用
for key in r.scan_iter("region:*"):
r.delete(key)
for key in r.scan_iter("sub:*"):
r.delete(key)
```
## 返回示例(读缓存)
缓存 value 即接口返回的 `showapi_res_body` JSON,结构同[返回字段全解](https://www.showapi.com/guides/region-query-response-fields-1149):
```json
{ "ret_code": 0, "data": [ {"areaName": "昆明市", "id": "530100000000", "wholeName": "中国,云南省,昆明市"} ], "allNum": 1, "maxSize": 20, "allPage": 1 }
```
## 进阶 / 边界
- **TTL 与更新对齐**:数据每月更新,TTL 设 30 天基本覆盖;更稳的做法是更新窗口后主动 `DELETE` 再懒加载。
- **省级可常驻**:省级仅约 34 条,适合常驻缓存(更长 TTL 甚至不过期,更新时刷新)。
- **分页 key 要带 page**:下级 > 20 条时分页,key 必须包含 `page`,否则不同页会被错乱覆盖。
- **不要缓存错误响应**:`ret_code != 0` 时不写缓存,避免把错误结果污染缓存。
## FAQ
**Q:免费接口缓存有什么意义?**
降低外部延迟、扛住自身峰值、减少依赖;即便免费,稳定可控的本地响应也是体验刚需。
**Q:TTL 设多久合适?**
数据每月更新,设 30 天自然覆盖一个周期;更稳妥是更新窗口(1-3 号)后主动失效刷新。
**Q:缓存会把错误也存进去吗?**
不应。`ret_code != 0` 的结果不要写缓存,仅缓存成功响应。
**Q:单机用 Redis 是不是太重?**
量级小(省级 34、市级数百)时本地内存字典即可;多实例 / 需共享才上 Redis。
## 相关能力 / 下一步阅读
- [行政区划查询返回字段全解:wholeName / 各级编码 / 拼音一文读懂](https://www.showapi.com/guides/region-query-response-fields-1149)
- [行政区划查询实战:省市区三级联动选择器前端实现](https://www.showapi.com/guides/region-query-cascade-1149)
- **本系列共 11 篇**:查看[行政区划查询指南总目录](https://www.showapi.com/guides/region-query-guides-1149)