В его первый В статье на Хабрахабре я писал об одной из главных проблем существующих ORM (Object-Relational-Mapping, объектно-реляционные отображения) — их производительности.
После рассмотрения и тестирования двух самых популярных и известных реализаций ORM на Python, Django и SQLAlchemy, я пришел к следующему выводу: Использование мощных универсальных ORM приводит к очень заметным потерям производительности.
При использовании быстрых механизмов СУБД, таких как MySQL, производительность доступа к данным снижается более чем 3-5 раз .
Недавно со мной связался один из разработчиков нового движка ORM под названием Pony и попросил поделиться своими мыслями об этом движке.
Я подумал, что эти соображения могут быть интересны сообществу Хабрахабра.
Краткое содержание
Я снова провел несколько тестов производительности, аналогичных описанным в предыдущей статье, и сравнил их результаты с результатами, показанными Pony ORM. Чтобы измерить производительность кэшированного параметризованного запроса, мне пришлось изменить тест извлечения объекта так, чтобы каждый новый запрос получал объект с новым ключом.Результат: Pony ORM превосходит лучшие результаты django и SQLAlchemy в 1,5-3 раза, даже без кэширования объектов.
Почему пони лучше?
Признаюсь сразу: мне не удалось поставить Pony ORM на равные с django и SQLAlchemy стандартными средствами.Это произошло потому, что если в django можно кэшировать только построенные конкретные запросы, а в SQLAlchemy можно кэшировать подготовленные параметризованные запросы (с некоторыми нетривиальными усилиями), то пони ORM кэширует все, что может .
Просмотр текста ORM пони по диагонали показал: кэшировано — готовый текст SQL-запроса для конкретной СУБД — структура запроса, составленная из текста — трансляция отношений - связи - созданные объекты - читать и изменять объекты - запросы на ленивое чтение — запросы на создание, обновление и удаление объектов — запросы для поиска объектов - блокировка запросов — запросы для навигации по связям и их изменения - возможно есть что-то еще, что я пропустил Такое кэширование позволяет коду, который его использует, выполняться максимально быстро, не беспокоясь о причудливых трюках с производительностью, подобных тому, который я придумал в отчаянии и описал здесь, в одной из моих предыдущих статей.
Конечно, кэширование иногда приносит некоторые неудобства .
Например, кэширование объектов не позволяет легко сравнить состояние объекта в памяти с его изображением в таблице — иногда это необходимо для корректной обработки данных, обрабатывающихся одновременно в разных процессах.
В одном из выпусков пони лично мне бы хотелось увидеть параметры, позволяющие опционально отключить определенные виды кэширования для участка кода .
Пожелания
Чего мне еще не хватает в Pony ORM, чтобы полноценно сравнивать его с другими ORM? — миграция данных — абсолютно необходимая процедура для крупных проектов, использующих ORM — адаптеры для некоторых популярных СУБД, например MS SQL — полная абстракция от типа СУБД в коде — доступ ко всем метаданным объекта — настройка типов полей - полная документация Чего мне не хватает в современных ОРМ, что можно было бы реализовать в пони-ОРМ, пока этот проект еще не дорос до состояния стагнации? — использование смешанных фильтров (одновременный доступ к полям и методам объекта в фильтре) — вычисляемые поля и индексы по ним - составные поля (хранятся в нескольких полях таблицы) - поле вложенного объекта (поле, которое является обычным объектом Python) — связывание объектов из разных баз данных И конечно, хотелось бы видеть целостный фреймворк для создания приложений, использующий Pony ORM в качестве основы для эффективного доступа к базе данных.
обновление 3 августа 2013 г.
Если вы хотите получить ответы на свои вопросы от авторов Pony ORM, вы можете обратиться к ним по адресам: [email protected] и [email protected]. Приглашения приветствуются.
Приложения
Результаты теста
>>> import test_native >>> test_native.test_native() get row by key: native req/seq: 3050.80815908 req time (ms): 0.327782 get value by key: native req/seq: 4956.05711955 req time (ms): 0.2017733
>>> import test_django
>>> test_django.test_django()
get object by key: django req/seq: 587.58369836 req time (ms): 1.7018852
get value by key: django req/seq: 779.4622303 req time (ms): 1.2829358
>>> import test_alchemy
>>> test_alchemy.test_alchemy()
get object by key: alchemy req/seq: 317.002465265 req time (ms): 3.1545496
get value by key: alchemy req/seq: 1827.75593609 req time (ms): 0.547119
>>> import test_pony
>>> test_pony.test_pony()
get object by key: pony req/seq: 1571.18299553 req time (ms): 0.6364631
get value by key: pony req/seq: 2916.85249448 req time (ms): 0.3428353
Тестовый код
test_native.py import datetime
def test_native():
from django.db import connection, transaction
cursor = connection.cursor()
t1 = datetime.datetime.now()
for i in range(10000):
cursor.execute("select username,first_name,last_name,email,password,is_staff,is_active,is_superuser,last_login,date_joined from auth_user where id=%s limit 1" % (i+1))
f = cursor.fetchone()
u = f[0]
t2 = datetime.datetime.now()
print "get row by key: native req/seq:",10000/(t2-t1).
total_seconds(),'req time (ms):',(t2-t1).
total_seconds()/10.
t1 = datetime.datetime.now()
for i in range(10000):
cursor.execute("select username from auth_user where id=%s limit 1" % (i+1))
f = cursor.fetchone()
u = f[0][0]
t2 = datetime.datetime.now()
print "get value by key: native req/seq:",10000/(t2-t1).
total_seconds(),'req time (ms):',(t2-t1).
total_seconds()/10.
test_django.py
import datetime
from django.contrib.auth.models import User
def test_django():
t1 = datetime.datetime.now()
q = User.objects.all()
for i in range(10000):
u = q.get(id=i+1)
t2 = datetime.datetime.now()
print "get object by key: django req/seq:",10000/(t2-t1).
total_seconds(),'req time (ms):',(t2-t1).
total_seconds()/10.
t1 = datetime.datetime.now()
q = User.objects.all().
values('username')
for i in range(10000):
u = q.get(id=i+1)['username']
t2 = datetime.datetime.now()
print "get value by key: django req/seq:",10000/(t2-t1).
total_seconds(),'req time (ms):',(t2-t1).
total_seconds()/10.
test_alchemy.py
import datetime
from sqlalchemy import *
from sqlalchemy.orm.session import Session as ASession
from sqlalchemy.ext.declarative import declarative_base
query_cache = {}
engine = create_engine(' mysql://testorm:[email protected]/testorm ', execution_options={'compiled_cache':query_cache})
session = ASession(bind=engine)
Base = declarative_base(engine)
class AUser(Base):
__tablename__ = 'auth_user'
id = Column(Integer, primary_key=True)
username = Column(String(50))
password = Column(String(128))
last_login = Column(DateTime())
first_name = Column(String(30))
last_name = Column(String(30))
email = Column(String(30))
is_staff = Column(Boolean())
is_active = Column(Boolean())
date_joined = Column(DateTime())
def test_alchemy():
t1 = datetime.datetime.now()
for i in range(10000):
u = session.query(AUser).
filter(AUser.id==i+1)[0]
t2 = datetime.datetime.now()
print "get object by key: alchemy req/seq:",10000/(t2-t1).
total_seconds(),'req time (ms):',(t2-t1).
total_seconds()/10.
table = AUser.__table__
sel = select(['username'],from_obj=table,limit=1,whereclause=table.c.id==bindparam('ident'))
t1 = datetime.datetime.now()
for i in range(10000):
u = sel.execute(ident=i+1).
first()['username']
t2 = datetime.datetime.now()
print "get value by key: alchemy req/seq:",10000/(t2-t1).
total_seconds(),'req time (ms):',(t2-t1).
total_seconds()/10.
test_pony.py
import datetime
from datetime import date, time
from pony import *
from pony.orm import *
db = Database('mysql', db='testorm', user='testorm', passwd='testorm')
class PUser(db.Entity):
_table_ = 'auth_user'
id = PrimaryKey(int, auto=True)
username = Required(str)
password = Optional(str)
last_login = Required(date)
first_name = Optional(str)
last_name = Optional(str)
email = Optional(str)
is_staff = Optional(bool)
is_active = Optional(bool)
date_joined = Optional(date)
db.generate_mapping(create_tables=False)
def test_pony():
t1 = datetime.datetime.now()
with db_session:
for i in range(10000):
u = select(u for u in PUser if u.id==i+1)[:1][0]
t2 = datetime.datetime.now()
print "get object by key: pony req/seq:",10000/(t2-t1).
total_seconds(),'req time (ms):',(t2-t1).
total_seconds()/10.
t1 = datetime.datetime.now()
with db_session:
for i in range(10000):
u = select(u.username for u in PUser if u.id==i+1)[:1][0]
t2 = datetime.datetime.now()
print "get value by key: pony req/seq:",10000/(t2-t1).
total_seconds(),'req time (ms):',(t2-t1).
total_seconds()/10.
Теги: #python #django #orm #производительность #Высокая производительность #python #django
-
Идея. Бесплатный Государственный Хостинг
19 Oct, 24 -
Ошибки Начинающих Менеджеров По Продажам
19 Oct, 24 -
Игрушка Для Кошек С Питанием От Usb
19 Oct, 24 -
Usb-Homoludens
19 Oct, 24