Heroku部署Selenium抓取Messenger数据触发MySQL max_questions错误
错误根因
你遇到的报错是MySQL对单用户的每小时查询次数限制触发的,和Facebook登录授权只是时间上的巧合:
- 你的
repeater函数是无终止条件的递归调用,没有任何等待间隔,程序启动后会无限制高频发起SELECT * FROM senders查询 - 免费MySQL实例(比如Heroku默认提供的ClearDB免费层)的
max_questions默认值就是3600次/小时,你的高频查询会在几秒内就把额度耗尽,触发报错 - 同时递归无终止的写法还会导致调用栈持续增长,很快就会触发栈溢出导致进程崩溃
解决方案
按优先级执行以下优化即可:
- 替换递归逻辑为带休眠的循环,先从根源降低查询频率,同时避免栈溢出:
def repeater(): while True: senders = [sender['name'] for sender in database.execute(text("SELECT * FROM senders")).fetchall()] # 原有处理新消息的逻辑 time.sleep(10) # 按需调整间隔,10秒查询1次的话每小时仅请求360次,远低于3600的限制 repeater()
- 增加本地缓存,进一步消除不必要的查询:仅在程序启动时全量拉取一次发送者列表存在本地内存中,后续检测到新发送者入库时再更新本地缓存,不需要每次循环都查库:
# 初始化时仅查询1次 cached_senders = set(sender['name'] for sender in database.execute(text("SELECT * FROM senders")).fetchall()) def repeater(): while True: # 从Messenger拉取新消息、拿到发送者的原有逻辑 new_sender = 从消息中提取的发送者名称 if new_sender not in cached_senders: # 新发送者写入数据库 database.execute(text("INSERT INTO senders (name) VALUES (:name)"), {"name": new_sender}) # 更新本地缓存 cached_senders.add(new_sender) time.sleep(10) repeater()
- 如果业务确实需要更高的查询配额,可以升级你的MySQL实例规格,后台调整
max_questions参数到更高值即可。 - 临时恢复服务可以等待1小时,MySQL的
max_questions是按小时滚动计数,周期结束后配额会自动重置。
内容的提问来源于stack exchange,提问作者Jae Woo Kim
相关产品推荐
相关产品推荐

