Pull to refresh

Comments 19

Не то…
Покажите аналог кода, приведенного в посте, для Redis.
Готов поспорить, получится сильно уродливей…
Упс, не весь комментарий отправился
Я хотел сказать что Redis в бекенды было бы неплохо добавить.
Redis добавлю, но замечу, что серверные хранилища сильно медленнее встроенных (типа tc).
явно проще не ограничивать себя в горизонтальной масштабируемости изначально
Хмм… конечно, проще завести полезные стереотипы и следовать им. Но в общении не забывайте, что это стереотипы. Например, зачем вам ГМ кеша?
github.com/iamteem/redisco — аналогичная штука для Redis, было бы интересно сравнить производительность с odbm, особенно более сложных выборок, а не просто get/set
В pymorphy тоже делал похожие обертки над tc, cdb, bsddb и sqlite. Я тогда там не стал выдумывать свой api, а унаследовался от DbfilenameShelf из стандартной библиотеки (решение и решение). Плюс запись была почти не нужна, интересовало главным образом чтение.

Выводы:

1. marshal быстрый, но не переносимый. JSON (с каким-нибудь C-расширением вроде simplejson) примерно такой же быстрый (точных цифр не помню, то-ли быстрее, то-ли медленнее немного), но его можно читать где угодно, и формат стабильный. Произвольные питоньи объекты он, впрочем, дампить не умеет. Может, это и хорошо.

2. Из «тупых» key-value хранилищ самое быстрое — cdb (и памяти ест немного меньше tc). Меньше всего оперативной памяти ест bsddb (причем значительно меньше).

3. Sqlite нужен. Он медленнее в несколько раз своих аналогов, но там дико стабильный формат хранения данных, и sqlite-базу можно просто скопировать и перенести куда угодно, все будет работать. Sqlite есть, к тому же, в стандартной библиотеке (начиная с 2.5), так что для использования не нужно ничего ставить и компилировать. Идеальный вариант, когда нужна переносимость и легкость установки. Только не забыть, что там есть проблемы с многопоточностью.
1. Json сильно медленнее marshal, не уверен что имеет смысл привязываться к переносимости вообще. Привязываться к переносимости при хранении данных. Хранить нужно в том, что быстрее (marshal), а нужно перенести — дампим в переносимый формат. А так мы скоро придём к идее, что разные версии бд не совместимы и начнём как-то её обходить.

2. Вроже ж, cdb не перезаписываемая? Записал раз, а потом только читать или целиком переписывать бд. Или что-то изменилось?

3. Sqlite не понял зачем? Опять же, п.1. Какой смысл пользоваться key-value хранилищем, если оно будет медленным? Переносимость нужна? Тогда вон fsdbm, она pure-python :)
json сильно медленнее marshal без С-расширений, с ними у меня в тестах не медленнее было.

cdb — хм, да, точно. Мне-то запись не нужна была, внимания поэтому на это не обратил)

Sqlite — затем, чтобы можно было попробовать api и решить, нужно ли это все, без всяких хлопот. Sqlite не медленный.

Какой смысл пользоваться key-value хранилищем, если оно будет медленным?


А зачем пользоваться обертками над key/value, если они будут медленными? А простые обертки будет медленными на всем, кроме получения по ключу и записи по ключу. Я ведь правильно понимаю, что User.find(filter=lambda ...) выбирает все записи и фильтрует их на питоне, что использовать этот API на реальных данных смысла будет мало, и тот же sqlite с индексом «уделает» любой такой запрос? Альтернатива — строить свои индексы в k/v хранилищах. Тогда нужна их поддержка в обертке какая-то. Или авто-использование каких-то средств БД (индексы в sqlite, поиск по префиксу в tree db у токио, последовательный перебор в деревьях). Не представляю, как можно реализовать все это, сохранив API с лямбдой. Хотя в монго вроде умеют индекс строить при первом запросе, не разбирался детально. Тут можно извернуться, в принципе: исходный код лямбды через интроспекцию получить, где-то список индексов хранить и обновлять их при сохранении, при совпадении фильтров делать выборку по индексу. Жесь какая-то) Или что-то такое: github.com/dcramer/django-indexer

Разные БД имеют разные возможности. И если цель — скорость, то, скорее всего, все равно же придется использовать хитрые возможности конкретной БД. А если уж завязываться на конкретную БД, то можно и драйвер использовать без обертки, у них обычно api почти такой же простой.

Т.е. теперь не понял, зачем все это: думал, все это для удобства делается, для простоты, для экспериментов, а не для скорости. Если для скорости, то, думаю, может иметь смысл выкинуть метод find: упростится код и не будет соблазна этот метод вызывать.
import cPickle as pickle
import simplejson as json
import marshal
from time import time

data = range(100)

for dumper in [json, pickle, marshal]:
    start = time()
    for i in xrange(1000):
        dumper.dumps(data)
    print dumper.__name__, time() - start


simplejson 0.0725080966949
cPickle 0.0695569515228
marshal 0.00670003890991

Смотрите, у меня на более сложных объектах сериализации cPickle показал наихудший результат.

Ваш вариант:
simplejson 0.0211260318756
cPickle 0.0203590393066
marshal 0.00218415260315

data = [{'var1': 100, 'other_variable': 'empty text', 'index': i} for i in range(100)]

simplejson 0.129426956177
cPickle 0.256888866425
marshal 0.0233998298645

Интересно что marshal вообще практически не замедлился.
Пардон, marshal-таки значительно замедлился, проглядел нолик.
Относительно cPickle то же самое. А json в любом случае не походит, ибо коверкает данные:

>>> j = lambda x: json.loads(json.dumps(x))
>>> j((1, 2))
[1, 2]
>>> j('ыыы')
u'\u044b\u044b\u044b'
По sqlite понял, добавлю.

> А зачем пользоваться обертками над key/value, если они будут медленными? А простые обертки будет медленными на всем, кроме получения по ключу и записи по ключу.

Ну, вот в случаях, когда этого достаточно и пользоваться. И на практике таких случаев большая часть. Как только мышление настроится, чтобы их замечать :)

Лямбды — да. Они по большей части для администрирования бд, не для пользовательских запросов.
>Т.е. теперь не понял, зачем все это: думал, все это для удобства делается, для простоты, для экспериментов, а не для скорости. Если для скорости, то, думаю, может иметь смысл выкинуть метод find: упростится код и не будет соблазна этот метод вызывать.

Для скорости. А find() для эксперементов :) Хотя на табличках <= 1к записей, уверен, он порвёт sqlite с индексами. А если условие выборки будет посложнее, то и на больших табличках. В лямбдах можно очень хитрое условие обработать, а попробуй переложить это на sql, очень жёстко получится.
Трансляцию лямбды в SQL успешно делает GeniuSQL, но это единичный случай, тк странно и сложно.
А теперь осталось найти готовые решения для обеспечения многопроцессорного доступа к данным с передачей процессам событий изменения…

Задачи вида — 2,3,… процесса работают с объемной общей базой и нужны быстрые средства синхронизации изменений данных между ними, хотя бы с рабочим пулом/кешем, в идеале без лага.
Вариант с перечитыванием в цикле данных в ожидании изменений точно не катит, с чем сейчас работаю — собственная обертка над методами работы с БД, но пока это key-value база — все хорошо, но чуть больше захотеть и начинаются сложности.
Sign up to leave a comment.

Articles