Алексей Малашкевич - Автор и разработчик Pony ORM / Pony ORM / Россия, Санкт-Петербург
Александр Козловский - Автор и разработчик Pony ORM / Pony ORM / Россия, Санкт-Петербург
Pony ORM - маппер, который позволяет работать с базой данных с помощью генераторных выражений языка Питон. С помощью такого подхода Pony позволяет формулировать очень компактные и понятные запросы, которые автоматически транслируются в оптимизированный SQL. Pony обладает графическим редактором ER диаграмм - удобным инструментом для создания и редактирования модели данных.
В докладе разработчики Pony ORM расскажут про процесс перевода объектно-ориентированного запроса в запрос на языке SQL, о том какие оптимизации Pony применяет на каждом этапе обработки запроса, какие сложности стояли при разработке высокопроизводительного ORM и как Pony ORM облегчает и ускоряет разработку приложений.
http://www.it-sobytie.ru/events/2040
3. Что такое Pony ORM?
• Объектно-реляционный маппер
• Реализует паттерн Identity Map
• Транслирует генераторы в SQL
• Включает редактор ER диаграмм
• Автоматически оптимизирует SQL запросы
• Поддерживает Python 2.5 – 2.7
• Скоро появится поддержка Python 3
4. Демо (15 минут)
• Редактор ER диаграмм
• Создание БД и заполнение данными
• Работа с данными в интерактивном
режиме
5. Pony ORM:
select(p for p in Person
if p.name.startswith('A') and p.tel is None
or p.dob.year < 2012
)
Способы построения запроса
8. select(p for p in Person
if p.name.startswith('A') and p.tel is None
or p.dob.year < 2012
)
Person.objects.filter(
Q(name__startswith('A'), tel__isnull=True)
| Q(dob__year__lt=2012)
)
session.query(Person).filter(
(Person.name.startswith('A') & (Person.tel == None))
| (extract('year', Person.dob) < 2012)
)
9. Преимущества использования
синтаксиса генераторов:
• Несколько проще для запоминания
• Сложные запросы пишутся с меньшим
количеством «наворотов» (типа “Q”)
• Трансляция запросов в SQL
осуществляется гораздо быстрее!
(можно кешировать результат
трансляции и использовать
объект кода генератора как ключ)
10. Как Pony ORM транслирует
питоновские генераторы в SQL?
11. Трансляция генератора в SQL
1. Декомпиляция байткода,
восстановление абстрактного
синтаксического дерева (AST)
2. Трансляция AST в “абстрактный SQL”
(представленный в виде списков)
3. Трансляция “абстрактного SQL”
в конкретный диалект языка SQL
12. Трансляция генератора в SQL
1. Декомпиляция байткода,
восстановление абстрактного
синтаксического дерева (AST)
2. Трансляция AST в “абстрактный SQL”
(представленный в виде списков)
3. Трансляция “абстрактного SQL”
в конкретный диалект языка SQL
13. Декомпиляция байткода
• Используем паттерн Visitor
• Методы визитора соответствуют
операциям байткода
• Храним фрагменты восстановленного
AST на стеке
• Каждый метод или помещает на
вершину стека новый фрагмент AST,
или комбинирует имеющиеся
фрагменты
14. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
15. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
16. Декомпиляция байткода
(a + b.c) in x.y
> LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
17. Декомпиляция байткода
(a + b.c) in x.y
> LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
Name('a')
18. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
> LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
Name('a')
19. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
> LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
Name('b')
Name('a')
20. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
> LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
Name('b')
Name('a')
21. (a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
> LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
Getattr(Name('b'), 'c')
Name('a')
Декомпиляция байткода
22. (a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
> BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
Getattr(Name('b'), 'c')
Name('a')
Декомпиляция байткода
23. (a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
> BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
Add(Name('a'),
Getattr(Name('b'), 'c'))
Декомпиляция байткода
24. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
> LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
Add(Name('a'),
Getattr(Name('b'), 'c'))
25. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
> LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Стек
Name('x')
Add(Name('a'),
Getattr(Name('b'), 'c'))
26. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
> LOAD_ATTR y
COMPARE_OP in
Стек
Name('x')
Add(Name('a'),
Getattr(Name('b'), 'c'))
27. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
> LOAD_ATTR y
COMPARE_OP in
Стек
Getattr(Name('x'), 'y')
Add(Name('a'),
Getattr(Name('b'), 'c'))
28. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
> COMPARE_OP in
Стек
Getattr(Name('x'), 'y')
Add(Name('a'),
Getattr(Name('b'), 'c'))
29. Декомпиляция байткода
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
> COMPARE_OP in
Стек
Compare('in',
Add(…), Getattr(…))
31. Трансляция генератора в SQL
1. Декомпиляция байткода,
восстановление абстрактного
синтаксического дерева (AST)
2. Трансляция AST в “абстрактный SQL”
(представленный в виде списков)
3. Трансляция “абстрактного SQL”
в конкретный диалект языка SQL
32. Трансляция AST
• Используем паттерн Visitor
• Выполняем обход дерева вглубь
• Обрабатываем каждый узел на выходе
33. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
34. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
37. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
38. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
39. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
40. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
41. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
42. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
43. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
44. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
45. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
46. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
47. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
48. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
49. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
50. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
51. • В какой SQL должно транслироваться?
• Зависит от типов переменных
• “+” может означать сложение чисел или
конкатенацию строк – транслируется в +,
CONCAT или ||
• “in” может транслироваться в подзапрос
или в LIKE
(a + b.c) in x.y
Трансляция AST
52. • (? + “b”.“c”) IN (SELECT …)
• CONCAT(?, “b”.“c”) IN (SELECT …)
• “x”.“y” LIKE ‘%’ || ? || “b”.“c” || ‘%’
(a + b.c) in x.y
Трансляция AST
53. • Трансляция операции, такой как “+”, “in”
или взятие атрибута, зависит от смысла
подчиненных выражений
• Если класс транслятора сам выполняет
весь необходимый анализ, логика
транслятора становится чрезмерно
сложной
• На помощь приходят монады
(a + b.c) in x.y
Трансляция AST
54. • Инкапсулируют в себе результат
анализа AST
• Умеют генерировать результат
трансляции – “абстрактный SQL”
• Умеют комбинировать себя с другими
монадами
• Обрабатывая узел AST, транслятор дает
команду монадам нижележащих узлов
скомбинировать себя и получить новую
монаду
Монады
56. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
57. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
58. (a + b.c) in x.y
Трансляция AST
in
.y
x
+
a .c
b
59. (a + b.c) in x.y
монада
Трансляция AST
in
.y
x
+
a .c
b
60. (a + b.c) in x.y
монада
Трансляция AST
in
.y
x
+
a .c
b
61. (a + b.c) in x.y
монада
Трансляция AST
in
.y
x
+
a .c
b
62. (a + b.c) in x.y
монада
Трансляция AST
монада
in
.y
x
+
a .c
b
63. (a + b.c) in x.y
Трансляция AST
монада
монада
in
.y
x
+
a .c
b
64. (a + b.c) in x.y
монада
Трансляция AST
монада
монада
in
.y
x
+
a .c
b
65. (a + b.c) in x.y
монада монада
Трансляция AST
монада
in
.y
x
+
a .c
b
66. (a + b.c) in x.y
монада монада
Трансляция AST
монада
монада
in
.y
x
+
a .c
b
67. (a + b.c) in x.y
монада монада
монада
Трансляция AST
монада
in
.y
x
+
a .c
b
68. (a + b.c) in x.y
монада монада
Трансляция AST
монада
монада
in
.y
x
+
a .c
b
69. (a + b.c) in x.y
монада монада монада
Трансляция AST
монада
монада
in
.y
x
+
a .c
b
70. (a + b.c) in x.y
монада монада монада
Трансляция AST
монада
монада
in
.y
x
+
a .c
b
71. (a + b.c) in x.y
монада монада монада
монада
Трансляция AST
монада
монада
in
.y
x
+
a .c
b
72. (a + b.c) in x.y
монада монада монада
монада
Трансляция AST
монада
монада
in
.y
x
+
a .c
b
73. (a + b.c) in x.y
монада монада монада
монада
монада
Трансляция AST
монада
монада
in
.y
x
+
a .c
b
74. Aбстрактный SQL
(a + b.c) in x.y
['LIKE', ['COLUMN', 't1', 'y'],
['CONCAT',
['VALUE', '%'], ['PARAM', 'p1'],
['COLUMN', 't2', 'c'], ['VALUE', '%']
]
]
- Позволяет абстрагироваться от специфики
конкретного диалекта
75. Трансляция генератора в SQL
1. Декомпиляция байткода,
восстановление абстрактного
синтаксического дерева (AST)
2. Трансляция AST в “абстрактный SQL”
(представленный в виде списков)
3. Трансляция “абстрактного SQL”
в конкретный диалект языка SQL
90. Решение проблемы N+1 query
orders = select(o for o in Order
if o.price > 1000)
for o in orders:
print o.total_price, o.customer.name
SELECT o.id, o.total_price, o.customer_id, …
FROM “Order” o
WHERE o.price > 1000
91. Решение проблемы N+1 query
Order 1
Order 3
Order 4
Order 7
Order 9
Customer 1
Customer 4
Customer 7
92. Решение проблемы N+1 query
orders = select(o for o in Order
if o.price > 1000)
for o in orders:
print o.total_price, o.customer.name
SELECT c.id, c.name, …
FROM “Customer” c
WHERE c.id IN (?, ?, ?)
93. Решение проблемы N+1 query
Order 1
Order 3
Order 4
Order 7
Order 9
Customer 1
Customer 4
Customer 7
98. db_session
• Pony автоматически отслеживает какие
объекты были изменены
• В момент выхода из db_session, если не
возникло исключений, происходит
сохранение всех измененных объектов
в рамках единой транзакции
• Пользователь не обязан сам вызывать
save для сохранения объектов
99. Оптимистические транзакции
• Pony отслеживает какие атрибуты
пользователь читал а какие изменял
• Если объект не был заблокирован в БД
в момент выборки, при сохранении
объекта Pony автоматически добавляет
проверки для оптимистических
блокировок
102. Особенности Pony ORM
• Использование генераторов и лямбд
для формулирования запросов
• Автоматическая оптимизация запросов
• Identity Map
• Решение проблемы N+1
• Оптимистические транзакции
• Графический редактор ER-диаграмм
• В перспективе – поддержка NoSQL