我们都知道,php是不能直接操作 mysql的,他需要通过扩展提供接口调用,php的mysql扩展也好几个,只支持面向过程的mysql,既支持面向过程也支持面向对象的mysqli,只支持面向对象的PDO,当然无论是那个扩展,也只是php语法写法上的区别而已,底层其实是一样的。

今天我们不讲语法这些老掉牙的东西,我们随便找一个扩展,来分析一下 php底层 和 mysql 之间的通信原理。

首先我们来理解一下 php-fpm 的工作原理,php-fpm 是一个 php-cgi 进程管理器,其实就是一个连接池,它和nginx配合的工作原理如下。

我们先从最简单的静态方式入手观察他的工作原理

vim php-fpm.ini

  1. [www]
  2. pm = static
  3. pm.max_children = 5
  4. pm.max_requests = 2

上面三句话的含义是什么呢:

1、static 表示静态以静态方式生成 php-fpm 进程

2、pm.max_children = 5 表示当 php-fpm 启动时就启动 5 个 php-fpm 子进程 等待处理 nginx 发过来的请求

3、pm.max_requests = 2 表示每个 php-fpm 子进程处理 2 个请求就销毁,当然父进程每次看到有销毁的自然也就会生成新的子进程

我们来简单验证一下这个说法:

首先重启 php-fpm,让它复位一下

接下来写一条简单的语句输出当前进程ID

  1. echo "当前 php-fpm 进程ID:".posix_getpid();

不断刷新浏览器观察输出变化

当前 php-fpm 进程ID:24548

当前 php-fpm 进程ID:24549

当前 php-fpm 进程ID:24550

当前 php-fpm 进程ID:24547

当前 php-fpm 进程ID:24551

当前 php-fpm 进程ID:24548

当前 php-fpm 进程ID:24549

当前 php-fpm 进程ID:24550

当前 php-fpm 进程ID:24547

当前 php-fpm 进程ID:24551

当前 php-fpm 进程ID:24563

当前 php-fpm 进程ID:24564

当前 php-fpm 进程ID:24565

当前 php-fpm 进程ID:24566

当前 php-fpm 进程ID:24567

当前 php-fpm 进程ID:24563

当前 php-fpm 进程ID:24564

当前 php-fpm 进程ID:24565

当前 php-fpm 进程ID:24566

当前 php-fpm 进程ID:24567

当前 php-fpm 进程ID:24568

当前 php-fpm 进程ID:24569

当前 php-fpm 进程ID:24570

当前 php-fpm 进程ID:24571

当前 php-fpm 进程ID:24572

当前 php-fpm 进程ID:24568

当前 php-fpm 进程ID:24569

当前 php-fpm 进程ID:24570

当前 php-fpm 进程ID:24571

当前 php-fpm 进程ID:24572

可以看得出,第一批id不是按照顺序执行的,进程id为24547的进程是在第四位处理的,然后从下面开始,所有id都是顺序执行的而且每次生成的一批id都是递增,是不是有种mysql自增主键的赶脚呢?

这里需要注意的是,无论是静态还是下面的动态配置方式,只要没有设置 max_requests ,那么进程是不会销毁的,也就是说当一个进程里面出现死循环或者内存溢出等导致进程僵死的情况出现的时候,处理的进程就会少一个了

好吧理解了静态的处理方式,我们其实也很容易知道这个方式的弊端了,当然我们平时服务器不可能就开5个进程每个进程处理2个请求,我们来做一个简单的加减乘除,看看一个服务器应该开多少个 php-fpm 合适

首先我们来看看一个简单的echo需要多少内存:

  1. $size = memory_get_usage();
  2. $unit = array('b','kb','mb','gb','tb','pb');
  3. $memory = @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
  4. echo "当前 php-cgi 进程所使用内存:".$memory;

观察浏览器我们可以得到一下数据:

当前 php-cgi 进程所使用内存:227.17 kb

也就是说一个简单的什么都不干的php就已经占用了200多K的内存,当然这也不算多。

不过进程多了cpu切换进程速度就会变慢,所以这个数还是需要通过ab等测试工具才能测试出具体应该开多少比较合理

我们先从200开始,不断的增加,架设增加到800的时候,效率和400一样,那我们就没必要开800那么多进程浪费内存了。

