Каковы недостатки использования постоянного подключения в PDO

181

В PDO соединение можно сделать постоянным с помощью PDO::ATTR_PERSISTENTатрибута. Согласно руководству по PHP -

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

В руководстве также рекомендуется не использовать постоянное соединение при использовании драйвера PDO ODBC, поскольку это может помешать процессу пула соединений ODBC.

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

MD Саим Ахмед
источник
Вау, вы заплатили 1000 повторений за этот простой вопрос?
Pacerier
@Pacerier, нет, это был кто-то еще .
Чарльз

Ответы:

287

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


При использовании PDO существуют те же недостатки, что и с любым другим интерфейсом базы данных PHP, который выполняет постоянные соединения: если ваш скрипт неожиданно завершает работу в середине операций с базой данных, следующий запрос, который получает оставшееся соединение, будет обнаружен там, где остановился мертвый скрипт. Соединение поддерживается открытым на уровне диспетчера процессов (Apache для mod_php, текущий процесс FastCGI, если вы используете FastCGI и т. Д.), А не на уровне PHP, и PHP не сообщает родительскому процессу, чтобы соединение умирало, когда скрипт завершается ненормально.

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

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

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

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

Кроме того, большинство современных баз данных (включая PostgreSQL) имеют свои собственные предпочтительные способы выполнения пулов соединений, которые не имеют непосредственных недостатков, которые имеют обычные постоянные соединения на основе PHP.


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

Независимо от того, когда люди на складе обрабатывают несколько сотен входящих деталей, и каждая часть занимает три с половиной секунды вместо полсекунды, нам пришлось принять меры, прежде чем они похитят нас всех и заставят нас помочь им. Итак, мы включили несколько кусочков в наше доморощенное чудовище ERP / CRM / CMS и ощутили все ужасы постоянных соединений из первых рук. Нам потребовались недели, чтобы отследить все тонкие маленькие проблемы и странное поведение, которые происходили, казалось бы, наугад. Оказалось, что те фатальные ошибки раз в неделю, которые наши пользователи старательно выдавливали из нашего приложения, оставляли заблокированные таблицы, отмененные транзакции и другие неудачные состояния.

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

Чарльз
источник
2
Я надеюсь, что прочитал этот ответ перед запускомSELECT orders.* FROM orders LEFT JOIN items USING(item_id)
Аст Дерек
31
Я знаю большой веб-сайт, который использует постоянные соединения в течение почти десятилетия. Хитрость заключается в том, чтобы использовать слой над расширением БД и помнить о том, что необходимо очистить с помощью register_shutdown_function(). Если процесс умирает, соединение тоже умирает. Если это не так, соединение сбрасывается в чистое состояние (например, открытые транзакции откатываются). Если это не удается, соединение закрывается, и при следующем запросе к тому же процессу будет открыто новое. Нет необходимости демонизировать постоянные связи.
Уолтер Тросс
Мне любопытно @Charles ... была ли ваша проблема решена?
Чаллака
@MichaelDibbets Мы заменили сервер приложений несколько месяцев назад и включили pconnect, чтобы проверить, сохранилась ли еще трехсекундная ошибка. Не было Я полагаю, это было разрешено прокси. Приведенный ниже ответ по- mysqli_change_userпрежнему, вероятно, является лучшим обходным решением для людей, которым приходится устанавливать постоянные соединения в приложении, не предназначенном для решения проблем состояния.
Чарльз
5
У нас была задержка соединения 5 секунд, которую нам удалось выделить как проблему DNS + IPv6. Сервер искал адрес v6, отказывал, а затем использовал адрес IPv4.
Найджел Аткинсон
45

В ответ на проблему Чарльза выше,

От: http://www.php.net/manual/en/mysqli.quickstart.connections.php -

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

Расширение mysqli поддерживает обе интерпретации постоянного соединения: состояние сохраняется, и состояние сбрасывается перед повторным использованием. По умолчанию сбрасывается. Перед повторным использованием постоянного соединения расширение mysqli неявно вызывает mysqli_change_user()сброс состояния. Постоянное соединение отображается для пользователя, как будто оно было только что открыто. Никаких артефактов от предыдущих использований не видно.

mysqli_change_user()Функция является дорогостоящей операцией. Для лучшей производительности пользователи могут захотеть перекомпилировать расширение с установленным флагом компиляции MYSQLI_NO_CHANGE_USER_ON_PCONNECT.

Пользователь может выбирать между безопасным поведением и лучшей производительностью. Оба являются действительными целями оптимизации. Для простоты использования безопасное поведение было установлено по умолчанию за счет максимальной производительности.

