Intereting Posts

Лучший способ решения проблем параллелизма

У меня есть LAPP (linux, apache, postgresql и php), но вопрос такой же, как у Postgres, так и для Mysql.

У меня создано приложение cms, которое обрабатывает клиенты, документы (оценки, счета-фактуры и т. Д.) И другие данные, структурированные в 1 postgres DB со многими схемами (по одному для каждого нашего клиента, использующего приложение); допустим около 200 схем, каждый из которых используется одновременно 15 человек (avg).

EDIT : у меня есть поле timestamp с именем last_update для каждой таблицы и триггер, который обновляет временную метку каждый раз, когда строка обновляется.

Ситуация такова:

  1. Люди Foo и Bar редактируют документ 0001, используя форму с каждой информацией о документе.
  2. Например, Foo изменяет детали отправки.
  3. Бар изменяет номера телефонов и некоторые элементы в документе.
  4. Foo нажмите кнопку «Сохранить», приложение обновит db.
  5. Бар нажмите кнопку «Сохранить» после строки, повторно отправив форму со старыми деталями отправки.
  6. В базе данных изменения Foo были потеряны.

Ситуация, в которой я хочу:

  1. Люди Foo, Bar, John, Mary, Paoul редактируют документ 0001, используя форму с каждой информацией о документе.
  2. Например, Foo изменяет детали отправки.
  3. Бар и другие меняют что-то другое.
  4. Foo нажмите кнопку «Сохранить», приложение обновит db.
  5. Бар и другие получают предупреждение «Предупреждение! этот документ был изменен кем-то другим. Нажмите здесь, чтобы загрузить фактические данные ».

Я хотел бы использовать ajax для этого; просто используя скрытое поле с идентификатором документа и последней обновленной меткой времени, каждые 5 секунд проверяют, является ли последнее обновленное время одинаковым и ничего не делать, иначе отобразите диалоговое окно предупреждения.

Итак, страница check-last-update.php должна выглядеть примерно так:

<?php //[connect to db, postgres or mysql] $documentId = isset($_POST['document-id']) ? $_POST['document-id'] : 0; $lastUpdateTime = isset($_POST['last-update-time']) ? $_POST['last-update-time'] : 0; //in the real life i sanitize the data and use prepared statements; $qr = pg_query(" SELECT last_update_time FROM documents WHERE id = '$documentId' "); $ray = pg_fetch_assoc($qr); if($ray['last_update_time'] > $lastUpdateTime){ //someone else updated the document since i opened it! echo 'reload'; }else{ echo 'ok'; } ?> 

Но я не люблю подчеркивать db каждые 5 секунд для каждого пользователя, у которого есть один (или более …) документов.

Итак, что может быть другим эффективным решением без nuking db?

Я думал использовать файлы, создавая, например, пустой файл txt для каждого документа, и каждый раз, когда документ обновляется, я 'касаюсь' файла, обновляющего «последнее измененное время», а также … но я думаю, что это будет медленнее чем db и дают проблемы, когда у меня много пользователей, редактирующих один и тот же документ.

Если у кого-то есть лучшая идея или какое-либо предложение, пожалуйста, опишите это подробно!

* – – – – – ОБНОВИТЬ – – – – – *

Я определенно выбрал NOT, чтобы не ударить db для проверки «последней отметки времени обновления», не возражайте, если запрос будет довольно быстрым, (основной) сервер базы данных имеет другие задачи для заполнения, не нравится идея увеличить его перегрузку для этой вещи ,