那么问题就来了,如果同一时间请求出超过400呢?有人说会排队等待,真的会排队等待吗?答案明显是 php-fpm 是没能力排队了,因为处理请求的php-fpm子进程都用完了,那么等待也就只能是在 nginx 等待,通常一个 nginx 也不只是转发请求给 php-fpm 就完事了,他还要处理静态文件呢?如果这些php请求导致nginx的请求数过多一直在等待,那么访问静态文件自然也会卡了,这时候我们就需要配置成下面的动态处理方式。

  1. [www]
  2. pm.max_children = 10
  3. pm.start_servers = 5
  4. pm.min_spare_servers = 2
  5. pm.max_spare_servers = 8
  6. ;pm.max_requests = 2

上面五句话的含义是什么呢:

1、dynamic 表示静态以动态方式生成 php-fpm 进程

2、pm.max_children = 10 同时活动的进程数 10个

3、pm.start_servers = 5 表示当 php-fpm 主进程启动时就启动 5 个 php-fpm 子进程

4、pm.min_spare_servers = 2 表示最小备用进程数

5、pm.max_spare_servers = 8 表示最大备用进程数

6、pm.max_requests = 2 上面说过就不说了

当前 php-fpm 进程ID:2270

当前 php-fpm 进程ID:2271

当前 php-fpm 进程ID:2272

当前 php-fpm 进程ID:2273

当前 php-fpm 进程ID:2274

当前 php-fpm 进程ID:2270

当前 php-fpm 进程ID:2271

当前 php-fpm 进程ID:2272

当前 php-fpm 进程ID:2273

当前 php-fpm 进程ID:2274

当前 php-fpm 进程ID:2270

当前 php-fpm 进程ID:2271

当前 php-fpm 进程ID:2272

当前 php-fpm 进程ID:2273

当前 php-fpm 进程ID:2274

为什么这里没有重新生成新的进程?因为pm.max_requests = 2被注释掉了,这个上面其实已经提及过一次了

我们也可以从 ps 看出这批进程id

ps aux|grep php

  1. root 2269 0.0 0.1 134560 4616 ? Ss 14:27 0:00 php-fpm: master process (/etc/php/php-fpm.ini)
  2. www-data 2270 0.2 0.2 136736 9188 ? S 14:27 0:00 php-fpm: pool www
  3. www-data 2271 0.2 0.2 136740 9192 ? S 14:27 0:00 php-fpm: pool www
  4. www-data 2272 0.2 0.2 134684 7284 ? S 14:27 0:00 php-fpm: pool www
  5. www-data 2273 0.2 0.2 136732 9120 ? S 14:27 0:00 php-fpm: pool www
  6. www-data 2274 0.1 0.2 134684 7244 ? S 14:27 0:00 php-fpm: pool www

从上面我们可以看到一个 id 为 2269 的 php-fpm 主进程 管理着 id 为 2270、2271、2272、2273、2274 的5个php-fpm 子进程

这里需要注意的是,当并发大过start_servers数的处理能力是,备用进程才会启动,当并发数小的时候,备用进程也会销毁掉,所以无论什么时候,ps 出来的进程都是上面那5个

下面来看看php-fpm+mysql的效果

  1. mysql> show processlist;
  2. +----+------------------+-----------+------+---------+------+----------------+-------------------------+
  3. | Id | User | Host | db | Command | Time | State | Info |
  4. +----+------------------+-----------+------+---------+------+----------------+-------------------------+
  5. | 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |
  6. +----+------------------+-----------+------+---------+------+----------------+-------------------------+

接下来我们看短连接:

  1. $conn = new mysqli("192.168.0.170", "redol", "redol", "test_db");

然后不断访问上面的php文件,每次看到的都是

  1. +----+---------+-----------+------+---------+------+----------------+--------------------+
  2. | Id | User | Host | db | Command | Time | State | Info |
  3. +----+---------+-----------+------+---------+------+----------------+--------------------+
  4. | 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |
  5. +----+---------+-----------+------+---------+------+----------------+--------------------+

这也是php神奇的地方,居然不用close的,每次请求完了他就自己给你close掉和mysql的连接了,这点确实也让很多新手少了不少下面的烦恼啊

Warning: mysqli::mysqli(): (HY000/1040): Too many connections in ...

不要以为语言应该都是这样那就打错特错了,去看看golang吧

下面看看长连接

$conn = new mysqli("p:192.168.0.170", "redol", "redol", "test_db");

