关于 MYSQL auto_increment_offset和auto_increment_increment

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 实际上两个值是这样的: 我们理解auto_increment_offset为0开始的偏移量 auto_increment_increment是一个步长 auto_increment_offset+(N-1)*auto_increment_increment N代表的是插入的次数。
实际上两个值是这样的:
我们理解auto_increment_offset为0开始的偏移量
auto_increment_increment是一个步长
auto_increment_offset+(N-1)*auto_increment_increment
N代表的是插入的次数。这算出来实际上是在0-+∽ 之间可以设置的值。
打个比方
mysql> set auto_increment_offset=2;
Query OK, 0 rows affected (0.00 sec)

mysql> set auto_increment_increment=5;
Query OK, 0 rows affected (0.00 sec)

这样我们允许的值是2  7  12 17 ....
我们建立一个表

mysql> create table testcr11(id int primary key auto_increment)  AUTO_INCREMENT=1;
Query OK, 0 rows affected (0.22 sec)
mysql> insert into testcr11 values(NULL);
Query OK, 1 row affected (0.01 sec)
mysql> select * from testcr11;
+----+
| id |
+----+
|  2 |
+----+
1 row in set (0.00 sec)

可以看到值并不是1开始而是2,在插入一行

mysql> insert into testcr11 values(NULL);
Query OK, 1 row affected (0.20 sec)
mysql> select * from testcr11;
+----+
| id |
+----+
|  2 |
|  7 |
+----+
2 rows in set (0.00 sec)
可以看到没有问题

但是问题是遇到如下一个提示:
When the value of auto_increment_offset is greater than that of
auto_increment_increment, the value of auto_increment_offset is ignored

也就是如果auto_increment_offset>auto_increment_increment ,auto_increment_offset将被忽略。
这个也可以理解,比如
auto_increment_offset = 10
auto_increment_increment = 5

按照公式我们第一次插入的值是10 15 20 ,但是我们发现在0-+∽这样一个线性范围内,我们丢掉了一个
这个值就是10-5 = 5,如果我们这样理解就理解得通了,但是事实真是这样吗?
我打开源码:
看到如下的计算方式
inline ulonglong
compute_next_insert_id(ulonglong nr,struct system_variables *variables)
{
  const ulonglong save_nr= nr;
  if (variables->auto_increment_increment == 1)
    nr= nr + 1; // optimization of the formula below
  else
  {
    nr= (((nr+ variables->auto_increment_increment -
           variables->auto_increment_offset)) /
         (ulonglong) variables->auto_increment_increment);
    nr= (nr* (ulonglong) variables->auto_increment_increment +
         variables->auto_increment_offset);
  }

  if (unlikely(nr <= save_nr))
    return ULLONG_MAX;
  return nr;
}


我使用了GDB进行断点调试如下:
(gdb) p nr
1=0(gdb)n3479if(variables>autoincrementincrement==1)(gdb)psavenr2 = 0
(gdb) p variables->auto_increment_increment 
3=5(gdb)pvariables>autoincrementoffset4 = 10
(gdb) n
3485             (ulonglong) variables->auto_increment_increment);
(gdb) p nr
5=0(gdb)n3487variables>autoincrementoffset);(gdb)pnr6 = 3689348814741910322
(gdb) n
3490      if (unlikely(nr <= save_nr))
(gdb) p save_nr
7=0(gdb)pnr8 = 4
(gdb) n
3493      return nr;

这样我们找到了问题所在
(gdb) p nr
6=3689348814741910322(((nr+variables>autoincrementincrementvariables>autoincrementoffset))/(ulonglong)variables>autoincrementincrement);variables>autoincrementincrementvariables>autoincrementoffsetlonglong6 = 3689348814741910322
这里出现了异常最后得到了一个数字 4
然后我们插入的就是4
mysql> select * from testcr5;
+----+
| id |
+----+
|  4 |
+----+
1 row in set (0.00 sec)

也许如果auto_increment_offset>auto_increment_increment会由于转换问题得到一个
不确定的结果干脆叫做
When the value of auto_increment_offset is greater than that of
auto_increment_increment, the value of auto_increment_offset is ignored
------------------------------------------------------------------------------------------------------------------
下面是具体计算过程:
如果我们要刨根问题为什么是4这个问题需要涉及到很多东西我们先来看变量的类型

