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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介:

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

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


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
8天前
|
关系型数据库 MySQL PHP
源码编译安装LAMP(HTTP服务,MYSQL ,PHP,以及bbs论坛)
通过以上步骤,你可以成功地在一台Linux服务器上从源码编译并安装LAMP环境,并配置一个BBS论坛(Discuz!)。这些步骤涵盖了从安装依赖、下载源代码、配置编译到安装完成的所有细节。每个命令的解释确保了过程的透明度,使即使是非专业人士也能够理解整个流程。
40 18
|
1月前
|
SQL 关系型数据库 MySQL
MySQL底层概述—10.InnoDB锁机制
本文介绍了:锁概述、锁分类、全局锁实战、表级锁(偏读)实战、行级锁升级表级锁实战、间隙锁实战、临键锁实战、幻读演示和解决、行级锁(偏写)优化建议、乐观锁实战、行锁原理分析、死锁与解决方案
115 24
MySQL底层概述—10.InnoDB锁机制
|
1月前
|
存储 缓存 关系型数据库
MySQL底层概述—5.InnoDB参数优化
本文介绍了MySQL数据库中与内存、日志和IO线程相关的参数优化,旨在提升数据库性能。主要内容包括: 1. 内存相关参数优化:缓冲池内存大小配置、配置多个Buffer Pool实例、Chunk大小配置、InnoDB缓存性能评估、Page管理相关参数、Change Buffer相关参数优化。 2. 日志相关参数优化:日志缓冲区配置、日志文件参数优化。 3. IO线程相关参数优化: 查询缓存参数、脏页刷盘参数、LRU链表参数、脏页刷盘相关参数。
MySQL底层概述—5.InnoDB参数优化
|
1月前
|
存储 SQL 关系型数据库
MySQL底层概述—4.InnoDB数据文件
本文介绍了InnoDB表空间文件结构及其组成部分,包括表空间、段、区、页和行。表空间是最高逻辑层,包含多个段;段由若干个区组成,每个区包含64个连续的页,页用于存储多条行记录。文章还详细解析了Page结构,分为通用部分(文件头与文件尾)、数据记录部分和页目录部分。此外,文中探讨了行记录格式,包括四种行格式(Redundant、Compact、Dynamic和Compressed),重点介绍了Compact行记录格式及其溢出机制。最后,文章解释了不同行格式的特点及应用场景,帮助理解InnoDB存储引擎的工作原理。
MySQL底层概述—4.InnoDB数据文件
|
1月前
|
存储 缓存 关系型数据库
MySQL底层概述—3.InnoDB线程模型
InnoDB存储引擎采用多线程模型,包含多个后台线程以处理不同任务。主要线程包括:IO Thread负责读写数据页和日志;Purge Thread回收已提交事务的undo日志;Page Cleaner Thread刷新脏页并清理redo日志;Master Thread调度其他线程,定时刷新脏页、回收undo日志、写入redo日志和合并写缓冲。各线程协同工作,确保数据一致性和高效性能。
MySQL底层概述—3.InnoDB线程模型
|
5天前
|
关系型数据库 MySQL 数据库连接
docker拉取MySQL后数据库连接失败解决方案
通过以上方法,可以解决Docker中拉取MySQL镜像后数据库连接失败的常见问题。关键步骤包括确保容器正确启动、配置正确的环境变量、合理设置网络和权限,以及检查主机防火墙设置等。通过逐步排查,可以快速定位并解决连接问题,确保MySQL服务的正常使用。
108 82
|
2月前
|
关系型数据库 MySQL 数据库连接
数据库连接工具连接mysql提示:“Host ‘172.23.0.1‘ is not allowed to connect to this MySQL server“
docker-compose部署mysql8服务后,连接时提示不允许连接问题解决
|
8天前
|
消息中间件 缓存 NoSQL
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
|
1月前
|
关系型数据库 MySQL 数据库
Docker Compose V2 安装常用数据库MySQL+Mongo
以上内容涵盖了使用 Docker Compose 安装和管理 MySQL 和 MongoDB 的详细步骤,希望对您有所帮助。
196 42
|
13天前
|
SQL 关系型数据库 MySQL
MySQL生产环境迁移至YashanDB数据库深度体验
这篇文章是作者将 MySQL 生产环境迁移至 YashanDB 数据库的深度体验。介绍了 YashanDB 迁移平台 YMP 的产品相关信息、安装步骤、迁移中遇到的各种兼容问题及解决方案,最后总结了迁移体验,包括工具部署和操作特点,也指出功能有优化空间及暂不支持的部分,期待其不断优化。

推荐镜像

更多