php mysql 实现原理_PHP底层和mysql的通信原理

要清楚的几个概念:

FPM进程:进程数在php-fpm.ini中设置。没有设置 max_requests ,那么进程是不会销毁的,也就是说当一个进程里面出现死循环或者内存溢出等导致进程僵死的情况出现的时候,处理的进程就会少一个。

mysql连接数:一个进程连接到mysql的一个库,算是一个连接。连接数默认100,我们线上是5000,进程数在my.cnf中设置。mysql连接数要大于等于FPM进程数,否则会报错。

长连接和短连接:FPM短连接MYSQL的时候,无需调用CLOSE函数,因为在RSHUTDOWN的时候,会调用清理。长连接的时候,需要调用close,长连接会一直霸占资源,直到进程死掉。

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

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

vim php-fpm.ini

[www]

pm = static

pm.max_children = 5

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

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需要多少内存:

$size = memory_get_usage();

$unit = array('b','kb','mb','gb','tb','pb');

$memory = @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];

echo "当前 php-cgi 进程所使用内存:".$memory;

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

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

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

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

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

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

[www]

pm.max_children = 10

pm.start_servers = 5

pm.min_spare_servers = 2

pm.max_spare_servers = 8

;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

root 2269 0.0 0.1 134560 4616 ? Ss 14:27 0:00 php-fpm: master process (/etc/php/php-fpm.ini)

www-data 2270 0.2 0.2 136736 9188 ? S 14:27 0:00 php-fpm: pool www

www-data 2271 0.2 0.2 136740 9192 ? S 14:27 0:00 php-fpm: pool www

www-data 2272 0.2 0.2 134684 7284 ? S 14:27 0:00 php-fpm: pool www

www-data 2273 0.2 0.2 136732 9120 ? S 14:27 0:00 php-fpm: pool www

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的效果

mysql> show processlist;

+----+------------------+-----------+------+---------+------+----------------+-------------------------+

| Id | User | Host | db | Command | Time | State | Info |

+----+------------------+-----------+------+---------+------+----------------+-------------------------+

| 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |

+----+------------------+-----------+------+---------+------+----------------+-------------------------+

接下来我们看短连接:

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

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

+----+---------+-----------+------+---------+------+----------------+--------------------+

| Id | User | Host | db | Command | Time | State | Info |

+----+---------+-----------+------+---------+------+----------------+--------------------+

| 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |

+----+---------+-----------+------+---------+------+----------------+--------------------+

这也是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也是摸不着头脑吧?

第一次访问:

+----+-------+-------------------------+-------+---------+------+-------+------------------+

| Id | User | Host | db | Command | Time | State | Info |

+----+-------+-------------------------+-------+---------+------+-------+------------------+

| 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |

| 10 | redol | bbs.demo.kkk5.com:16650 | redol | Sleep | 34 | | NULL |

+----+-------+-------------------------+-------+---------+------+-------+------------------+

刷新一下网页

+-----+-------+-------------------------+-------+---------+------+-------+------------------+

| Id | User | Host | db | Command | Time | State | Info |

+-----+-------+-------------------------+-------+---------+------+-------+------------------+

| 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |

| 10 | redol | bbs.demo.kkk5.com:16650 | redol | Sleep | 4 | | NULL |

| 727 | redol | bbs.demo.kkk5.com:16657 | redol | Sleep | 1 | | NULL |

+-----+-------+-------------------------+-------+---------+------+-------+------------------+

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

+-----+-------+-------------------------+-------+---------+------+-------+------------------+

| Id | User | Host | db | Command | Time | State | Info |

+-----+-------+-------------------------+-------+---------+------+-------+------------------+

| 9 | root | localhost | NULL | Query | 0 | NULL | show processlist |

| 10 | redol | bbs.demo.kkk5.com:16650 | redol | Sleep | 4 | | NULL |

| 727 | redol | bbs.demo.kkk5.com:16657 | redol | Sleep | 1 | | NULL |

| 728 | redol | bbs.demo.kkk5.com:16659 | redol | Sleep | 16 | | NULL |

| 729 | redol | bbs.demo.kkk5.com:16661 | redol | Sleep | 12 | | NULL |

| 730 | redol | bbs.demo.kkk5.com:16663 | redol | Sleep | 8 | | NULL |

+-----+-------+-------------------------+-------+---------+------+-------+------------------+

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

Warning: mysqli::mysqli(): MySQL server has gone away in   ###也就是连接已经存在

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

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

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

vim /etc/mysql/my.cnf

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子进程销毁了嘛

作者:fujun_195a

链接:https://www.jianshu.com/p/d955a5413c7e

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/340585.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通过READ-BEHIND CACHE来控制缓慢的生产者

在我们的互联世界中,我们经常使用我们不拥有或无权改善的API中的数据。 如果一切顺利,他们的表现就会很好,每个人都会感到高兴。 但是太多次,我们不得不使用延迟小于最佳延迟的 API。 当然,答案是缓存该数据 。 但是&…

mysql用户名锁定_MySQL用户锁定

修改方式:set global log_warnings2;MySQL 5.5版本官方文档中,并没有用户锁的状态。因此不存在用户会被锁。。The account-locking capability depends on the presence of the account_locked column in themysql.user table. For upgrades to MySQL 5.…

python package和目录_PyCharm中Directory与Python package的区别

对于Python而言,有一点是要认识明确的,python作为一个相对而言轻量级的,易用的脚本语言(当然其功能并不仅限于此,在此只是讨论该特点),随着程序的增长,可能想要把它分成几个文件,以便逻辑更加清…

mysql过滤器_MYSQL复制过滤器

