什么是連接池?
顧名思義,連接池就是一堆預(yù)先創(chuàng)建好的連接,跟容器會(huì)有點(diǎn)像。連接池主要是在某種需要網(wǎng)絡(luò)連接的服務(wù),提前把連接建立好存起來(lái),然后存放在一個(gè)池子里面,需要用到的時(shí)候取出來(lái)用,用完之后再還回去。
MySQL連接過(guò)程
client 建立連接的認(rèn)證過(guò)程
- 1、server 監(jiān)聽(tīng)端口
- 2、client 向server建立TCP連接
- 3、server 向client發(fā)送挑戰(zhàn)碼報(bào)文(報(bào)文詳細(xì)內(nèi)容在下文中有分析)
- 4、client 使用挑戰(zhàn)碼加密密碼,將加密后的密碼包含在回包中,發(fā)送給server
- 5、server 根據(jù)client的回包,驗(yàn)證密碼的有效性,給client發(fā)送ok包或error包
- 6、client發(fā)送SQL執(zhí)行
- 7、關(guān)閉MySQL關(guān)閉、TCP連接

為什么使用連接池?

從圖可以看到想要執(zhí)行一條SQL語(yǔ)句每次都要走 圖:3.5-1都過(guò)程,至少要7步才可以成功。MySQL會(huì)有連接數(shù)上限的限制,而且每次都執(zhí)行那么多都步驟,頻繁把時(shí)間浪費(fèi)在了網(wǎng)絡(luò)IO上。
沒(méi)有連接池的做法類似我們買菜做飯,比如我們要做十個(gè)菜,每做一個(gè)菜就跑一趟菜市場(chǎng),挑菜、討價(jià)還價(jià)、回家、洗菜、下鍋、起鍋、洗鍋;這樣是不是很浪費(fèi)時(shí)間?那我們想要做十個(gè)菜,提前把這十個(gè)菜的材料都買回來(lái),都洗好備用,然后每次炒都時(shí)候直接下鍋炒就好了。連接池就是提前買好菜,洗好菜(創(chuàng)建連接、驗(yàn)證賬號(hào)密碼),在要炒菜的時(shí)候直接下鍋(執(zhí)行SQL)炒。

