PHP的PDO-commit函数在tidb上返回值错误的分析
问题是这样的, tidb使用乐观型事务, 在事务提交的一瞬间才进行冲突监测,
但是在PDO的commit()函数上就发生了诡异的事情, 失败提交失败了,
commit()函数返回成功且没有抛出异常.
经过两天的努力, 终于发现了问题所在, 虽然...
目录:
- 测试脚本
- 正确执行结果协议分析
- 错误执行结果协议分析
- 通过mysqlnd调试信息修复bug
- 再次通过xhprof修复bug
- 再再次通过mysqlnd调试信息修复bug
测试脚本
测试脚本的思路是这样的:
首先创建一张表, 里面有个库存字段, 然后同时发起10个并发请求,
对该字段进行减库存操作, 同时新增一条纪录.
按照理论分析, 应该只有一个请求成功把库存减成0, 并新增了一条纪录.
tidb的最终结果也是这样的, 但是
PDO的commit()在10个并发上都返回成功了,
导致我们认为10次库存都减成功了, 这会给我们的后续判断带来误导.
test.php
<?php
// Copy from laravel
$options = $options = [
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => true,
];
$pdo = new PDO('mysql:host=192.168.12.254;port=4000;dbname=test', 'root', '', $options);
// $pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', '', $options);
// Test table
// create table test (id bigint(20) auto_increment primary key, stock int(10) unsigned not null default 0);
// First
// insert into test(id, stock) values(1, 10);
// or update test set stock = 10 where id = 1;
// Second, decrement stock and insert a value
$ret = $pdo->beginTransaction();
file_log(sprintf('begin ret %s', $ret));
if ($ret) {
try {
$ret = $pdo->exec('update test set stock = stock - 10 where id = 1');
if (!$ret) {
throw new \Exception('update stock failed');
}
$ret = $pdo->exec(sprintf('insert into test(stock) values(%d)', rand(0, 9999)));
if (!$ret) {
throw new Exception('insert failed');
}
$id = $pdo->lastInsertId();
file_log(sprintf('last insertId %d', $id));
$ret = $pdo->commit();
file_log(sprintf('commit ret %d', $ret));
$res = $pdo->query(sprintf('select * from test where id = %d', $id));
$row = $res->fetch(PDO::FETCH_ASSOC);
file_log(sprintf('inserted data %s', json_encode($row)));
} catch (Exception $e) {
$ret = $pdo->rollBack();
file_log(sprintf('rollback %d exception %s', $ret, $e->getTraceAsString()));
} catch (Throwable $e) {
$ret = $pdo->rollBack();
file_log(sprintf('rollback %d exception %s', $ret, $e->getTraceAsString()));
}
}
// Third, one terminal 'php -S 127.0.0.1:9093'
// another terminal 'ab -c 10 -n 10 http://127.0.0.1:9093/bug_test.php'
function file_log($msg)
{
static $rId = null;
if (is_null($rId)) {
$rId = uniqid();
}
file_put_contents(__DIR__ . '/test.log', sprintf("%s %s\n", $rId, $msg), FILE_APPEND);
}
正确执行结果协议分析
我们首先使用tcpdump在本地监听tidb的4000端口, 然后执行"php test.php", 对tidb发起一次正确的请求.
然后使用wireshark分析抓取到的数据.
MYSQL协议的分析文档: PAGE_PROTOCOL MySQL协议分析
监听4000端口:
# tcpdump port 4000 -s 0 -w 4000.pacp
安装wireshark
$ brew install wireshark --with-qt
把4000.pacp导入wireshark, 分析mysql协议.
我们重点分析COMMIT包:
请求包:
080000 00 03 434f4d4d495420
0-2字节代表payload为8个字节, 3字节是序列号0,
4字节代表命名类型, 3为query命令的值, 后面跟上命令内容,
当前为"COMMIT ".
响应包:
070000 01 00 00 00 0200 0000
0-2字节代表payload有7字节, 01为序列号,
4字节代表响应类型, 0x00为正确响应,
5字节00代表影响行数, 6字节00代表自增ID,
7-8字节2代表"SERVER_STATUS_AUTOCOMMIT"开启,
9-10字节00代表没有warnings.
错误执行结果协议分析
首先还是用tcpdump监听4000端口, 然后使用"ab -c 10 -n 10 http://xxx.com/test.php"
发起10个并发请求.
# tcpdump port 4000 -s 0 -w 4000_2.pacp
抓好包后同样导入wireshark进行分析,
使用
frame.len == 78
进行过滤, 结果出来10个结果, 查看内容后同上面的COMMIT包完全一致.
这里记下它们的序号, 方便纪录各自的响应.
80 98 110 120 130 132 136 177 189 196
然后纪录10次COMMIT请求的响应,
使用 tcp.srcport == 47640 or tcp.dstport == 47640 过滤端口
响应80: 0700000100000002000000
响应98: 3b000001ff9a06233232303033424947494e5420554e5349...
响应100: 3b000001ff9a06233232303033424947494e5420554e5349...
响应120: 3b000001ff9a06233232303033424947494e5420554e5349...
3b0000 01 ff 9a06 23 3232303033 424947494e5420554e5349...
依次代表:
内容有59字节
序列号0x01
错误响应0xff
23是符号"#"
错误状态22003
错误信息"BIGINT UNSIGNED value is out range in '(0, 10)'"
响应80的包, 跟上面正确的一致,
剩下的包都返回超出限制
下面是脚本纪录的日志:
5968bf2e9d38b begin ret 1
5968bf2e9d38b last insertId 15014
5968bf2e9e07b begin ret 1
5968bf2e9ea59 begin ret 1
5968bf2e9eaae begin ret 1
5968bf2e9e07b last insertId 15015
5968bf2e9f0bb begin ret 1
5968bf2e9f22a begin ret 1
5968bf2e9f437 begin ret 1
5968bf2e9ea59 last insertId 15016
5968bf2e9fe35 begin ret 1
5968bf2e9f437 last insertId 15017
5968bf2e9f22a last insertId 15018
5968bf2e9f0bb last insertId 15019
5968bf2ea09e8 begin ret 1
5968bf2e9eaae last insertId 15020
5968bf2ea0b79 begin ret 1
5968bf2e9d38b commit ret 1
5968bf2e9eaae commit ret 1
5968bf2e9f0bb commit ret 1
5968bf2e9f22a commit ret 1
5968bf2e9d38b inserted data {"id":"15014","stock":"288"}
5968bf2e9f22a inserted data false
5968bf2e9f0bb inserted data false
5968bf2e9eaae inserted data false
5968bf2ea09e8 last insertId 15021
5968bf2ea09e8 commit ret 1
5968bf2ea09e8 inserted data false
5968bf2e9fe35 last insertId 15022
5968bf2e9fe35 commit ret 1
5968bf2ea0b79 last insertId 15023
5968bf2e9fe35 inserted data false
5968bf2ea0b79 commit ret 1
5968bf2ea0b79 inserted data false
5968bf2e9ea59 commit ret 1
5968bf2e9ea59 inserted data false
5968bf2e9e07b commit ret 1
5968bf2e9e07b inserted data false
5968bf2e9f437 commit ret 1
5968bf2e9f437 inserted data false
可以看出commit()函数都返回了1, 从而可以判断是PDO处理错误了.
测试用的PHP版本是ubuntu上的5.6.30,
而编译了一个7.0.16版本的PHP7, 同样有这个问题.
其输出如下:
59699b53bfea5 begin ret 1
59699b53c0480 begin ret 1
59699b53bfea5 last insertId 6
59699b53c0480 last insertId 7
59699b53bfea5 commit ret 1
59699b53bfea5 inserted data {"id":6,"stock":6824}
59699b53c42a2 begin ret 1
59699b53c42a2 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
59699b53c5bab begin ret 1
59699b53c5bab rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
59699b53c7817 begin ret 1
59699b53c7817 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
59699b53c98a4 begin ret 1
59699b53c98a4 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
59699b53cbe88 begin ret 1
59699b53cbe88 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
59699b53ce030 begin ret 1
59699b53ce030 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
59699b53d02c6 begin ret 1
59699b53d02c6 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
59699b53d214e begin ret 1
59699b53d214e rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
59699b53c0480 commit ret 1
59699b53c0480 inserted data false
通过mysqlnd调试信息修复bug
通过gdb单步调试, 基本理清php与mysql的交互流程,
第一个思路是首先纪录php从mysql处读取的数据是否正确.
其从网络读取数据的代码如下:
static enum_func_status
MYSQLND_METHOD(mysqlnd_net, network_read_ex)(MYSQLND_NET * const net, zend_uchar * const buffer, const size_t count,
MYSQLND_STATS * const stats, MYSQLND_ERROR_INFO * const error_info)
{
enum_func_status return_value = PASS;
php_stream * net_stream = net->data->m.get_stream(net);
size_t old_chunk_size = net_stream->chunk_size;
size_t to_read = count, ret;
zend_uchar * p = buffer;
DBG_ENTER("mysqlnd_net::network_read_ex");
DBG_INF_FMT("count="MYSQLND_SZ_T_SPEC, count);
net_stream->chunk_size = MIN(to_read, net->data->options.net_read_buffer_size);
while (to_read) {
if (!(ret = php_stream_read(net_stream, (char *) p, to_read))) {
DBG_ERR_FMT("Error while reading header from socket");
return_value = FAIL;
break;
}
p += ret;
to_read -= ret;
}
MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
net_stream->chunk_size = old_chunk_size;
DBG_RETURN(return_value);
}
为了方便查看, 我们将数据转换成16进制的字符串, 其函数如下:
#include <stdio.h>
void tohex(unsigned char * in, size_t insz, char * out, size_t outsz)
{
unsigned char * pin = in;
const char * hex = "0123456789ABCDEF";
char * pout = out;
for(; pin < in+insz; pout +=3, pin++){
pout[0] = hex[(*pin>>4) & 0xF];
pout[1] = hex[ *pin & 0xF];
pout[2] = ':';
if (pout + 3 - out > outsz){
/* Better to truncate output string than overflow buffer */
/* it would be still better to either return a status */
/* or ensure the target buffer is large enough and it never happen */
break;
}
}
pout[-1] = 0;
}
写入日志的函数如下:
#include <syslog.h>
syslog(LOG_DEBUG, "test info");
在mysqlnd_net.c添加如下代码:
const int len = count * 3;
char debug_str[len];
tohex(buffer, count, debug_str, len);
syslog(LOG_DEBUG, debug_str);
syslog(LOG_DEBUG, buffer);
MYSQLND_INC_CONN_STATISTIC_W_VALUE(stats, STAT_BYTES_RECEIVED, count - to_read);
net_stream->chunk_size = old_chunk_size;
DBG_RETURN(return_value);
最后编译php
# ./configure --enable-debug --enable-fpm --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --prefix=/home/liuyong/php7016d
# make && make install
启动php-fpm配置nginx然后接着测试我们的脚本,
使用ab发起10个并发请求.
这次php脚本输出同样有问题, syslog也打印了响应的日志:
5969ae91c79bc begin ret 1
5969ae91c812b begin ret 1
5969ae91c79bc last insertId 8
5969ae91c812b last insertId 9
5969ae91c79bc commit ret 1
5969ae91c79bc inserted data {"id":8,"stock":9983}
5969ae91cc21d begin ret 1
5969ae91cc21d rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969ae91ce327 begin ret 1
5969ae91ce327 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969ae91d0040 begin ret 1
5969ae91d0040 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969ae91d1b0a begin ret 1
5969ae91d1b0a rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969ae91d35f3 begin ret 1
5969ae91d35f3 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969ae91d5018 begin ret 1
5969ae91d5018 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969ae91d6851 begin ret 1
5969ae91d6851 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969ae91d8326 begin ret 1
5969ae91d8326 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969ae91c812b commit ret 1
5969ae91c812b inserted data false
syslog
Jul 15 14:40:52 dev : pool www: 3C:00:00:00
Jul 15 14:40:52 dev : pool www: <
Jul 15 14:40:52 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:EA:5B:04:00:58:40:7C:5B:4F:05:59:3C:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:62:10:58:51:25:73:72:6E:26:78:3A:10:00
Jul 15 14:40:52 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:52 dev : pool www: 3C:00:00:00
Jul 15 14:40:52 dev : pool www: <
Jul 15 14:40:52 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:EB:5B:04:00:0C:12:2F:7A:79:52:69:35:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:12:01:54:65:31:43:3E:3F:5A:49:3E:30:00
Jul 15 14:40:52 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:52 dev : pool www: 07:00:00:02
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:02
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:01:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:01:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:01:0A:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:01:0B:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 0C:00:00:01
Jul 15 14:40:52 dev : pool www: #014
Jul 15 14:40:52 dev : pool www: 00:01:00:00:00:02:00:00:00:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 20:00:00:02
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 03:64:65:66:04:74:65:73:74:00:04:74:65:73:74:00:02:69:64:0C:3F:00:14:00:00:00:08:83:02:00:00:00
Jul 15 14:40:52 dev : pool www: #003def#004test
Jul 15 14:40:52 dev : pool www: 23:00:00:03
Jul 15 14:40:52 dev : pool www: #
Jul 15 14:40:52 dev : pool www: 03:64:65:66:04:74:65:73:74:00:04:74:65:73:74:00:05:73:74:6F:63:6B:0C:3F:00:0A:00:00:00:03:A1:00:00:00:00
Jul 15 14:40:52 dev : pool www: #003def#004test
Jul 15 14:40:52 dev : pool www: 05:00:00:04
Jul 15 14:40:52 dev : pool www: #005
Jul 15 14:40:52 dev : pool www: FE:00:00:02:00
Jul 15 14:40:52 dev : pool www: �
Jul 15 14:40:52 dev : pool www: 01:00:00:01
Jul 15 14:40:52 dev : pool www: #001
Jul 15 14:40:52 dev : pool www: 02
Jul 15 14:40:52 dev : pool www: #002
Jul 15 14:40:52 dev : pool www: 1E:00:00:02
Jul 15 14:40:52 dev : pool www: #036
Jul 15 14:40:52 dev : pool www: 03:64:65:66:00:04:74:65:73:74:00:02:69:64:02:69:64:0C:3F:00:14:00:00:00:08:83:02:00:00:00
Jul 15 14:40:52 dev : pool www: #003def
Jul 15 14:40:52 dev : pool www: 24:00:00:03
Jul 15 14:40:52 dev : pool www: $
Jul 15 14:40:52 dev : pool www: 03:64:65:66:00:04:74:65:73:74:00:05:73:74:6F:63:6B:05:73:74:6F:63:6B:0C:3F:00:0A:00:00:00:03:A1:00:00:00:00
Jul 15 14:40:52 dev : pool www: #003def
Jul 15 14:40:52 dev : pool www: 05:00:00:04
Jul 15 14:40:52 dev : pool www: #005
Jul 15 14:40:52 dev : pool www: FE:00:00:02:00
Jul 15 14:40:52 dev : pool www: �
Jul 15 14:40:52 dev : pool www: 0E:00:00:05
Jul 15 14:40:52 dev : pool www: #016
Jul 15 14:40:52 dev : pool www: 00:00:0B:00:00:00:00:00:00:00:FC:04:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 05:00:00:06
Jul 15 14:40:52 dev : pool www: #005
Jul 15 14:40:52 dev : pool www: FE:00:00:02:00
Jul 15 14:40:52 dev : pool www: �
Jul 15 14:40:52 dev : pool www: 3C:00:00:00
Jul 15 14:40:52 dev : pool www: <
Jul 15 14:40:52 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:EC:5B:04:00:3E:55:4F:1E:65:39:44:4A:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:1D:08:56:68:49:2D:78:67:70:73:1A:7C:00
Jul 15 14:40:52 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:52 dev : pool www: 07:00:00:02
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3B:00:00:01
Jul 15 14:40:52 dev : pool www: ;
Jul 15 14:40:52 dev : pool www: FF:9A:06:23:32:32:30:30:33:42:49:47:49:4E:54:20:55:4E:53:49:47:4E:45:44:20:76:61:6C:75:65:20:69:73:20:6F:75:74:20:6F:66:20:72:61:6E:67:65:20:69:6E:20:27:28:30:2C:20:31:30:29:27
Jul 15 14:40:52 dev : pool www: ��#006#22003BIGINT UNSIGNED value is out of range in '(0, 10)'s�
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3C:00:00:00
Jul 15 14:40:52 dev : pool www: <
Jul 15 14:40:52 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:ED:5B:04:00:08:0D:18:01:67:3D:43:78:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:5E:66:2F:2F:79:41:63:55:5C:79:4E:64:00
Jul 15 14:40:52 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:52 dev : pool www: 07:00:00:02
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3B:00:00:01
Jul 15 14:40:52 dev : pool www: ;
Jul 15 14:40:52 dev : pool www: FF:9A:06:23:32:32:30:30:33:42:49:47:49:4E:54:20:55:4E:53:49:47:4E:45:44:20:76:61:6C:75:65:20:69:73:20:6F:75:74:20:6F:66:20:72:61:6E:67:65:20:69:6E:20:27:28:30:2C:20:31:30:29:27
Jul 15 14:40:52 dev : pool www: ��#006#22003BIGINT UNSIGNED value is out of range in '(0, 10)'s�
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3C:00:00:00
Jul 15 14:40:52 dev : pool www: <
Jul 15 14:40:52 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:EE:5B:04:00:71:7A:01:2E:54:4F:33:21:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:65:1F:28:61:5F:63:05:6E:60:0B:6B:0B:00
Jul 15 14:40:52 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:52 dev : pool www: 07:00:00:02
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3B:00:00:01
Jul 15 14:40:52 dev : pool www: ;
Jul 15 14:40:52 dev : pool www: FF:9A:06:23:32:32:30:30:33:42:49:47:49:4E:54:20:55:4E:53:49:47:4E:45:44:20:76:61:6C:75:65:20:69:73:20:6F:75:74:20:6F:66:20:72:61:6E:67:65:20:69:6E:20:27:28:30:2C:20:31:30:29:27
Jul 15 14:40:52 dev : pool www: ��#006#22003BIGINT UNSIGNED value is out of range in '(0, 10)'s�
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3C:00:00:00
Jul 15 14:40:52 dev : pool www: <
Jul 15 14:40:52 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:EF:5B:04:00:60:5F:61:66:69:01:4F:38:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:09:01:05:6F:25:48:2D:4B:04:33:05:0B:00
Jul 15 14:40:52 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:52 dev : pool www: 07:00:00:02
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3B:00:00:01
Jul 15 14:40:52 dev : pool www: ;
Jul 15 14:40:52 dev : pool www: FF:9A:06:23:32:32:30:30:33:42:49:47:49:4E:54:20:55:4E:53:49:47:4E:45:44:20:76:61:6C:75:65:20:69:73:20:6F:75:74:20:6F:66:20:72:61:6E:67:65:20:69:6E:20:27:28:30:2C:20:31:30:29:27
Jul 15 14:40:52 dev : pool www: ��#006#22003BIGINT UNSIGNED value is out of range in '(0, 10)'s�
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3C:00:00:00
Jul 15 14:40:52 dev : pool www: <
Jul 15 14:40:52 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:F0:5B:04:00:34:5F:13:42:1B:42:42:75:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:17:7C:68:6D:64:0F:19:58:67:19:6D:63:00
Jul 15 14:40:52 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:52 dev : pool www: 07:00:00:02
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3B:00:00:01
Jul 15 14:40:52 dev : pool www: ;
Jul 15 14:40:52 dev : pool www: FF:9A:06:23:32:32:30:30:33:42:49:47:49:4E:54:20:55:4E:53:49:47:4E:45:44:20:76:61:6C:75:65:20:69:73:20:6F:75:74:20:6F:66:20:72:61:6E:67:65:20:69:6E:20:27:28:30:2C:20:31:30:29:27
Jul 15 14:40:52 dev : pool www: ��#006#22003BIGINT UNSIGNED value is out of range in '(0, 10)'s�
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3C:00:00:00
Jul 15 14:40:52 dev : pool www: <
Jul 15 14:40:52 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:F1:5B:04:00:53:03:1B:55:52:53:51:72:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:57:14:6B:70:0D:1D:61:2B:7D:6F:46:05:00
Jul 15 14:40:52 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:52 dev : pool www: 07:00:00:02
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 07:00:00:01
Jul 15 14:40:52 dev : pool www: #007
Jul 15 14:40:52 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:52 dev : pool www:
Jul 15 14:40:52 dev : pool www: 3B:00:00:01
Jul 15 14:40:52 dev : pool www: ;
Jul 15 14:40:53 dev : pool www: FF:9A:06:23:32:32:30:30:33:42:49:47:49:4E:54:20:55:4E:53:49:47:4E:45:44:20:76:61:6C:75:65:20:69:73:20:6F:75:74:20:6F:66:20:72:61:6E:67:65:20:69:6E:20:27:28:30:2C:20:31:30:29:27
Jul 15 14:40:53 dev : pool www: ��#006#22003BIGINT UNSIGNED value is out of range in '(0, 10)'s�
Jul 15 14:40:53 dev : pool www: 07:00:00:01
Jul 15 14:40:53 dev : pool www: #007
Jul 15 14:40:53 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:53 dev : pool www:
Jul 15 14:40:53 dev : pool www: 3C:00:00:00
Jul 15 14:40:53 dev : pool www: <
Jul 15 14:40:53 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:F2:5B:04:00:03:44:51:62:69:29:18:59:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:68:13:79:22:6C:77:43:3D:3D:04:22:29:00
Jul 15 14:40:53 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:53 dev : pool www: 07:00:00:02
Jul 15 14:40:53 dev : pool www: #007
Jul 15 14:40:53 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:53 dev : pool www:
Jul 15 14:40:53 dev : pool www: 07:00:00:01
Jul 15 14:40:53 dev : pool www: #007
Jul 15 14:40:53 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:53 dev : pool www:
Jul 15 14:40:53 dev : pool www: 3B:00:00:01
Jul 15 14:40:53 dev : pool www: ;
Jul 15 14:40:53 dev : pool www: FF:9A:06:23:32:32:30:30:33:42:49:47:49:4E:54:20:55:4E:53:49:47:4E:45:44:20:76:61:6C:75:65:20:69:73:20:6F:75:74:20:6F:66:20:72:61:6E:67:65:20:69:6E:20:27:28:30:2C:20:31:30:29:27
Jul 15 14:40:53 dev : pool www: ��#006#22003BIGINT UNSIGNED value is out of range in '(0, 10)'s�
Jul 15 14:40:53 dev : pool www: 07:00:00:01
Jul 15 14:40:53 dev : pool www: #007
Jul 15 14:40:53 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:53 dev : pool www:
Jul 15 14:40:53 dev : pool www: 3C:00:00:00
Jul 15 14:40:53 dev : pool www: <
Jul 15 14:40:53 dev : pool www: 0A:35:2E:37:2E:31:2D:54:69:44:42:2D:31:2E:30:00:F3:5B:04:00:3B:21:3E:76:0B:35:32:21:00:8F:A2:53:02:00:13:00:15:00:00:00:00:00:00:00:00:00:00:23:66:41:42:23:2A:7B:70:2F:0E:5A:17:00
Jul 15 14:40:53 dev : pool www: #0125.7.1-TiDB-1.0
Jul 15 14:40:53 dev : pool www: 07:00:00:02
Jul 15 14:40:53 dev : pool www: #007
Jul 15 14:40:53 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:53 dev : pool www:
Jul 15 14:40:53 dev : pool www: 07:00:00:01
Jul 15 14:40:53 dev : pool www: #007
Jul 15 14:40:53 dev : pool www: 00:00:00:03:00:00:00
Jul 15 14:40:53 dev : pool www:
Jul 15 14:40:53 dev : pool www: 3B:00:00:01
Jul 15 14:40:53 dev : pool www: ;
Jul 15 14:40:53 dev : pool www: FF:9A:06:23:32:32:30:30:33:42:49:47:49:4E:54:20:55:4E:53:49:47:4E:45:44:20:76:61:6C:75:65:20:69:73:20:6F:75:74:20:6F:66:20:72:61:6E:67:65:20:69:6E:20:27:28:30:2C:20:31:30:29:27
Jul 15 14:40:53 dev : pool www: ��#006#22003BIGINT UNSIGNED value is out of range in '(0, 10)'s�
Jul 15 14:40:53 dev : pool www: 07:00:00:01
Jul 15 14:40:53 dev : pool www: #007
Jul 15 14:40:53 dev : pool www: 00:00:00:02:00:00:00
Jul 15 14:40:53 dev : pool www:
Jul 15 14:40:53 dev : pool www: 3B:00:00:01
Jul 15 14:40:53 dev : pool www: ;
Jul 15 14:40:53 dev : pool www: FF:9A:06:23:32:32:30:30:33:42:49:47:49:4E:54:20:55:4E:53:49:47:4E:45:44:20:76:61:6C:75:65:20:69:73:20:6F:75:74:20:6F:66:20:72:61:6E:67:65:20:69:6E:20:27:28:30:2C:20:31:30:29:27
Jul 15 14:40:53 dev : pool www: ��#006#22003BIGINT UNSIGNED value is out of range in '(0, 10)'
Jul 15 14:40:53 dev : pool www: 0C:00:00:01
Jul 15 14:40:53 dev : pool www: #014
Jul 15 14:40:53 dev : pool www: 00:01:00:00:00:02:00:00:00:00:00:00
Jul 15 14:40:53 dev : pool www:
Jul 15 14:40:53 dev : pool www: 20:00:00:02
Jul 15 14:40:53 dev : pool www:
Jul 15 14:40:53 dev : pool www: 03:64:65:66:04:74:65:73:74:00:04:74:65:73:74:00:02:69:64:0C:3F:00:14:00:00:00:08:83:02:00:00:00
Jul 15 14:40:53 dev : pool www: #003def#004test
Jul 15 14:40:53 dev : pool www: 23:00:00:03
Jul 15 14:40:53 dev : pool www: #
Jul 15 14:40:53 dev : pool www: 03:64:65:66:04:74:65:73:74:00:04:74:65:73:74:00:05:73:74:6F:63:6B:0C:3F:00:0A:00:00:00:03:A1:00:00:00:00
Jul 15 14:40:53 dev : pool www: #003def#004test
Jul 15 14:40:53 dev : pool www: 05:00:00:04
Jul 15 14:40:53 dev : pool www: #005
Jul 15 14:40:53 dev : pool www: FE:00:00:02:00
Jul 15 14:40:53 dev : pool www: �
Jul 15 14:40:53 dev : pool www: 01:00:00:01
Jul 15 14:40:53 dev : pool www: #001
Jul 15 14:40:53 dev : pool www: 02
Jul 15 14:40:53 dev : pool www: #002
Jul 15 14:40:53 dev : pool www: 1E:00:00:02
Jul 15 14:40:53 dev : pool www: #036
Jul 15 14:40:53 dev : pool www: 03:64:65:66:00:04:74:65:73:74:00:02:69:64:02:69:64:0C:3F:00:14:00:00:00:08:83:02:00:00:00
Jul 15 14:40:53 dev : pool www: #003def
Jul 15 14:40:53 dev : pool www: 24:00:00:03
Jul 15 14:40:53 dev : pool www: $
Jul 15 14:40:53 dev : pool www: 03:64:65:66:00:04:74:65:73:74:00:05:73:74:6F:63:6B:05:73:74:6F:63:6B:0C:3F:00:0A:00:00:00:03:A1:00:00:00:00
Jul 15 14:40:53 dev : pool www: #003def
Jul 15 14:40:53 dev : pool www: 05:00:00:04
Jul 15 14:40:53 dev : pool www: #005
Jul 15 14:40:53 dev : pool www: FE:00:00:02:00
Jul 15 14:40:53 dev : pool www: �
Jul 15 14:40:53 dev : pool www: 05:00:00:05
Jul 15 14:40:53 dev : pool www: #005
Jul 15 14:40:53 dev : pool www: FE:00:00:02:00
Jul 15 14:40:53 dev : pool www: �
通过上面的日志内容, 看出10个请求有9个报错, 可以判断出pdo至少在接收数据上没有出错.
下面我们开启mysqlnd的调试日志, 重新纪录一次请求日志:
打开日志纪录:
mysqlnd.debug = 1
mysqlnd.log_mask = 0
其日志输出为:
// note: update 时删除
日志大概有1400行, 因为并没有发现什么重要的东西, 因此省略掉了.
不过通过git的提交日志, 还可以看到的.
对应的测试脚本输出:
5969c2f3187b1 begin ret 1
5969c2f3188bc begin ret 1
5969c2f3188bc last insertId 12
5969c2f3187b1 last insertId 13
5969c2f3188bc commit ret 1
5969c2f3188bc inserted data {"id":12,"stock":5312}
5969c2f31ec26 begin ret 1
5969c2f31ec26 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969c2f321f4f begin ret 1
5969c2f321f4f rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969c2f3242ce begin ret 1
5969c2f3242ce rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969c2f3267a8 begin ret 1
5969c2f3267a8 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969c2f328cbd begin ret 1
5969c2f328cbd rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969c2f32b1e7 begin ret 1
5969c2f32b1e7 rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969c2f32d5fd begin ret 1
5969c2f32d5fd rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969c2f32fabc begin ret 1
5969c2f32fabc rollback 1 exception #0 /data/test/bug_test.php(24): PDO->exec('update test set...')
#1 {main}
5969c2f3187b1 commit ret 1
5969c2f3187b1 inserted data false
观察了半天也没发现上面日志有什么不对的地方.
再次通过xhprof修复bug
昨天通过mysqlnd的trace日志, 并没有看出任何异常的地方,
这次我们使用xhprof跟踪函数的执行流程, 来诊断究竟是哪个函数没有正确处理数据.
安装好xhprof后, 我们首先修改脚本, 将xhprof日志文件名跟我们的脚本输出对应起来.
<?php
// Copy from laravel
$options = $options = [
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO('mysql:host=10.80.90.30;port=4000;dbname=test', 'root', '', $options);
//$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'being2015', $options);
// Test table
// create table test (id bigint(20) auto_increment primary key, stock int(10) unsigned not null default 0);
// First
// insert into test(id, stock) values(1, 10);
// or update test set stock = 10 where id = 1;
// Second, decrement stock and insert a value
global $rid;
$rid = uniqid();
xhprof_enable();
$ret = $pdo->beginTransaction();
file_log(sprintf('begin ret %s', $ret));
if ($ret) {
try {
$ret = $pdo->exec('update test set stock = stock - 10 where id = 1');
if (!$ret) {
throw new \Exception('update stock failed');
}
$ret = $pdo->exec(sprintf('insert into test(stock) values(%d)', rand(0, 9999)));
if (!$ret) {
throw new Exception('insert failed');
}
$id = $pdo->lastInsertId();
file_log(sprintf('last insertId %d', $id));
$ret = $pdo->commit();
file_log(sprintf('commit ret %d', $ret));
$res = $pdo->query(sprintf('select * from test where id = %d', $id));
$row = $res->fetch(PDO::FETCH_ASSOC);
file_log(sprintf('inserted data %s', json_encode($row)));
} catch (Exception $e) {
$ret = -1;//$pdo->rollBack();
file_log(sprintf('rollback %d exception %s', $ret, $e->getTraceAsString()));
} catch (Throwable $e) {
$ret = -1;//$pdo->rollBack();
file_log(sprintf('rollback %d exception %s', $ret, $e->getTraceAsString()));
}
}
file_put_contents((ini_get('xhprof.output_dir') ? : '/tmp') . '/' . $rid . '.xhprof.xhprof', serialize(xhprof_disable()));
// Third, one terminal 'php -S 127.0.0.1:9093'
// another terminal 'ab -c 10 -n 10 http://127.0.0.1:9093/bug_test.php'
function file_log($msg)
{
global $rid;
file_put_contents(__DIR__ . '/test.log', sprintf("%s %s\n", $rid, $msg), FILE_APPEND);
}
然后是脚本输出日志:
596ad99b2fd95 begin ret 1
596ad99b2fe22 begin ret 1
596ad99b2fd95 last insertId 22
596ad99b2fe22 last insertId 23
596ad99b2fd95 commit ret 1
596ad99b2fd95 inserted data {"id":22,"stock":1585}
596ad99b2fe22 commit ret 1
596ad99b2fe22 inserted data false
596ad99b34c38 begin ret 1
596ad99b34c38 rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596ad99b36baf begin ret 1
596ad99b36baf rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596ad99b379da begin ret 1
596ad99b379da rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596ad99b39645 begin ret 1
596ad99b39645 rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596ad99b3a3fb begin ret 1
596ad99b3a3fb rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596ad99b3c445 begin ret 1
596ad99b3c445 rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596ad99b3c976 begin ret 1
596ad99b3c976 rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596ad99b3e500 begin ret 1
596ad99b3e500 rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
然后是数据库当前内容:
mysql> select * from test;
+----+-------+
| id | stock |
+----+-------+
| 1 | 0 |
| 22 | 1585 |
+----+-------+
2 rows in set (0.00 sec)
mysql> select @@version;
+----------------+
| @@version |
+----------------+
| 5.7.1-TiDB-1.0 |
+----------------+
1 row in set (0.01 sec)
然后是xhprof的输出:
日志我放到github上了.
然后用xhprof的可视化网页查看, 才反应过来, xhprof只能纪录php内置或用户写的函数,
并不能纪录全部的c函数调用流程.
这条路也走不通了, 花费了半个小时.
再再次通过mysqlnd调试信息修复bug
重新整理了一下思路, 一直的思路应该是没有问题, 首先排除mysql协议的问题,
确定是pdo的问题, 那么通过mysqlnd的内置的调制方法调试, 才是正确的方式.
之前没有找到正确的mysqlnd配置方式, 整理思路后重新查找了一下, 真的找到了.
地址在这里.
查看了之后, 将mysqlnd的配置修改为:
mysqlnd.debug = "d:F:i:L:n:t,2000:x:A,/tmp/mysqlnd.trace"
mysqlnd.log_mask = 2043
脚本日志输出:
596aede076fc2 begin ret 1
596aede077912 begin ret 1
596aede078c5e begin ret 1
596aede076fc2 last insertId 26
596aede079ba5 begin ret 1
596aede07d689 begin ret 1
596aede07e25a begin ret 1
596aede07e63d begin ret 1
596aede07e263 begin ret 1
596aede076fc2 commit ret 1
596aede07d9fe begin ret 1
596aede07ebf1 begin ret 1
596aede077912 last insertId 27
596aede07e63d rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596aede07e25a last insertId 28
596aede078c5e last insertId 29
596aede07e263 rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596aede07ebf1 rollback -1 exception #0 /data/test/bug_test.php(30): PDO->exec('update test set...')
#1 {main}
596aede079ba5 last insertId 30
596aede07d9fe last insertId 31
596aede078c5e commit ret 1
596aede077912 commit ret 1
596aede07e25a commit ret 1
596aede07d9fe commit ret 1
596aede079ba5 commit ret 1
596aede07e25a inserted data false
596aede076fc2 inserted data {"id":26,"stock":1262}
596aede077912 inserted data false
596aede079ba5 inserted data false
596aede07d9fe inserted data false
596aede078c5e inserted data false
596aede07d689 last insertId 32
596aede07d689 commit ret 1
596aede07d689 inserted data false
数据库的内容为:
+----+-------+
| id | stock |
+----+-------+
| 1 | 0 |
| 26 | 1262 |
+----+-------+
2 rows in set (0.01 sec)
mysqlnd.trace因为文件太大, 我还是放到github上了.
第一次的时候, 日志都混在一起了, 根本区分不了是哪个的日志,
然后我把php-fpm的进程数直接调到30, 然后通过进程号把文件拆分成10个文件,
这下日志就清晰了. 地址在这里.
看了半天的日志和代码, 终于找到问题所在了.
mysqlnd中status的返回值定义为:
/* Follow libmysql convention */
typedef enum func_status
{
PASS = 0,
FAIL = 1
} enum_func_status;
可以看出PASS=0, FAIL=1.
但是commit()函数的返回值是mysql_handle_commit()函数返回的,
仔细观察, 这个错误其实很简单的, 就是判断的时候, 无论PASS还是FAIL都返回true,
这就不能区分commit()函数是否提交成功了.
static int mysql_handle_commit(pdo_dbh_t *dbh)
{
PDO_DBG_ENTER("mysql_handle_commit");
PDO_DBG_INF_FMT("dbh=%p", dbh);
#if MYSQL_VERSION_ID >= 40100 || defined(PDO_USE_MYSQLND)
PDO_DBG_RETURN(0 <= mysql_commit(((pdo_mysql_db_handle *)dbh->driver_data)->server));
#else
PDO_DBG_RETURN(0 <= mysql_handle_doer(dbh, ZEND_STRL("COMMIT")));
#endif
}
好了, 问题得以解决, 直接判断返回值是否等于0即可.