SunshinePHP Developer Conference 2015

Постоянни връзки с бази от данни

Постоянните връзки са връзки, които не прекъсват при приключване на работата на скрипта. Когато бъде заявена постоянна връзка, PHP проверява дали вече съществува идентична постоянна връзка (която е заявена и оставена непрекъсната от по-рано) и ако такава съществува, тя бива използвана. Ако такава връзка не съществува, тя ще бъде създадена. "Идентична" връзка е такава, която е отворена от същия хост, със същото потребителско име и същата парола (където е приложима такава).

Хората, които не са напълно запознати с естеството на работа на уеб-сървърите и начина, по който те разпределят натоварването, могат да объркат постоянните връзки с това което те не са. По-специално те не ви дават възможност да правите 'потребителски сесии' в същата връзка, не ви дават възможност да увеличите ефективността на транзакциите, не ви дават възможност да правите и един куп други неща. Всъщност, за да бъдем пределно ясни, постоянните връзки не дават никаква допълнителна функционалност, която техните непостоянни еквиваленти да не могат да ви предоставят.

Защо?

Проблемът е обвързан с естеството на работа на уеб-сървърите. Има три начина уеб-сървърът да накара PHP да генерира уеб-страници.

Първият метод е PHP да се използва като "опаковка" от тип CGI. По този начин при всяка заявка към (PHP страница на) уеб сървъра ви се създава и унищожава една инстанция на интерпретатора на PHP. Точно поради унищожаването на тази инстанция след всяка заявка, всеки ресурс, който тя използва (например връзка към база от данни) също бива унищожен. В този случай не печелите нищо от употребата на постоянни връзки - те не остават отворени.

Вторият и най-популярен метод е стартирането на PHP като модул на уеб-сървър в многопроцесен режим, което понастоящем включва единствено Apache. Обикновено уеб сървърът в многопроцесен режим има един процес (родителски), който координира група процеси (наследници), които всъщност извършват генерирането на уеб-страници. Когато постъпи заявка от потребител, тя бива прехващана от някой от процесите наследници, който в момента не обслужва друг потребител. Това означава, че когато същият потребител направи друга заявка впоследствие, той може да бъде обслужен от различен процес наследник от този, който го е обслужвал преди това. При отварянето на постоянна връзка, всяка заявка към база от данни направена от този потребител, използва един и същи ресурс за връзка с нея.

Последният метод е PHP да се използва като приставка към многонишков уеб-сървър. Понастоящем PHP4 има поддръжка за ISAPI, WSAPI, и NSAPI (за Windows), всеки от които позволява PHP да се използва като приставка за многонишков сървър, като например Netscape FastTrack (iPlanet), Microsoft's Internet Information Server (IIS) и O'Reilly's WebSite Pro. Поведението е съвсем същото като при многопроцесния модел описан по-горе.

След като постоянните връзки нямат никаква допълнителна функционалност, защо трябва да ги използваме?

Отговорът е изключително прост - ефективност. Постоянните връзки са подходящи в случаите, когато усилието за осъществяване на връзка с базата от данни е голямо. А дали това усилие е голямо или не се определя от много фактори. Например, каква е базата от данни, дали работи на същата машина на която работи и вашия уеб-сървър, колко натоварена е тя и т.н. Равносметката е, че при голямо натоварване и голям трафик към базата от данни, постоянните връзки повишават ефективността значително. Причината е, че на процеса наследник му се налага да осъществи връзка към базата от данни само веднъж и той я използва през цялото врема на работа, вместо да осъществява връзка всеки път когато се обработва страница. Това означава също така, че на всеки процес наследник, който е отворил постоянна връзка, се полага по една такава. Например, ако имате 20 различни наследствени процеса, всеки един от които е образувал постоянна връзка към база от данни, ще имате 20 различни дълготрайни връзки, по една от всеки процес наследник.

Забележете, че това може да има някои недостатъци, ако използвате база от данни с ограничен брой налични отворени връзки и този брой е надхвърлен от броя на процесите, които се опитват да осъществят връзка. Ако базата от данни има ограничение от 16 връзки, а точно в този момент 17 процеса заявяват връзка с нея, един от тях няма да успее. Ако има програмни грешки във вашия скрипт, които не позволяват на връзките да бъдат спрени (например при безкрайни цикли), базата от данни със само 16 отворени връзки може да бъде блокирана. Потърсете информация за обработка на изоставени или чакащи връзки в документацията на базата от данни, която използвате.

Предупреждение

Има няколко неща, които трябва да имате наум, когато използвате постоянни връзки. На първо място, когато използвате заключване на таблица заедно с постоянна връзка, ако скриптът по някаква причина не може да отключи таблицата, то тогава последващите скриптове, използващи същата постоянна връзка, ще бъдат блокирани. Ще ви се наложи да рестартирате уеб-сървъра или сървъра на базата от данни. Също така, когато използвате транзакции, имайте предвид, че транзакционният блок ще се пренесе върху следващия изпълнен скрипт, ако изпълнението на скрипта приключи преди да е приключило изпълнението на транзакцията. В който и да е от тези случаи, използвайте register_shutdown_function(), за да стартирате почистваща функция, която да отключи заключените ви таблици или да превърти транзакциите ви. Най-добре не използвайте постоянни връзки, когато използвате заключване на таблици или транзакции (можете да ги използвате при всички останали случаи).

