LINUX.ORG.RU

[python] no nosql или redis vs dict :)

 


0

1

Дорогие друзья,

наконец-то появилась свободная минутка пофлудить на лоре чтобы померять оверхед redis. То что что-то не так я сразу понял когда увидел «сырые» цифры производительности.

Итак, был вот такой код:

import redis
r = redis.Redis()

for x in xrange(1263138015,1263152415):
    r.set("uptime_%s" % x, 1000)
    r.set("memory_%s" % x, 1000)
    r.set("cpu_%s" % x, 1000)
    r.set("disk_%s" % x, 1000)
    r.set("disk2_%s" % x, 1000)
    r.set("disk3_%s" % x, 1000)
    r.set("ping_%s" % x, 1000)
    r.set("ping2_%s" % x, 1000)
    r.set("swap_%s" % x, 1000)
    r.set("la_%s" % x, 1000)

Не важно что это и для чего(конечно же велосипед :)), главное что оно на моём мощнейшем железе отрабатывает 20сек. Имхо это очень плохо.

Теперь используем dict для хранения ключей. Результат: 0.16 в тех же условиях. На лицо гейн в 125 раз :). При этом ещё может хранить объекты посложнее строк. В общем, как я и подозревал, расходы на сериализацию, ipc и переключение контекста очень большие.

Цена скорости это отсутствие некоторых фич. Самые важные на мой взгляд это репликация и то что база может быть одна для нескольких процессов, а dict сугубо embeeded.

Ну а сохранять базу в фоновом режиме на диск любой дурак может :). Мой страшненький код который позволяет дампить импровизированную базу в фоновом режиме через pickle: http://dpaste.com/hold/563439/ (вместе с тестами которые он не совсем проходит:)). Там ещё и траблы с английским, но заметил это как всегда после того как запостил, исправлять лень.

Чё сказать-то хотел... Да ничего, просто проснулась мания к графоманству :). Всяко полезнее чем обсуждать MD OS Ice.

★★★★★
Ответ на: комментарий от o

Эээ, тебя интересует memcached vs redis или вместе с питоновской обвязкой? Потом вариаций на тему memcached великое множество, его много раз форкали.

Ну тесты тоже могут быть разные. Одно дело сто тыщ ключей хранить, другое дело миллионы. И, кстати, у питона весьма неплохой dict, он на миллионах ключей не забуксует, проверено моим напарником.

Забыл добавить, redis у меня «изкоробочный» убунтовочный, версии 2.0.0.

true_admin ★★★★★
() автор топика

Ой, я баг нашёл. Похоже что оно при save() сначала делает truncate файла а потом уже получает блокировку на запись O_o. Вот это я лажанул... То-то смотрю на тестах проблемы были.

Люди, не используйте слепой копипаст с лора в своих проектах.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

А всё потому что fcntl не умеет with-statement. Суки! Можно притянуть какой-нить костыль в виде lockfile или locknix, но это как-то чересчур.

true_admin ★★★★★
() автор топика

Сдается мне, что расходы на сериализацию, ipc и переключение контекста это все минор по сравнению с тем, что на каждую операцию идет синхронный запрос по сокету.

Сделайте запросы асинхронными и получится гораздо быстрее.

Kpoxman ★★
()

dict весьма хорош, то да (пока в память влазит). У меня конвертеры огроменных геологических моделей из дурацкого текстового формата заказчика в мой бинарный именно на дикте сделаны, работает быстро.

Сериализация через cPickle (!!!) тоже обычно не тормозит. Ну а всякие сокеты-шмокеты... а чего Вы с-но хотели?;-)

А вот насчет дампа на диск - любой дурак то сделает, но когда кто то пишет а другой читает возникают некузявости (pickle верещит что данных нету). Я спасался файловыми блокировками или несколькими попытками чтения, уж и не вспомню что в итоге в серию пошло, но при небольшой нагрузке работает.

AIv ★★★★★
()

А в примере не лучше ль снятие блокировки в ветку finally загнать? А то не запиклится че нить и привет...

AIv ★★★★★
()
Ответ на: комментарий от Kpoxman

Согласен, ухищрениями типа multiput/multiget и асинхронным режимом можно выжать больше потому что это минимизурет оверхед от пересылки данных. Собстно mset дал результат в 5 секунд, уже неплохо :). Думаю что если запихнуть больше ключей то будет ещё быстрее. Что интересно, больше проца оно жрать не стало, суммарно около 100% CPU жрут python+redis.