你没看错,mysqli的长连接和mysql不同,是在host前面加 p:,没有mysqli_pconnet 的用法,估计很多刚开始用mysqli也是摸不着头脑吧?

第一次访问:

  1. +----+-------+-------------------------+-------+---------+------+-------+------------------+
  2. | Id | User | Host | db | Command | Time | State | Info |
  3. +----+-------+-------------------------+-------+---------+------+-------+------------------+
  4. | 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |
  5. | 10 | redol | bbs.demo.kkk5.com:16650 | redol | Sleep | 34 | | NULL |
  6. +----+-------+-------------------------+-------+---------+------+-------+------------------+

刷新一下网页

  1. +-----+-------+-------------------------+-------+---------+------+-------+------------------+
  2. | Id | User | Host | db | Command | Time | State | Info |
  3. +-----+-------+-------------------------+-------+---------+------+-------+------------------+
  4. | 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |
  5. | 10 | redol | bbs.demo.kkk5.com:16650 | redol | Sleep | 4 | | NULL |
  6. | 727 | redol | bbs.demo.kkk5.com:16657 | redol | Sleep | 1 | | NULL |
  7. +-----+-------+-------------------------+-------+---------+------+-------+------------------+

再刷新一下网页,效果我就不发了,反正你知道最后无论怎么刷新,都是如下即可

  1. +-----+-------+-------------------------+-------+---------+------+-------+------------------+
  2. | Id | User | Host | db | Command | Time | State | Info |
  3. +-----+-------+-------------------------+-------+---------+------+-------+------------------+
  4. | 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |
  5. | 10 | redol | bbs.demo.kkk5.com:16650 | redol | Sleep | 4 | | NULL |
  6. | 727 | redol | bbs.demo.kkk5.com:16657 | redol | Sleep | 1 | | NULL |
  7. | 728 | redol | bbs.demo.kkk5.com:16659 | redol | Sleep | 16 | | NULL |
  8. | 729 | redol | bbs.demo.kkk5.com:16661 | redol | Sleep | 12 | | NULL |
  9. | 730 | redol | bbs.demo.kkk5.com:16663 | redol | Sleep | 8 | | NULL |
  10. +-----+-------+-------------------------+-------+---------+------+-------+------------------+

也就是说,长连接是真的会一直霸占mysql连接的,那么问题就来了,如果我没有重启 php-fpm,只重启了mysql,会出现什么问题呢?答案是第一次连接的时候会报下面错误

Warning: mysqli::mysqli(): MySQL server has gone away in

所以用长连接query前还是先判断有没有连接,没有就close连接,注意一定要close,再连接,否则连接是失效的。

下面我们来试试 Too many connections 的错误吧

先调整一下mysql的最大连接数

  1. vim /etc/mysql/my.cnf
  2. max_connections = 3

你没看错,我只给了他3个连接,而上面的php-fpm是5个,所以结果不用我说都知道了吧,如期的出现

Warning: mysqli::mysqli(): (HY000/1040): Too many connections in ...

所以线上的mysql,还是注意一下这个max_connections数吧,我只能告诉你他默认是100,如果你觉得100不够用的话,自己改去吧

从上面可知,短连接是不用close也会自动关闭的,那如果是设置了 pm.max_requests = 2,每个php-fpm处理两个请求就销毁,销毁了会close么?我就不截图了,直接告诉答案吧,会的,所以无论如何,看到的mysql都是1、2、3、4、5、4、3、2、1、2、3、4、5这样的连接数,就是慢慢增加,再慢慢减少,减少是因为php-fpm子进程销毁了嘛

好吧,论文结束了,好好把php-fpm的原理已经mysql长短连接理解了一遍

