Горизонтальное масштабирование PostgreSQL с помощью PL/Proxy.

    Очень тяжело начать писать статью. Т.е очень тяжело придумать вступительное слово. Хочется рассказать обо всём и сразу :) Но нет. Будем последовательны.
    Начну с того что совсем недавно проходил Highload++ 2008 на котором мне удалось побывать.
    Скажу сразу — мероприятие было проведено по высшему клаcсу, докладов было много и все были очень интересными.
    Одной из самых запомнившихся презентаций была лекция Аско Ойя об инфраструктуре серверов баз данных в Skype. Лекция в большей степени касалась различных средств с помощью которых достигается такая производительность серверов.
    По словам Аско, база данных Skype выдержит даже если все жители Земли захотят подключится к скайп в один момент.

    Приехав домой очень захотелось это всё попробовать в живую. О чём я сейчас и расскажу. Сразу оговорюсь — структура базы данных для теста, взята из примера на сайте самих разработчиков и естественно не имеет ничего общего с реальной загрузкой.
    В статье будет описано что распределением нагрузки надо заниматься после того как уже припекло и база падает, но это не совсем так. С помощью данной статьи я как раз хочу подготовить начинающих и не опытных разработчиков и заодно заставить их задуматься о том, что предусматривать возможность распределения нагрузки между серверами надо ещё при проектировании системы. И это не будет считаться той самой «преждевременной оптимизацией» о которой так много пишут и которой так боятся.

    UPD: Как правильно заметил хабраюзер descentspb в статье присутствует досаднейшая ошибка. В следствие своей невнимательности я подумал что PgBouncer надо устанавливать между прокси и клиентом. Но, как оказалось, та проблема которую я решал с помощью PgBouncer не решится если установить его именно так. Правильнее надо устанавливать боунсер между нодами и прокси. Мало того, именно так и рекомендуется делать в оффициальном мануале на сайте PL/Proxy.
    В любом случае использование PgBouncer так как указано на моей схеме также даст прирост производительности. (Разгрузит Proxy).




    1.Кто виноват?


    Итак, если вы разработчик и создаете что-то большое и достаточно высоконагруженное вы рано или поздно столкнетесь с тем что база данных не выдерживают нагрузку. Запросов приходит много и железо просто не в состоянии справится с ними.
    Методы решения этой проблемы уже не раз обсуждались, я лишь приведу список того что мне кажется наиболее действенным.

    — Оптимизируем код.
    — Наращиваем мощность сервера.
    — Кеширование (ищем по тегам статьи о memcache).
    — Распределяем нагрузку между серверами.

    Остановимся на последнем пункте.

    2.Что делать?


    Итак код оптимизирован, сервера круче некуда вся база лежит в кеше и тем не менее падает от одного запроса. Пришло время заняться горизонтальным масштабированием.
    Ах да, я до сих пор не упомянул что статья о PostgreSQL. А вы что до сих пор пользуетесь MySQL? Тогда мы идём к вам :)
    По моему скромному мнению если проект действительно серёзный то и база должна быть немного по серьёзней чем MySQL. Тем более что для Postgres'a существуют такие замечательные средства для масштабирования. (Может быть и для MySQL есть? Жду ответную статью :) ).

    3. А с чем это едят?


    PL/Proxy представляет из себя язык для удалённого вызова функций на серверах баз данных PostgreSQL, а также для партицирования данных.
    Схема работы показана на картинке. О PgBouncer я расскажу ниже.
    image

    Обычно ваше приложения просто делает запрос к базе данных. В нашем случае приложение тоже делает обычный запрос к базе данных. Только вызывает оно не чистый SQL-код, а заранее написанную функцию.
    Далее база данных определяет на каком из нодов расположены требуемые данные.
    И перенаправляет запрос на нужный сервер.
    Запрос выполняется и возвращается на главный сервер после чего данные возвращаются в приложение.

    Все вроде бы хорошо но при большом количестве запросов, PL/Proxy создаёт большое
    количество соединений к нодам, а это создаёт новый процесс Postgres (fork) что не очень хорошо влияет на производительность. Что бы решить эту проблему и нужен PgBouncer.
    PgBouncer является… мм… как бы это сказать так что бы не налажать… Мультиплексором соединений. Он выглядит как обычный процесс Postgres, но внутри он управляет очередями запросов что позволяет в разы ускорить работу сервера. Из тысяч запросов поступивших к PgBouncer до базы данных дойдет всего несколько десятков.
    Что бы оценить бонус от использования этого замечательного средства достаточно взглянуть на график загрузки сервера базы данных на двух сайтах до и после включения PgBouncer. Картинка взята из презентации Николая Самохвалова «Производительность Postgres».
    image

    4.Дайте же и мне этих мягких французских булок


    4.1.Установка PgBouncer

    Процесс установки нисколько не оригинален:
    Качаем пакет (на момент написания статьи последняя версия была 1.2.3)
    pgfoundry.org/frs/?group_id=1000258
    Распаковываем:
    #tar -xzvf pgbouncer-1.2.3.tgz
    Компилим и ставим:
    #cd pgbouncer-1.2.3
    #./configure
    #make
    #make install


    Создаём конфигурационный файл:
    /etc/pgbouncer/pgbouncer.ini

    [databases]
    testdb = host=localhost port=5432 dbname=testdb

    [pgbouncer]
    listen_port = 6543
    listen_addr = 127.0.0.1
    auth_type = md5
    auth_file = users.txt
    logfile = /var/log/pgbouncer.log
    pidfile = /var/run/pgbouncer/pgbouncer.pid
    pool_mode = statement #Если вы не планируете использовать PL/Proxy эту строчку указывать не надо
    admin_users = root

    Создаём файл с аутентификацией.
    /etc/pgbouncer/users.txt
    "testdb_user" "testdb_user_password"

    Запускаем:
    /usr/local/bin/pgbouncer -d /etc/pgbouncer/pgbouncer.ini -u postgres
    Ключ -d указывает на то что надо запускать в режиме демона, а ключ -u указывает от чьего имени надо запускать процесс pgbouncer.

    Для пользователей gentoo приятный сюрприз в виде стартового скрипта:
    /etc/init.d/pgbouncer
    #!/sbin/runscript

    depend() {
    need postgresql
    use pgsql
    }
    start() {
    ebegin "Starting Pgbouncer"
    start-stop-daemon --start --background --exec /usr/local/bin/pgbouncer --chdir /etc/pgbouncer/ -- -d pgbouncer.ini -u postgres
    eend $? "Failed to start Pgbouncer"
    }
    stop() {
    ebegin "Stopping Pgbouncer"
    start-stop-daemon --pidfile /var/run/pgbouncer/pgbouncer.pid --stop
    eend $? "Failed to stop Pgbouncer"
    }


    Теперь в качестве DSN в своём приложении надо будет поменять только порт на который подключаться к базе данных с 5432 на 6543 и начать сравнивать загруженность сервера до и после.

    4.2 Установка Pl/Proxy

    Для проведения этого эксперимента нам понадобится 3 сервера Postgres.
    Один из них, назовём его proxy, будет проксировать запросы на два других.
    Назовём их node1 и node2.
    Для корректной работы pl/proxy рекомендуется использовать количество нод равное степеням двойки.
    Предполагаю что сам Postgres у вас уже установлен.
    Устанавливаем PL/Proxy на сервере proxy.
    Качаем свежую версию pl/proxy: pgfoundry.org/frs/?group_id=1000207
    Как обычно:
    #./configure
    #make
    #make install
    Вот тут надо бы перезапустить сам Postgres.
    А теперь начинается самое интересное.

    Для теста создадим на каждой из нод новую базу данных proxytest:
    CREATE DATABASE proxytest
         WITH OWNER = postgres
           ENCODING = 'UTF8';


    И а внутри этой базы создадим Schema c названием plproxy. В официальной инструкции этого пункта не было но у меня почему-то все вызываемые функции пытались вызываться именно так: plproxy.functioname().
    CREATE SCHEMA plproxy
       AUTHORIZATION postgres;
      GRANT ALL ON SCHEMA plproxy TO postgres;
      GRANT ALL ON SCHEMA plproxy TO public;



    И добавим в неё одну табличку:
    CREATE TABLE plproxy.users
      (
       user_id bigint NOT NULL DEFAULT nextval('plproxy.user_id_seq'::regclass),
       username character varying(255),
       email character varying(255),
       CONSTRAINT users_pkey PRIMARY KEY (user_id)
      )
      WITH (OIDS=FALSE);
      ALTER TABLE plproxy.users OWNER TO postgres;



    Теперь создадим функцию для добавления данных в эти таблицы:
    CREATE OR REPLACE FUNCTION plproxy.insert_user(i_username text, i_emailaddress   text)
      RETURNS integer AS
      $BODY$
      INSERT INTO plproxy.users (username, email) VALUES ($1,$2);
         SELECT 1;
      $BODY$
       LANGUAGE 'sql' VOLATILE;
      ALTER FUNCTION plproxy.insert_user(text, text) OWNER TO postgres;



    С нодами покончено. Приступим к настройке сервера.
    Как и на всех нодах, на главном сервере (proxy) должна присутствовать база данных:
    CREATE DATABASE proxytest
         WITH OWNER = postgres
           ENCODING = 'UTF8';



    И соответсвующая schema:
    CREATE SCHEMA plproxy
       AUTHORIZATION postgres;
      GRANT ALL ON SCHEMA plproxy TO postgres;
      GRANT ALL ON SCHEMA plproxy TO public;



    Теперь надо укзать серверу что эта база данных управляется с помощьюpl/proxy:
    CREATE OR REPLACE FUNCTION plproxy.plproxy_call_handler()
       RETURNS language_handler AS
      '$libdir/plproxy', 'plproxy_call_handler'
       LANGUAGE 'c' VOLATILE
      COST 1;
      ALTER FUNCTION plproxy.plproxy_call_handler() OWNER TO postgres;
      -- language
      CREATE LANGUAGE plproxy HANDLER plproxy_call_handler;



    Также, для того что бы сервер знал где и какие ноды него есть надо создать 3 сервисные функции которые pl/proxy будет использовать в своей работе:
    CREATE OR REPLACE FUNCTION plproxy.get_cluster_config(IN cluster_name text,   OUT "key" text, OUT val text)
       RETURNS SETOF record AS
      $BODY$
      BEGIN
        -- lets use same config for all clusters
        key := 'connection_lifetime';
        val := 30*60; -- 30m
        RETURN NEXT;
        RETURN;
      END;
      $BODY$
       LANGUAGE 'plpgsql' VOLATILE
       COST 100
       ROWS 1000;
      ALTER FUNCTION plproxy.get_cluster_config(text) OWNER TO postgres;  



    Важная функция код которой надо будет подправить. В ней надо будет указать DSN нод:
    REATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text)
     RETURNS SETOF text AS
    $BODY$
    BEGIN
      IF cluster_name = 'clustertest' THEN
        RETURN NEXT 'dbname=proxytest host=node1 user=postgres';
        RETURN NEXT 'dbname=proxytest host=node2 user=postgres';
        RETURN;
      END IF;
      RAISE EXCEPTION 'Unknown cluster';
    END;
    $BODY$
     LANGUAGE 'plpgsql' VOLATILE
     COST 100
     ROWS 1000;
    ALTER FUNCTION plproxy.get_cluster_partitions(text) OWNER TO postgres;



    И последняя:
    CREATE OR REPLACE FUNCTION plproxy.get_cluster_version(cluster_name text)
     RETURNS integer AS
    $BODY$
    BEGIN
      IF cluster_name = 'clustertest' THEN
        RETURN 1;
      END IF;
      RAISE EXCEPTION 'Unknown cluster';
    END;
    $BODY$
     LANGUAGE 'plpgsql' VOLATILE
     COST 100;
    ALTER FUNCTION plproxy.get_cluster_version(text) OWNER TO postgres;



    Ну и собственно самая главная функция которая будет вызываться уже непосредственно в приложении:
    CREATE OR REPLACE FUNCTION plproxy.insert_user(i_username text, i_emailaddress text)
     RETURNS integer AS
    $BODY$
     CLUSTER 'clustertest';
     RUN ON hashtext(i_username);
    $BODY$
     LANGUAGE 'plproxy' VOLATILE
     COST 100;
    ALTER FUNCTION plproxy.insert_user(text, text) OWNER TO postgres;



    Вопросы по коду функций принимаются в комментах, однако примите во внимание что я не гуру Postgres, а всего лишь ученик.

    А теперь тестрируем! :)

    Подключаемся к серверу proxy на порт 6543 (будем сразу работать через PgBouncer).
    И заносим данные в базу:
    SELECT insert_user('Sven','sven@somewhere.com');
    SELECT insert_user('Marko', 'marko@somewhere.com');
    SELECT insert_user('Steve','steve@somewhere.com');



    Теперь можно подключится на каждую из нод и если вы всё сделали правильно и без ошибок то первые две записи будут на ноде node1, а третья запись на ноде node2.

    Пробуем извлечь данные.
    Для этого напишем новую серверную функцию:
    CREATE OR REPLACE FUNCTION plproxy.get_user_email(i_username text)
     RETURNS SETOF text AS
    $BODY$
     CLUSTER 'clustertest';
     RUN ON hashtext(i_username) ;
     SELECT email FROM plproxy.users WHERE username = i_username;
    $BODY$
     LANGUAGE 'plproxy' VOLATILE
     COST 100
     ROWS 1000;
    ALTER FUNCTION plproxy.get_user_email(text) OWNER TO postgres;



    И попробуем её вызвать:
    select plproxy.get_user_email('Steve');


    Вопщем то, у меня всё получилось.

    5.А почему ты такое бедный раз такой умный?


    Как видно на тестовом примере ничего сложного в работе с pl/proxy нет. Но, я думаю все кто смог дочитать до этой строчки уже поняли что в реальной жизни все не так просто.
    Представьте что у вас 16 нод. Это же надо как-то синхронизировать код функций. А что если ошибка закрадётся — как её оперативно исправлять?
    Этот вопрос был задан и на конференции, на что Аско ответил что соответствующие средства уже реализованы внутри самого Skype, но ещё не достаточно готовы для того что бы отдавать их на суд сообществе opensource.
    Второй проблема которая не дай бог коснётся вас при разработке такого рода системы, это проблема перераспределения данных в тот момент когда нам захочется добавить ещё нод в кластер.
    Планировать эту масштабную операцию прийдётся очень тщательно, подготовив все сервера заранее, занеся данные и потом в один момент подменив код функции get_cluster_partitions.

    6.Дополнитеьлные материалы


    Проекты PlProxy и PgBouncer на сайте разработчиков Skype.

    Презентация Аско на Highload++
    Производительность Postgres Николай Самохвалов (Постгресмен)

    7. Бонус для внимательных


    Уже после того как я опубликовал статью я обнаружил в ней одну ошибку и один недочёт.
    Опишу тут так как править уже написанную статью тяжело.
    1) В таблицах используюет sequence с именем user_id_seq. Но SQL кода для неё нигде не приведено. Соответственно если кто то будет просто копипастить код — у него ничего не выйдет. Исправляюсь:
    CREATE SEQUENCE plproxy.user_id_seq
     INCREMENT 1
     MINVALUE 0
     MAXVALUE 9223372036854775807
     START 1
     CACHE 1;
    ALTER TABLE plproxy.user_id_seq OWNER TO postgres;


    2) Во время вставки данных в базу генерируется последовательность для поля user_id. Однако этих последовательностей две. И каждая работает на своей ноде. Что неминуемо приведёт к тому что два разных пользователя будут иметь одинаковые user_id.
    Соответственно функцию insert_user надо исправить таким образом, что бы новый user_id брался из последовательности размещённой на сервере proxy а не на нодах. Таким образом можно избежать дублирование в поле user_id.

    З.Ы: Весь SQL-код подсвечен в Source Code Highliter
    Share post

    Similar posts

    Comments 46

      +2
      Для мускула есть такая интересная чтука: http://forge.mysql.com/wiki/MySQL_Proxy
        0
        Ага. Интересная штука… хоть я как вы уже наверно догадались, с недавних пор не очень люблю MySQL :)
          0
          почему? был какой-то негативный опыт? поделитесь, плз )
            +2
            Это все после посещения Хайлоада. :)
            А точнее после лекции Петра Зайцева, не в упрёк ему будет сказано.
            Лекция была интересная, но я сделал для себя вывод что MySQL подходит для большого круга задач но при этом эти задачи очень просты.

            Если мы пишем что то большое MySQL предоставляет не очень много средств для того что бы упростить нам жизнь. Любой DBA напишет вам с десяток примеров когда MySQL показывает свою несостоятельность.
            Но опять же, это всего лишь моё мнение. Каждый в праве использовать то что ему нравится и, самое главное, то в чём он лучше разбирается.
            Призываю всех, всё таки не разжигать дискуссий на тему «MySQL vs PostgreSQL».
        0
        в mysqlnd( php ) есть возможность проксировать запрос «куда надо» через надстройку mysqlnd
        те без внешних утилит, просто на усмотрение скрипта
          +3
          Ой боюсь какбэ топик не перерос в противостояние адептов PostgreSQL и MySQL. :)
            +1
            Обе хороши, при желании можно найти серьёзные проекты и на PostgreSQL и на MySQL.
            Статья очень интересная, понравилась.
            0
            За статью спасибо, только грамматические ошибки глаз режут :).

            По сути — если на каждой ноде лежат уникальные данные, то надёжность всей базы обратно пропорциональна кол-ву нод.
            Как быть если одна из нод вышла из строя?
            Есть ли какие-то средства восстановления целостности данных, и будет ли система вообще работоспособна при выключении какой-то из ноды?
              +1
              По поводу ошибок — пишите же сюда. Я всё исправлю.
              У меня, к сожалению, OpenOffice почему-то отказывается проверять ошибки после некоторого количества текста. Странный глюк.
              По поводу выхода из строя.
              Я думаю параллельно надо разворачивать реплицирующую базу данных. Ну и рэйды какбы тожне должны снизить риск.
                +1
                Ну и о надёжности:
                Как раз наоборот.

                Предположим у меня 1 сервер. Что то на нём падает и всё. Весь сайт лежит.
                Предположим у меня 5 серверов из которых 1 прокси.
                Если упадёт одна из нод — то на сьйте просто не будут доступны те данные которые были расположены на этой ноде. А все остальные ноду будут работать и часть пользователей удасться обслужить.
                  +2
                  Тут как раз помог бы ответ на мой вопрос: «Будет ли система вообще работоспособна при выключении какой-то из нод». Проведены ли подобные «крэштесты» на кластерах работающих под реальной нагрузкой? Это ведь самый критичный и распространённый кейс любого кластера, о котором в статье ни слова.

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

                  Если, например, данные распраделены по нодам, как бы это сказать, «не однородно», или для работы сервиса нужна агрегация данных, или сервис интенсивно использует целостность данных, или и т.д. — то выход из строя одной ноды будет приводить к НЕПРАВИЛЬНОЙ работе системы, люди будут видеть не верные данные и возможно совершать на их основе не верные действия, а это часто гораздо хуже чем просто временный отказ всей системы.

                    0
                    Согласен на все сто. Для этого надо устраивать такие вот краш-тесты.
                    В данный момент я как раз занимаюсь проектированием довольно большой системы и планирую применять PL/Proxy. Естественно перед тем как использовать будем проверять и устраивать тестирование на предмет отказа.
                    Думаю что материала на статью на собирается :)
                  0
                  Решению вопроса масштабируемости в этой статье как ни парадоксально, было отведено совсем мало места. Ну а про надежность такого решения речь как бы совсем не шла (и это правильно, поскольку это задача немного другого характера). Так вот, если рассмотреть представленую архитектуру, действительно получается, что каждая нода содержит некие уникальные данные, которые по хорошему должны быть защищены, если речь идет о высоконадежной системе. Поэтому для решения этой задачи понадобится делать реплицирующие сервера, на каждый существующий сервер, с возможным резделением ролей на Master-Slave\Master для оптимизации производительности. Еще одной single point of failure является сервер, на котором крутится PL/Proxy и PGBouncer, которую тоже неплохо было бы продублировать. И речь тут еще не идет о самом бизнес приложении, которое эту базу использует. Таким макаром получаем целый зоопарк дорогостоящего оборудования, который не каждая компания может себе позволить. Как результат разработчики высоконагруженых\кластерных систем выбирают для себя наиболее приемлимый уровень надежности, по соотношению цена\качество.
                  0
                  Спасибо, интересно, поскольку сам работаю с высоконагружеными серверами (и там чаще Postgresql используется). Значит PgBouncer увеличит производительность, даже если сервер Postgresql будет один?
                  И ещё — какой метод вы юзали в примере? Session Pooling, Transaction Pooling или Statement Pooling? И что посоветуйте?
                    0
                    Для PL/Proxy можно использвать только Statement Pooling.
                    А если сервер один то надо экспериментировать и смотреть на сколько много в системе транзакций.
                    Ну и конечно лучше об этом почитать на сьайте PgBouncer. :)
                      0
                      >>А если сервер один то надо экспериментировать и смотреть на сколько много в системе транзакций.

                      Например чат (веб старница, которая каждые 3 секунды делает запрос в бд на новые сообщения). Запросов много.

                      >>Ну и конечно лучше об этом почитать на сьайте PgBouncer
                      Спасибо, уже читаю
                        0
                        >> Например чат (веб старница, которая каждые 3 секунды делает запрос в бд на новые сообщения). Запросов много.

                        Под транзакциями я понимаю именно «транзакции» а не запросы к базе данных. В вашем случае я думаю транзакций нет и можно юзать Session Pooling.

                        Но всё таки лучше почитать что бы удостоверится :)
                      0
                      PgBouncer — единственный адекватный connection-pooler для postgresql. По собственному опыту настоятельно рекомендую — абсолютно стабильная вещь и работает как часы.

                      Правда производительности он особо не добавляет (разве что — сокращение времени соединения с сервером), но позволяет сократить использование ресурсов системы за счёт очень значительного сокращения количества postgresql-процессов.

                      Я считаю, что правильнее всего использовать метод Session Pooling — от греха подальше — так есть гарантия, что не нарвётесь ни ни какие коллизии.
                      0
                      Не понятно только как выборки делать в таком случае. Предположим нам нужно вывести _вторую_ страницу по пользователям отсортированным по дате создания и лимитом 10?
                        0
                        Элементарно Ватсон :)

                        Создаём функцию:

                        CREATE OR REPLACE FUNCTION plproxy.get_user_list(u_order text, limit_start int, limit_count int)
                        RETURNS SETOF text AS
                        $BODY$
                        CLUSTER 'clustertest';
                        RUN ON hashtext(i_username);
                        SELECT * FROM plproxy.users order by u_order limit limit_start, limit_count;
                        $BODY$
                        LANGUAGE 'plproxy' VOLATILE
                        COST 100
                        ROWS 1000;
                          0
                          Вообще при таком подходе все SQL-запросы мигрируют в базу в виде функций.
                          А в коде мы только вызываем их: select get_user_list(«user_date_create», 10*(1), 10);
                            0
                            ааа… я понял вопрос… Данные то распределены по шардам… ммм… вот тут надо подумать :)
                              0
                              Знач можно делать вот так:
                              там где у нас RUN ON hashtext(i_username);

                              Пишем:
                              RUN ON ALL;

                              Както так.
                                0
                                :) «как то так» конечно несколько не понятно, что происходит если мы запустим это на всех шардах. Данные каким то образом агрегируются и сортируются? А если у вас уже все работает может попробуете и поделитесь результатами.
                                  0
                                  работает :)
                                  Код функции выглядит так:

                                  CREATE OR REPLACE FUNCTION plproxy.get_users()
                                  RETURNS SETOF text AS
                                  $BODY$
                                  CLUSTER 'clustertest';
                                  RUN ON ALL;
                                  SELECT email FROM plproxy.users;
                                  $BODY$
                                  LANGUAGE 'plproxy' VOLATILE
                                  COST 100
                                  ROWS 1000;
                                  ALTER FUNCTION plproxy.get_users() OWNER TO postgres;

                                  Данные агрегируются.
                                    0
                                    Вопрос по поводу сортировки остаётся открытым, и надо будет сортировать и лимитировать уже пришедшие с шардов данные.
                                    Как это делать я представляю пока очень отдалённо так как не являюсь знатоком PL.
                                      0
                                      Так вот этот вопрос пожалуй самый актуальный. На HL++ тоже был, интересно было послушать.
                              0
                              имхо удобней пользоваться шардингом реализованным в прослойке приложение-бд
                              например для ruby есть sequel — удобный ORM ( пример )
                                0
                                Ойда. Если ещё например вспомнить про питон и Django там вроде как тоже есть что-то подобнее. Но вот для PHP, я так думаю, такое решение будет побыстрее чем если бы тоже самое реализовывать на самом php.
                                0
                                > Но вот для PHP, я так думаю, такое решение будет побыстрее чем если бы тоже самое реализовывать на самом php.
                                pardon?
                                  0
                                  Я имел ввиду что реализация шардинга средствами приложения на php была бы медленнее чем релизация на уровне базы данных.
                                    0
                                    хм, быстрее в человеко-часах? или быстрее выполнение запроса?
                                    если первое — тут готовое решение выигрывает, и не нужно писать сложные (и трудно поддерживаемые) PL функции для каждой операции, причем добавить сервер дешевле (окупается за месяц-два), чем нанимать отдельного человека на адаптацию и поддержку
                                    если второе, то что мешает сделать простейший шардинг, например разделяя записи по диапазону какого-то поля или вычисляя остаток отделения от PK? (кстати pl/sql компилируется в байт код?)

                                    сказать сложно что будет быстрее выполнятся — надо проводить бенчмарки
                                • UFO just landed and posted this here
                                    0
                                    А чего сложного то?
                                    Если используется MVC то весь SQL в одном месте и такой перенос даже для большого проекта займёт 1 день. При этом человек который это будет делать может даже не знать что за проект и как оно работает.

                                    Ну и ещё Бизнесс логика она ж в Application Layer. И в моих примерах её нигде нет. Только выборки данных.
                                    • UFO just landed and posted this here
                                        0
                                        >>> Связи между «MVC» и «весь SQL в одном месте» решительно никакой нет.
                                        эээ… Вы забыли о чём спросили выше. Речь шла не про связь между этими фактами. А о том «легко или тяжело переносить SQL код из приложения в базу данных».
                                        Но если чесно я усматриваю в ваших словах скрытую неприязнь ко всем php-программистам.

                                        >>> Плюс к этому, потребуется чудовищная работа по переделыванию ORM слоя на хранимые процедуры.
                                        О. ДОшли до ОРМ.
                                        Всё зависит от фреймворка. В том который используется мной, это проблем не составляет.

                                        Вы не верите что это легко? Я цифру в 1 день взял не с потолка ;)
                                        • UFO just landed and posted this here
                                            0
                                            Чорд… Ну тогда я вас убедить не смогу.

                                            Ну и ещё. Почему мы думаем что приложение надо будет «переводить на новую структуру»?
                                            Надо его проектировать таким образом изначально. Нигде установок на «все быстренько ринулись переписывать все свои проекты за 1 день» не было. :)

                                            А то что уже работает — врядли кто то будет переписывать.
                                    +2
                                    С официального сайта Pg/Boucner https://developer.skype.com/SkypeGarage/DbProjects/PgBouncer:

                                    PL/Proxy launches connections into each part from each backend, so in heavy-duty environment is necessary to put pooler between PL/Proxy and partitions.

                                    А у вас на схеме Pg/Bouncer стоит над PL/Proxy. Это ошибка, или я не прав?
                                      0
                                      Ошибся, прошу прощения:

                                      это с официального сайта PL/Proxy https://developer.skype.com/SkypeGarage/DbProjects/PlProxy
                                        0
                                        Ойойой. Это действительно ошибка.
                                        Но не такая страшная как может показаться на первый взгляд.
                                        Фактически pgBouncer нужен и там и там.
                                        Другое дело что я когда писал я не правильно понял эту фразу. И думал что решаю проблему для таких вот heavy-duty enviroments устанавливая боунсер _перед_ прокси.

                                        Даже и не знаю что делать.
                                        Ведь если изменить схему то надо будет еще и переписать часть статьи. Но в таком варианте — она тоже верна. Буду надеятся что народ читает комментарии. Добавлю хедер сслыку на ваш комментарий.
                                          0
                                          Как можно заметить по рейтингу комментариев, читают в основном те комментарии, которые вначале. Тем более статья месячной давности. Я думаю, что для начала, в самом начале статьи стОит жирным, или даже красным, обратить внимание на ошибку.
                                        0
                                        Столкнулся с такой неприятностью, касательно заметки насчет sequence'ов.
                                        Так как базы-ноды не могут генерировать sequence, им его нужно передавать (в insert-функциях). Соответственно должен быть дополнительный аргумент со значением sequence.

                                        В базе-прокси определение функции должно быть таким же, как и в нодах (а именно, кол-во аргументов). То есть в соответствующей функции, в базе-прокси будет лишний аргумент sequence.

                                        Приложение же не знает этого sequence, и не должно его знать, то есть оно должно выполнять функцию без аргумента sequence. Получается, что для каждой функции в базе-прокси, написанной на PL/proxy необходимо делать враппер на другом PL языке, который будет принимать n-1 аргумент, брать номер sequence, и выполнять функцию на языке Proxy уже с n аргументами? Есть идеи на этот счет?
                                          0
                                          ммм… я вот все еще не силен в PL
                                          Но мне кажется что должна быть возможность вызывать любую функцию нода с любым количеством аргументов из прокси-функции.
                                          Функции на прокси могут работать со всей базойданных самого прокси, т.е и использовать сиквенсы этой базы. Вот эти сиквенсы и предполагается использовать.
                                          Это первый вариант — но надо ковырять что бы проверить так ли это. Мне очень жалко что я не могу удалить этому вопросу много времнени — начался большой проект и я не могу поковыряться всласть :)

                                          Второй вариант — это использовать UUID www.postgresql.org/docs/8.3/interactive/datatype-uuid.html (новый тип данных с 8.3) вместо int для ID в базе. Генерировать его надо будет на стороне приложения так как пока в постгресе нет средств генерации :( Но он будет уникальным с очень большой степенью вероятности. Соответственно необходимость в сиквенсах отпадет.

                                          0
                                          У меня такая проблема.
                                          Сделал как в примере, но постоянно получаю такую ошибку:

                                          # select plproxy.get_user_email('Steve');
                                          ERROR: Pl/Proxy: cluster not found: clustertest

                                          У кого есть какие соображения по этому поводу?
                                            0
                                            2 года прошло, а ошибка существует та же самая

                                            Only users with full accounts can post comments. Log in, please.