Итак, im так:

  1. Каждый раз, когда документ обновляется кем-то, я должен что-то сделать, чтобы подписать новую временную метку вне среды db, например, не спрашивая db. Мои идеи:
    1. Файловая система: для каждого документа я создаю файлы empry txt, названные как идентификатор документа, каждый раз, когда документ обновляется, я «касаюсь» файла. Я ожидаю, что у вас будут тысячи этих пустых файлов.
    2. APC, php cache: это, вероятно, будет более гибким, чем первый, но им интересно, сохраняя постоянное хранение тысяч и тысяч данных в apc, чтобы замедлить выполнение самого php или использовать память сервера. Я немного боюсь выбирать этот путь.
    3. Другие db, sqlite или mysql (которые быстрее и легче с простыми структурами db) используются для хранения только идентификаторов документов и временных меток.
  2. Каким бы способом я ни выбрал (файлы, apc, sub-db), я серьезно думал использовать другой веб-сервер (lighttp?) В поддомене, чтобы обрабатывать все эти запросы с длинным запросом.

ДАЙТЕ ДРУГОЕ ИЗМЕНЕНИЕ:

Путь файла не работает.

APC может быть решением.

Нажатие на БД также может быть решением, создающим таблицу для обработки временных меток (с двумя столбцами, document_id и last_update_timestamp), которые должны быть такими же быстрыми и легкими, насколько это возможно.

Длительный опрос: именно так я выберу, используя lighttpd под apache для загрузки статических файлов (изображения, css, js и т. Д.) И только для этого типа длительного опроса; Это облегчит загрузку apache2, особенно для опроса.

Apache будет проксировать все эти запросы на lighttpd.

Теперь мне нужно решить только решение db и решение APC.

ps: спасибо всем, кто уже ответил мне, вы были действительно полезны!

Я согласен с тем, что я, вероятно, не попал бы в базу данных для этого. Я полагаю, что для хранения этой информации я бы использовал кеш APC (или какой-то другой кеш в памяти). То, что вы описываете, явно оптимистично блокируется на подробном уровне записи. Чем выше уровень в структуре базы данных, тем меньше вам нужно иметь дело. Похоже, вы хотите проверить несколько таблиц внутри структуры.

Я бы сохранил кэш (в APC) идентификаторов и временные метки последнего обновленного времени, заданного именем таблицы. Так, например, у меня может быть массив имен таблиц, где каждая запись вводится по идентификатору, а фактическое значение – это последняя обновленная временная метка. Вероятно, есть много способов установить это с помощью массивов или других структур, но вы получите эту идею. Я бы, вероятно, добавил тайм-аут в кеш, чтобы записи в кеше удалялись через определенный промежуток времени, т. Е. Я бы не хотел, чтобы кеш вырос, и предположим, что 1-дневные записи больше не полезны).

С этой архитектурой вам нужно будет сделать следующее (в дополнение к настройке APC):

  • при любом обновлении любой (применимой) таблицы обновите запись кэша APC новой меткой времени.

  • в пределах ajax просто зайдите так «назад», как php (чтобы получить кеш APC для проверки записи), а не весь путь «назад» к базе данных.

Я думаю, вы можете использовать условие в инструкции UPDATE, например WHERE ID =? И LAST_UPDATE = ?.

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

Для каждой записи вам понадобится поле типа штампа. Что это не имеет значения, если вы можете гарантировать, что внесение каких-либо изменений в запись приведет к тому, что штамп этой версии будет отличаться. Лучшая практика заключается в том, чтобы затем проверить и убедиться, что печать версии загруженной записи совпадает с печатью версии в БД, когда пользователь нажимает кнопку «Сохранить», и если она отличается от нее.

Как вы справляетесь с этим, зависит от вас. По крайней мере, вы хотите предложить перезагрузить из БД, чтобы пользователь мог проверить, что они все еще хотят сохранить. Один из них – попытаться объединить свои изменения в новую запись БД, а затем попросить их проверить правильность работы слияния.

Если вы хотите периодически опросить любой БД, способный обрабатывать вашу систему, должен иметь возможность загружать опрос. 10 опросов пользователей каждые 5 секунд – 2 транзакции в секунду. Это тривиальная нагрузка, и это не должно быть проблемой. Чтобы средняя нагрузка была близка к фактической нагрузке, слегка дрожайте время опроса (вместо того, чтобы делать это ровно через каждые 5 секунд, делайте это каждые 4-6 секунд, например).

