侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

PHP并发查询MySQL的实例代码

php  /  管理员 发布于 7年前   122

最近在研究PHP,很喜欢,碰到PHP并发查询MySQL的问题,研究了一下,顺便留个笔记:

同步查询

这是我们最常的调用模式,客户端调用Query[函数],发起查询命令,等待结果返回,读取结果;再发送第二条查询命令,等待结果返回,读取结果。总耗时,会是两次查询的时间之和。简化一下过程,例如下图:

 

例图,由1.1到1.3为一个Query[函数]的调用,两次查询,就要串行经历1.1、1.2、1.3、2.1、2.2、2.3,尤其在1.2和2.2会阻塞等待,进程没法做其他事情。

同步调用的好处是,符合我们的直观思维,调用和处理都简单。缺点是进程阻塞在等待结果返回,增加额外的运行时间。
如果,有多条查询请求,或者进程还有其他的事情处理,那么能否把等待的时间也合理利用起来,提高进程的处理能力呢,显然是可以的。

拆分

现在,我们把Query[函数]打碎,客户端在1.1后,马上返回,客户端跳过1.2,在1.3有数据达到后再去读取数据。这样进程在原来的1.2阶段就解放了,可以做更多的事情,例如…再发起一条sql查询[2.1],是否看到了并发查询的雏形了。

并发查询

相对于同步查询的下一条查询的发起都在上一条完成后,并发查询,可以在上一条查询请求发起后,立刻发起下一条查询请求。简化一下过程,下图:

 

例图,在1.1.1成功发送完请求后,立马返回[1.1.2],最终查询结果的返回时在遥远的1.2 。但是在,1.1.1到1.2中间,还发起了另一个查询请求,这时间段内,就同时发起了两条查询请求,2.2先于1.2到达,那么两条查询的总耗时,只相当于第一条查询的时间。

并发查询的优点是,可以提高进程的使用率,避免阻塞等待服务器处理查询,缩短了多条查询的耗时。但缺点也很明显,发起N条并发查询,就需要建立N条数据库链接,对于有数据库连接池的应用来说,可以避免这种情况。

退化

理想情况下,我们希望并发N条查询,总耗时等于查询时间最长的一条查询。但也有可能并发查询会[退化]为[同步查询]。What?例图中,如果1.2在2.1.1前就返回了,那么并发查询就[退化]为[同步查询]了,但付出的代价却比同步查询要高。

多路复用

  • 发起query1
  • 发起query2
  • 发起query3
  • ………
  • 等待query1、query2、query3
  • 读取query2结果
  • 读取query1结果
  • 读取query3结果

那么,怎么等待知道什么时候查询结果返回了,又是哪个的查询结果返回呢?

对每个查询IO调用read?如果是遇上阻塞IO,这样就会阻塞在一个IO上,其他IO有结果返回了,也没法处理。那么,如果是非阻塞IO,那不用怕会阻塞在其中一个IO上了,确实是,但又会造成不断地轮询判断,浪费CPU资源。

对于这种情况可以使用多路复用轮询多个IO。

PHP实现并发查询MySQL

PHP的mysqli(mysqlnd驱动)提供多路复用轮询IO(mysqli_poll)和异步查询(MYSQLI_ASYNC、mysqli_reap_async_query),使用这两个特性实现并发查询,示例代码:

query($sql, MYSQLI_ASYNC); // 发起异步查询,立即返回  $links[$link->thread_id] = $link; } $llen = count($links); $process = 0; do {  $r_array = $e_array = $reject = $links;  // 多路复用轮询IO  if(!($ret = mysqli_poll($r_array, $e_array, $reject, 2))) {   continue;  }  // 读取有结果返回的查询,处理结果  foreach ($r_array as $link) {   if ($result = $link->reap_async_query()) {    print_r($result->fetch_row());    if (is_object($result))     mysqli_free_result($result);   } else {   }   // 操作完后,把当前数据链接从待轮询集合中删除   unset($links[$link->thread_id]);   $link->close();   $process++;  }  foreach ($e_array as $link) {   die;  }  foreach ($reject as $link) {   die;  } }while($process < $llen); $tvs = microtime(); $tv = explode(' ', $tvs); $end = $tv[1] * 1000 + (int)($tv[0] * 1000); echo $end - $start,PHP_EOL;

