關於 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
是必要的。
下面是一個簡單程式演示這個過程:
跑一下如下:
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
我們理解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
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2125766/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- mysql的auto_increment_offset和auto_increment_increment配置MySqlREM
- 關於MySQL主主配置的auto_increment_offset和auto_increment_increment引數的解釋MySqlREM
- mysql 關於exists 和in分析MySql
- 關於MySQLMySql
- 關於點贊業務對MySQL和Redis和MongoDB的思考MySqlRedisMongoDB
- MySQL關於timestamp和mysqldump的一個“bug”MySql
- 關於SHELL+MYSQLMySql
- 關於SUNONE+MYSQLNoneMySql
- MySQL 5.7關於日期和時間的函式整理MySql函式
- 關於MYSQL中FLOAT和DOUBLE型別的儲存MySql型別
- 關於ORACLE MYSQL NOT IN和NOT exists需要注意的 NULL值OracleMySqlNull
- 關於Oracle和MySQL中的無密碼登入OracleMySql密碼
- 關於mysql和jsp的中文問題~謝謝MySqlJS
- 關於mysql的優化MySql優化
- 【mysql】關於binlog格式MySql
- mysql關於mysql.server的總結MySqlServer
- mysql關於FLUSH TABLES和FLUSH TABLES WITH READ LOCK的理解MySql
- 關於mysql,需要掌握的基礎(二):JDBC和DAO層MySqlJDBC
- 【Mysql】關於mysql存入emoji表情的問題MySql
- mysql~關於mysql分割槽表的測試MySql
- MySQL 關於Table cache設定MySql
- 關於mysql的query_cacheMySql
- 面試關於 MySQL 的編寫面試MySql
- Mysql 關於event的詳解MySql
- 關於mysql的最佳化MySql
- mysql關於variable的總結MySql
- 關於MySQL使用的時長MySql
- 關於MYSQL flush table的作用MySql
- 【轉】關於MySQL許可權MySql
- 關於mysql許可權管理MySql
- 關於PR 和PO關係
- 關於mysql字元和數字型別轉換的問題研究MySql字元型別
- 關於對MySQL的SQL_NO_CACHE的理解和用法舉例MySql
- 關於ORACLE和MYSQL中文字元亂碼的根源剖析OracleMySql字元
- 一個不錯的關於mysql和posgresql比較的帖子MySql
- 關於Session和CookieSessionCookie
- 關於ASMM和AMMASM
- 關於BOOT和DUMPboot