CodeIgniter源码分析 7.3 - 数据库驱动之事务处理

事务处理

事务处理有两种方式:手动和自动;

事务的自动处理避免了我们根据执行结果进行手动的 rollback/commit,简化了我们处理事务的流程!

事务处理主要由下面几个函数组成:

自动处理:trans_start() , trans_complete()
手动处理:trans_begin() ,trans_rollback(),trans_commit();

自动处理

trans_start

public function trans_start($test_mode = FALSE)
{
        //首先判断事务是否开启,那为什么要判断事务是否开启呢?是因为 trans_off() 可以关闭事务
        if ( ! $this->trans_enabled)
        {
            return;
        }

        // 事务有可能是嵌套的,其实我们可以看出来,CI 框架里的事务嵌套只是对数字的处理而已,什么意思?
        // 意思就是尽管你用如下的事务嵌套,可能你会认为它是每组提交一次,但是通过这里的代码我们看出,
        // 它对事务的嵌套就是用了一个数字做了嵌套的层级的累加而已,所以我们有必要怀疑在后面 trans_complete
        // 中它会递减这个 _trans_depth,然后将所谓嵌套的事务一次性提交
        /*
         trans_start
               trans_start
                  sql....
               trans_complete
        trans_complete
        */
        if ($this->_trans_depth > 0)
        {
            $this->_trans_depth += 1;
            return;
        }

        // 多层事务嵌套只做一次 trans_begin(),这个 trans_begin() 位于每个驱动中!
        // $test_mode 在文档中被叫做 '测试模式',如果为 true 传入trans_begin 函数后会导致 _trans_failure 这个变量为true,
        // 而这个 _trans_failure 用在事务提交函数 trans_complete() 中会导致事务回滚,文档中也说了 :
        // 你可以选择性的将你的事务系统设置为 “测试模式”,这将导致你的所有 查询都被回滚,就算查询成功执行也一样
        $this->trans_begin($test_mode);
        $this->_trans_depth += 1;
}

通过分析这段代码可以看到事务的嵌套处理原来只是对层级的数字累计而已,只是调用了一次真实的事务处理,并且我们也看到事务的 '自动处理' 在内部其实调用了手动处理事务的函数 (trans_begin())!

trans_complete

public function trans_complete()
{
       //还是判断事务是否可用
       if ( ! $this->trans_enabled)
       {
           return FALSE;
       }

       //看到没,事务的提交真的将 _trans_depth 递减,并且当 _trans_depth=0 时会提交事务
       if ($this->_trans_depth > 1)
       {
           $this->_trans_depth -= 1;
           return TRUE;
       }
       else
       {
           $this->_trans_depth = 0;
       }

       //判断 sql 的执行结果,如果执行失败(query() 函数中 sql 失败会修改 _trans_status 的状态),则回滚事务
       if ($this->_trans_status === FALSE OR $this->_trans_failure === TRUE)
       {
           $this->trans_rollback();

           // trans_strict 用来控制嵌套的多组事务的回滚机制,一旦 trans_strict=true,我们看到 _trans_status 会变成true,
           // 那么非常肯定的是当下组事务如果 sql 执行成功的话,一定会提交事务;
           // 对于 trans_strict 其实在文档中也有说明,叫做事务的 ’严格模式‘,具体见:https://codeigniter.org.cn/user_guide/database/transactions.html
           if ($this->trans_strict === FALSE)
           {
               $this->_trans_status = TRUE;
           }

           log_message('debug', 'DB Transaction Failure');
           return FALSE;
       }
       
       
       $this->trans_commit();
       return TRUE;
}

事务提交的源码中,我们看到对嵌套事务像剥洋葱一样,处理到最外层时则会提交事务;同时也看到对于嵌套的多组事务通过事务的 严格模式 控制了它们的回滚机制!

手动处理

在看事务的手动处理之前,我们有必要说下数据库事务 自动提交 (autocommit)!

那什么是事务的自动提交?

在 mysql 中每个查询都被当做一个单独的事务自动执行,也就是说每个执行的 sql 都会被当成一次事务,这就叫事务的自动提交!mysql 默认是开启了事务的自动提交。

我们使用如下的命令可以查看事务是否开启了自动提交:

image.png

那我们就有个疑问?

既然每条执行的 sql 都启用了事务的自动提交的话,那包在多组 sql 外的事务组岂不是鸡肋?如果不是,又是如何处理事务的呢?

trans_begin

public function trans_begin($test_mode = FALSE)
{
        // 如果 _trans_depth 不为0,那就是继续向外剥嵌套的事务呗
        if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
        {
            return TRUE;
        }

        // $test_mode 造成的影响我们在分析 trans_start() 函数时说了:
        //测试模式会导致事务回滚,而事务提交中就是依靠这个 _trans_failure 来决定是否回滚事务
        $this->_trans_failure = ($test_mode === TRUE);

        //原来在处理事务提交时,将 sql 事务的自动提交给关了
        $this->conn_id->autocommit(FALSE);
        return is_php('5.5')
            ? $this->conn_id->begin_transaction()
            : $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK
}

通过分析上面的开启事务的源码我们发现,原来框架事务处理中先将 事务的自动提交给关了,这样事务的提交就只能靠手动方式了,也就是 trans_commit() 了!

trans_commit

public function trans_commit()
{
        // When transactions are nested we only begin/commit/rollback the outermost ones
        if ( ! $this->trans_enabled OR $this->_trans_depth > 0)
        {
            return TRUE;
        }
        
        //提交事务,并将事务状态设置为默认状态
        if ($this->conn_id->commit())
        {
            $this->conn_id->autocommit(TRUE);
            return TRUE;
        }

        return FALSE;
}

至此整个数据库驱动的源码分析就结束了,关于本节事务 自动提交 的还有个问题有必要说下:

你有没有觉得当我们往数据库写入100万条数据时,如果每条 sql 都 commit 一次事务会给数据库带来性能问题?如果你有兴趣你可以测试一下100万条数据 '加事务的入库' 和 '不加事务入库' 的区别,有惊喜哦!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342