Prashant
источник
+1, если бы не тот факт, что мы убрали беспорядок другими способами, я хотел бы посмотреть, исправит ли ручной вызов change_user наши причудливые проблемы неизвестного состояния.
Чарльз
Что эквивалентно для постоянных соединений PDO Postgres? У меня есть подобные проблемы, как у @Charles, когда через некоторое время пользователи будут получать ошибки, такие как fetch sql - server неожиданно закрыл соединение. Это, вероятно, означает, что сервер аварийно завершил работу при выполнении простого запроса SELECT (даже транзакций).
Кармагеддон
1
@Carmageddon, это больше подходит для нового вопроса, но tl; dr заключается в том, что Postgres не выполняет pconnect, и вместо этого вам следует использовать один из пулов внешних подключений.
Чарльз
@ Чарльз, что ты имеешь в виду под этим? использование постоянного подключения PDO не эквивалентно использованию «пулов внешних подключений»? или что ты имел ввиду?
Carmageddon
@Carmageddon, я имею в виду, что сообщество Postgres выбрало пул соединений как лучшее решение, чем pconnect. Проверьте pgbouncer или pgpool-II. Я не уверен, что PDO в любом случае использует Postgres pconnect, но я могу быть совершенно не в себе.
Чарльз
13

Постоянные соединения - это хорошая идея, только когда требуется (относительно) много времени для соединения с вашей базой данных. В наше время это почти никогда не бывает. Самый большой недостаток постоянных подключений заключается в том, что он ограничивает количество пользователей, которые могут просматривать ваш сайт: если MySQL настроен на одновременное разрешение только 10 одновременных подключений, тогда, когда одиннадцатый человек пытается просматривать ваш сайт, он не будет работать для них. ,

PDO не управляет постоянством. Драйвер MySQL делает. Он повторно использует соединения, когда а) они доступны и хост / пользователь / пароль / база данных совпадают. Если какие-либо изменения, то он не будет повторно использовать соединение. В лучшем случае чистый эффект состоит в том, что эти ваши соединения будут запускаться и останавливаться так часто, потому что у вас есть разные пользователи на сайте, и их постоянство не приносит никакой пользы.

Главное, что нужно знать о постоянных соединениях, это то, что вы НЕ должны использовать их в большинстве веб-приложений. Они звучат заманчиво, но они опасны и в значительной степени бесполезны.

Я уверен, что на этом есть другие потоки, но постоянное соединение опасно, потому что оно сохраняется между запросами. Если, например, вы заблокируете таблицу во время запроса, а затем не сможете разблокировать ее, эта таблица останется заблокированной на неопределенный срок. Постоянные соединения также в значительной степени бесполезны для 99% ваших приложений, поскольку у вас нет возможности узнать, будет ли одно и то же соединение использоваться между различными запросами. Каждый веб-поток будет иметь свой собственный набор постоянных соединений, и вы не сможете контролировать, какой поток будет обрабатывать какие запросы.

Процедурная библиотека mysql PHP имеет функцию, посредством которой последующие вызовы mysql_connect будут возвращать ту же ссылку, а не открывать другое соединение (как можно было бы ожидать). Это не имеет ничего общего с постоянными соединениями и является специфическим для библиотеки mysql. PDO не проявляет такого поведения


Ссылка на ресурс: ссылка

В общем, вы можете использовать это как грубый "набор правил" ::

ДА , используйте постоянные соединения, если:

  • Существует только несколько приложений / пользователей, обращающихся к базе данных, т.е. вы не получите 200 открытых (но, вероятно, неактивных) соединений, поскольку на одном хосте совместно используются 200 разных пользователей.
  • База данных работает на другом сервере, к которому вы обращаетесь по сети

  • (Одно) приложение обращается к базе данных очень часто

НЕТ , не используйте постоянные соединения, если:

  • Вашему приложению нужен только доступ к базе данных 100 раз в час.

  • У вас есть много, много веб-серверов, обращающихся к одному серверу базы данных

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

Проблема в том, что в «конфигурации по умолчанию» MySQL допускает только 1000 параллельных «открытых каналов». После этого в новых соединениях будет отказано (вы можете настроить этот параметр). Поэтому, если у вас есть, скажем, 20 веб-серверов с каждыми 100 клиентами, и каждый из них имеет доступ только к одной странице в час, простая математика покажет вам, что вам потребуется 2000 параллельных подключений к базе данных. Это не сработает.

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

Джонатан Х.
источник
4
После строки ваш ответ - скопировать и вставить из stackoverflow.com/a/51583/718224
Тони Старк,
1
«ДА, используйте постоянные соединения, если: [...] Есть только несколько приложений / пользователей, обращающихся к базе данных», - противоречит «Используйте его только для приложений с большим количеством запросов». Последнее, однако, правильно. Ситуация: тысячи запросов в секунду приведут к сотням активных подключений к базе данных. Когда система линейно масштабируется, она также линейно масштабирует количество подключений к базе данных. Таким образом, больше запросов (больше пользователей) приведет к большему количеству соединений. Поэтому вам нужно ограниченное (!) Количество активных соединений, когда у вас много запросов (пользователей)
Кен Ван Хойландт
12