vim /etc/my.cnf.d/mariadb-server.cnf[mysqld]binlog-do-dbdb1 #白名单模式,仅允许主服务器上生成db1的二进制日志,此选项不支持一行指定多个参数,需要每个参数写一行binlog-do-dbdb2重启服务systemctl restart mariadb.service主服务器…

mysql锁表查询_Mysql数据库锁情况下开启备份导致数据库无法访问处理分享

[背景简介]MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),因为其速度、可靠性和适应性而备受关注。大多数人都认为在不需要事务化处理的情况下,MySQL是管理内容最好的选择。mysql虽然功能未必很强大,但因为它的开源、广泛传播&#xf…

junit编写测试代码_编写数据访问代码测试-不测试框架

junit编写测试代码当我们向数据访问代码编写测试时,是否应该测试其公共API的每种方法? 一开始听起来很自然。 毕竟,如果我们不测试所有内容,那么如何知道我们的代码可以按预期工作? 这个问题为我们提供了重要的线索&…

mysql直接执行文件格式_Windows 环境下执行 .sql 格式文件方式

windows 命令行中有2种执行 .sql 文件的方式:直接行文件 和 先进入mysql命令行然后执行文件。具体操作如下:1. 直接在windows命令行执行。打开windows命令行(winR打开运行窗口然后输入cmd,回车),进入mysql的本机地址,如果配置了环…

python输入多个坐标点_判断多个坐标是否在同一条直线上|Python练习系列[13]

练习内容:判断多个坐标是否在同一条直线上完整代码和注释如下print(请输入几个点的横纵坐标,程序将会返回这几个点是否在同一条直线上)def coor_nums():#获得每个值的横纵坐标int_list[]#初始化坐标列表wrong_list[]#初始化错误列表judgement#判断是否需要修正坐标值while True…

Java大数据处理的流行框架

大数据挑战 在公司需要处理不断增长的数据量的各个领域中,对大数据的概念有不同的理解。 在大多数这些情况下,需要以某种方式设计所考虑的系统,以便能够处理该数据,而不会随着数据大小的增加而牺牲吞吐量。 从本质上讲&#xff0c…

工信部python证书多少钱_python requests SSL证书问题

错误信息如下:requests.exceptions.SSLError: ("bad handshake: Error([(SSL routines, tls_process_server_certificate, certificate verify failed)],)",)python做爬虫,对于有的网站,需要验证证书,比如:1…

mysql binlog线程恢复_使用MySQL SQL线程回放Binlog实现恢复

[toc]1. 需求部分1.1 基于MySQL复制同步特性,尝试使用Replication的SQL线程来回放binlog,可基于以下逻辑模拟场景做全量xtrabackup备份模拟日常备份执行sysbench压测4张表,20个线程,压测10分钟,模拟大量binlog删除实例…

带有Prometheus的Spring Boot和测微表第6部分:保护指标

以前,我们使用Prometheus成功启动了Spring Boot应用程序。 Spring应用程序中的一个端点正在公开我们的指标数据,以便Prometheus能够检索它们。 想到的主要问题是如何保护此信息。 Spring已经为我们提供了强大的安全框架 因此,将其轻松用于…

列举python中常用的数据类型_列举Python常用数据类型并尽量多的写出其中的方法...

#1 把字符串的第一个字符大写string.capitalize()#2 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串string.center(width)#3 返回 str 在 string 里面出现的次数,如果 beg 或者 end 指定则返回指定范围内 str 出现的次数string.count(str, beg0, endl…

mysql 二元分词_MySQL 中文分词原理

一,首先我们来了解一下其他几个知识点:1. Mysql的索引意义?索引是加快访问表内容的基本手段,尤其是在涉及多个表的关联查询里。当然,索引可以加快检索速度,但是它也同时降低了索引列的插入,删除…

python 元类 type_Python 使用元类type创建类对象常见应用详解

本文实例讲述了Python 使用元类type创建类对象。分享给大家供大家参考,具体如下:type("123") 可以查看变量的类型;同时 type("类名",(父类),{类属性:值,类属性2:值}) 可以创建一个类。在Python中不建议一个函数具有不同的功能(重载)…

使用AWS Elastic Beanstalk轻松进行Spring Boot部署

朋友不允许朋友写用户身份验证。 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK。 在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。 几乎所有应用程序都依赖于身份验证。 开发人员以及雇用他们的公司都想确认谁在发出请求&…

mysql报错乱码_连接mysql服务器报错时,出现乱码

页头用了header(content-type:text/html;charsetutf-8);try{$this->dbonew PDO($dsn,$dbuser,$dbpassword);}catch(Exception $e){echo $e->getMessage();}连接失败时会报错,但是乱码,IE下编码查看是UTF-8,但是是乱码,如果选…

自学python条件_自学Python2.8-条件(if、if...else)

自学Python2.8-条件(if、if...else)1.if 判断语句if语句是用来进行判断的,其使用格式如下:if 要判断的条件:条件成立时,要做的事情当“判断条件”成立(True)时,才执行语句;反之,则不执行。执行语句可以为多…

mac lion 安装 mysql_mac osx下安装mysql

操作系统版本:mac osx 10.11mysql版本:官网下载dmg v5.6.33 https://www.mysql.com/安装步骤1.双击dmg安装2.开启mysql服务系统偏好设置-底部-mysql-打开服务这个时候还不能使用mysql命令,需要配置mysql命令的路径。3.配置环境变量mysql的路…

python爬虫实训日志_Python学习学习日志——爬虫《第一篇》(BeautifulSoup)

爬虫简介(学习日志第一篇)一、爬虫介绍爬虫:一段自动抓取互联网信息的程序,从互联网上抓取对于我们有价值的信息。二、Pyyhon爬虫架构Python 爬虫架构主要由五个部分组成,分别是调度器、URL管理器、网页下载器、网页解析器、应用程序(爬取的有…