php中mysql_pconnect()的实现方式.
网上有人说要想让mysql_pconnect正常稳定的工作,必须保证mysql max_connect参数设定大于或等于apache的最大线程(进程)数。这句话是有一定道理的。这要简单了解mysql_pconnect的工作机制才能说明白。简单的说就是apache的每一个进程数都可能保留一个mysql的连接。
比如max_connect参数设置为100,apache的最大进程数位101。刚好apache有100个进程存放了mysql的连接,但是也刚好apache分配第101个进程去执行一个php脚本,也刚好这个脚本也要连接php,结果就悲剧了,肯定获取连接失败,被mysqld拒绝了,因为mysqld不允许客户端存在第101的线程。最不爽的是,apache并不是每一个进程都访问php脚本,它还可以是图片、静态页面等等。但是,这个进程是不会主动放弃连接的,毕竟随意放弃也就是去了p的意义了。 所以用apache+mysql_pconnect要把握好配置,个人感觉高访问db的,这个组合不太靠谱,mysql_connect非p模式靠谱。
上面说了apache+mysql_pconnect不靠谱,那要怎要进行db的优化呢。p就是为了减少连接,既然存在毛病,那就用逻辑来减少连接,使用apache+mysql_connect。常用的,肯定是用memc或redis缓存select操作,这个是要求逻辑的。还有另外一个,就是主从库,或者分库。这个也是减少每个db服务器的连接数,2个db就会使连接减半。这个也要求逻辑hash。
其实mysql_pconnect()本身并没有做太多的处理, 它唯一做的只是在php运行结束后不主动close掉mysql的连接.
mysql_pconnect()与此同时mysql_connect()的区别:
cgi方式下:
在php经cgi方式运行时pconnect和connect是基本没有区别的, 因为cgi方式是每一个php访问起一个进程, 访问结束后进程也就结束了, 资源也全释放了.
apache模块方式下:
区别在于当php以apache模块方式运行时, 由于apache有使用进程池, 一个httpd进程结束后会被放回进程池, 这也就使得用pconnect打开的的那个mysql连接资源不被释放, 于是有下一个连接请求时就可以被复用.
这就使得在apache并发访问量不大的时候, 由于使用了pconnect, php节省了反复连接db的时间, 使得访问速度加快. 这应该是比较好理解的.
但是在apache并发访问量大的时候, 如果使用pconnect, 会由于之前的一些httpd进程占用的mysql连接没有close, 则可能会因为mysql已经达到最大连接着, 使得之后的一些请求永远得不到满足.
例如:
若mysql最大连接数设为500, 而apache的最大同时访问数设为2000
假设所有访问都会要求访问db, 而且操作时间会比较长
当前500个请求的httpd都没有结束的时候...之后的httd进程都是无法连接到mysql的(因已经达到mysql最大连接数). 只有当前500个httpd进程结束或被复用才可以连接得到了mysql.
其实这个也很好解释了xgy_p的测试中若操作比较简单, pconnect比connect效率高很多, 而且跟使用jsp的连接池的速度比较接近. 因为这个时候httpd进程可以不断的给复用.
而 当DB操作复杂, 耗时较长时, 因httpd会fork很多并发进程处理, 而先产生的httpd进程不释放db连接, 使得后产生的httpd进程无法连上db. 因为这样没有复用其它httpd进程的mysql连接. 于是会就产生很多连接超时, 像一开始的1000个并发连接测试说几乎都是连接超时就是这个原因.
---
(反进来看jsp用的如果是纯粹的db连接池, 则不会有因为达到mysql连接上限而连不上的问题, 因为jsp的连接池会使得可以等待其它连接使用完毕并复用. )
因此在并发访问量不高时,使用pconnect可以简单提高访问速度, 但在并发量增大后, 是否再使用pconnect就要看程序员的选择了..
就我个人认为, php现在对mysql的连接并没有真正用到连接池, pconnect也只是相当于借了apache的进程池来用, 所以在并发访问量大的时候pconnect并不能很好的提高访问DB效率. 在这一点上. php的确比不上jsp.
就目前的这种情况, 如果并发量大的话, 我个人建议最好还用mysql_connect.
永久的数据库连接是指在您的脚本结束运行时不关闭的连接。当收到一个永久连接的请求时。PHP 将检查是否已经存在一个(前面已经开启的)相同的永久连接。如果存在,将直接使用这个连接;如果不存在,则建立一个新的连接。所谓“相同”的连接是指用相同的用户名和密码到相同主机的连接。
对 WEB 服务器的工作和分布负载没有完全理解的读者可能会错误地理解永久连接的作用。特别的,永久连接不会在相同的连接上为您提供建立“用户会话”的能力,也不提供有效建立事务的能力。实际上,从严格意义上来讲,永久连接不会给您提供任何非永久连接无法提供的特殊功能。
为什么?
这和 WEB 服务器工作的方式有关。您的 WEB 服务器可以用三种方法来利用 PHP 生成 WEB 页面。
第一种方法是将 PHP 用作一个“外壳”。以这种方法运行,PHP 会为向您的 WEB 服务器提出的每个 PHP 页面请求生成并结束一个 PHP 解释器线程。由于该线程会随每个请求的结束而结束,因此任何在这个线程中利用的任何资源(例如指向 SQL 数据库服务器的连接)都会随线程的结束而关闭。在这种情况下,您使用永久连接不会获得任何地改变――因为它们根本不是永久的。
第二,也是最常用的方法,是把 PHP 用作多过程 WEB 服务器的一个模块,这种方法目前只适用于 Apache。对于一个多过程的服务器,其典型特征是有一个父过程和一组子过程协调运行,其中实际生成 WEB 页面的是子过程。每当客户端向父过程提出请求时,该请求会被传递给还没有被其它的客户端请求占用的子过程。这也就是说当相同的客户端第二次向服务端提出请求时,它将有可能被一个不同的子过程来处理。在开启了一个永久连接后,所有请求 SQL 服务的后继页面都能够重新使用这个已经建立的 SQL Server 连接。
最后一种方法是将 PHP 用作多线程 WEB 服务器的一个插件。目前 PHP 4 已经支持 ISAPI、WSAPI 和 NSAPI(在 Windows 环境下),这些使得 PHP 可以被用作诸如 Netscape FastTrack (iPlanet)、Microsoft's Internet Information Server (IIS) 和 O'Reilly's WebSite Pro 等多线程 WEB 服务器的插件。永久连接的行为和前面所描述的多过程模型在本质上是相同的。注意 PHP 3 不支持 SAPI。
如果永久连接并没有任何附加的功能,那么我们使用它有什么好处?
答案非常简单――效率。当客户端对您的 SQL 服务器的连接请求非常频繁时,永久连接将更加高效。连接请求频繁的标准取决于很多因素。例如,数据库的种类,数据库服务和 WEB 服务是否在同一台服务器上,SQL 服务器如何加载负载等。但我们至少知道,当连接请求很频繁时,永久连接将显著的提高效率。它使得每个子过程在其生命周期中只做一次连接操作,而非每次在处理一个页面时都要向 SQL 服务器提出连接请求。这也就是说,每个子过程将对服务器建立各自独立的永久连接。例如,如果您有 20 个不同的子过程运行某脚本建立了永久的 SQL 服务器永久连接,那么实际上您向该 SQL 服务器建立了 20 个不同的永久连接,每个过程占有一个。
注意,如果永久连接的子过程数目超过了您设定的数据库连接数限制,系统将会产生一些缺陷。如果您的数据库的同时连接数限制为 16,而在繁忙会话的情况下,有 17 个线程试图连接,那么有一个线程将无法连接。如果这个时候,在您的脚本中出现了使得连接无法关闭的错误(例如无限循环),则该数据库的 16 个连接将迅速的受到影响。请查阅您使用的数据库的文档,以获取关于如何处理已放弃的及闲置的连接的方法。
警告
在使用永久连接时还有一些特别的问题需要注意。例如在永久连接中使用数据表锁时,如果脚本不管什么原因无法释放该数据表锁,其随后使用相同连接的脚本将会被永久的阻塞,使得您需要重新启动 httpd 服务或者数据库服务。另外,在使用事务处理时,如果脚本在事务阻塞产生前结束,则该阻塞也会影响到使用相同连接的下一个脚本。不管在什么情况下,您都可以通过使用 register_shutdown_function() 函数来注册一个简单的清理函数来打开数据表锁,或者滚回事务。或者更好的处理方法,是不在使用数据表锁或者事务处理的脚本中使用永久连接,这可以从根本上解决这个问题(当然您也可以在其它地方使用永久连接)。
以下是一点重要的总结。永久连接是为通常连接建立一对一的分布而设计的。这意味着您必须能够保证在将永久连接替换为非永久连接时,您脚本的行为不会改变。使用永久连接将(非常)有可能改变您脚本的效率,但不改变其行为!