You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过分页从Firebase Realtime DB获取子节点键

问题:Firebase Realtime DB 批量获取子节点键

我需要从Firebase Realtime DB中获取子节点键,数据结构示例如下:

persons
    p1
        first_name: John
        surname: Blue
    p2
        first_name: Johnny
        surname: Red
    p3
        first_name: Mike
        surname: Orange

目标是得到键列表 [p1, p2, p3],但实际节点数量超过10K(比如[p1, p2, ..., p11000])。

我原本用以下代码实现:

def get_keys_only(db_path):
    ref = db.reference(db_path) 

    # first arg must be False for shallow queries
    # ref.get(includeEtag, shallowQuery)
    valueAtRef_as_dict = ref.get(False, True)

    # The returned data can be None if the collection doesn't exist in DB
    if valueAtRef_as_dict is not None:
        # Keys returned in a list
        return_list = [*valueAtRef_as_dict]
    else:
        return_list = []

    return return_list

但运行时抛出firebase_admin.exceptions.UnavailableError,异常详情:

in get_keys_only valueAtRef_as_dict = ref.get(False, True) File "/usr/local
/lib/python3.8/site-packages/firebase_admin/db.py", line 221, in get return 
self._client.body('get', self._add_suffix(), params=params) 
File "/usr/local/lib/python3.8/site-packages/firebase_admin/_http_client.py", line 131, 
in body resp = self.request(method, url, **kwargs) 
File "/usr/local/lib/python3.8/site-packages/firebase_admin/db.py", 
line 931, in request raise _Client.handle_rtdb_error(error) 
firebase_admin.exceptions.UnavailableError: Request has timed out

测试发现小数据量时代码正常,推测需要分页实现,但不知道怎么操作。请问如何通过分页获取大量键?如果分页不可行,还有其他解决办法吗?


一、分页实现方案

Firebase Realtime DB支持通过orderByKey()结合startAt()、limitToFirst()进行分页查询,配合shallow模式只获取键。具体实现代码如下:

def get_keys_paginated(db_path, page_size=1000):
    ref = db.reference(db_path)
    all_keys = []
    last_key = None

    while True:
        query = ref.order_by_key()
        # 若存在上一页最后一个键,以此为起点查询下一页
        if last_key:
            # 多取1条数据,用来判断是否还有后续分页
            query = query.start_at(last_key).limit_to_first(page_size + 1)
        else:
            query = query.limit_to_first(page_size)
        
        # 启用shallow模式,仅返回键的字典结构
        result = query.get(False, True)
        if not result:
            break
        
        current_keys = list(result.keys())
        # 若返回数量等于page_size+1,说明还有下一页,去掉最后一条并更新last_key
        if len(current_keys) == page_size + 1:
            current_keys = current_keys[:-1]
            last_key = current_keys[-1]
        else:
            last_key = None
        
        all_keys.extend(current_keys)
        # 无后续分页则退出循环
        if not last_key:
            break
    
    return all_keys

说明

  • page_size可根据网络情况调整(比如500或1000),避免单次请求数据量过大导致超时
  • 多取1条数据的目的是判断是否还有后续分页,确保所有键都能被获取
  • 始终保持shallow模式,仅传输键数据,减少网络负载

二、替代解决方案

如果分页仍遇到超时问题,可尝试以下方案:

1. 调整SDK超时时间

通过自定义HTTP客户端配置,延长请求超时阈值:

from firebase_admin import db
from firebase_admin._http_client import HttpClient

# 设置超时时间为30秒(可按需调整)
custom_http_client = HttpClient(timeout=30)
# 重新初始化DB客户端,使用自定义配置
db._initialize_app(你的DB_APP实例, {'databaseURL': '你的数据库URL'}, http_client=custom_http_client)

2. 预优化数据结构

在写入数据时同步维护一个专门存储键的节点,比如person_keys:

persons
    p1: {...}
    p2: {...}
person_keys
    p1: true
    p2: true

后续直接读取person_keys节点的键即可,shallow查询小节点不会触发超时。

3. 直接调用REST API并添加重试机制

使用requests库直接调用Firebase REST API,配合分页参数和重试逻辑:

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def get_keys_via_rest(db_url, path, page_size=1000):
    session = requests.Session()
    # 添加重试机制,针对超时等异常自动重试
    retry_strategy = Retry(
        total=3,
        backoff_factor=1,
        status_forcelist=[429, 500, 502, 503, 504]
    )
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("https://", adapter)
    
    all_keys = []
    last_key = None
    base_url = f"{db_url}/{path}.json?shallow=true&orderBy=%22%24key%22"

    while True:
        url = base_url
        if last_key:
            url += f"&startAt=%22{last_key}%22&limitToFirst={page_size + 1}"
        else:
            url += f"&limitToFirst={page_size}"
        
        response = session.get(url)
        response.raise_for_status()
        result = response.json()
        
        if not result:
            break
        
        current_keys = list(result.keys())
        if len(current_keys) == page_size + 1:
            current_keys = current_keys[:-1]
            last_key = current_keys[-1]
        else:
            last_key = None
        
        all_keys.extend(current_keys)
        if not last_key:
            break
    
    return all_keys

内容的提问来源于stack exchange,提问作者edn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:07:10