laravel项目中出现mysql嵌套事务的分析

自己对于项目代码架构了解的真的是太少了,也没有时间去看、研究,整天忙于各种业务代码。今天想在项目中添加上事务处理!碰到了如下的一个问题:
	1.定义了一个公共方法,里面进行了一系列的数据库操作,为了保证异常时,可以恢复到最初状态,采用了 '事务机制'。

		// 增加用户积分
		function add_point(){
    		DB::beginTransaction();
    		try{
    			// 各种操作
    			DB::commit();
    		} catch (\Exception $e) {
    			DB::rollBack();
    		} catch (\Throwable $e) {
    			DB::rollBack();
    		}
		}

    2.当在其他方法内,同样进行了一系列数据操作,又使用了 '事务机制',同时,使用了之前定义了的公共方法 'add_point()'

		function register(){
    		DB::beginTransaction();
    		try{

    			// 1.先增加用户积分
    			add_point();

    			// 2.其他各种操作

    			DB::commit();
    		} catch (\Exception $e) {
    			DB::rollBack();
    		} catch (\Throwable $e) {
    			DB::rollBack();
    		}
		}

	3.发现了问题:
		出现了2次事务机制,且是嵌套的2个事务!这种情况可以吗???
		(我是真心不知道。。。所以说,架构的一些东西,差的太多了,还没人问,只能自己摸索!真希望有个大牛师父,我保证跟着他好好学习、提高!)

		只能百度、google,发现了一篇博客,这里引用下:
			PHP中实现MySQL嵌套事务的两种解决方案(http://blog.csdn.net/hello_katty/article/details/45220825)
		/*
			1.非常感谢这些大牛,解决了我的困惑,而且里面提到了目前项目中用的 laravel 框架!
			2.再一次感谢laravel这么好的框架,这些牛人的架构,让人敬畏!
		 */

	4.问题分析(这里会复制一些上面博客的内容)
		1>在MySQL的官方文档中有明确的说明不支持嵌套事务:
				Transactions cannot be nested. This is a consequence of the implicit commit performed for any current transaction when you issue a START TRANSACTION statement or one of its synonyms. 
			翻译过来的意思就是:
				MySQL不可嵌套事务。一旦嵌套了事务,当第二次 '开启事务' 时,会隐式(默认)执行上一个事务的 '事务提交'。
			/*
				这种情况不是我们想要的!MySQL本身不支持!
			 */

		2>所幸的是在一些成熟的ORM框架中都做了对嵌套的支持,比如doctrine或者laravel。
			我们可以通过程序,来模拟出 '事务嵌套'。这里我简单看了下laravel关于数据库的源码:
			/*
				我只想说laravel太复杂了,真的是我这种人努力一辈子也达不到的境界
			 */
			1)目录分析:
				laravel数据库源码目录为:vendor/laravel/framework/src/Illuminate/Database/
				Capsule/		// 注册服务容器
				Connectors/		// 数据库连接(支持mysql,postgres,sqlite,sqlserver)
				Console/		// 命令行
					Migrations/	// 迁移
					Seeds/		// 填充
				Eloquent/		// Eloquent ORM
				Events/			// 事件处理
				Migrations/		// 迁移
				Query/			// 查询构建器
				Schema/			// 表、字段操作
				connection.php 		// 数据库连接、基本操作核心文件(table, select, update, insert, delete,事务等...)
				...剩余文件...

			2)在connection.php中,找到事务处理相关的代码:
			    public function transaction(Closure $callback)
			    {
			        $this->beginTransaction();

			        // We'll simply execute the given callback within a try / catch block
			        // and if we catch any exception we can rollback the transaction
			        // so that none of the changes are persisted to the database.
			        try {
			            $result = $callback($this);

			            $this->commit();
			        }

			        // If we catch an exception, we will roll back so nothing gets messed
			        // up in the database. Then we'll re-throw the exception so it can
			        // be handled how the developer sees fit for their applications.
			        catch (Exception $e) {
			            $this->rollBack();

			            throw $e;
			        } catch (Throwable $e) {
			            $this->rollBack();

			            throw $e;
			        }

			        return $result;
			    }

			    public function beginTransaction()
			    {
			        ++$this->transactions;

			        if ($this->transactions == 1) {
			            try {
			                $this->getPdo()->beginTransaction();
			            } catch (Exception $e) {
			                --$this->transactions;

			                throw $e;
			            }
			        } elseif ($this->transactions > 1 && $this->queryGrammar->supportsSavepoints()) {
			            $this->getPdo()->exec(
			                $this->queryGrammar->compileSavepoint('trans'.$this->transactions)
			            );
			        }

			        $this->fireConnectionEvent('beganTransaction');
			    }

			    public function commit()
			    {
			        if ($this->transactions == 1) {
			            $this->getPdo()->commit();
			        }

			        $this->transactions = max(0, $this->transactions - 1);

			        $this->fireConnectionEvent('committed');
			    }

			    public function rollBack()
			    {
			        if ($this->transactions == 1) {
			            $this->getPdo()->rollBack();
			        } elseif ($this->transactions > 1 && $this->queryGrammar->supportsSavepoints()) {
			            $this->getPdo()->exec(
			                $this->queryGrammar->compileSavepointRollBack('trans'.$this->transactions)
			            );
			        }

			        $this->transactions = max(0, $this->transactions - 1);

			        $this->fireConnectionEvent('rollingBack');
			    }

			    public function transactionLevel()
			    {
			        return $this->transactions;
			    }

	    	3)简单解释下:
	    		laravel5.2,事务文档:
	    			http://laravelacademy.org/post/2942.html

		    		相关的几个方法:
		    			自动事务:
			    			DB::transaction();
			    		手动事务:
			    			DB::beginTransaction();
			    			DB::rollBack();
			    			DB::commit();

			   	其实提到的就是上面源码的几个函数
		    		DB::transactions(); 只是将 '手动事务' 的3个方法封装了下,同时包含了异常处理!
		    		/*
					    关于异常处理,为什么如此处理,查看我之前写的异常处理分析:
						    laravel异常分析(http://blog.csdn.net/beyond__devil/article/details/78327433)
		    		 */
			    所以,我们不想用自动事务,传递 '闭包函数',自己来使用异常定制:
		    		DB::beginTransaction();
		    		try{
		    			// 各种操作
		    			DB::commit();
		    		} catch (\Exception $e) {
		    			DB::rollBack();
		    		} catch (\Throwable $e) {
		    			DB::rollBack();
		    		}

		    4>主要要解释下,事务嵌套的实现:
		    	主要是通过 '$this->transactions' 来判断:
		    	如果 $this->transactions == 1 	// 非事务嵌套,正常处理即可
		    	如果 $this->transactions > 1		// 事务嵌套,通过是否支持 '不同嵌套等级的存储点',来进行不同的回滚!

		    	vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/Grammar.php
		    	最终发现了

		    		// 支持 'savepoint'
				    public function supportsSavepoints()
				    {
				        return true;
				    }

				    // 标记一个 'savepoint trans2'
				    public function compileSavepoint($name)
				    {
				        return 'SAVEPOINT '.$name;
				    }

				    // 回滚到 'savepoint trans2'
				    public function compileSavepointRollBack($name)
				    {
				        return 'ROLLBACK TO SAVEPOINT '.$name;
				    }

				/*
					搜索了好多代码,以为是laravel框架自己实现的!
					最终发现,这是 MySQL '部分事务' 的语句!
				 */

				到此,我们基本就明白了原理!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值