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 */
    }
    

      

     
     
     
     

     

     

     
     
     
  • 相关阅读:
    Linux 管道命令(pipe)
    SQLITE入门逐步讲解SQLITE命令行(二)
    用flash上传多个图片、文件的插件TinyBrowser
    PHP的时区问题GMT8
    解决Apache+PHP服务器提示HTTP 500问题
    SQLITE入门逐步讲解SQLITE命令行(四)
    SQLITE入门逐步讲解SQLITE命令行(五)
    linux tar命令使用范例
    SQLITE入门逐步讲解SQLITE命令行(六)
    Tinymce配置智能的Url
  • 原文地址:https://www.cnblogs.com/cchust/p/3281774.html
Copyright © 2011-2022 走看看