怎么理解MYSQL的auto

54次阅读
没有评论

共计 7468 个字符,预计需要花费 19 分钟才能阅读完成。

行业资讯    
数据库    
MySQL 数据库    
怎么理解 MYSQL 的 auto_increment_offset 和 auto_increment_increment 值

本篇内容主要讲解“怎么理解 MYSQL 的 auto_increment_offset 和 auto_increment_increment 值”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“怎么理解 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 = save_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 = save_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_increment variables- auto_increment_offset
  那么值都是不确定的这里的 nr 是存储上一次来的自增值,初始为 0
 nr+ variables- auto_increment_increment – variables- auto_increment_offset
所以基于这个原因,建议大家注意 auto_increment_increment 大于 auto_increment_offset
是必要的。 

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

点击 (此处) 折叠或打开

#include stdio.h

typedef unsigned long long ulonglong;

typedef unsigned long ulong;

int main(void)

{

 ulonglong nr = 0;

 ulonglong nr1;

 ulong auto_increment_increment = 5;

 ulong auto_increment_offset = 10;

 ulonglong t1=-5;

 ulonglong test1;

 printf(ulonglong size is:%lu ulong size is:%lu\n ,sizeof(unsigned long long),sizeof(unsigned long));

 printf(nr init values is:%llu\n ,nr);

 printf(auto_increment_increment is:%lu\n ,auto_increment_increment);

 printf(auto_increment_offset is :%lu\n ,auto_increment_offset);

 nr= (((nr+ auto_increment_increment – auto_increment_offset))/(ulonglong)auto_increment_increment );

 printf(-5 ulonglong is :%llu\n ,t1);

 printf(nr+ auto_increment_increment – auto_increment_offset))/(ulonglong)auto_increment_increment is:%llu\n ,nr);

 test1 = nr* (ulonglong)auto_increment_increment;

 nr= (nr* (ulonglong)auto_increment_increment + auto_increment_offset);

 printf(nr* (ulonglong)auto_increment_increment is: %llu\n ,test1);

 printf(last nr is: %llu\n ,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 值”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-19发表,共计7468字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)