zoukankan      html  css  js  c++  java
  • mysql源码解读之事务提交过程(一)

         mysql是一种关系型数据库,关系型数据库一个重要的特性就是支持事务,这是区别于no-sql产品的一个核心特性。当然了,no-sql产品支持键值查询,不能支持sql语句,这也是一个区别。今天主要讨论下事务的提交流程,由于mysql插件式存储架构,导致开启binlog后,事务提交实质是二阶段提交,通过两阶段提交,来保证存储引擎和二进制日志的一致。本文仅讨论binlog未打卡状态下的提交流程,后续会讨论打开binlog选项后的提交逻辑。源码调试环境如下:

    测试环境:
    OS:windows
    DB:mysql 5.6.12
    engine:innodb
     
    测试前置条件:
    set autocommit=0;
    create table tt(col1 int, col2 varchar(100));
     
    测试语句:
    insert into tt values(1, 'abcdef');
    commit;
     
        无论对于dml语句【insert,update,delete等】还是dcl语句【commit,rollback】,mysql提供了公共接口mysql_execute_command,我们先分析mysql_execute_command接口的基本流程:
    mysql_execute_command
    {
       switch (command)
       {
           case SQLCOM_INSERT:
                    mysql_insert();
                    break;
     
           case SQLCOM_UPDATE:
                    mysql_update();
                    break;
     
           case SQLCOM_DELETE:
                    mysql_delete();
                    break;
           ...... 
       }
       
       if thd->is_error()  //语句执行错误
         trans_rollback_stmt(thd);
      else
        trans_commit_stmt(thd); 
    }
    

      可以看到执行任何语句最后,都会执行trans_rollback_stmt,或trans_commit_stmt,这两个调用分别是语句级提交和语句级回滚。语句级提交,对于非自动模式提交情况下,主要作两件事情,一是释放autoinc锁,这个锁主要用来处理多个事务互斥地获取自增列值,因此,无论最后该语句提交或是回滚,该资源都是需要而且可以立马放掉的。二是标识语句在事务中位置,方便语句级回滚。执行commit后,可以进入commit流程,现在来看看具体事务提交的流程是怎样的。

    mysql_execute_command
        trans_commit
    ha_commit_trans(thd, FALSE);
    {
        TC_LOG_DUMMY:ha_commit_low
            ha_commit_low()    
                innobase_commit
                {
                    //获取innodb层对应的事务结构
                    trx = check_trx_exists(thd);
    
                    if(单个语句,且非自动提交)
                    {
                         //释放自增列占用的autoinc锁资源
                         lock_unlock_table_autoinc(trx);
                         //标识sql语句在事务中的位置,方便语句级回滚
                         trx_mark_sql_stat_end(trx);
                    }
                    else 事务提交
                    {
                         innobase_commit_low()
                         {   
                            trx_commit_for_mysql(); 
                                trx_commit(trx);  
                         }
    
                //确定事务对应的redo日志是否落盘【根据flush_log_at_trx_commit参数,确定redo日志落盘方式】
                         trx_commit_complete_for_mysql(trx);
                  trx_flush_log_if_needed_low(trx->commit_lsn);
                    log_write_up_to(lsn); 
                    }
                }
    }

    trx_commit
        trx_commit_low
            {
                trx_write_serialisation_history
                {
                    trx_undo_update_cleanup //供purge线程处理,清理回滚页
                }
                trx_commit_in_memory
                {
                    lock_trx_release_locks //释放锁资源
                    trx_flush_log_if_needed(lsn) //刷日志
                    trx_roll_savepoints_free //释放savepoints
                }
            }
       

        mysql通过WAL方式,来保证数据库事务的一致性和持久性,即ACID特性中的C(consistent)和D(durability)。WAL(Write-Ahead Logging)是一种实现事务日志的标准方法,具体而言就是修改记录前,一定要先写日志;事务提交过程中,一定要保证日志先落盘,才能算事务提交完成。通过WAL方式,在保证事务特性的情况下,可以提交数据库的性能。从上述流程可以看出,提交过程中,主要做了4件事情,首先是清理undo段信息,对于innodb存储引擎的更新操作来说,undo段需要purge,这里的purge主要职能是,真正删除物理记录。在执行delete或update操作时,实际旧记录没有真正删除,只是在记录上打了一个标记,而是在事务提交后,purge线程真正删除,释放物理页空间。因此,提交过程中会将undo信息加入purge列表,供purge线程处理。然后是释放锁资源,mysql通过锁互斥机制保证不同事务不同时操作一条记录,事务执行后才会真正释放所有锁资源,并唤醒等待其锁资源的其他事务;再就是刷redo日志,前面我提到了,mysql实现事务一致性和持久性的机制。通过redo日志落盘操作,保证了即使修改的数据页没有即使更新到磁盘,只要日志是完成了,就能保证数据库的完整性和一致性;最后就是清理保存点列表,每个语句实际都会有一个savepoint(保存点),保存点作用是为了可以回滚到事务的任何一个语句执行前的状态,由于事务都已经提交了,所以保存点列表可以被清理了。

         关于里面提到的mysql的锁机制,purge原理,redo日志,undo段等内容,其实都是数据库的核心,里面内容也很多,后面学习研究后再给大家分享。后面附录是有关事务的关键数据结构及其成员。
    struct trx_t{
    
            trx_rseg_t*	rseg;		/*!< rollback segment assigned to the
    					transaction, or NULL if not assigned
    
            trx_undo_t*	insert_undo;	/*!< pointer to the insert undo log, or
    					NULL if no inserts performed yet */
    	trx_undo_t*	update_undo;	/*!< pointer to the update undo log, or
    					NULL if no update performed yet */
    	const char*	mysql_log_file_name;
    					/*!< if MySQL binlog is used, this field
    					contains a pointer to the latest file
    					name; this is NULL if binlog is not
    					used */
    	ib_int64_t	mysql_log_offset;
    					/*!< if MySQL binlog is used, this
    					field contains the end offset of the
    					binlog entry */
    }
    
    
    /* The rollback segment memory object */
    struct trx_rseg_t{
    	/* Fields for update undo logs */
    	UT_LIST_BASE_NODE_T(trx_undo_t) update_undo_list;
    					/* List of update undo logs */
    	UT_LIST_BASE_NODE_T(trx_undo_t) update_undo_cached;
    					/* List of update undo log segments
    					cached for fast reuse */
    	/*--------------------------------------------------------*/
    	/* Fields for insert undo logs */
    	UT_LIST_BASE_NODE_T(trx_undo_t) insert_undo_list;
    					/* List of insert undo logs */
    	UT_LIST_BASE_NODE_T(trx_undo_t) insert_undo_cached;
    					/* List of insert undo log segments
    					cached for fast reuse */
    }
    

      

     
     
     
     

     

     

     
     
     
  • 相关阅读:
    thinkphp5 tp5 命名空间 报错 Namespace declaration statement has to be the very first statement in the script
    开启 php 错误 提示 php-fpm 重启 nginx 500错误 解决办法 wdlinux lnmp 一键包 php脚本无法解析执行
    js 设置 cookie 定时 弹出层 提示层 下次访问 不再显示 弹窗 getCookie setCookie setTimeout
    php 二维数组 转字符串 implode 方便 mysql in 查询
    nginx 重启 ps -ef|grep nginx kill -HUP 主进程号
    jquery bootstrap help-block input 表单 提示 帮助 信息
    jquery 倒计时 60秒 短信 验证码 js ajax 获取
    jQuery如何获取同一个类标签的所有的值 遍历
    linux下C语言文件操作相关函数
    gcc,gdb用法
  • 原文地址:https://www.cnblogs.com/cchust/p/3281774.html
Copyright © 2011-2022 走看看