php-fpm 和 mysql 之间的关系的更多相关文章

  1. 配置apache、php、mysql之间的关系

    1.index.php文件放入/usr/local/apache2/htdocs 目录下 其中index.php里面内容为: <?php phpinfo(); $dbc= mysql_conne ...

  2. mysql中相关,无关子查询,表与表之间的关系以及编码和乱码的解决

    ※MySQL中的字符编码(注意,utf8中没有'-',跟Java中不一样)SHOW VARIABLES; //查看系统变量//查询字符编码相关的系统变量SHOW VARIABLES WHERE var ...

  3. MySQL表与表之间的关系详解

    外键 说到表与表之间的关系就不得不说到一个关键词:外键 MySQ中的外键是什么,和表与表之间有什么关联? 外键(foreign key)又叫外连接, 在数据库中发挥着重要的作用 尤其是对于表和表之间的 ...

  4. MySQL数据库:SQL语句基础、库操作、表操作、数据类型、约束条件、表之间的关系

    数据库相关概念: 1. 数据库服务器:运行数据库管理软件的计算机 2. 数据库管理软件:MySQL.Oracle.db2.slqserver 3. 库:文件夹,用来组织文件/表 4. 表:文件(类似于 ...

  5. MySql系列表之间的关系

    foreign key 快速理解foreign key   员工信息表有三个字段:工号  姓名  部门 公司有3个部门,但是有1个亿的员工,那意味着部门这个字段需要重复存储,部门名字越长,越浪费 数据 ...

  6. MySQL 表之间的关系

    表之间的关系 # 定义一张部门员工表 emp id name gander dep_name dep_desc 1 ming male 教学部 教书 2 lilei male 教学部 教书 3 ham ...

  7. mysql 中表与表之间的关系

    如何找出两张表的对应关系 分析步骤: 1.先找出左表的角度去找 ​ 是否左表的多条记录可以对应右表的一条记录,如果是,则证明左表的一个字段foreign key 右表一个字段 (通常是id) 2.再站 ...

  8. Mysql 表与表之间的关系

    一.前言 二.表与表之间的关系 (一) 一对多 (二) 一对一 (三) 多对多 一.前言 研究表与表之间的关系前,先要知道将所有数据存放在一张表中的弊端: 1.结构不清晰 ---> 不致命 2. ...

  9. mysql数据库表格之间的关系

    外键 昨日内容回顾: 字段类型 约束条件 创建表的完整语法 create table 表名( 字段名 字段类型[(宽度) 约束条件], 字段名 字段类型[(宽度) 约束条件], 字段名 字段类型[(宽 ...

随机推荐

  1. nodejs socket server 强制关闭客户端连接

    nodejs socket server 强制关闭客户端连接: client.destroy()

  2. XSS - html过滤

    JS 根据白名单过滤HTML http://jsxss.com/zh/index.html   方案一: java的一个方案, 可以参考:  http://winnie825.iteye.com/bl ...

  3. Archimate

    archimate语言提供了一种用于表示企业体系结构的图形化语言,包括策略,转换和迁移规划,以及架构的动机和基本原理.该标准的设计尽可能紧凑,但仍可用于大多数企业体系结构建模需求.下图显示了Archi ...

  4. Cuckoo Hash——Hash冲突的解决办法

    参考文献: 1.Cuckoo Filter hash算法 2.cuckoo hash 用途: Cuckoo Hash(布谷鸟散列).问了解决哈希冲突的问题而提出,利用较少的计算换取较大的空间.占用空间 ...

  5. octotree神器 For Github and GitLab 火狐插件

    Code tree for GitHub and GitLabExtension to show code tree for GitHub and GitLab. Useful for develop ...

  6. php分类树

    class Category{ public $sonName; public $parentName; public function __consturt($id,$parent_id){ $th ...

  7. OCI编程

    OCI编码步骤: .定义OCI数据结构 .连接到Oracle数据库:使用orlon函数调用还可以并发连接多个数据库. .打开光标.通过调用oopen来打开一个光标. .分析语句.使用oparse来分析 ...

  8. CentOS 5下freeswitch中集成使用ekho实现TTS功能三

    四:在freeswitch中调用ekho 注:在测试过程中该语音包好像没用 FreeSWITCH 中文语音包测试版fssounds.zip 在/usr/local/freeswitch/sounds/ ...

  9. Docker容器技术-自动化部署

    一.用Chef自动化部署Docker 1.为什么需要自动化部署? Docker引擎需要配置很多参数(cgroups.内存.CPU.文件系统等) 识别Docker容器运行在哪个宿主机上 耗时且容易出错, ...

  10. 案例:1 Ionic Framework+AngularJS+ASP.NET MVC WebApi Jsonp 移动开发

    落叶的庭院扫的一干二净之后,还要轻轻把树摇一下,抖落几片叶子,这才是Wabi Sabi的境界. 介绍:Ionic是移动框架,angularjs这就不用说了,ASP.Net MVC WebApi提供数据 ...