Ответ Донни (опрос), вероятно, ваш лучший вариант – простой и работает. Он будет охватывать почти каждый случай (маловероятный простой поиск PK может повредить производительность даже на очень популярном сайте).

Для полноты, и если вы хотите избежать опроса, вы можете использовать push-модель . Существуют различные способы, описанные в статье Википедии. Если вы можете сохранить кэш записи (каждый раз, когда вы обновляете запись, вы обновляете кеш), то вы почти полностью можете исключить загрузку базы данных.

Однако не используйте столбец «last_updated» timestamp. Изменения в пределах одной секунды не являются неслыханными. Если вы добавите дополнительную информацию (сервер, который сделал обновление, удаленный адрес, порт и т. Д.), Вы можете уйти от него, чтобы убедиться, что, если два запроса пришли в ту же секунду, на тот же сервер, вы можете обнаружить разницу. Если вам нужна эта точность, вы можете использовать уникальное поле редактирования (это необязательно должно быть инкрементирующее целое, просто уникальное в течение срока действия этой записи).

Кто-то упомянул о постоянных соединениях – это снизит стоимость установки запросов на опрос (каждое соединение, естественно, потребляет ресурсы в базе данных и хост-машине). Вы должны поддерживать единственное соединение (или как можно меньше) все время (или как можно дольше) и использовать его (в сочетании с кешированием и записью, если это необходимо).

Наконец, существуют инструкции SQL, которые позволяют добавлять условие в UPDATE или INSERT. Мой SQl действительно ржавеет, но я думаю, что это что-то вроде UPDATE ... WHERE ... Чтобы соответствовать этому уровню защиты, вам нужно будет сделать свою собственную блокировку строк перед отправкой обновления (и вся обработка ошибок и очистка, которые могут повлечь за собой). Вряд ли вам это понадобится; Я просто упоминаю об этом для полноты.

Редактировать:

Ваше решение отлично звучит (отметки времени кеша, запросы опроса прокси на другой сервер). Единственное изменение, которое я сделал бы, это обновить кешированные метки времени при каждом сохранении. Это позволит сохранить кеш более свежим. Я также проверил бы временную метку непосредственно из db при сохранении, чтобы предотвратить кражу данных из-за устаревших данных кеша.

Если вы используете APC для кеширования, то второй HTTP-сервер не имеет смысла – вам придется запускать его на одном компьютере (APC использует общую память). Такая же физическая машина будет выполнять эту работу, но с дополнительными служебными данными второго HTTP-сервера. Если вы хотите отключить запросы опроса на второй сервер (lighttpd, в вашем случае), тогда было бы лучше настроить lightttpd перед Apache на второй физической машине и использовать общий кэш-сервер (memcache), чтобы Lighttpd-сервер может читать кэшированные метки времени, и Apache может обновлять временные метки кэширования. Обоснование для установки lighttpd перед Apache заключается в том, что если большинство запросов являются запросами на опрос, чтобы избежать использования процесса Apache с более тяжелым весом.

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

Ваш подход к поиску базы данных является лучшим. Если вы делаете это каждые 5 секунд, и у вас есть 15 одновременных пользователей, то вы смотрите ~ 3 вопроса в секунду. Это тоже очень маленький запрос, возвращающий только одну строку данных. Если ваша база данных не может обрабатывать 3 транзакции секунду, вам, возможно, придется искать лучшую базу данных, потому что 3 запроса / секунда – ничто.

Отметьте метки в таблице, чтобы вы могли быстро увидеть, изменилось ли что-либо, не изменяя каждое поле.

Для этого Hibernate использует поле версии. Дайте каждой таблице такое поле и используйте триггер, чтобы увеличить его при каждом обновлении. При сохранении обновления сравните текущую версию с версией, когда данные были прочитаны ранее. Если они не совпадают, выведите исключение. Используйте транзакции, чтобы сделать ассемблер check-and-update.

Это немного не по теме, но вы можете использовать пакет PEAR (или пакет PECL, я забыл, какой) xdiff чтобы отправить хорошее руководство пользователя, когда вы получаете столкновение.

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

