怎么理解MYSQL的auto_increment_offset和auto_increment_increment值


本篇内容主要讲解“怎么理解MYSQL的auto_increment_offset和auto_increment_increment值”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解MYSQL的auto_increment_offset和auto_increment_increment值”吧!
实际上两个值是这样的:
我们理解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 return ULLONG_MAX;
return nr;
}

我使用了GDB进行断点调试如下:
(gdb) p nr
$1 = 0
(gdb) n
3479 if (variables->auto_increment_increment == 1)
(gdb) p save_nr
$2 = 0
(gdb) p variables->auto_increment_increment
$3 = 5
(gdb) p variables->auto_increment_offset
$4 = 10
(gdb) n
3485 (ulonglong) variables->auto_increment_increment);
(gdb) p nr
$5 = 0
(gdb) n
3487 variables->auto_increment_offset);
(gdb) p nr
$6 = 3689348814741910322
(gdb) n
3490 if (unlikely(nr (gdb) p save_nr
$7 = 0
(gdb) p nr
$8 = 4
(gdb) n
3493 return nr;

这样我们找到了问题所在
(gdb) p nr
$6 = 3689348814741910322

这里
(((nr+ variables->auto_increment_increment –
variables->auto_increment_offset)) /
(ulonglong) variables->auto_increment_increment);
variables->auto_increment_increment –
variables->auto_increment_offset
这里出现了负数,但是运算的时候是无符号longlong类型,自动类型转换后得到
了一个非常大的
$6 = 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 &test
$2 = (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_incrementauto_increment_offset
那么值都是不确定的这里的nr是存储上一次来的自增值,初始为0
nr+ variables->auto_increment_increment – variables->auto_increment_offset
所以基于这个原因,建议大家注意auto_increment_increment 大于 auto_increment_offset
是必要的。

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

点击(此处)折叠或打开 #include

typedef unsigned long long ulonglong;
typedef unsigned long ulong;

int main(void)
{
ulonglong nr = 0;
ulonglong nr1;
ulong auto_increment_increment = 5;
ulong a开发云主机域名uto_increment_offset = 10;
ulonglong t1=-5;
ulonglong test1;
printf(“ulonglong size is:%lu ulong size is:%lun”,sizeof(unsigned long long),sizeof(unsigned long));
printf(“nr init values is:%llun”,nr);
printf(“auto_increment_increment is:%lun”,auto_increment_increment);
printf(“auto_increment_offset is :%lun”,auto_increment_offset);
nr= (((nr+ auto_increment_increment – auto_increment_offset))/(ulonglong)auto_increment_increment );
printf(“-5 ulonglong is :%llun”,t1);
printf(“nr+ auto_increment_increment – auto_increment_offset))/(ulonglong)auto_increment_increment is:%llun”,nr);
test1 = nr* (ulonglong)auto_increment_increment;
nr= (nr* (ulonglong)auto_increment_increment + auto_increment_offset);
printf(“nr* (ulonglong)auto_increment_increment is: %llun”,test1);
printf(“last nr is: %llun”,nr);

}
跑一下如下:
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
到此,相信大家对“怎么理解MYSQL的auto_increment_offset和auto_increment_increment值”有了更深的了解,不妨来实际操作一番吧!这里是开发云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

相关推荐: 分析MySQL中关于Bug的解决方法

这篇文章主要介绍“分析MySQL中关于Bug的解决方法”,在日常操作中,相信很多人在分析MySQL中关于Bug的解决方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”分析MySQL中关于Bug的解决方法”的疑惑有所帮助!接下来,…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 06/27 21:59
下一篇 06/27 21:59

相关推荐