先给出计算源码
  typedef unsigned long long ulonglong;
  typedef unsigned long ulong;
  
     nr= (((nr+ variables->auto_increment_increment -
           variables->auto_increment_offset)) /
         (ulonglong) variables->auto_increment_increment);
     
     nr= (nr* (ulonglong) variables->auto_increment_increment +
         variables->auto_increment_offset);
         
给出类型

 nr                                  (ulonglong *)  =0(初始)
 variables->auto_increment_increment (ulong *)  =5
 variables->auto_increment_offset    (ulong *)  =10 
 
 在64位LINUX上ULONG 和ULONGLONG都是8字节,所以我们认为他们表示的范围相同,他们则相同
 同时我们还需要知道ulonglong是不能存储负数的
 而variables->auto_increment_increment - variables->auto_increment_offset =-5 他转换为
 ulong正数就是 18446744073709551611 为什么是这么多呢?
 首先我们要看5的ulong的表示如下:
 0 0000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101 最开始的是符号位
 反码
 0 1111111 11111111 11111111 11111111 11111111 11111111 1111111111111010 
 补码
 0 1111111 11111111 11111111 11111111 11111111 11111111 11111111 11111011
 我们都没有动符号位,实际上负数的符号位是1所以是
 1 1111111 11111111 11111111 11111111 11111111 11111111 11111111 11111011
 好下面我们看看他的16进制表示
 FF FF FF FF FF FF FF FB 这就是-5long的表示,因为ULONG没有负数那么将符号位作为数字表示位
 那么转换为10进制实际上就是
 18446744073709551611
 下面是我GDB 出来的,因为小端Little_endian是不管在内存和磁盘中存储都是内存的低地址存储数值的低位数
 实际上0xfb    0xff    0xff    0xff    0xff    0xff    0xff    0xff 
 fb是低位
