MySQL连接池

在传统的网站开发中,比如LNMP模式,由Nginx的master进程接收请求然后分给多个worker进程,每个worker进程再链接php-fpm的master进程,php-fpm再根据当前情况去调用其worker进程然后处理PHP,如果需要MySQL,在与MySQL建立连接,这个时候,如果有1000个请求打过来,就需要与MySQL建立1000个连接。如果请求达到万级或者百万级,这时数据库的压力就会非常大了。连接池技术就可以派上用场了,可以大大降低数据库连接数,还可以降低IO消耗。

为什么可以降低IO消耗?

MySQL短连接每次请求操作数据库都需要建立与MySQL服务器建立TCP连接,这是需要时间开销的。TCP连接需要3次网络通信。这样就增加了一定的延时和额外的IO消耗。请求结束后会关闭MySQL连接,还会发生3/4次网络通信。

而连接池采用的是MySQL的长连接模式,会一直保持与MySQL的连接,重用连接进行MySQL的操作,从而节省了建立连接和断开连接的消耗。

为什么可以降低数据连接数呢?

数据的连接池为维持若干个长连接,当新请求到达的时候,如果连接池空闲,就分配给连接池去处理,否则,后面的数据库连接请求将被加入到等待队列中。

用swoole实现一个简单的MySQL连接池

<?php
$serv = new swoole_server('0.0.0.0', 9509);
$serv->set(array(
'worker_num' => 50, //worker进程数量
'task_worker_num' => 10, //task进程数量 即为维持的MySQL连接的数量
)); function my_onReceive($serv, $fd, $from_id, $data)
{
echo "收到数据".$data.PHP_EOL;
//taskwait就是投递一条任务,这里直接传递SQL语句了
//然后阻塞等待SQL完成,并返回结果
$result = $serv->taskwait($data);
echo "任务结束".PHP_EOL;
if ($result !== false) {
list($status, $db_res) = explode(':', $result, 2);
if ($status == 'OK') {
//数据库操作成功了,执行业务逻辑代码,这里就自动释放掉MySQL连接的占用
//将处理结果发送给客户端
$serv->send($fd, var_export(unserialize($db_res), true) . "\n");
} else {
$serv->send($fd, $db_res);
}
return;
} else {
$serv->send($fd, "Error. Task timeout\n");//如果返回的是false,则说明taskwait等待超时,可以设置相应的等待超时时间
}
} function my_onTask($serv, $task_id, $from_id, $sql)
{
echo "开始做任务 task id:".$task_id.PHP_EOL;
static $link = null;
HELL:
if ($link == null) {
$link = @mysqli_connect("127.0.0.1", "root", "passwd", "database");
if (!$link) {
$link = null;
$serv->finish("ER:" . mysqli_error($link));
return;
}
}
$result = $link->query($sql);
if (!$result) { //如果查询失败了
if(in_array(mysqli_errno($link), [2013, 2006])){//错误码为2013,或者2006,则重连数据库,重新执行sql
$link = null;
goto HELL;
}else{
$serv->finish("ER:" . mysqli_error($link));
return;
}
}
if(preg_match("/^select/i", $sql)){//如果是select操作,就返回关联数组
$data = $result->fetch_assoc();
}else{//否则直接返回结果
$data = $result;
}
$serv->finish("OK:" . serialize($data));//调用finish方法,用于在task进程中通知worker进程,投递的任务已完成
//return "OK:".serialize($data);
} function my_onFinish($serv, $task_id, $data)
{
echo "任务完成";//taskwait 没有触发这个函数。。
echo "AsyncTask Finish:Connect.PID=" . posix_getpid() . PHP_EOL;
} $serv->on('receive', 'my_onReceive');
$serv->on('task', 'my_onTask');
$serv->on('Finish', 'my_onFinish'); $serv->start();//启动server

这里就不再说代码了,注释很清楚了,如果对swoole函数有什么疑问,请自行查看官方文档。

接下来,写了个客户端代码来做测试

<?php
$client = new swoole_client(SWOOLE_SOCK_TCP);//创建swoole tcp客户端
$client->connect('*.*.*.*', 9509, 10) or die("连接失败");//连接server
while(true){
echo "请输出要执行的sql: ";
$sql = trim(fgets(STDIN));
if($sql=='exit'){
break;
}
$client->send($sql);//发送要执行的sql
$data = $client->recv();//阻塞接受返回的结果
var_dump($data);//打印
}
$client->close();//关闭连接

运行,首先启动server

[root@VM_114_93_centos swoole]# php DBserver_task.php

然后运行client