import redis
r = redis.Redis()

for x in xrange(1263138015,1263152415):
    data = {"uptime_%s" % x : 1000,
    "memory_%s" % x : 1000,
    "cpu_%s" % x : 1000,
    "disk_%s" % x : 1000,
    "disk2_%s" % x: 1000,
    "disk3_%s" % x: 1000,
    "ping_%s" % x: 1000,
    "ping2_%s" % x: 1000,
    "swap_%s" % x: 1000,
    "la_%s" % x: 1000 }
    r.mset(data)

Да, чтобы быть честным, dict, я тестировал на третьем питоне а редис на втором. Ну для качественного сравнения пойдёт :)

true_admin ★★★★★
() автор топика
Ответ на: комментарий от Kpoxman

Так, сделал всё одним апдейтом, результат 2 секунды в таком тесте:

import redis
r = redis.Redis()
data = {}
for x in xrange(1263138015,1263152415):
    data.update({"uptime_%s" % x : 1000,
        "memory_%s" % x : 1000,
        "cpu_%s" % x : 1000,
        "disk_%s" % x : 1000,
        "disk2_%s" % x: 1000,
        "disk3_%s" % x: 1000,
        "ping_%s" % x: 1000,
        "ping2_%s" % x: 1000,
        "swap_%s" % x: 1000,
        "la_%s" % x: 1000 })
r.mset(data)

true_admin ★★★★★
() автор топика
Ответ на: комментарий от AIv

А в примере не лучше ль снятие блокировки в ветку finally загнать? А то не запиклится че нить и привет...

Ох ну я и дятел! То-то чувствую что-то смутно гложит с этими блокировками... Так и знал что человек самое слабое звено. Короче, перегоняю это всё к with statement к хренам собачьим...

true_admin ★★★★★
() автор топика
Ответ на: комментарий от AIv

пока в память влазит

ну так это и было сравнение in-memory databases :). Хотя вру, redis позволяет работать с виртуальным адресным пространством и сбрасываться на диск.

cPickle

В третьем питоне его нету :(. Зря они его не оставили, ну да ладно. Я бы вообще marshal запихнул, но имеет слишком ограниченные способности к сериализации.

а чего Вы с-но хотели?;-)

Правды :).

Я спасался файловыми блокировками

у мну так же :)

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> у мну так же :)

На самом деле у меня опционально (на откуп юзеру) есть возможность поднимать/опускать файловую блокировку, но по умолчанию этого не делается, просто при ошибке чтения через некоторый таймаут производится повтор (ну так долбится через 1,2,4,8 сек а потом вылетает).

Просто когда только прочитать надо в информативных целях с блокировками лень связываться, да и с правами вроде могут проблемы возникнуть? Под небольшой нагрузкой вполне стабильно работает.

AIv ★★★★★
()
Ответ на: комментарий от AIv

Да ну, какая лень, пара строк. Зато это надёжный подход, мало ли как pickle бошку свернёт от недописанного файла(на самом деле там, похоже, защита есть, но в общем случае лучше на это не надеяться).

да и с правами вроде могут проблемы возникнуть?

Какие могут быть проблемы? Читается тем же процессом что и пишется, не должно быть проблем. Или ты про то что лучше бы предусмотреть этот случай? Да, согласен, лучше предусматреть такой случай в продакшене.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

> Да ну, какая лень, пара строк. Зато это надёжный подход, мало ли как pickle бошку свернёт от недописанного файла(на самом деле там, похоже, защита есть, но в общем случае лучше на это не надеяться).

Файловые блокировки по NFS не работают;-( Пикл ничего особо не сворачивает, выкидывает EOFError (ну или еще чего), там хорошо данные чекаются. У меня головной узел кластера на котором репозиторий с расчетами ожидающими запуска и и 20 счетных узлов которые на головной лезут. Повторы сильно код не усложняют, но как то гарантируют что данные будут прочитаны;-)

С правами - я к тому, что если файл чужой, на него блокировку можно повесить? У меня то процессы и юзеры могут быть разные...

С блокировками я в итоге тоже выкрутился - счетный узел по ssh пускает на головном скрипт и общается с ним гоняя запикленные данные через стандартный ввод/вывод, а уже скрипт работает с блокировками.

AIv ★★★★★
()
Ответ на: комментарий от AIv

Файловые блокировки по NFS не работают

