最近在项目中处理一个关于商品数据重复需要删除多余的商品记录,但是删除一条商品必然要把关联的其他表商品的id和其他商品信息更换为正确的,删除一个商品记录,同时要去修改100多张表的关联商品数据,在项目中引用了tp orm 1.2版本,由于项目是php5.6版本,没法使用最新orm,在代码中每处理1个商品则开启1个事务,相当于循环的事务,下面的代码做个简单的演示(非真实):
$list = Db::table('member')->select(); //循环事务 foreach ($list as $item) { try { //开启事务 Db::startTrans(); //部分数据不需要处理 if ($item['sex'] == 2) { continue; } //更新数据 $isUpdate = Db::table('member')->where(['id' => $item['id']])->update([ 'name' => 'chen' ]); if (!$isUpdate) { throw new Exception('更新数据失败'); } //更新其他表(假装这里写了代码) // 提交事务 Db::commit(); } catch (Exception $e) { Db::rollback(); echo '异常事务回滚:' . $e->getMessage() . PHP_EOL; } } echo '执行完成'.PHP_EOL;
上面的代码中每循环一条数据开启1个事务,执行完成提交事务,异常会回滚。但是在预发布环境执行出现前面的数据可以执行,后面的很多数据未执行。于是我在代码中加了文件日志,保存了生成的sql,甚至在提交事务后面加了日志,确保执行到提交事务后面了。最终结果是程序输出了"执行完成",且执行过程中未报错,无任何异常抛出,包括未捕捉的异常。由于我们每次提交代码都需要找运维,本来是想揪出原因,但是怕麻烦运维的小伙伴,于是简单粗暴的使用了tp的事务闭包函数。tp会自动处理错误,然后你可以捕捉tp这个方法抛出的异常来记录日志即可,不需要自己处理事务。
Db::transaction(function (){ //逻辑写在这里 });
个人比较较真,一直找不到原因非常不爽,于是抽空下班后完整模拟了下终于发现问题。原来tp在开启事务会在orm中记录事务的次数+1,事务提交和事务回滚都会将事务的次数进行-1操作。例如:
/** * 启动事务 * @access public * @return void * @throws \PDOException * @throws \Exception */ public function startTrans() { $this->initConnect(true); if (!$this->linkID) { return false; } ++$this->transTimes; try { if (1 == $this->transTimes) { $this->linkID->beginTransaction(); } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { $this->linkID->exec( $this->parseSavepoint('trans' . $this->transTimes) ); } } catch (\Exception $e) { if ($this->isBreak($e)) { --$this->transTimes; return $this->close()->startTrans(); } throw $e; } } /** * 事务回滚 * @access public * @return void * @throws PDOException */ public function rollback() { $this->initConnect(true); if (1 == $this->transTimes) { $this->linkID->rollBack(); } elseif ($this->transTimes > 1 && $this->supportSavepoint()) { $this->linkID->exec( $this->parseSavepointRollBack('trans' . $this->transTimes) ); } $this->transTimes = max(0, $this->transTimes - 1); } /** * 用于非自动提交状态下面的查询提交 * @access public * @return void * @throws PDOException */ public function commit() { $this->initConnect(true); if (1 == $this->transTimes) { $this->linkID->commit(); } --$this->transTimes; }
通过上面commit方法我们可以看到当事务次数只有1的时候才会执行事务提交,上面我们的业务代码直接continue导致下次执行的时候事务的次数一直在递增,因为tp orm是静态的,因为只要continue就会导致普通commit事务执行失败,这块tp文档未说明,且在方法中未作异常处理,开发者特别容易进坑。
<?PHP $a=0.5*100; if(is_int($a)){ echo'int'; } else{ echo'not int'; } ...
在PHP中,大部分变量类型,如字符串,整型,浮点,数组等都是值类型的,而类和对象是引用类型.和其他语言有点差距.(1).在值类型中我们直接使用&符号表示指向对应变量的内存地址,当前变量和被指向的变量只要有1个的值被修改都会直接影响另外一个变量的值发生变化。(ps:还是非常节省内存的,可以使用...
PHP不像net支持多继承,自身只支持单继承,为了解决这个问题,php出了Trait这个特性,减少单继承语言的限制。并且能让代码复用率更高。说白了就是一个对象的属性和方法扩展工具一样。例如:trait exts { public f...
重构框架的时候想要考虑支持下cli模式,于是参考了thinkphp的底层。/** * 获取应用根目录 * @return string */ public static function getRootP...
在编写多进程的实例中我在每个进程中使用如下代码://调用等待信号的处理器 while (true) { pcntl_signal_dispatch(); }开启5个进程,cpu直接100%修正之后的代码://调用等待信号的处理器 while&...
php7.1引入了PHP异步信号处理函数pcntl_async_signals() 来处理阻塞问题。在php7之前信号处理方式有2种,第一种是基于ticks来每执行一行代码来触发执行信号监听,第二种是直接while(true){ //监听信号 }第一种方式如果某行的代码阻塞时间较长会影响...