[root@VM_114_93_centos swoole]# php DBclient.php
请输出要执行的sql: select * from s_lvs_vip;
string(103) "array (
0 =>
array (
'id' => '3',
'protocol' => 'tcp',
'lb_algo' => 'ip_hash',
),
)
"
请输出要执行的sql: insert into s_lvs_vip set protocol='tcp',lb_algo='ip_hash';
string(5) "true
"
请输出要执行的sql: select * from s_lvs_vip;
string(196) "array (
0 =>
array (
'id' => '3',
'protocol' => 'tcp',
'lb_algo' => 'ip_hash',
),
1 =>
array (
'id' => '4',
'protocol' => 'tcp',
'lb_algo' => 'ip_hash',
),
)
"
请输出要执行的sql: delete from s_lvs_vip where id=3;
string(5) "true
"
请输出要执行的sql: select * from s_lvs_vip;
string(103) "array (
0 =>
array (
'id' => '4',
'protocol' => 'tcp',
'lb_algo' => 'ip_hash',
),
)
"
请输出要执行的sql: exit

在看server端的输出结果:

[root@VM_114_93_centos swoole]# php DBserver_task.php
收到数据select * from s_lvs_vip;
开始做任务 task id:0
任务结束
收到数据insert into s_lvs_vip set protocol='tcp',lb_algo='ip_hash';
开始做任务 task id:1
任务结束
收到数据select * from s_lvs_vip;
开始做任务 task id:2
任务结束
收到数据delete from s_lvs_vip where id=3;
开始做任务 task id:3
任务结束
收到数据select * from s_lvs_vip;
开始做任务 task id:4
任务结束

性能测试

同样执行一条查询

不用连接池的PHP代码

<?php
$conn = @mysqli_connect("***.***.***.***","root","905407339",'hulk');
if($conn){
//mysqli_select_db($conn,"hulk");
$res=mysqli_query($conn,'select * from s_lvs_vip');
$row=mysqli_fetch_assoc($res);
var_dump($row);
}else{
echo "ERROR";
}

用连接池的PHP代码

<?php
$sql = 'select * from s_lvs_vip';
$client = new swoole_client(SWOOLE_SOCK_TCP);
$client->connect('***.***.***.***', 9509, 10) or die("连接失败");
$client->send($sql);
$data = $client->recv();
var_dump($data);
$client->close();

都用2000并发去测试多次,取平均值