маны пишут что fcntl должен пахать. Что с ним не так?

если файл чужой, на него блокировку можно повесить?

Если сможешь открыть то наверняка можно.

Блин, блокировки это зло. Тут и в питоне накосячили(lockf имеет ключи от flock, например) и сам интерфейс fcntl очень голимый. Ну и ещё для атомарности операций лучше не сразу писать в файл а положить его рядом а потом mv сделать (это нужно на случай если моргнёт свет во время записи). В итоге код усложняется сильно.

true_admin ★★★★★
() автор топика

И что ты этим хотел сказать? 700 сетов в секунду это на каком железе? Как настроен редис? Оплог или раз в какое то время дампится или вообще не персистентный?

А теперь сделай дикт на мультитридинге и сравни.

А еще есть вкусности в ввиде ttl, реплик, мультиекзеки, сейчас добавили сервер сайт скриптинг

anonymous
()
Ответ на: комментарий от true_admin

> маны пишут что fcntl должен пахать. Что с ним не так?

Когда я этот вопрос изучал неск лет назад, все кто работают с БД хором кричали что работает не всех реализациях NFS и результат непредсказуемый, и поэтому в частности mysql через NFS строилось с проблемами. По личному опыту - латентность NFS (время между записью файла с одного хоста и его появлением на другом хосте) при работе под нагрузкой может достигать минуты и более, какие уж тут блокировки...

Если сможешь открыть то наверняка можно.

А если не смогу?;-)

Блин, блокировки это зло.

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

AIv ★★★★★
()
Ответ на: комментарий от AIv

А если не смогу?;-)

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

работает не всех реализациях NFS

Нужно однородное окружение. Линух вроде как умеет.

mysql через NFS строилось с проблемами.

эээ, оно толком не будет работать ибо там блокировка на каждый чих. Конечно будут дикие тормоза. Я проверял на ocfs2.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Не 700 а 7000, но всё равно маловато для localhost-а. Редис настроен стандартно, дампится раз в минуту, но это к делу не относится.

дикт на мультитридинге и сравни.

O_O питон треснет с тредами.

А еще есть вкусности

кто спорит.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

Какие 7000? In [1]: abs(1263138015 - 1263152415)/20. Out[1]: 720.0

На моем _ноуте_ кстати ровно в 2 раза быстрей.

O_O питон треснет с тредами.

Поэтому и не понятно, что ты хочешь доказать. Какую задачу решаем-с?

anonymous
()
Ответ на: комментарий от anonymous

Какие 7000?

это кол-во итераций, а в каждой итерации 9 или 10 ключей добавляется. Неужели ты думаешь что редис настолько тормозной что жмёт меньше тыщи ключей в секунду? :).

Кстати, multi у редис это хрень полная. Оно собрало все недостатки пайплайнов(пока запрос целиком не придёт он не начнёт выполняться) и обычных запросов(большие задержки). В общем, лучше такую вещь реализовать в обёртке чтобы оно multi само в пайплайн преобразовывавло. Типа такого:

with redis:
  redis.set()
  redis.set()
  redis.set()
  ...
Т.е. чтобы вместо кучи запросов оно по выходе из with отправило всё в один присест.

Какую задачу решаем-с?

Пофлудить на лоре. На самом деле я сайтик забабахал небольшой, решил что redis оверкилл и лишняя зависимость. Вот и всё.

true_admin ★★★★★
() автор топика
Ответ на: комментарий от true_admin

ох, что-то я с утра тупанул, да.

anonymous
()
Ответ на: комментарий от true_admin

а redis-benchmark сколько выдаёт?

на моём компе (очень старый athlon какой-то типа pentium 4) твоя прога выполняется 14 секунд.

Стандартный benchmark MSET (10 keys) = 12594.46 requests per second

adzeitor
()
Ответ на: комментарий от true_admin

А можно так:

import redis
r = redis.Redis()
data = {}
for x in xrange(1263138015,1263152415):
    data.update({"uptime": 1000,
        "memory": 1000,
        "cpu" : 1000,
        "disk"  : 1000,
        "disk2": 1000,
        "disk3": 1000,
        "ping": 1000,
        "ping": 1000,
        "swap": 1000,
        "la" : 1000 })
    r.hmset("client:%s" % x, data)

Результаты такие:

adzeitor@r515:~$ time ./lor_redis2.py 

real	0m2.901s
user	0m1.720s
sys	0m0.244s

adzeitor
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.