Во-вторых, запросите отметку времени последнего обновления, если у вас есть старая временная метка, а затем текущая версия в базе данных, а затем выпустите предупреждение клиенту.

В-третьих, это как-то подталкивает эту информацию к клиенту, хотя какое-то постоянное соединение с сервером, позволяя одновременное двухстороннее соединение.

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

…..
Я знаю, что это не то, о чем вы просили, но … почему бы не изменить один синглтон?
Синглтон может быть столбцом userID в таблице документов.
Если пользователь хочет отредактировать документ, документ заблокирован для редактирования другими пользователями.

Или можете редактировать отдельные синглтоны по отдельным полям / группам информации.

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

При использовании singleton нет опроса и проверяется только одна метка времени, когда пользователь «прикасается» и / или хочет редактировать документ.

Но, возможно, одноэлементный механизм не подходит вашей системе.

С уважением
Sigersted

Ahhh, хотя это было легче.

Итак, давайте сделаем вывод: у меня есть общая база данных (pgsql или mysql не имеет значения), которая содержит много общих объектов.

У меня есть $ x (на самом деле $ x = 200, но растет, надеюсь, скоро достигнет 1000) точной копии этой базы данных, а для каждого из них до 20 (10 пользователей) в течение 9 часов в день.

Если один из этих пользователей просматривает запись, любую запись, я должен посоветовать ему, если кто-то отредактирует одну и ту же запись.

Скажем, Foo смотрит на документ 0001, садится за кофе, открывает бар и редактирует тот же документ, когда Foo возвращается, он должен увидеть «Предупреждение, кто-то другой отредактировал этот документ! нажмите здесь, чтобы обновить страницу. '.

Это все, что мне нужно, возможно, я продолжу эту ситуацию, добавив способ увидеть изменения и откаты, но это не главное.

Некоторые из вас предложили проверить временную метку «последнее обновление» только тогда, когда foo пытается сохранить документ; Может быть и решением, но мне нужно что-то в режиме реального времени (10 секунд deelay).

Длинный опрос, плохой способ, но, похоже, единственный.

Итак, что я сделал:

  1. Установленный Lighttp на моей машине (и php5 как fastcgi);
  2. Загруженный прокси-модуль apache2 (все или ошибка 403 поразит вас);
  3. Изменен порт lighttpd с 80 (который используется apache2) до 81;
  4. Настроенный apache2 для проксирования запроса от mydomain.com/polling/* к polling.mydomain.com (подается с Lighttp)
  5. Теперь у меня есть другая вспомогательная http-служба, которую я буду использовать как для опроса, так и для загрузки статического контента (изображений и т. Д.), Чтобы уменьшить нагрузку apache2.
  6. Becose я не хочу nuke базы данных для проверки метки времени, я попробовал некоторые системы кэшей (которые можно вызвать из php).
    1. APC: довольно простая установка и управление, очень легкая и быстрая, это был бы мой первый выбор. Если бы только кеш был бы доступен для совместного использования между двумя процессами cgi (мне нужно хранить в кеше значение из php-процесса apache2 и читать его из процесса php lighttpd)
    2. Memcached: примерно в 4-5 раз медленнее, чем APC, но запускается как один процесс, который можно затронуть везде в моей среде. Я пойду с этим, атм. (даже если это медленнее, использование, которое я сделаю, относительно простое).

Теперь мне просто нужно попробовать эту систему, загружая некоторые тестовые данные, чтобы увидеть, что ho будет двигаться «под давлением» и оптимизировать его.

Я полагаю, что эта среда будет работать для других ситуаций с большим количеством опросов (чат?)

Спасибо всем, кто дал мне послушать!

Я предлагаю: при первом запросе записи, которая может быть изменена, вставьте ее в локальную копию. Когда «обновление», сравните копию в заблокированной таблице / строке с вашей копией, и если она будет изменена, отбросьте ее обратно пользователю.