sql >> Database teknologi >  >> RDS >> Mysql

Hvorfor er indlæsning af SQLAlchemy-objekter via ORM 5-8x langsommere end rækker via en rå MySQLdb-markør?

Her er SQLAlchemy-versionen af ​​dit MySQL-script, der udfører på fire sekunder, sammenlignet med tre for MySQLdb:

from sqlalchemy import Integer, Column, create_engine, MetaData, Table
import datetime

metadata = MetaData()

foo = Table(
    'foo', metadata,
    Column('id', Integer, primary_key=True),
    Column('a', Integer(), nullable=False),
    Column('b', Integer(), nullable=False),
    Column('c', Integer(), nullable=False),
)


class Foo(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

engine = create_engine('mysql+mysqldb://scott:[email protected]/test', echo=True)
start = datetime.datetime.now()

with engine.connect() as conn:
    foos = [
        Foo(row['a'], row['b'], row['c'])
        for row in
        conn.execute(foo.select().limit(1000000)).fetchall()
    ]


print "total time: ", datetime.datetime.now() - start

runtime:

total time:  0:00:04.706010

Her er et script, der bruger ORM til at indlæse objektrækker fuldt ud; ved at undgå oprettelsen af ​​en fast liste med alle 1M objekter på én gang ved hjælp af udbytte pr., kører denne på 13 sekunder med SQLAlchemy master (18 sekunder med rel 0.9):

import time
from sqlalchemy import Integer, Column, create_engine, Table
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Foo(Base):
    __table__ = Table(
        'foo', Base.metadata,
        Column('id', Integer, primary_key=True),
        Column('a', Integer(), nullable=False),
        Column('b', Integer(), nullable=False),
        Column('c', Integer(), nullable=False),
    )


engine = create_engine('mysql+mysqldb://scott:[email protected]/test', echo=True)

sess = Session(engine)

now = time.time()

# avoid using all() so that we don't have the overhead of building
# a large list of full objects in memory
for obj in sess.query(Foo).yield_per(100).limit(1000000):
    pass

print("Total time: %d" % (time.time() - now))

Vi kan derefter opdele forskellen mellem disse to tilgange og kun indlæse individuelle kolonner med ORM:

for obj in sess.query(Foo.id, Foo.a, Foo.b, Foo.c).yield_per(100).limit(1000000):
    pass

Ovenstående kører igen om 4 sekunder .

Sammenligningen af ​​SQLAlchemy Core er den mere passende sammenligning med en rå MySQLdb-markør. Hvis du bruger ORM, men forespørger efter individuelle kolonner, er det omkring fire sekunder i de seneste versioner.

På ORM-niveau skyldes hastighedsproblemerne, at det er langsomt at oprette objekter i Python, og SQLAlchemy ORM anvender en stor mængde bogholderi til disse objekter, når den henter dem, hvilket er nødvendigt for at det kan opfylde sin brugskontrakt, inklusive enhed af arbejde, identitetskort, ivrig lastning, samlinger osv.

For at fremskynde forespørgslen dramatisk skal du hente individuelle kolonner i stedet for hele objekter. Se teknikkerne påhttp://docs .sqlalchemy.org/da/latest/faq/performance.html#result-fetching-slowness-orm som beskriver dette.

Til din sammenligning med PeeWee er PW et meget enklere system med meget færre funktioner, herunder at det ikke gør noget med identitetskort. Selv med PeeWee, en så simpel ORM, som det er muligt, tager det stadig 15 sekunder , hvilket er bevis på, at cPython er virkelig virkelig langsom sammenlignet med den rå MySQLdb-hentning, som er i lige C.

Til sammenligning med Java er Java VM meget meget hurtigere end cPython . Dvale er latterligt kompliceret, men alligevel er Java VM ekstremt hurtig på grund af JIT, og selv al den kompleksitet ender med at køre hurtigere. Hvis du vil sammenligne Python med Java, skal du bruge Pypy.



  1. MySQL regex-forespørgsel ufølsom

  2. Sådan importeres en JSON-fil til en SQL Server-tabel

  3. UNION resultaterne af flere lagrede procedurer

  4. Går gennem mysql_fetch_array i PHP