Skip to content

Commit 04ebc86

Browse files
author
Ethan
committed
reconnect
1 parent 7bf0744 commit 04ebc86

File tree

2 files changed

+101
-44
lines changed

2 files changed

+101
-44
lines changed

src/Connection.php

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Connection
3131
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
3232
PDO::ATTR_STRINGIFY_FETCHES => false, //禁止提取的时候将数值转换为字符串
3333
PDO::ATTR_EMULATE_PREPARES => false, //禁止模拟预处理语句
34-
PDO::ATTR_CASE => PDO::CASE_NATURAL,
34+
PDO::ATTR_CASE => PDO::CASE_NATURAL, //列名原样返回,不做大小写转换
3535
PDO::ATTR_TIMEOUT => 1, //连接超时秒数
3636
),
3737
'slave' => array(/*
@@ -47,8 +47,9 @@ class Connection
4747
),*/
4848
),
4949

50-
//服务器超时并关闭连接时,是否自动重连 (常驻内存时需要开启)
51-
'serverGoneAwayReconnect' => false,
50+
//服务器超时并关闭连接时,是否自动重连 (常驻内存时按需开启)
51+
//MySQL server has gone away
52+
'reconnect' => false,
5253
);
5354

5455
//连接类型
@@ -88,23 +89,6 @@ public function setReadPdo(PDO $pdo)
8889
$this->readPdo = $pdo;
8990
}
9091