Важна информация. Постоянните връзки са проектирани да имат съвсем същата функционалност, каквато имат и обикновените такива. Това означава, че би трябвало при всички случаи да можете да замените постоянните връзки с непостоянни и това да не промени по никакъв начин начина на работа на скриптовете ви. Това може да доведе до промяна на ефективността (и вероятно ще доведе), но не и до промяна на крайния резултат.

Вж. още fbsql_pconnect(), ibase_pconnect(), ifx_pconnect(), ingres_pconnect(), msql_pconnect(), mssql_pconnect(), mysql_pconnect(), ociplogon(), odbc_pconnect(), ora_plogon(), pfsockopen(), pg_pconnect() и sybase_pconnect().

add a note add a note

User Contributed Notes 12 notes

up
5
fabio
8 years ago
You can in fact provide a port for the connection, take a look at http://de2.php.net/manual/en/function.mysql-pconnect.php#AEN101879

Just use "hostname:port" for the server address.
up
4
christopher dot jones at oracle dot com
7 years ago
For the oci8 extension it is not true that " [...] when using transactions, a transaction block will also carry over to the next script which uses that connection if script execution ends before the transaction block does.".  The oci8 extension does a rollback at the end scripts using persistent connections, thus ending the transaction.  The rollback also releases locks. However any ALTER SESSION command (e.g. changing the date format) on a persistent connection will be retained over to the next script.
up
3
ambrish at php dot net
4 years ago
In IBM_DB2 extension v1.9.0 or later performs a transaction rollback on persistent connections at the end of a request, thus ending the transaction. This prevents the transaction block from carrying over to the next request which uses that connection if script execution ends before the transaction block does.
up
1
php at alfadog dot net
9 months ago
One additional not regarding odbc_pconnect  and possibly other variations of pconnect:

If the connection encounters an error (bad SQL, incorrect request, etc), that error will return with  be present in odbc_errormsg for every subsequent action on that connection, even if subsequent actions don't cause another error.

For example:

A script connects with odbc_pconnect.
The connection is created on it's first use.
The script calls a query "Select * FROM Table1".
Table1 doesn't exist and odbc_errormsg contains that error.

Later(days, perhaps), a different script is called using the same parameters to odbc_pconnect.
The connection already exists, to it is reused.
The script calls a query "Select * FROM Table0".
The query runs fine, but odbc_errormsg still returns the error about Table1 not existing.

I'm not seeing a way to clear that error using odbc_ functions, so keep your eyes open for this gotcha or use odbc_connect instead.
up
1
RQuadling at GMail dot com
8 years ago
If you have multiple databases on the same server AND you are using persistent connections, you MUST prefix all the table names with the specific database name.

Changing the database using the xxx_select_db functions alters the database for the connection for all users who are sharing that connection (assuming PHP is running shared and not CGI/CLI).

If you have 2 databases (live and archive) and your script is talking to both, you cannot use 2 persistent connections and change the database for each one.

Internally, persistent connections are used even if you do not specify that you want to use persistent connections. This is why new_link was added to mysql_connect/mssql_connect (PHPV4.2.0+).
up
0
ynzhang from lakeheadu canada
5 years ago
It seems that using pg_pconnect() will not persist the temporary views/tables. So if you are trying to create temporary views/tables with the query results and then access them with the next script of the same session, you are out of luck. Those temporary view/tables are gone after each PHP script ended. One way to get around this problem is to create real view/table with session ID as part of the name and record the name&creation time in a common table. Have a garbage collection script to drop the view/table who's session is expired.
up
-1
whatspaz at g NO dot SPAM mail dot c o m
7 years ago
in response to web at nick, have you tried FLUSH PRIVILEGES. this should reload those privileges.
up
-3
Tom
4 years ago
There's a third case for PHP: run on a fastCGI interface. In this case, PHP processes are NOT destroyed after each request, and so persistent connections do persist. Set PHP_FCGI_CHILDREN << mysql's max_connections and you'll be fine.
up
-4
andy at paradigm-reborn dot com
7 years ago
To those using MySQL and finding a lot of leftover sleeping processes, take a look at MySQL's wait_timeout directive. By default it is set to 8 hours, but almost any decent production server will have been lowered to the 60 second range. Even on my testing server, I was having problems with too many connections from leftover persistent connections.
up
-7
jean_christian at myrealbox dot com
12 years ago
If anyone ever wonders why the number of idle db process (open connections) seems to grow even though you are using persistent connections, here's why:

"You are probably using a multi-process web server such as Apache. Since
database connections cannot be shared among different processes a new
one is created if the request happen to come to a different web server
child process."
up
-7
aaryal at foresightint dot com
10 years ago
this one bit quite a bit of chunk out of my you-know-what. seems like if you're running multiple database servers on the same host (for eg. MySQL on a number of ports) you can't use pconnect since the port number isn't part of the key for database connections. especially if you have the same username and password to connect to all the database servers running on different ports. but then it might be php-MySQL specific. you might get a connection for an entirely different port than the one you asked for.
up
-13
jorgeleon85 at gmail dot com
3 years ago
I've been looking everywhere for a benchmark or at least comparison of the overhead used by oci_connect and oci_pconnect.
Just saying "oci_connect is slower because the overhead..." is not enough for me. For than I wrote a couple scripts to compare perfomance.
At the end I found out an average of 34% more time using a oci_connect than oci_pconnect, using a query of 50 rows and 100 columns.
Obviously this wasn't a real benchmark however it gives a simple idea of the efficiency of each function.
To Top