如何使用tormysql实现异步URL插入?yield控制流相关疑问
解决tormysql异步插入URL到数据库的问题
看起来你卡在了异步插入的执行环节,核心问题大概率是协程没有被正确调度,或者对tormysql的异步流程和yield的用法理解有偏差。我来一步步帮你解决:
首先,明确tormysql的异步核心逻辑
tormysql是基于Tornado协程的异步MySQL驱动,所有IO操作(拿连接、执行SQL、提交事务)都是异步的,必须通过yield(旧版Tornado)或await(新版Tornado)来等待操作完成;同时,协程函数需要被Tornado的IOLoop调度才能真正执行——直接调用协程函数只会生成一个协程对象,不会跑起来。
第一步:正确初始化连接池
先把连接池配置好,这是复用连接的基础:
import tormysql from tornado import ioloop from tornado.gen import coroutine # 初始化连接池,根据你的数据库信息修改参数 db_pool = tormysql.ConnectionPool( max_connections=20, # 最大连接数 idle_seconds=300, # 连接闲置超时 wait_connection_timeout=3, # 获取连接超时时间 host="localhost", user="your_username", passwd="your_password", db="your_database", charset="utf8mb4" )
第二步:写正确的异步插入函数
这里分两种写法,对应不同版本的Tornado:
写法1:用@coroutine装饰器(兼容旧版Tornado)
@coroutine def insert_to_db(url): conn = None try: # 从连接池异步获取连接,必须用yield等待 conn = yield db_pool.Connection() with conn.cursor() as cursor: # 异步执行插入SQL,同样需要yield sql = "INSERT INTO urls (url) VALUES (%s)" yield cursor.execute(sql, (url,)) # 异步提交事务 yield conn.commit() print(f"成功插入URL: {url}") except Exception as e: print(f"插入失败: {url}, 错误: {str(e)}") # 出错回滚事务 if conn: yield conn.rollback() finally: # 释放连接回池,必须做 if conn: conn.close()
写法2:用async/await(Tornado 5.0+推荐)
async def insert_to_db(url): conn = None try: conn = await db_pool.Connection() async with conn.cursor() as cursor: sql = "INSERT INTO urls (url) VALUES (%s)" await cursor.execute(sql, (url,)) await conn.commit() print(f"成功插入URL: {url}") except Exception as e: print(f"插入失败: {url}, 错误: {str(e)}") if conn: await conn.rollback() finally: if conn: conn.close()
第三步:确保协程被调度执行
这是你最可能忽略的点!如果只是直接调用insert_to_db("http://xxx.com"),函数根本不会执行——你需要把它放到Tornado的IO循环里调度:
场景1:批量爬取URL后插入
如果是批量处理URL列表,写一个入口协程,然后用run_sync启动:
@coroutine def batch_process(url_list): # 遍历URL,逐个异步插入(也可以用gen.multi并发插入,效率更高) for url in url_list: # 必须用yield等待插入完成,或者用gen.multi并发 yield insert_to_db(url) # 启动IO循环执行协程 if __name__ == "__main__": # 假设这是你爬取到的URL列表 crawled_urls = [ "http://example.com", "http://test.com", "http://sample.com" ] ioloop.IOLoop.current().run_sync(lambda: batch_process(crawled_urls))
场景2:爬取到URL就立即插入(比如异步爬虫中)
如果是用Tornado的异步HTTP客户端爬取,在回调协程里直接yield或await插入函数即可:
@coroutine def crawl_single_url(url): # 模拟异步爬取逻辑 from tornado.httpclient import AsyncHTTPClient client = AsyncHTTPClient() response = yield client.fetch(url) # 爬取完成后立即异步插入 yield insert_to_db(url)
关键误区提醒
- 不要直接调用协程函数:
insert_to_db(url)只会返回一个协程对象,必须用yield/await或者交给IOLoop调度才会执行。 - 所有异步操作都要等待:获取连接、执行SQL、提交/回滚,这些步骤都是异步IO,必须用
yield/await等待完成,否则会跳过操作直接执行后续代码。 - 必须释放连接:不管插入成功还是失败,都要把连接放回池里,否则会导致连接耗尽。
内容的提问来源于stack exchange,提问作者Daniel Paczuski Bak
相关产品推荐
相关产品推荐