( http://blog.itpub.net/7728585/viewspace-2124159/ 关于大端小端)
 (gdb) p test
1 = 18446744073709551611   (gdb) p &test2 = (ulonglong *) 0x7fffffffea28
(gdb) x/8bx 0x7fffffffea28
0x7fffffffea28: 0xfb    0xff    0xff    0xff    0xff    0xff    0xff    0xff
既然
nr+ variables->auto_increment_increment = 18446744073709551611
我们来看下一步
/(ulonglong) variables->auto_increment_increment
实际上就是
18446744073709551611 / 5 = 3689348814741910322 
为什么是3689348814741910322 明显丢掉了一个1
实际上
3689348814741910322*5 = 18446744073709551610
因为整数是不能表示浮点数的,在C语言中使用丢弃小数点后的值。这里就丢了1,这其实就是为什么是4 而不是 5的原因
那么(初始的nr=0)
     nr= (((nr+ variables->auto_increment_increment -
           variables->auto_increment_offset)) /
         (ulonglong) variables->auto_increment_increment);
     nr = 3689348814741910322
接下来做的是
       nr= (nr* (ulonglong) variables->auto_increment_increment +variables->auto_increment_offset);       
nr* (ulonglong) variables->auto_increment_increment 我们已经说了他的值就是
       3689348814741910322*5 = 18446744073709551610
       然后 
       18446744073709551610+variables->auto_increment_offset
       就是
       18446744073709551610+10
       我来看一下 18446744073709551610 二进制
       11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111010
       10的二进制
       1010 低位相加       
       11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111010
       +                                                                  1010
       -----------------------------------------------------------------------
     1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000100            
     我们明显的看到了溢出。溢出就抛弃掉了剩下就是
       00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000100
     就是十进制的4 。
 这就是4计算出来的原因。
 所以MYSQL官方文档使用一个忽略来表示,实际上是不确定的值如果        
 如果
 auto_increment_offset 远远大于 variables->auto_increment_increment
 比如auto_increment_offset=1000
     auto_increment_increment = 2
 那么只要
 nr+ variables->auto_increment_increment< variables->auto_increment_offset
 那么值都是不确定的这里的nr是存储上一次来的自增值,初始为0
 nr+ variables->auto_increment_increment - variables->auto_increment_offset
所以基于这个原因,建议大家注意auto_increment_increment 大于 auto_increment_offset
是必要的。 

下面是一个简单程序演示这个过程:

点击(此处)折叠或打开

  1. #include<stdio.h>

  2. typedef unsigned long long ulonglong;
  3. typedef unsigned long ulong;

  4. int main(void)
  5. {
  6.         ulonglong nr = 0;
  7.         ulonglong nr1;
  8.         ulong auto_increment_increment = 5;
  9.         ulong auto_increment_offset = 10;
  10.         ulonglong t1=-5;
  11.         ulonglong test1;
  12.         printf("ulonglong size is:%lu ulong size is:%lu\n",sizeof(unsigned long long),sizeof(unsigned long));
  13.         printf("nr init values is:%llu\n",nr);
  14.         printf("auto_increment_increment is:%lu\n",auto_increment_increment);
  15.         printf("auto_increment_offset is :%lu\n",auto_increment_offset);
  16.         nr= (((nr+ auto_increment_increment - auto_increment_offset))/(ulonglong)auto_increment_increment );
  17.         printf("-5 ulonglong is :%llu\n",t1);
  18.         printf("nr+ auto_increment_increment - auto_increment_offset))/(ulonglong)auto_increment_increment is:%llu\n",nr);
  19.         test1 = nr* (ulonglong)auto_increment_increment;
  20.         nr= (nr* (ulonglong)auto_increment_increment + auto_increment_offset);
  21.     printf("nr* (ulonglong)auto_increment_increment is: %llu\n",test1);
  22.         printf("last nr is: %llu\n",nr);

  23. }

跑一下如下:
ulonglong size is:8 ulong size is:8
nr init values is:0
auto_increment_increment is:5
auto_increment_offset is :10
-5 ulonglong is :18446744073709551611
nr+ auto_increment_increment - auto_increment_offset))/(ulonglong)auto_increment_increment is:3689348814741910322
nr* (ulonglong)auto_increment_increment is: 18446744073709551610
last nr is: 4


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
打赏
0
0
0
0
91
分享
相关文章
MySQL auto_increment_increment,auto_increment_offset 用法
    MySQL中对于表上ID自增列可以在创建表的时候来指定列上的auto_increment属性;等同于SQL server中的identity属性;Oracle则是通过Sequence方式来实现。
1277 0
Docker Compose V2 安装常用数据库MySQL+Mongo
以上内容涵盖了使用 Docker Compose 安装和管理 MySQL 和 MongoDB 的详细步骤,希望对您有所帮助。
151 42
如何排查和解决PHP连接数据库MYSQL失败写锁的问题
通过本文的介绍,您可以系统地了解如何排查和解决PHP连接MySQL数据库失败及写锁问题。通过检查配置、确保服务启动、调整防火墙设置和用户权限,以及识别和解决长时间运行的事务和死锁问题,可以有效地保障应用的稳定运行。
96 25
云数据库:从零到一,构建高可用MySQL集群
在互联网时代,数据成为企业核心资产,传统单机数据库难以满足高并发、高可用需求。云数据库通过弹性扩展、分布式架构等优势解决了这些问题,但也面临数据安全和性能优化挑战。本文介绍了如何从零开始构建高可用MySQL集群,涵盖选择云服务提供商、创建实例、配置高可用架构、数据备份恢复及性能优化等内容,并通过电商平台案例展示了具体应用。
数据库数据恢复——MySQL简介和数据恢复案例
MySQL数据库数据恢复环境&故障: 本地服务器,安装的windows server操作系统。 操作系统上部署MySQL单实例,引擎类型为innodb,表空间类型为独立表空间。该MySQL数据库没有备份,未开启binlog。 人为误操作,在用Delete命令删除数据时未添加where子句进行筛选导致全表数据被删除,删除后未对该表进行任何操作。
【深入了解MySQL】优化查询性能与数据库设计的深度总结
本文详细介绍了MySQL查询优化和数据库设计技巧,涵盖基础优化、高级技巧及性能监控。
435 0
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
85 3
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
163 3
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE &#39;log_%&#39;;`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
134 2