В моих тестах у меня было время соединения более секунды с моим локальным хостом, таким образом, предполагая, что я должен использовать постоянное соединение. Дальнейшие тесты показали, что это была проблема с localhost:

Результаты теста в секундах (измеряются php microtime):

  • размещенный веб: connectDB: 0.0038912296295166
  • localhost: connectDB: 1.0214691162109 (более одной секунды: не использовать localhost!)
  • 127.0.0.1: connectDB: 0.00097203254699707

Интересно: следующий код работает так же быстро, как и 127.0.0.1:

$host = gethostbyname('localhost');
// echo "<p>$host</p>";
$db = new PDO("mysql:host=$host;dbname=" . DATABASE . ';charset=utf8', $username, $password,
    array(PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
Гуннар Бернштейн
источник
Похоже, у PDO есть трудности с переводом доменных имен! Спасибо, мне было интересно, почему каждое соединение на моей четырехъядерной машине занимало много времени!
Мустафа
@ Гуннар Бернштейн +1 хорошая находка. «localhost» определенно занимает больше времени, и это несколько улучшило скорость моего веб-приложения (оно создает много соединений).
imperium2335
1
Это круто. Что-то не так с разрешением на моей машине для разработки ... при использовании IP-адреса мой сценарий переместился с 6.1 с на 1.1 с
Пит
localhostиспользует сокетное соединение, сокетное соединение известно как плохое на большом количестве соединений
mente
@mente Любая ссылка, ресурс, который может доказать этот факт? Я склонен думать, что UDS предпочтительнее TCP. Спасибо.
Nuxwin
6

Постоянные соединения должны значительно повысить производительность. Я не согласен со сборкой, что вы должны «избегать» упорства ..

Похоже, что приведенные выше жалобы вызваны тем, что кто-то использует таблицы MyIASM и взламывает собственные версии транзакций, захватывая блокировки таблиц. Ну, конечно, вы зашли в тупик! Используйте beginTransaction () PDO и переместите ваши таблицы в InnoDB.

Стивен
источник
2
Год спустя, я понимаю, но для справки: мой рассказ происходит из базы данных, состоящей полностью из таблиц InnoDB, за исключением нескольких ненормализованных клонов, застрявших в трясине MyISAM для поддержки полнотекстовой индексации.
Чарльз
Пфф, Сфинкс стар и разорен, ElasticSearch - это новая горячность. В один прекрасный день мы будем использовать его для наших старых приложений, а не только для новых ...
Charles
Полнотекстовый поиск в PostgreSQL - настоящий победитель. Это удивительно Не требует другого работающего инструмента / сервера для выполнения своей работы. Не нужно беспокоиться о синхронизации данных. Очень детальный контроль. Несколько словарей или написать свой. А поскольку PostgreSQL автоматически использует многоиндексные запросы, вы можете просто вставить его в любой другой запрос, который вы выполняете.
Брайтбол
2
MySQL 5.6 предлагает полнотекстовую поддержку таблиц InnoDB.
время
2

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

Crayon Violent
источник
Часто тратится много человеческого времени на микросекунды компьютерного времени
Энди Чейз
1

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

Самые первые неприятности с постоянными связями ...

Если вы создаете 1000 соединений в секунду, вы обычно не гарантируете, что они остаются открытыми в течение очень долгого времени, а Операционная система делает это. Основываясь на протоколе TCP / IP, порты нельзя перерабатывать мгновенно, а также им приходится тратить время на этап «FIN», ожидая, пока они не будут переработаны.

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

Многие люди просто не понимают, что вы можете увеличить переменную * max_connections * и получить более 100 одновременных соединений с MySQL, другие были побеждены старыми проблемами Linux из-за невозможности передать более 1024 соединений с MySQL.

Позволяет теперь поговорить о том, почему постоянные соединения были отключены в расширении mysqli. Несмотря на то, что вы можете неправильно использовать постоянные соединения и получить низкую производительность, что не было главной причиной. Фактическая причина в том, что вы можете получить гораздо больше проблем с этим.

Постоянные соединения были введены в PHP во всех случаях MySQL 3.22 / 3.23, когда MySQL не был таким сложным, что означает, что вы можете легко перезапускать соединения без проблем. В более поздних версиях, однако, возникло множество проблем: если вы перезапускаете соединение с незафиксированными транзакциями, у вас возникают проблемы. Если вы перезапускаете соединения с конфигурациями пользовательских наборов символов, вы снова подвергаетесь опасности, а также, возможно, трансформируете переменные для каждого сеанса.

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

Тони Старк
источник
0

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

Джеймс
источник