使用連接池之后,只有在連接池初始化的時(shí)候就進(jìn)行連接然后存到一個(gè)容器里面。每次要執(zhí)行SQL語(yǔ)句的時(shí)候先來(lái)這個(gè)池獲取連接對(duì)象,然后再發(fā)送SQL語(yǔ)句,當(dāng)SQL語(yǔ)句執(zhí)行完之后,再把這個(gè)連接歸還給連接池。
使用連接池每次執(zhí)行SQL語(yǔ)句只需要執(zhí)行 圖:3.5-1 的第6步就行了,復(fù)用了MySQL連接,在高并發(fā)情況下,節(jié)省了每次連接帶來(lái)的其他開(kāi)銷。
連接池有什么?
- 最小連接數(shù)
- 最大連接數(shù)
- 當(dāng)前連接數(shù)
- 連接池對(duì)象
- 獲取連接池超時(shí)時(shí)間
- 健康度檢查
實(shí)戰(zhàn):Swoole實(shí)現(xiàn)連接池
MysqlPool.php
/**
* 1、設(shè)置配置信息
* 2、創(chuàng)建連接對(duì)象
* 3、獲取連接對(duì)象
* 4、獲取連接對(duì)象,空閑連接不夠創(chuàng)建到最大連接數(shù)
* 5、執(zhí)行sql語(yǔ)句
* 6、歸還連接
*/
use SwooleCoroutineChannel;
class MysqlPool
{
// 最小連接數(shù)
private $min;
// 最大連接數(shù)
private $max;
// 當(dāng)前連接數(shù)
private $count = 0;
// 獲取超時(shí)時(shí)間
private $timeOut = 0.2;
// 連接池對(duì)象容器
private $connections;
// 配置信息
private $config = [];
// 連接池對(duì)象
private static $instance;
public function __construct(array $config){
$this->config = $config;
$this->min = $this->config['min'] ?? 2;
$this->max = $this->config['max'] ?? 4;
$this->timeOut = $this->config['time_out'] ?? 0.2;
$this->connections = new Channel($this->max);
}
/**
* 獲取連接池對(duì)象
* @param null $config
* @return MysqlPool
*/
public static function getInstance($config = null){
if (empty(self::$instance)) {
if (empty($config)) {
throw new RuntimeException("mysql config empty");
}
self::$instance = new static($config);
}
return self::$instance;
}
/**
* 初始化連接池
* @throws Exception
*/
public function init(){
for ($i = 0; $i < $this->min; $i++) {
$this->count++;
$mysql = $this->createDb();
$this->connections->push($mysql);
}
}
/**
* 創(chuàng)建數(shù)據(jù)庫(kù)連接對(duì)象
* @return PDO
* @throws Exception
*/
private function createDb(){
$dsn = "mysql:dbname={$this->config['database']};host={$this->config['db_host']}";
try {
$mysql = new PDO($dsn, $this->config['db_user'], $this->config['db_passwd']);
return $mysql;
} catch (PDOException $e) {
throw new Exception($e->getMessage());
}
}
/**
* 獲取數(shù)據(jù)庫(kù)連接
* @return mixed|null|PDO
* @throws Exception
*/
public function getConnection(){
$mysql = null;
// 判斷是否為空,如果池空了,判斷當(dāng)前連接數(shù)是否下于最大連接數(shù)
// 如果小于最大連接數(shù)創(chuàng)建新連接數(shù)
if ($this->connections->isEmpty()) {
if ($this->count < $this->max) {
$this->count++;
$mysql = $this->createDb();
} else {
$mysql = $this->connections->pop($this->timeOut);
}
} else {
$mysql = $this->connections->pop($this->timeOut);
}
// 獲取不到數(shù)據(jù)庫(kù)連接拋出異常
if (!$mysql) {
throw new Exception('沒(méi)有連接了');
}
// 當(dāng)協(xié)程結(jié)束之后歸還連接池
defer(function () use ($mysql) {
$this->connections->push($mysql);
});
return $mysql;
}
/**
* 調(diào)試打印連接池的容量,非主要代碼
* @param $str
*/
public function printLenth($str){
echo $str . $this->connections->length() . "n";
}
}
server.php
include './MysqlPool.php';
//創(chuàng)建http server
$http = new SwooleHttpServer("0.0.0.0", 9501);
$http->set(["worker_num" => 2]);
$http->on('WorkerStart', function ($serv, $worker_id) {
$config = [
'min' => 3,
'max' => 5,
'time_out' => 1,
'db_host' => '127.0.0.1',
'db_user' => 'root',
'db_passwd' => 'sunny123',
'database' => 'lv'
];
MysqlPool::getInstance($config)->init();
});
$http->on('request', function ($request, $response) {
try {
MysqlPool::getInstance()->printLenth(SwooleCoroutine::getCid() . '獲取前:');
$mysql = MysqlPool::getInstance()->getConnection();
MysqlPool::getInstance()->printLenth(SwooleCoroutine::getCid() . '歸還前:');
$result = $mysql->query("select * from sunny_member");
$row = $result->fetch(MYSQLI_ASSOC);
MysqlPool::getInstance()->printLenth(SwooleCoroutine::getCid() . '歸還后:');
$response->end($row['content']);
} catch (Exception $e) {
$response->end($e->getMessage());
}
});
$http->start();
本案例實(shí)現(xiàn):
- 最小連接數(shù)
- 最大連接數(shù)
- 當(dāng)前連接數(shù)
- 連接池對(duì)象
- 獲取連接池超時(shí)時(shí)間
思考:怎么實(shí)現(xiàn)健康度檢查?