mysqli_poll源码:

#ifndef PHP_WIN32#define php_select(m, r, w, e, t) select(m, r, w, e, t)#else#include "win32/select.h"#endif/* {{{ mysqlnd_poll */PHPAPI enum_func_statusmysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, int * desc_num){ struct timeval tv; struct timeval *tv_p = NULL; fd_set   rfds, wfds, efds; php_socket_t max_fd = 0; int    retval, sets = 0; int    set_count, max_set_count = 0; DBG_ENTER("_mysqlnd_poll"); if (sec < 0 || usec < 0) {  php_error_docref(NULL, E_WARNING, "Negative values passed for sec and/or usec");  DBG_RETURN(FAIL); } FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); // 从所有mysqli链接中获取socket链接描述符 if (r_array != NULL) {  *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array);  set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd);  if (set_count > max_set_count) {   max_set_count = set_count;  }  sets += set_count; } // 从所有mysqli链接中获取socket链接描述符 if (e_array != NULL) {  set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd);  if (set_count > max_set_count) {   max_set_count = set_count;  }  sets += set_count; } if (!sets) {  php_error_docref(NULL, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");  DBG_ERR_FMT(*dont_poll ? "All arrays passed are clear":"No stream arrays were passed");  DBG_RETURN(FAIL); } PHP_SAFE_MAX_FD(max_fd, max_set_count); // select轮询阻塞时间 if (usec > 999999) {  tv.tv_sec = sec + (usec / 1000000);  tv.tv_usec = usec % 1000000; } else {  tv.tv_sec = sec;  tv.tv_usec = usec; } tv_p = &tv; // 轮询,等待多个IO可读,php_select是select的宏定义 retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p); if (retval == -1) {  php_error_docref(NULL, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",      errno, strerror(errno), max_fd);  DBG_RETURN(FAIL); } if (r_array != NULL) {  mysqlnd_stream_array_from_fd_set(r_array, &rfds); } if (e_array != NULL) {  mysqlnd_stream_array_from_fd_set(e_array, &efds); } // 返回可操作的IO数量 *desc_num = retval; DBG_RETURN(PASS);}

并发查询操作结果

为了更直观地看效果,我找了一个1.3亿数据量并且没有优化过的表进行操作。

并发查询的结果:

 

同步查询的结果:

 

从结果来看,同步查询的总耗时是所有查询的时间的累加;而并发查询的总耗时在这里其实是查询时间最长的那一条(同步查询的第四条,耗时是10几秒,符合并发查询的总耗时),而且并发查询的查询顺序和结果到达的顺序是不一样的。

多条耗时较短的查询对比

使用多条查询时间较短的sql进行对比一下

并发查询的测试1结果(数据库链接时间也统计进去):

 

同步查询的结果(数据库链接时间也统计进去):

 

并发查询的测试2结果(不统计数据库链接时间):

 

从结果上看,并发查询测试1并没有讨到好处。从同步查询上看,每条查询耗时大概3-4ms左右。但如果不把数据库链接时间统计进去(同步查询只有一次数据库链接),并发查询的优势又能体现出来了。

结语

这里探讨了一下PHP实现并发查询MySQL,从实验上结果直观地认识了并发查询的优缺点。建立数据库连接的时间在一条优化了的sql查询上,占得比重还是很大。#没有连接池,要你何用

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

您可能感兴趣的文章:

  • php并发对MYSQL造成压力的解决方法
  • 解析php mysql 事务处理回滚操作(附实例)
  • php实现mysql事务处理的方法
  • php下pdo的mysql事务处理用法实例
  • PHP+Mysql基于事务处理实现转账功能的方法
  • php+Mysqli利用事务处理转账问题实例
  • PHP通过插入mysql数据来实现多机互锁实例
  • PHP+MySQL高并发加锁事务处理问题解决方法


  • 上一条:
    PHP编程实现脚本异步执行的方法
    下一条:
    Yii2框架中使用PHPExcel导出Excel文件的示例
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 用Time Warden监控PHP中的代码处理时间(0个评论)
    • 在PHP中使用array_pop + yield实现读取超大型目录功能示例(0个评论)
    • Property Hooks RFC在PHP 8.4中越来越接近现实(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-11
    • 2017-12
    • 2018-01
    • 2018-02
    • 2018-03
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-09
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-09
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客