本文只分析了insert语句执行的主路径,和路径上部分关键函数,很多细节没有深入,留给读者继续分析

create table t1(id int);

insert into t1 values(1)

略过建立连接,从 mysql_parse() 开始分析

void mysql_parse(THD *thd, char *rawbuf, uint length,Parser_state *parser_state)
{/* ...... *//* 检查query_cache,如果结果存在于cache中,直接返回 */if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)   {LEX *lex= thd->lex;/* 解析语句 */bool err= parse_sql(thd, parser_state, NULL);/* 整理语句格式,记录 general log *//* ...... *//* 执行语句 */error= mysql_execute_command(thd);/* 提交或回滚没结束的事务(事务可能在mysql_execute_command中提交,用trx_end_by_hint标记事务是否已经提交) */if (!thd->trx_end_by_hint)         {if (!error && lex->ci_on_success)trans_commit(thd);if (error && lex->rb_on_fail)trans_rollback(thd);}

进入 mysql_execute_command()

  /*  *//* ...... */case SQLCOM_INSERT:{  /* 检查权限 */if ((res= insert_precheck(thd, all_tables)))break;/* 执行insert */res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,lex->update_list, lex->value_list,lex->duplicates, lex->ignore);/* 提交或者回滚事务 */if (!res){trans_commit_stmt(thd);trans_commit(thd);thd->trx_end_by_hint= TRUE;}else if (res){trans_rollback_stmt(thd);trans_rollback(thd);thd->trx_end_by_hint= TRUE;}

进入 mysql_insert()

bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, /* insert 的字段 */List<List_item> &values_list, /* insert 的值 */List<Item> &update_fields,List<Item> &update_values,enum_duplicates duplic,bool ignore)
{ /*对每条记录调用 write_record */while ((values= its++)){if (lock_type == TL_WRITE_DELAYED){LEX_STRING const st_query = { query, thd->query_length() };DEBUG_SYNC(thd, "before_write_delayed");/* insert delay */error= write_delayed(thd, table, st_query, log_on, &info);DEBUG_SYNC(thd, "after_write_delayed");query=0;}else /* normal insert */error= write_record(thd, table, &info, &update);}/*这里还有thd->binlog_query()写binlogmy_ok()返回ok报文,ok报文中包含影响行数*/

进入 write_record

/*COPY_INFO *info 用来处理唯一键冲突,记录影响行数COPY_INFO *update 处理 INSERT ON DUPLICATE KEY UPDATE 相关信息
*/
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
{if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE){/* 处理 INSERT ON DUPLICATE KEY UPDATE 等复杂情况 */}/* 调用存储引擎的接口 */else if ((error=table->file->ha_write_row(table->record[0]))){DEBUG_SYNC(thd, "write_row_noreplace");if (!ignore_errors ||table->file->is_fatal_error(error, HA_CHECK_DUP))goto err; table->file->restore_auto_increment(prev_insert_id);goto ok_or_after_trg_err;}
}

进入ha_write_row、write_row

/* handler 是各个存储引擎的基类,这里我们使用InnoDB引擎*/
int handler::ha_write_row(uchar *buf)
{/* 指定log_event类型*/Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;error= write_row(buf);
}

进入引擎层,这里是innodb引擎,handler对应ha_innobase
插入的表信息保存在handler中

int
ha_innobase::write_row(
/*===================*/uchar*  record) /*!< in: a row in MySQL format */
{error = row_insert_for_mysql((byte*) record, prebuilt);
}
UNIV_INTERN
dberr_t
row_insert_for_mysql(
/*=================*/byte*           mysql_rec,      /*!< in: row in the MySQL format */row_prebuilt_t* prebuilt)       /*!< in: prebuilt struct in MySQLhandle */
{/*记录格式从MySQL转换成InnoDB*/row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec);thr->run_node = node;thr->prev_node = node;/*插入记录*/row_ins_step(thr);
}
UNIV_INTERN
que_thr_t*
row_ins_step(
/*=========*/que_thr_t*      thr)    /*!< in: query thread */
{/*给表加IX锁*/err = lock_table(0, node->table, LOCK_IX, thr);/*插入记录*/err = row_ins(node, thr);
}

InnoDB表是基于B+树的索引组织表

如果InnoDB表没有主键和唯一键,需要分配隐含的row_id组织聚集索引

row_id分配逻辑在row_ins中,这里不详细展开

static __attribute__((nonnull, warn_unused_result))
dberr_t
row_ins(
/*====*/ins_node_t*     node,   /*!< in: row insert node */que_thr_t*      thr)    /*!< in: query thread */
{if (node->state == INS_NODE_ALLOC_ROW_ID) {/*若innodb表没有主键和唯一键,用row_id组织索引*/row_ins_alloc_row_id_step(node);/*获取row_id的索引*/node->index = dict_table_get_first_index(node->table);node->entry = UT_LIST_GET_FIRST(node->entry_list);}/*遍历所有索引,向每个索引中插入记录*/while (node->index != NULL) {if (node->index->type != DICT_FTS) {/* 向索引中插入记录 */err = row_ins_index_entry_step(node, thr);if (err != DB_SUCCESS) {return(err);}}                                                                                                                                                                                           /*获取下一个索引*/node->index = dict_table_get_next_index(node->index);node->entry = UT_LIST_GET_NEXT(tuple_list, node->entry);}}
}

插入单个索引项

static __attribute__((nonnull, warn_unused_result))
dberr_t
row_ins_index_entry_step(
/*=====================*/ins_node_t*     node,   /*!< in: row insert node */que_thr_t*      thr)    /*!< in: query thread */
{dberr_t err;/*给索引项赋值*/row_ins_index_entry_set_vals(node->index, node->entry, node->row);/*插入索引项*/err = row_ins_index_entry(node->index, node->entry, thr);return(err);
}
static
dberr_t
row_ins_index_entry(
/*================*/dict_index_t*   index,  /*!< in: index */dtuple_t*       entry,  /*!< in/out: index entry to insert */que_thr_t*      thr)    /*!< in: query thread */
{if (dict_index_is_clust(index)) {/* 插入聚集索引 */return(row_ins_clust_index_entry(index, entry, thr, 0));} else {/* 插入二级索引 */return(row_ins_sec_index_entry(index, entry, thr));}
}

row_ins_clust_index_entry 和 row_ins_sec_index_entry 函数结构类似,只分析插入聚集索引

UNIV_INTERN
dberr_t
row_ins_clust_index_entry(
/*======================*/dict_index_t*   index,  /*!< in: clustered index */dtuple_t*       entry,  /*!< in/out: index entry to insert */que_thr_t*      thr,    /*!< in: query thread */ulint           n_ext)  /*!< in: number of externally stored columns */
{if (UT_LIST_GET_FIRST(index->table->foreign_list)) {err = row_ins_check_foreign_constraints(index->table, index, entry, thr);if (err != DB_SUCCESS) {return(err);}}/* flush log,make checkpoint(如果需要) */log_free_check();/* 先尝试乐观插入,修改叶子节点 BTR_MODIFY_LEAF */err = row_ins_clust_index_entry_low(0, BTR_MODIFY_LEAF, index, n_uniq, entry, n_ext, thr, &page_no, &modify_clock);if (err != DB_FAIL) {DEBUG_SYNC_C("row_ins_clust_index_entry_leaf_after");return(err);}    /* flush log,make checkpoint(如果需要) */log_free_check();/* 乐观插入失败,尝试悲观插入 BTR_MODIFY_TREE */return(row_ins_clust_index_entry_low(0, BTR_MODIFY_TREE, index, n_uniq, entry, n_ext, thr,&page_no, &modify_clock));

row_ins_clust_index_entry_low 和 row_ins_sec_index_entry_low 函数结构类似,只分析插入聚集索引

UNIV_INTERN
dberr_t
row_ins_clust_index_entry_low(
/*==========================*/ulint           flags,  /*!< in: undo logging and locking flags */ulint           mode,   /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE,depending on whether we wish optimistic orpessimistic descent down the index tree */dict_index_t*   index,  /*!< in: clustered index */ulint           n_uniq, /*!< in: 0 or index->n_uniq */dtuple_t*       entry,  /*!< in/out: index entry to insert */ulint           n_ext,  /*!< in: number of externally stored columns */que_thr_t*      thr,    /*!< in: query thread */ulint*          page_no,/*!< *page_no and *modify_clock are used to decidewhether to call btr_cur_optimistic_insert() duringpessimistic descent down the index tree.in: If this is optimistic descent, then *page_nomust be ULINT_UNDEFINED. If it is pessimisticdescent, *page_no must be the page_no to which anoptimistic insert was attempted last timerow_ins_index_entry_low() was called.out: If this is the optimistic descent, *page_no is setto the page_no to which an optimistic insert wasattempted. If it is pessimistic descent, this value isnot changed. */ullint*         modify_clock) /*!< in/out: *modify_clock == ULLINT_UNDEFINEDduring optimistic descent, and the modify_clockvalue for the page that was used for optimisticinsert during pessimistic descent */
{/* 将cursor移动到索引上待插入的位置 */btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode,                                                                                                                                     &cursor, 0, __FILE__, __LINE__, &mtr);/*根据不同的flag检查主键冲突*/err = row_ins_duplicate_error_in_clust_online(n_uniq, entry, &cursor,&offsets, &offsets_heap);err = row_ins_duplicate_error_in_clust(flags, &cursor, entry, thr, &mtr);/*如果要插入的索引项已存在,则把insert操作改为update操作索引项已存在,且没有主键冲突,是因为之前的索引项对应的数据被标记为已删除本次插入的数据和上次删除的一样,而索引项并未删除,所以变为update操作     */if (row_ins_must_modify_rec(&cursor)) {/* There is already an index entry with a long enough commonprefix, we must convert the insert into a modify of anexisting record */mem_heap_t*     entry_heap      = mem_heap_create(1024);/* 更新数据到存在的索引项 */err = row_ins_clust_index_entry_by_modify(flags, mode, &cursor, &offsets, &offsets_heap,entry_heap, &big_rec, entry, thr, &mtr);/*如果索引正在online_ddl,先记录insert*/if (err == DB_SUCCESS && dict_index_is_online_ddl(index)) {row_log_table_insert(rec, index, offsets);}/*提交mini transaction*/mtr_commit(&mtr);mem_heap_free(entry_heap);} else {rec_t*  insert_rec;if (mode != BTR_MODIFY_TREE) {/*进行一次乐观插入*/err = btr_cur_optimistic_insert(flags, &cursor, &offsets, &offsets_heap,entry, &insert_rec, &big_rec,n_ext, thr, &mtr);} else {/*如果buffer pool余量不足25%,插入失败,返回DB_LOCK_TABLE_FULL处理DB_LOCK_TABLE_FULL错误时,会回滚事务防止大事务的锁占满buffer pool(注释里写的)*/if (buf_LRU_buf_pool_running_out()) {err = DB_LOCK_TABLE_FULL;goto err_exit;}if (/*太长了,略*/) {/*进行一次乐观插入*/err = btr_cur_optimistic_insert(flags, &cursor,&offsets, &offsets_heap,entry, &insert_rec, &big_rec,n_ext, thr, &mtr);} else {err = DB_FAIL;}if (err == DB_FAIL) {/*乐观插入失败,进行悲观插入*/err = btr_cur_pessimistic_insert(flags, &cursor,&offsets, &offsets_heap,entry, &insert_rec, &big_rec,n_ext, thr, &mtr);}}}

btr_cur_optimistic_insert 和 btr_cur_pessimistic_insert 涉及B+树的操作,内部细节很多,以后再做分析

MySQL · 源码分析 · 一条insert语句的执行过程相关推荐

  1. mysql insert执行过程_MySQL · 源码分析 · 一条insert语句的执行过程

    本文只分析了insert语句执行的主路径,和路径上部分关键函数,很多细节没有深入,留给读者继续分析 create table t1(id int); insert into t1 values(1) ...

  2. vue源码分析系列三:render的执行过程和Virtual DOM的产生

    render 手写 render 函数,仔细观察下面这段代码,试想一下这里的 createElement 参数是什么 . new Vue({el: '#application',render(crea ...

  3. 转 MySQL源码分析

    看到一个不错的介绍,原址如下: http://software.intel.com/zh-cn/blogs/2010/08/20/mysql0/ MySQL源码分析(0):编译安装及调试 作者: Yu ...

  4. mysql 执行概况_转mysql源码分析之SQL执行过程简介

    本人打算从SQL语句的执行开始学习和分析MYSQL源码,首先了解MYSQL是如何执行一条SQL语句的,详细了解它的执行过程之后,再深入学习执行一条SQL语句的运行原理. 1)从执行一条SQL语句的堆栈 ...

  5. MySQL 源码分析 binlog 编号上限

    MySQL 源码分析 binlog 编号上限 更新时间:2022-10-30 文章目录 MySQL 源码分析 binlog 编号上限 内容声明 问题描述 测试想法 问题测试 源码说明 MAX_LOG_ ...

  6. 执行计划 分析一条sql语句的效率 mysql_MySQL中一条SQL语句的执行过程

    MySQL中一条SQL语句的执行过程 发布时间:2018-11-24 18:35, 浏览次数:390 , 标签: MySQL SQL 查询语句的执行顺序: 1.客户端通过TCP连接发送连接请求到mys ...

  7. MySQL 源码分析 v2.0

    第一节 mysql编译 (一).参考 https://blog.jcole.us/innodb/ https://www.cnblogs.com/zengkefu/p/5674503.html htt ...

  8. mysql源码分析书籍_从源码分析 MySQL 死锁问题入门

    链接:https://juejin.im/post/5ce287326fb9a07ea8039d70 这篇文章主要讲的是如何通过调试 MySQL 源码,知道一条 SQL 真正会拿哪些锁,不再抓虾,瞎猜 ...

  9. mysql源码分析——索引的数据结构

    引子 说几句题外话,在京被困三个月之久,不能回家,所以这个源码分析就中断了.之所以在家搞这个数据库的源码分析,主要是在家环境齐全,公司的电脑老旧不堪.意外事件往往打断正常的习惯和运行轨迹,但这却是正常 ...

最新文章

  1. Bootstrap select 多选并获取选中的值
  2. 厦大AI研究院今日揭牌成立:数学系校友陈纯院士领衔
  3. linux哪些端口占用了,如何查看某个端口被谁占用(Linux如何查询哪些端口被占用)...
  4. linux vss rss区别,关于VSS / RSS / PSS / USS的解释是否准确?
  5. 土地利用转移矩阵图怎么做_肺癌骨转移有哪些早期症状?做什么检查可以发现?怎么治疗?...
  6. Spring Bean InitializingBean和DisposableBean实例
  7. 17、Java并发性和多线程-避免死锁
  8. WebSocket之JS发送二进制
  9. 转:So Easy!让开发人员更轻松的工具和资源
  10. TCP/IP——TCP数据包分析
  11. 史陶比尔staubli机器人手柄控制器维修操作屏修理
  12. python编写自动更换ip工具的代码
  13. 【历史上的今天】3 月 13 日:Windows NT 之父出生;首届无人车顶级赛事;微软上市
  14. 华为交换机配置syslog发送_配置华为交换机推送syslog到日志服务器
  15. 可乐要加冰才好喝啊---装饰模式
  16. Linux(6)磁盘管理和文件系统
  17. 番外篇15:libevent简单理解(附libevent官方代码解析,和跨平台服务器、客户端链接代码)
  18. 微型计算机原理与接口技术-实验一
  19. python while true跳出条件_while(true)何时跳出循环?
  20. Above the MedianDueling GPSs

热门文章

  1. MyBatisPlus中自定义全局操作流程
  2. IDEA中SpringBoot项目使用@Data要安装Lombok插件
  3. 什么是服务的幂等?为什么要实现幂等?
  4. Java 网络 socket 编程
  5. 数据恢复软件哪个好用比特数据恢复当仁不让
  6. 2013年展望:大数据发展十大趋势分析
  7. Ubuntu ls可以查看到文件,图形界面却看不到
  8. Maven实战(二)——POM重构之增还是删
  9. linux下rpm方式安装mysql5.6及问题解决
  10. 微软职位内部推荐-SW Engineer II for WinCE