如何让SQLAlchemy
在Tornado
是async
? 我发现例如MongoDB的上异步蒙戈例子 ,但我找不到像什么motor
为SQLAlchemy
。 有谁知道如何使SQLAlchemy
查询与执行tornado.gen
(我使用MySQL
下面SQLAlchemy
,此刻我的处理程序从数据库,并返回结果来阅读,我想使这个异步)。
Answer 1:
奥姆斯是适合于明确的异步编程不佳,也就是说,当程序员必须随时产生明确的回调的东西,使用网络接入发生。 这样做的一个主要原因是奥姆斯广泛利用的延迟加载模式,这是更或明确的异步少不兼容。 看起来像这样的代码:
user = Session.query(User).first()
print user.addresses
实际上会发出两个单独的查询-一个当你说first()
来加载一排,而下时,你说user.addresses
,在该情况下.addresses
集已不存在,或已过期。 从本质上讲,代码与ORM构建交易,对IO可能会阻止几乎所有的线,所以你会在广泛的回调面条秒内-而更糟的是,绝大多数的代码行实际上不会阻塞IO,所以连接回调在一起,否则这将是简单的属性访问操作将会使你的程序大大效率较低太的所有开销。
有明确的异步模式的一个主要问题是,他们增加了巨大的Python函数调用开销的复杂系统 - 不只是在向用户侧像你懒加载得到,但在内部的一面,以及对于系统如何提供围绕抽象Python数据库API(DBAPI)。 对于SQLAlchemy的,甚至有基本的异步支持,将会对绝大多数不使用异步模式,甚至认为不高并发的异步程序的程序严重的性能损失。 考虑SQLAlchemy的,或者任何其他的ORM或抽象层,可能有类似下面的代码:
def execute(connection, statement):
cursor = connection.cursor()
cursor.execute(statement)
results = cursor.fetchall()
cursor.close()
return results
上面的代码执行什么似乎是一个简单的操作,连接上执行的SQL语句。 但是使用完全异步DBAPI像psycopg2的异步扩展,在IO上面的代码块至少三次。 所以写在明确异步风格上面的代码,即使有未使用任何异步发动机和回调实际上并没有阻止,是指由上述外部函数调用变为至少三个函数调用,而不是一个,不包括产生的开销通过明确的异步系统或调用DBAPI自己。 所以一个简单的应用程序会自动给出3倍的罚款周围围绕语句执行一个简单的抽象的函数调用的开销。 而在Python中, 函数调用的开销就是一切 。
基于这些原因,我继续小于兴奋周围明确异步系统,至少给一些人似乎想全力以赴异步的一切,像提供网页(见node.js的)程度的炒作。 我建议你使用隐式异步系统代替,最值得注意的是GEVENT ,你在哪里得到的所有异步模型的非阻塞IO利益和明确的回调没有结构性冗长/缺点。 我继续努力,以了解这两种方法的使用情况,所以我通过明确的异步方法作为解决一切问题,即上诉困惑的是你的node.js看到的 - 我们使用脚本语言在首先削减冗长和代码的复杂性,以及像提供网页简单的事情明确异步的地方,似乎什么也不做,但增加的样板,可以同样深受GEVENT或类似的是自动化的,如果阻塞IO是即使是这样的一个问题情况类似的(大量高容量网站做精与同步IO模型)。 基于GEVENT的系统是经过生产验证和他们越来越受欢迎,所以如果你喜欢的代码是自动化提供的ORM,你可能也想拥抱异步IO调度自动化,像一个GEVENT系统提供。
更新 :尼克·科格伦指出了他伟大的文章在明确与隐异步的主题 ,这也是一个必须阅读在这里。 而我也一直在更新的事实, PEP-3156现在欢迎互操作性GEVENT ,扭转GEVENT其如前所述感兴趣,这很大程度上要归功于尼克的文章。 因此,在未来我会用GEVENT数据库逻辑,一旦整合这些方法的系统可推荐龙卷风的混合体。
Answer 2:
我在过去同样的问题,我无法找到一个可靠的异步MySQL库。 然而,有使用一个很酷的解决方案ASYNCIO + Postgres的 。 你只需要使用aiopg库,它配备了SQLAlchemy的支持开箱即用:
import asyncio
from aiopg.sa import create_engine
import sqlalchemy as sa
metadata = sa.MetaData()
tbl = sa.Table('tbl', metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('val', sa.String(255)))
@asyncio.coroutine
def go():
engine = yield from create_engine(user='aiopg',
database='aiopg',
host='127.0.0.1',
password='passwd')
with (yield from engine) as conn:
yield from conn.execute(tbl.insert().values(val='abc'))
res = yield from conn.execute(tbl.select().where(tbl.c.val=='abc'))
for row in res:
print(row.id, row.val)
loop = asyncio.get_event_loop()
loop.run_until_complete(go())
Answer 3:
不是龙卷风,但是我们有点发SQLAlchemy的异步在ASYNCIO GINO项目 :
import asyncio
from gino import Gino, enable_task_local
from sqlalchemy import Column, Integer, Unicode, cast
db = Gino()
class User(db.Model):
__tablename__ = 'users'
id = Column(Integer(), primary_key=True)
nickname = Column(Unicode(), default='noname')
async def main():
await db.create_pool('postgresql://localhost/gino')
# Create object, `id` is assigned by database
u1 = await User.create(nickname='fantix')
print(u1.id, u1.nickname) # 1 fantix
# Retrieve the same row, as a different object
u2 = await User.get(u1.id)
print(u2.nickname) # fantix
# Update affects only database row and the operating object
await u2.update(nickname='daisy')
print(u2.nickname) # daisy
print(u1.nickname) # fantix
# Returns all user objects with "d" in their nicknames
users = await User.query.where(User.nickname.contains('d')).gino.all()
# Find one user object, None if not found
user = await User.query.where(User.nickname == 'daisy').gino.first()
# Execute complex statement and return command status
status = await User.update.values(
nickname='No.' + cast(User.id, Unicode),
).where(
User.id > 10,
).gino.status()
# Iterate over the results of a large query in a transaction as required
async with db.transaction():
async for u in User.query.order_by(User.id).gino.iterate():
print(u.id, u.nickname)
loop = asyncio.get_event_loop()
enable_task_local(loop)
loop.run_until_complete(main())
它看起来有点像,但实际上比SQLAlchemy的ORM 完全不同 。 因为我们只使用SQLAlchemy的核心的一部分,并建立了一个简单的ORM在它上面。 它采用asyncpg之下,所以它是唯一的PostgreSQL。
更新 :GINO支持旋风现在,得益于弗拉基米尔·冈察洛夫的贡献。 见文档在这里
Answer 4:
我使用的龙卷风在接下来的方式SQLAlchemy的:
from tornado_mysql import pools
from sqlalchemy.sql import table, column, select, join
from sqlalchemy.dialects import postgresql, mysql
# from models import M, M2
t = table(...)
t2 = table(...)
xxx_id = 10
j = join(t, t2, t.c.t_id == t2.c.id)
s = select([t]).select_from(j).where(t.c.xxx == xxx_id)
sql_str = s.compile(dialect=mysql.dialect(),compile_kwargs={"literal_binds": True})
pool = pools.Pool(conn_data...)
cur = yield pool.execute(sql_str)
data = cur.fetchone()
在这种情况下,我们可以使用SQLAlchemy模型和SQLAlchemy的工具constructig查询。