Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Вновь о продуктивности ORM, либо новейший многообещающий план — Pony ORM

Anna | 16.06.2014 | нет комментариев
В своей первой статье на Програпрогре я писал об одной из основных задач существующих ORM (Object-Relational-Mapping, объектно-реляционных отображений) — их продуктивности. Рассматривая и тестируя две из особенно знаменитых и знаменитых реализаций ORM на python, Django и SQLAlchemy, я пришел к итогу:Применение сильных многофункциональных ORM приводит к дюже приметным потерям продуктивности. В случае применения стремительных движков СУБД, таких как MySQL — продуктивность доступа к данным снижается больше чем в 3-5 раз.

Незадолго со мною связался один из разработчиков нового движка ORM под наименованием pony и попросил поделиться своими соображениями по поводу этого движка. Я подумал, что эти соображения могут быть увлекательны и сообществу Програпрогр.

Короткое резюме

Я опять провел некоторые тесты продуктивности, сходные с описанными в предыдущей статье, и сравнил их итоги с итогами, показанными pony ORM. Для измерения продуктивности в условиях кешированного параметризованного запроса, мне пришлось видоизменить тест приобретения объекта так, Дабы всякий новейший запрос получал объект с новым ключом.

Итог: pony ORM превосходит лучшие итоги django и SQLAlchemy в 1.5-3 раза, даже без кеширования объектов.

Отчего pony оказался отменнее

Сразу сознаюсь: мне не удалось штатными средствами поставить в равные данные pony ORM с django и SQLAlchemy. Случилось это потому, что если в django дозволено кешировать только сконструированные определенные запросы, а в SQLAlchemy — подготовленные параметризованные запросы (при некоторых нетривиальных усилиях), то pony ORM кеширует все, что только дозволено. Просмотр текста pony ORM по диагонали показал: кешируются

— готовый текст запроса SQL определенной СУБД
— конструкция компилированного из текста запроса
— трансляция отношений
— соединения
— создаваемые объекты
— прочитанные и измененные объекты
— запросы для отложенного чтения
— запросы для создания, обновления и удаления объектов
— запросы для поиска объектов
— запросы блокировки
— запросы для навигации по отношениям и их модификации
— может быть еще что-то, что я пропустил

Такое кеширование разрешает исполнять код, тот, что его использует, настоль стремительно, насколько это только вообще допустимо, не озабочиваясь при этом хитроумными выкрутасами по поводу возрастания продуктивности подобно того, тот, что я с отчаяния придумал и описал тут в одной из своих предыдущих статей.

Безусловно, кеширование изредка приносит некоторые неудобства. Скажем, кеширование объектов не разрешает легко сравнить имеющееся в памяти состояние объекта с его образом в таблице — это изредка необходимо для правильной обработки данных, конкурентно обрабатываемых в различных процессах. В одном из релизов pony мне лично хотелось бы увидеть параметры, разрешающие по желанию отключать отдельные виды кеширования для участка кода.

Пожелания

Чего мне пока не хватает в pony ORM, Дабы полновесно сравнить ее с другими ORM?

— миграция данных — абсолютно нужная процедура для крупных планов, использующих ORM
— адаптеры к некоторым знаменитым СУБД, скажем MS SQL
— полное абстрагирование от разновидности СУБД в коде
— доступ к полным метаданным объекта
— кастомизация типов полей
— полная документация

Чего мне не хватает в современных ORM, что дозволено было бы воплотить в pony ORM, пока данный план еще не разросся до состояния стагнации?

— применение смешанных фильтров (обращение к полям и способам объекта единовременно в фильтре)
— вычислимые поля и индексы по ним
— композитные поля (хранимые в нескольких полях таблицы)
— поле вложенного объекта (поле, представляющее собой обыкновенный объект python)
— связывание объектов из различных БД

Ну и безусловно, хотелось бы видеть целостный framework для создания приложений,использующий pony ORM как основу для результативного доступа к БД.

Приложения

Итоги проведенных тестов
>>> 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:testorm@127.0.0.1/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.
Источник: programmingmaster.ru
Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB