[MySQL源码] Innodb如何处理auto_inc值

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介:

——————————————————–

ha_innobase::write_row是向innodb写入记录的函数,进入函数时自增列的值还没被设置(如何是NULL的话),会调用handler::update_auto_increment来获取并更新自增列,然后再调用row_insert_for_mysql来实际插入记录。我们的精力主要集中在update_auto_increment及其调用的函数上。

先了解下handler的几个跟自增列相关的成员变量(根据注释及gdb推测):

 a. ulonglong next_insert_id;  

下一个插入自增列的值,当一次插入多行记录时(例如,insert select 操作),第一个没有指定自增列值的记录会从get_auto_increment函数获取的值并赋值给next_insert_id,这样对于剩下的记录就可以直接使用next_insert_id(每次都修改该值)。

b. ulonglong insert_id_for_cur_row; 

当前记录的insert id 。第一次成功插入后,这个值被存储到THD::first_successful_insert_id_in_cur_stmt

c. Discrete_interval auto_inc_interval_for_cur_row;

get_auto_increment函数获取的insert id区间。

Discrete_interval 结构体又包含三个值

interval_min  区间的最小值

interval_values 区间内可用自增值的个数

interval_max 最大值

d.  uint auto_inc_intervals_count;

当前插入语句保留的自增区间数

e.  estimation_rows_to_insert

估计将要插入的记录数,在函数handler::ha_start_bulk_insert()中被设置,值为0表示未知。

//////////////////////////////////////////////////////////////////////////////////////////////////////

下面来分析下handler::update_auto_increment主要流程

1.首先判断自增列是否已经赋值,或者是否不可以为NULL&&sql_mode为MODE_NO_AUTO_VALUE_ON_ZERO时,不做处理

if ((nr= table->next_number_field->val_int()) != 0 ||

      (table->auto_increment_field_not_null &&

      thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO)){

       handler::adjust_next_insert_id_after_explicit_value    // 根据nr和offset设置下一个自增值next_insert_id

       insert_id_for_cur_row= 0;

       DBUG_RETURN(0);

}

2.当预取的insert id区间用完时,需要取更多的insert id。

 if ((nr= next_insert_id) >= auto_inc_interval_for_cur_row.maximum()) 

{

a.如果预取了下一个区间,则使用下一个区间的server id。

      nr= forced->minimum();

      nb_reserved_values= forced->values();

b.否则:

(1)确定需要自增值的数目(nb_desired_values)

当没有预留值,且估值大于0时(auto_inc_intervals_count == 0) && (estimation_rows_to_insert > 0),设置nb_desired_values=estimation_rows_to_insert

否则:   (例如insert into t values (NULL,12),(98,51),(NULL,67),第3个记录会走到这个逻辑

–auto_inc_intervals_count <= AUTO_INC_DEFAULT_NB_MAX_BITS时

          nb_desired_values= AUTO_INC_DEFAULT_NB_ROWS *

            (1 << auto_inc_intervals_count);

          set_if_smaller(nb_desired_values, AUTO_INC_DEFAULT_NB_MAX);

           nb_desired_values值为1*(1<< auto_inc_intervals_count)且不大于AUTO_INC_DEFAULT_NB_MAX(65535)

–auto_inc_intervals_count> AUTO_INC_DEFAULT_NB_MAX_BITS时

      nb_desired_values值为最大值65535

实际上,对于innodb而言,请求的数量可能跟实际获得的自增值数量不同,这取决于trx->n_autoinc_rows

每写完一次记录后, trx->n_autoinc_rows 会被减1(在函数ha_innobase::write_row 中

例如,对于SQL:INSERT INTO T VALUES (NULL,12),(98,51),(NULL,67),(120,44),(NULL,123);

假设当前值为90

第一次调用update_auto_increment时,请求保留5个值

trx->n_autoinc_rows=5,保留5个值

{interval_min = 90, interval_values = 5, interval_max = 95, next = 0x0}

第二次调用update_auto_increment时,使用98

trx->n_autoinc_rows=4

第三次调用update_auto_increment时,请求保留2个值

trx->n_autoinc_rows=3, 保留3个值

{interval_min = 99, interval_values = 3, interval_max = 102, next = 0x0}

prebuilt->trx->n_autoinc_rows

第四次调用update_auto_increment时,使用120

trx->n_autoinc_rows==2

第五次调用update_auto_increment时,请求保留4个值

trx->n_autoinc_rows=1,保留1个值

{interval_min = 121, interval_values = 1, interval_max = 122, next = 0x0}

(2)调用ha_innobase::get_auto_increment获取自增区间,更新table->autoinc (函数后续详解)

(3)nr= compute_next_insert_id(nr-1, variables);  根据offset等配置项计算nr

(4)当(table->s->next_number_keypart == 0时,设置append=true

}

3.table->next_number_field->store((longlong) nr, TRUE)

4.append为true时(为false表示使用的是之前预取的值,无需执行如下步骤)

设置当前auto_inc_interval_for_cur_row

auto_inc_intervals_count++

如果为statement模式,则记录该自增值到binlog

5.insert_id_for_cur_row= nr

6.set_next_insert_id(compute_next_insert_id(nr, variables)); 设置next_insert_id的值

///////////////////////////////////////////////////////////////////////////////////////////////////

ha_innobase::get_auto_increment是实际分配自增值的主要函数。流程如下:

1.调用函数innobase_get_autoinc

a.

ha_innobase::innobase_lock_autoinc

根据不同的autoinc_lock_mode选择加锁模式,我们的环境下这个值都是1,因此选择的case是AUTOINC_NEW_STYLE_LOCKING

注意,以load 或者 insert ..select的方式插入数据时会回退到AUTOINC_OLD_STYLE_LOCKING

dict_table_autoinc_lock->mutex_enter(&table->autoinc_mutex);   //获取autoinc mutex

再判断是否有别的事务已经获取或在等待autoinc Lock,如果是的话,解除Mutex并回退到AUTOINC_OLD_STYLE_LOCKING,否则break,并返回。

在OLD STYLE LOCKING模式下,调用row_lock_table_autoinc_for_mysql加AUTO_INC LOCK(调用lock_table函数加LOCK_AUTO_INC锁),随后如果成功了,再调用dict_table_autoinc_lock(prebuilt->table)加autoinc mutex 

在lock_table->lock_table_create函数里会对table->n_waiting_or_granted_auto_inc_locks++

b.

调用dict_table_autoinc_read(prebuilt->table)获取table->autoinc值

2.

如果tx->n_autoinc_row=0,设置该值为nb_desired_values或者1(如果nb_desired_values=0),然后set_if_bigger(*first_value, autoinc),将first_value设置为当前的table->autoinc值

如果prebuilt->autoinc_last_value == 0,也就是不在一次muti-insert的中间,也设置set_if_bigger(*first_value, autoinc)

*nb_reserved_values = trx->n_autoinc_rows;

3.当不是使用AUTOINC_OLD_STYLE_LOCKING时

a.计算当前值current和需要保留的区间长度need

b.调用next_value = innobase_next_autoinc函数计算保留区间的最后一个值  (bug#61209 fix点)

c.prebuilt->autoinc_last_value = next_value

d.更新table->autoinc的值(dict_table_autoinc_update_if_greater

4.

prebuilt->autoinc_offset = offset;

prebuilt->autoinc_increment = increment;

dict_table_autoinc_unlock(prebuilt->table)->mutex_exit(&table->autoinc_mutex);    //解除mutex


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
5天前
|
存储 关系型数据库 MySQL
【MySQL系列笔记】InnoDB引擎-数据存储结构
InnoDB 存储引擎是MySQL的默认存储引擎,是事务安全的MySQL存储引擎。该存储引擎是第一个完整ACID事务的MySQL存储引擎,其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读,同时被设计用来最有效地利用以及使用内存和 CPU。因此很有必要学习下InnoDB存储引擎,它的很多架构设计思路都可以应用到我们的应用系统设计中。
27 4
|
10天前
|
存储 监控 关系型数据库
【MySQL】InnoDB 什么情况下会产生死锁
【MySQL】InnoDB 什么情况下会产生死锁
|
12天前
|
SQL 关系型数据库 MySQL
|
12天前
|
存储 缓存 关系型数据库
|
1天前
|
关系型数据库 MySQL Linux
Linux CentOs7 安装Mysql(5.7和8.0版本)密码修改 超详细教程
Linux CentOs7 安装Mysql(5.7和8.0版本)密码修改 超详细教程
|
1天前
|
SQL 缓存 关系型数据库
MySQL常见问题解决和自动化安装脚本
这篇内容包含了两个主要部分:解决MySQL登录问题和处理GPG密钥问题。当MySQL密码正确但无法登录时,可以通过执行SQL命令`ALTER USER`和`flush privileges`来修改和重置密码。对于MySQL安装时的GPG密钥错误,首先需要强制删除旧的MySQL仓库包,导入新的GPG公钥,然后安装MySQL服务器。如果遇到GPG检查错误,可以使用`--nogpgcheck`参数忽略检查来安装。最后,提供了一个自动化安装MySQL的脚本,用于检查旧版本、卸载残留、安装MySQL8并启动服务。
13 1
MySQL常见问题解决和自动化安装脚本
|
5天前
|
SQL 关系型数据库 MySQL
【MySQL-3】图形化界面工具DataGrip安装&配置&使用
【MySQL-3】图形化界面工具DataGrip安装&配置&使用
|
5天前
|
关系型数据库 MySQL Linux
【MySQL-2】MySQL的下载&安装&启停&配置环境变量【一条龙教程】
【MySQL-2】MySQL的下载&安装&启停&配置环境变量【一条龙教程】
|
7天前
|
弹性计算 关系型数据库 MySQL
在线安装MySQL5.7和MySQL8.0
【4月更文挑战第30天】
24 0
|
7天前
|
存储 关系型数据库 MySQL
docker安装mysql8忽略大小写
docker安装mysql8忽略大小写