我们都知道,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. mysql数据库中表记录的玩法

    一.增加表记录(相当于插入表记录) 1. 插入完整数据(顺序插入) 语法一: INSERT INTO 表名(字段1,字段2,字段3…字段n) VALUES(值1,值2,值3…值n); 语法二: INS ...

  2. 所有文本的 attributes 枚举,NSAttributedString

    // Predefined character attributes for text. If the key is not in the dictionary, then use the defau ...

  3. P3437 [POI2006]TET-Tetris 3D

    题目 P3437 [POI2006]TET-Tetris 3D 做法 一眼就是二维线段树,仔细想想,赋值操作怎么办??\(lazy\)标记放在一维,下一次又来放个标记二维就冲突了 正解:永久化标记 怎 ...

  4. 常用display属性

    table 1. 宽高由内容撑开 2.独占一行 3.可设置宽高 4.可设置margin.padding inline-table 与display: table大体一致,区别在于不独占一行,为行内元素 ...

  5. 恢复性训练day1

    DP: 0/1背包一个常见的错误是没有cmax(f[i][j],f[i-1][j]) 0/1背包的拓展中有转移式的变形,以及无限数量背包,分组背包等. 可化为背包问题的一般不会太难. 数组开小,出现大 ...

  6. PHP字符串函数大全

    无论哪种编程语言,字符串操作都是一个重要的基础,往往简单而重要.PHP为我们提供了大量的字符串操作函数,功能强大,使用也比较简单.在这里结合实例总结分析PHP字符串函数的功能. 1.addcslash ...

  7. web框架详解之三Modal

    一.Modal操作之创建表,添加数据 1. 配置Django中settings的设置连接mysql数据库,然后在mysql数据库中创建库 2. 在models中创建表.继承Model 3. 在sett ...

  8. UITableViewCell使用时注意事项

    1,注意使用重用机制(有利于提高效率) 2,做到通过改变模型去间接改变UI样式(做到永久改变,无论怎样拖动刷新,都不会恢复改变) 3,在通过传递模型给Cell控件布局时,记得完全覆盖(嗯,不好解释,主 ...

  9. iOS清除缓存功能开发

    在APP开发中,大量的图片或消息占用系统内存,造成一堆垃圾信息,所以开发清除缓存功能就显得必不可少了. 代码段1:获取文件的大小 - (long long) fileSizeAtPath:(NSStr ...

  10. mongodb $where 查询中的坑

    mongodb 查询中坑就是数字开头的字段不能用点号,只能用[""].例如: 即:db.datas.find({$where:"this['54bcfc6c329af61 ...