91-
protected function makePdo(array $config)
92-
{
93-
if (isset($config['dsn'])) {
94-
$dsn = $config['dsn'];
95-
} else {
96-
$dsn = 'mysql:host=' . $config['host'] . ';dbname=' . $config['database'];
97-
}
98-
99-
$pdo = new PDO($dsn, $config['username'], $config['password'], $config['options']);
100-
101-
if (strpos($dsn, 'mysql:') === 0) {
102-
$pdo->exec('SET NAMES ' . $pdo->quote($config['charset']));
103-
}
104-
105-
return $pdo;
106-
}
107-
10892
/**
10993
* 返回用于查询的PDO对象 (如果在事务中,将自动调用getPdo()以确保整个事务均使用主库)
11094
* @return PDO
@@ -144,11 +128,28 @@ public function getReadPdo()
144128
} while (count($slaveDbConfig) > 0);
145129

146130
// 从库不可用时使用主库
147-
// return $this->readPdo = $this->getPdo();
131+
// return $this->getPdo();
148132

149133
throw $ex;
150134
}
151135

136+
protected function makePdo(array $config)
137+
{
138+
if (isset($config['dsn'])) {
139+
$dsn = $config['dsn'];
140+
} else {
141+
$dsn = 'mysql:host=' . $config['host'] . ';dbname=' . $config['database'];
142+
}
143+
144+
$pdo = new PDO($dsn, $config['username'], $config['password'], $config['options']);
145+
146+
if (strpos($dsn, 'mysql:') === 0) {
147+
$pdo->exec('SET NAMES ' . $pdo->quote($config['charset']));
148+
}
149+
150+
return $pdo;
151+
}
152+
152153
/**
153154
* 返回表前缀
154155
* @return string
@@ -248,11 +249,20 @@ private function makeStatementAndExecute($connType, $sql, $params = array())
248249
try {
249250

250251
if ($connType == self::CONN_TYPE_READ) {
251-
$statement = $this->getReadPdo()->prepare($sql);
252+
$pdo = $this->getReadPdo();
252253
} else {
253-
$statement = $this->getPdo()->prepare($sql);
254+
$pdo = $this->getPdo();
254255
}
256+
257+
//已连接上的服务器如果超时关闭连接后或者连接被手动kill,此方法并不会抛出PDOException,而是触发一条警告
258+
//Warning: PDO::prepare(): MySQL server has gone away
259+
//set global wait_timeout=5 连上mysql执行一条sql后,php中sleep(10),再执行下一条,就会报这个警告
260+
$statement = @$pdo->prepare($sql);
261+
255262
$start = microtime(true);
263+
264+
//如果执行时间较长的sql,被手动kill,则会在execute时先触发一条警告
265+
//Warning PDOStatement::execute(): MySQL server has gone away
256266
$statement->execute($params);
257267
$this->logQuery($sql, $params, $this->getElapsedTime($start));
258268
return $statement;
@@ -284,9 +294,11 @@ private function makeStatementAndExecute($connType, $sql, $params = array())
284294
// $sql = $this->quoteSql($sql);
285295
//
286296
// if ($useReadPdo) {
287-
// $statement = $this->getReadPdo()->prepare($sql);
297+
// $pdo = $this->getReadPdo();
298+
// $statement = @$pdo->prepare($sql);
288299
// } else {
289-
// $statement = $this->getPdo()->prepare($sql);
300+
// $pdo = $this->getPdo();
301+
// $statement = @$pdo->prepare($sql);
290302
// }
291303
//
292304
// $start = microtime(true);
@@ -376,6 +388,12 @@ public function disconnect($connType = null)
376388
}
377389

378390
if ($connType == self::CONN_TYPE_READ) {
391+
392+
//没有配置读库,只使用了主库
393+
if (!is_array($this->config['slave']) || count($this->config['slave']) == 0) {
394+
$this->pdo = null;
395+
}
396+
379397
$this->readPdo = null;
380398
return;
381399
}
@@ -392,7 +410,10 @@ public function disconnect($connType = null)
392410
private function resolveDisconnect($connType, PDOException $ex)
393411
{
394412
// https://dev.mysql.com/doc/refman/5.7/en/gone-away.html
395-
if ($this->config['serverGoneAwayReconnect'] && in_array($ex->errorInfo[1], array(2006, 2013))) {
413+
if ($this->config['reconnect']
414+
&& $this->getTransactions() == 0
415+
&& in_array($ex->errorInfo[1], array(2006, 2013))) {
416+
396417
$this->disconnect($connType);
397418
return;
398419
}
@@ -401,7 +422,7 @@ private function resolveDisconnect($connType, PDOException $ex)
401422
}
402423

403424
/**
404-
* 解析SQL中的占位置("?"或":"),主要用于调试SQL
425+
* 解析SQL中的占位置("?"或":") 用于调试SQL
405426
* @param string $sql
406427
* @param array $params
407428
* @return string
@@ -495,7 +516,7 @@ protected function quoteSimpleColumnName($name)
495516
}
496517

497518
/**
498-
* 开启记录所有SQL, 如果不开启,默认只记录最后一次执行的SQL
519+
* 开启记录所有SQL, 如果不开启, 默认只记录最后一次执行的SQL
499520
*/
500521
public function enableQueryLog()
501522
{
@@ -526,13 +547,21 @@ protected function logQuery($sql, $params = array(), $time = null)
526547

527548
/**
528549
* 返回执行的SQL
550+
* @param bool $clear 是否清空
529551
* @return array
530552
*/
531-
public function getQueryLog()
553+
public function getQueryLog($clear = true)
532554
{
533-
return $this->queryLog;
555+
$data = $this->queryLog;
556+
557+
if ($clear) {
558+
$this->queryLog = array();
559+
}
560+
561+
return $data;
534562
}
535563

564+
536565
/**
537566
* 返回最近一次执行的sql语句
538567
* @return string
@@ -547,7 +576,7 @@ public function getLastSql()
547576
}
548577

549578
/**
550-
* 计算所使用的时间
579+
* 计算所使用的时间 毫秒
551580
*
552581
* @param int $start
553582
* @return float

tests/demo.php

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,63 @@
11
<?php
22

3-
include "./autoload.php";
3+
4+
error_reporting(E_ALL);
5+
6+
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
7+
8+
if (!(error_reporting() & $errno)) { //避免@失效
9+
return;
10+
}
11+
12+
//将Warning升级为Exception
13+
throw new \Exception($errstr);
14+
});
15+
16+
17+
set_exception_handler(function (\Throwable $exception) {
18+
echo 'Exception:' . $exception->getMessage() . ' #' . $exception->getLine() . PHP_EOL;
19+
exit;
20+
});
21+
22+
23+
include __DIR__ . "/autoload.php";
424

525
$config = array(
626
'dsn' => 'mysql:host=127.0.0.1;dbname=test',
727
'username' => 'root',
828
'password' => 'root',
929
'charset' => 'utf8',
10-
'tablePrefix' => '',
30+
'tablePrefix' => 'db_',
31+
'reconnect' => true,
1132
);
1233

1334
$db = new \PFinal\Database\Builder($config);
1435

1536
$db->getConnection()->enableQueryLog();
1637

17-
$res = $db->table('{{%tests}} as t')->orderBy(new \PFinal\Database\Expression('rand()'))->findAll();
38+
// ================= //
39+
40+
$db->getConnection()->query('select sleep(1)');
41+
var_dump(count($db->getConnection()->getQueryLog(false)));
1842

19-
//var_dump($res);
20-
//$db->table('tests')->where('id=?', [44])->increment('aa', 1, ['bb' => 6]);
21-
//
22-
//$id = $db->table('tests')->insertGetId(['username' => 134, 'status' => '0']);
23-
//
24-
//$count = $db->table('tests')->lockForUpdate()->count('id');
43+
sleep(10);
44+
//到mysql中kill此连接,或设置set global wait_timeout=5 模拟连接长时间空闲,被服务端超时关闭
2545

26-
//var_dump($count);
46+
// show full processlist;
47+
// kill <Id>
2748

28-
//$res = $db->table('tests')->field('status')->groupBy('status')->having('status>:status', ['status' => 1])->findAll();
49+
$db->getConnection()->query('select sleep(1)');
50+
var_dump(count($db->getConnection()->getQueryLog()));
51+
exit;
2952

30-
//var_dump($res);
3153

32-
dump($db->getConnection()->getQueryLog());
54+
// =================//
55+
$db->getConnection()->query('select sleep(1)');
56+
var_dump(count($db->getConnection()->getQueryLog(false)));
3357

58+
$db->getConnection()->query('select sleep(10)');
3459

60+
//此时到mysql中kill此连接,强制kill一个条正在执行的sql
61+
//这种情况,不应该再次重连,需要用set_error_handler,在检测到Warning时,终止运行
3562

63+
var_dump(count($db->getConnection()->getQueryLog()));

0 commit comments

Comments
 (0)