ab -n 2000 -c 100 http://***.***.***.***/swoole/mysqli.php
ab -n 2000 -c 100 http://***.***.***.***/swoole/DBclient.php
类型 未使用连接池 使用了连接池
总耗时 34.563 seconds 25.407 seconds
总请求量 2000 2000
并发量 100 100
Failed requests 4 0
每秒钟请求量 57.87 [#/sec] (mean) 78.72 [#/sec] (mean)
客户端平均请求等待时间 1728.160 [ms] (mean) 1270.370 [ms] (mean)
服务端平均请求响应时间 17.282 [ms] 12.704 [ms]

通过这些测试数据,可以看出用了连接池以后,效果还是很明显的,有一个质的改变。

注 : 博主用的是腾讯云 1核 1M 1G内存的云主机,测试数据仅供参考。

补充

小知识补充

断线重连

造成这样的原因一般是sql操作的时间过长,或者是传送的数据太大。

在mysql中有一个wait_timeout参数,默认设置为8个小时,当超过8个小时没有数据交互时,mysql服务器会主动关闭掉超时的连接,对应的mysql 错误码是2006,报错为MySQL server has gone away,当查询的结果集超过 max_allowed_packet 也会出现这样的报错

错误:2013 (CR_SERVER_LOST)

消息:查询过程中丢失了与MySQL服务器的连接。

关于以上两个错误码的详细介绍,参照 http://blog.csdn.net/djvc/article/details/31379155

 

https://blog.csdn.net/qq_28602957/article/details/78305039

转 Swoole】用swoole简单实现MySQL连接池的更多相关文章

  1. 用swoole简单实现MySQL连接池

    MySQL连接池 在传统的网站开发中,比如LNMP模式,由Nginx的master进程接收请求然后分给多个worker进程,每个worker进程再链接php-fpm的master进程,php-fpm再 ...

  2. 如何在 Swoole 中优雅的实现 MySQL 连接池

    如何在 Swoole 中优雅的实现 MySQL 连接池 一.为什么需要连接池 ? 数据库连接池指的是程序和数据库之间保持一定数量的连接不断开, 并且各个请求的连接可以相互复用, 减少重复连接数据库带来 ...

  3. Swoole MySQL 连接池的实现

    目录 概述 代码 扩展 小结 概述 这是关于 Swoole 入门学习的第八篇文章:Swoole MySQL 连接池的实现. 第七篇:Swoole RPC 的实现 第六篇:Swoole 整合成一个小框架 ...

  4. 实现一个协程版mysql连接池

    实现一个协程版的mysql连接池,该连接池支持自动创建最小连接数,自动检测mysql健康:基于swoole的chanel. 最近事情忙,心态也有点不积极.技术倒是没有落下,只是越来越不想写博客了.想到 ...

  5. Swoole4-swoole创建Mysql连接池

    一 .什么是mysql连接池 场景:每秒同时有1000个并发,但是这个mysql同时只能处理400个连接,mysql会宕机. 解决方案:连接池,这个连接池建立了200个和mysql的连接,这1000个 ...

  6. swoole4创建Mysql连接池

    一 .什么是mysql连接池 场景:每秒同时有1000个并发,但是这个mysql同时只能处理400个连接,mysql会宕机.   解决方案:连接池,这个连接池建立了200个和mysql的连接,这100 ...

  7. Java Mysql连接池配置和案例分析--超时异常和处理

    前言: 最近在开发服务的时候, 发现服务只要一段时间不用, 下次首次访问总是失败. 该问题影响虽不大, 但终究影响用户体验. 观察日志后发现, mysql连接因长时间空闲而被关闭, 使用时没有死链检测 ...

  8. 一个简单的MySql数据库连接池的实现

    package cn.hc.connectionPool; import java.io.IOException; import java.io.InputStream; import java.sq ...

  9. workerman如何写mysql连接池

    首先要了解为什么用连接池,连接池能为你解决什么问题 连接池主要的作用1.减少与数据服务器建立TCP连接三次握手及连接关闭四次挥手的开销,从而降低客户端和mysql服务端的负载,缩短请求响应时间2.减少 ...

随机推荐

  1. Docker镜像下载很慢,各种加速无效

    mcr.microsoft.com/dotnet/......   改成: dockerhub.azk8s.cn/dotnet/...... 或 mcr.azk8s.cn/dotnet/......

  2. 运行netcore2.2程序是报错

    运行netcore2.2程序是报错 C:\myself\WuZhui\WuZhui\bin\Release\netcoreapp2.2>dotnet WuZhui.DLLError: An as ...

  3. C#开发PACS医学影像处理系统(十八):Dicom使用LUT色彩增强和反色

    在医生阅片确诊的过程中,当发线疑似病灶时在灰度显示下有时并不清晰,这时候就需要色彩增强效果来使灰度图像变为彩色图像. LUT可以简单的理解为0-255的颜色映射值,例如:彩虹编码,将其打包成LUT格式 ...

  4. 告别硬编码,让你的POI导入导出拥抱变化

    GitHub地址 | 博客 | 中文 | English | 原文链接 为什么使用AutoExcel? Excel导入导出在软件开发中非常常见,只要你接触过开发,就一定会遇到.相信很多人会跟我一样选择 ...

  5. 想要搭建个论坛?Guide哥调研了100来个 Java 开源论坛系统,发现这 5 个最好用!

    大家好!我是 Guide 哥,Java 后端开发.一个会一点前端,喜欢烹饪的自由少年. 最近有点小忙.但是,由于前几天答应了一位读者自己会推荐一些开源的论坛系统,所以,昨晚就简单地熬了个夜,对比了很多 ...

  6. 关于KeePass基于csv格式的批量导入与导出

    在KeePass的导出选项中,有一个KeePass CSV(1.x),导出后格式如下: "Account","Login Name","Passwor ...

  7. lombok使用(给自己看的,只为不要忘记自己用过的技术)

    如何使用? 一.1)eclipse使用方法 1. 从项目首页下载lombok.jar 2. 双击lombok.jar, 将其安装到eclipse中(该项目需要jdk1.6+的环境) 2)idea使用方 ...

  8. Centos-bash-4.1$

    错误: -bash-4.1$ where? 登录Centos时候,会显示4行这样的错误信息-bash-4.1$ why? 1. 该用户家目录缺少 .bashrc .bash_logout .base_ ...

  9. centos7 下 docker 安装

    前提: 目前,CentOS 仅发行版本中的内核支持 Docker. Docker 运行在 CentOS 7 上,要求系统为64位.系统内核版本为 3.10 以上. Docker 运行在 CentOS- ...

  10. Python练习题 036:Project Euler 008:1000位数字中相邻13个数字最大的乘积

    本题来自 Project Euler 第8题:https://projecteuler.net/problem=8 # Project Euler: Problem 8: Largest produc ...