peewee vs sqlalchemy表现

Bdf*_*dfy 4 python sqlalchemy peewee

我有2个简单的脚本:

from sqlalchemy import create_engine, ForeignKey, Table
from sqlalchemy import Column, Date, Integer, String, DateTime, BigInteger, event
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.engine import Engine
from sqlalchemy.orm import relationship, backref, sessionmaker, scoped_session, Session

class Test(declarative_base()):
    __tablename__ = "Test"
    def __init__(self, *args, **kwargs):
        args = args[0]
        for key in args:
            setattr(self, key, args[key] )
    key = Column(String, primary_key=True)

data = []
for a in range(0,10000):
    data.append({ "key" : "key%s" % a})


engine = create_engine("sqlite:///testn", echo=False)
with engine.connect() as connection:
    Test.metadata.create_all(engine)
    session = Session(engine)
    list(map(lambda x: session.merge(Test(x)), data))
    session.commit()
Run Code Online (Sandbox Code Playgroud)

结果:

real    0m15.300s
user    0m14.920s
sys     0m0.351s
Run Code Online (Sandbox Code Playgroud)

第二个脚本:

from peewee import *

class Test(Model):
    key = TextField(primary_key=True,null=False)

dbname = "test"
db = SqliteDatabase(dbname)
Test._meta.database = db
data = []
for a in range(0,10000):
    data.append({ "key" : "key%s" % a })

if not Test.table_exists():
    db.create_tables([Test])
with db.atomic() as tr:
        Test.insert_many(data).upsert().execute()
Run Code Online (Sandbox Code Playgroud)

结果:

real    0m3.253s
user    0m2.620s
sys     0m0.571s
Run Code Online (Sandbox Code Playgroud)

为什么?

Ilj*_*ilä 12

这种比较并不完全有效,因为发出upsert样式查询与SQLAlchemy的做法有很大不同Session.merge:

Session.merge()检查源实例的主键属性,并尝试将其与会话中相同主键的实例进行协调.如果未在本地找到,则会尝试基于主键从数据库加载对象,如果找不到,则创建新实例.

在此测试用例中,这将导致针对数据库的10,000次加载尝试,这是昂贵的.

另一方面,当使用带有sqlite的peewee时,insert_many(data)和的组合upsert()会导致单个查询:

INSERT OR REPLACE INTO Test (key) VALUES ('key0'), ('key1'), ...
Run Code Online (Sandbox Code Playgroud)

没有会话状态可以协调,因为peewee是一种与SQLAlchemy非常不同的ORM,并且快速浏览看起来更接近CoreTables

在SQLAlchemy而不是list(map(lambda x: session.merge(Test(x)), data))你可以恢复使用Core:

session.execute(Test.__table__.insert(prefixes=['OR REPLACE']).values(data))
Run Code Online (Sandbox Code Playgroud)

关于这一点的一个主要问题是您必须INSERT手动编写数据库供应商特定的前缀.这也将颠覆会话,因为它没有关于新添加的行的信息或知识.

使用模型对象的批量插入与SQLAlchemy的关系更为复杂.非常简单地使用ORM是易用性和速度之间的权衡:

ORM基本上不适用于高性能批量插入 - 这是SQLAlchemy除了将ORM作为一流组件之外还提供Core的全部原因.