Опять О Производительности Orm, Или Новый Перспективный Проект — Pony Orm

В его первый В статье на Хабрахабре я писал об одной из главных проблем существующих 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

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.