弹窗出来qq,微信分享弹窗,却不知道分享给谁了,这

PHP中MySQL和Mongodb长连接的实现
MySQL长连接
在使用mysql扩展的时候时代,常听人说mysql长短连接,那么他们之间到底有什么区别呢?百度,google之答案都是千篇一律,都是些简单的介绍,即使有个别试图从源代码上解释,但是仔细分析也是错误的,抱着抛砖引玉的心态,试着重新解释一下其实现原理。
首先在MySQL中连接都是没有分别的,但是在PHP连数据库的时候,却有长连接短连接之分。那么它是怎么实现的呢?
于是查看相关函数实现,PHP里建立长连接的是mysql_pconnect()函数,这个函数和mysql_connet()的内部实现都是一样基于php_mysql_do_connect(),不同的是php_mysql_do_connect()的persistent参数,mysql_pconnet()传递的是1,mysql_connect()是0。
在php_mysql_do_connect()中看到这样几行代码:
zend_rsrc_list_entry *
(zend_hash_find(&EG(persistent_list),
hashed_details, hashed_details_length 1, (void **)
&le)==FAILURE) {
再说EG的作用是取运行时全局变量。persistent_list是PHP内核定义的一个全局hash表,用于存放各种长连接。其定义在Zend\zend_globals.h,Apache进程初始化在main.c里面的php_module_startup()里面通过一系列的调用,最终使用zend_init_rsrc_plist()初始化。
(调用关系是php_module_startup() -& zend_post_startup()
-& executor_globals_ctor() -&
executor_globals_ctor() -&
zend_init_rsrc_plist())
可以看到初始化发生在进程创建的时候。
接下来讲hashed_details,这个hash表的key获取方式有2种分别为正常模式和安全模式,以下第一种是正常模式,第二种是安全模式,应为安全模式不允许有传入的user,passwd等变量,所以直接用了系统user变量。
hashed_details_length =
spprintf(&hashed_details, 0, "mysql_%s_%s_%s_%ld",
SAFE_STRING(host_and_port), SAFE_STRING(user), SAFE_STRING(passwd),
client_flags);
hashed_details_length =
spprintf(&hashed_details, 0, "mysql__%s_",
当创建一个连接成功后,调用以下代码把它添加到persistent_list里面
(zend_hash_update(&EG(persistent_list),
hashed_details, hashed_details_length 1, (void *)
&new_le, sizeof(zend_rsrc_list_entry),
NULL)==FAILURE) {
free(mysql);
efree(hashed_details);
MYSQL_DO_CONNECT_RETURN_FALSE();
最后讲关闭操作,在mysql_close()里面调用的是zend_list_delete()方法去清理连接资源,但是这个方法只能清理regular_list这个hash表里面的value。所以它不能关闭长连接。
其清理是在进程退出时,在php_module_shutdown()里面调用zend_shuntdown()函数,然后再面调用zend_destroy_rsrc_list()去清理的。
总之persistent_list这个hash表,进程被创建的时候就存在一直到进程退出时的时候才被清理。
通过以上分析基本清楚,PHP里面mysql长链的实现原理。其长连接的有效性是基于进程的!
以后在使用的时候,该怎么考虑呢?
首先mysql在执行语句的时候,不内置ping()功能,所以如果长连接超时了(是的,你没看错,PHP里面mysql长连接会超时,时间参考你用命令行工具连接mysql
server),就会gone away!
其次由于一个进程占用一个或者多个(一个hashed_details占用一个连接)连接而且并不释放,所以你很可能一台web
server占用数百个mysql连接。mysql连接数可能会很快耗尽。
其实persistent_list通常会用来处理各种长连接,也包括mssql,oracle,pgsql等数据库。
Mongodb长连接实现过程
PHP官方的Mongodb扩展里面其实没有长短连接之分,都是长连接。了解MySQL的实现过程,再看mongodb的实现,其实他们原理类似,但实现过程不一样。
通过跟踪mongodb的连接过程(__construct()/connect() -&
php_mongo_connect() -&
mongo_get_read_write_connection() -&
mongo_get_read_write_connection_replicaset() -&
mongo_get_connection_single() -&
mongo_connection_create() -&
mongo_connection_connect())
可以发现,在mongo_get_connection_single()里面会先尝试从hash表里面去取连接。
mongo_manager_connection_find_by_hash(manager,
找到成功返回,不成功则尝试创建新的连接。成功创建连接后在注册到manager。
mongo_manager_connection_register(manager,
mongodb是通过manager这个全局变量来存储连接的。这个变量是怎么定义的呢,其作用范围有多大。
通过阅读源代码发现在PHP_GINIT_FUNCTION()方法里面初始化了全局的manager,在PHP_GSHUTDOWN_FUNCTION()里面释放了manager。
通过hack发现PHP_GINIT_FUNCTION()在几个INIT_FUNCTION里面,最先被执行,先于PHP_MINIT_FUNCTION()在接受请求之前被初始化,进程退出清理连接。
和MySQL不同的地方,Mongodb的close()可以关闭连接,Mongodb在每次执行查询的时候都会先去ping一下服务存在不存在。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
VIM发烧友-面向对象编程OOP-迷人的火狐插件
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
de&&&&&de&de&publicde& de&functionde& de&__construct(de&de&$serversde&de&, de&de&$optionsde&de&=de&de&arrayde&de&());de&de&}de& 参数$server是PHP-MongoDB-Driver标准连接字符串格式 mongodb://[username:password@]host1[:port1][,host2[:port2:],…]/database 参数$options是一个数组。不同于标准MongoDB连接字符串,PHP-Mongo-Driver不将options拼接到连接字串中。
参数$options默认值: de&$optionsde& de&= de&de&arrayde&de&(de&de&&&&&de&de&'connect'de& de&=& truede&de&);de& 可选配置: de&$optionsde& de&= de&de&arrayde&de&(de&de&&&&&de&de&'connect'de& de&=& true, de&de&// true表示Mongo构造函数中建立连接。de&de&&&&&de&de&'timeout'de&de&=&xxxx, de&de&// 配置建立连接超时时间,单位是msde&de&&&&&de&de&'replicaSet'de&de&=&de&de&'name'de&de&, de&de&// 配置replicaSet名称de&de&&&&&de&de&'username'de&de&=&de&de&''de&de&, de&de&// 覆盖$server字符串中的username段,如果username包含冒号:时,选用此种方式。de&de&&&&&de&de&'password'de&de&=&de&de&''de&de&, de&de&// 覆盖$server字符串中的password段,如果password包含符号@时,选用此种方式。de&de&&&&&de&de&'db'de&de&=&de&de&''de& de&// 覆盖$server字符串中的database段de&de&);de& 登陆验证 MongoDB启动参数包含—auth或—keyFile时,想要执行数据库操作,需要先进行登陆验证。 你可以在连接字符串中知道用户名和密码进行登陆。 当连接关闭,驱动程序会自动尝试重新建立连接并且登陆。 de&&?phpde&de&$mde& de&= de&de&newde& de&Mongo(de&de&"mongodb://${username}:${password}@localhost"de&de&);de&de&?&de& 当然也可以使用方法进行登陆验证。 此方法登陆验证的数据库连接在失效后,仍会自动尝试建立连接,但不会自动尝试重新进行登陆验证。 de&&?phpde&de&$mde& de&= de&de&newde& de&Mongo();de&de&$dbde& de&= de&de&$mde&de&-&de&de&$dbde&de&-&authenticate(de&de&$usernamede&de&, de&de&$passwordde&de&);de&de&?&de& Replica Sets 指定一个或多个数据库服务器,且使用replicaSet选项连接。 de&&?phpde&de&$mde& de&= de&de&newde& de&Mongo(de&de&"mongodb://localhost:27017"de&de&, de&de&arrayde&de&(de&de&'replicaSet'de&de&=&de&de&'myReplicaSetName'de&de&));de&de&?&de& PHP-Mongo-Driver会查询在列出的数据库服务器中,到底哪个是主服务器。列出的服务器中包含master服务器才能成功建立连接。如 果无法连接到所有列出服务器或在这些服务器中没有找到master,将抛出MongoConnectionException异常。 当master出现异常,不能正常工作时,slaves会花一段时间来推选一台slave为新的master。在选举期间,连接到数据库的任何写操作将被禁止(除到slaves读操作不受影响)。 一旦master选举完成,驱动程序在读写操作时将主动探测谁是新master,然后将以此作为主库连接继续常规操作。 关于更多Replica Sets,可以参考http://www.mongodb.org/display/DOCS/Replication 支持Socket连接(Domain Socket Support) 如果在本地运行MongoDB,那么可以通过socket文件连接到MongoDB。MongoDB默认会自动打开一个本地socket文件:/tmp/mongodb- .sock。 de&&?phpde&de&$mde& de&= de&de&newde& de&Mongo(de&de&"mongodb:///tmp/mongo-27017.sock"de&de&);de&de&?&de& 如果你想通过socket进行验证登录需要显示指定port为0以表示socket文件位置描述的结束。 de&&?phpde&de&$mde& de&= de&de&newde& de&Mongo(de&de&"mongodb://username:password@/tmp/mongo-27017.sock:0/foo"de&de&);de&de&?&de& 连接池(connection pool) 建立连接是Driver最费时的工作。即时网络良好,建立连接还是会花费数百毫秒。驱动程序建立了连接池增加连接复用,帮助你减少建立连接的开销。 当创建一个Mongo实例,所有需要的链接都将从连接池中获取(ReplicaSets模式下可能需要用到复数个连接,因为到每个成员都有一个连接)。 Mongo实例析构时,连接返回到连接池。PHP进程退出时会关闭连接池中所有的连接。 为什么我打开了那么多连接? 连接池可以保持大量的连接。事实上连接池确实保持了大量连接,我们来估算一下。 连接数和三方面有关: connections_per_pool 理论上连接池对能保持连接数没有限制(直到耗尽文件句柄限制为止)。但实际应用中,使用完的连接将返回到连接池以便重复利用。在同一scope中新建新多个连接,这样就无法重复利用连接池了,这么做需要慎重。 通过MongoPool::info()函数可以查看当前使用的连接数。连接数的修改统计到”in use”和”in pool”两个域,分别表示使用中的连接数 和 空闲的连接数。 pools_per_process 对于每台MongoDB服务器地址都有自己的连接池。 例如通过”example.net”, “example.net:27017″, “localhost:27017″, “/tmp/mongodb-27017.sock”会对应生成3个连接池。详情参见MongoPool::info()。 processes PHP进程间的连接池都是独立的。php-fpm和Apache一般会有N个php子进程。确认配置文件中设置的php-fpm最大子进程数。 使用PHP-FPM的情况,保险起见,以max_children和spare_servers+start_servers中较大者为最大处理子进程数,也就是最多有那么多组连接池。 估算最大的连接数,用上面三个变量相乘: de&connections_per_pool*pools_per_process*processesde& 假设我们每个连接池有30个连接,每个PHP进程有10个连接池,总共有128个PHP进程。那么这台机器就有38400个连接。操作系统的最大文件句柄数限制要设置得足够高。 点击查看更多信息。 长连接(Persistent Connections) 注:本节特性适用于1.2.0或以上版本。1.2.0+后,所有连接都是持久连接并且由连接池管理。 使用长连接的直接原因是效率高。因为连接复用而省去了大量的创建连接的开销。长连接由PHP进程维护。 举个简单的例子,写段程序连接1000次数据库: de&&?phpde&de&forde& de&(de&de&$ide&de&=0; de&de&$ide&de&&1000; de&de&$ide&de&++) {de&de&&&de&de&$mde& de&= de&de&newde& de&Mongo();de&de&}de&de&?&de& 这段代码执行了大约18秒。我们看看改用长连接的情况: de&&?phpde&de&forde& de&(de&de&$ide&de&=0; de&de&$ide&de&&1000; de&de&$ide&de&++) {de&de&&&de&de&$mde& de&= de&de&newde& de&Mongo(de&de&"localhost:27017"de&de&, de&de&arrayde&de&(de&de&"persist"de& de&=& de&de&"x"de&de&));de&de&}de&de&?&de& 执行代码花费时间小于0.2秒,因为只建立了一个数据库连接。 建立长连接需要一个标识字串(如上例中的x)确保唯一性。需要复用一个长连接,域名、端口、标识字串、用户名和密码(如果有密码的话)都需要完全匹配。否则就会创建一个不同的长连接。 记得在生产环境中总是推荐使用长连接,除非你有强而有力的理由让你不那么做。这些理由多数和关系型数据库有关,而且和MongoDB没有半毛钱关系。 最佳实践: 强烈建议在新建Mongo对象时加上try/catch,因为如果连接失败时在输出的异常信息里会包含username/password对(php默认设置会显示异常和错误信息)。 de&tryde&de&{de&de&$mde& de&= de&de&newde& de&Mongo(de&de&"mongodb://${username}:${password}@host"de&de&);de&de&}de&de&catchde&de&(Exception de&de&$ede&de&){de&de&…de&de&}de& 翻译整理:
阅读(10428)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_',
blogTitle:'PHP - MongoDB连接攻略',
blogAbstract:'这里的博客停止维护,请大家点下方的链接访问博主新家对应的博文:
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}}

我要回帖

更多关于 微信小程序自定义弹窗 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信