0 0

使用auto_increment产生间隙!20

表结构如下:

SQL code
mysql> show create table tb_user \G
*************************** 1. row ***************************
       Table: tb_user
Create Table: CREATE TABLE `tb_user` (
  `pk_user` int(10) NOT NULL AUTO_INCREMENT COMMENT 'PK主键',
  `true_name` varchar(20) DEFAULT NULL COMMENT '用户姓名,必填',
  `country` varchar(4) DEFAULT NULL 
,
  `province` varchar(4) DEFAULT NULL,
  `city` varchar(4) DEFAULT NULL,
  `company_name` varchar(30) DEFAULT NULL ,
  `department` varchar(20) DEFAULT NULL ,
  `position` varchar(4) DEFAULT NULL ,
  `address` varchar(100) DEFAULT NULL ,
  `phone` varchar(20) DEFAULT NULL ,
  `company_property` varchar(4) DEFAULT NULL ,
  `company_product` varchar(4) DEFAULT NULL ,
  `company_person_count` varchar(4) DEFAULT NULL ,
  `info_mode` varchar(4) DEFAULT NULL ,
  `register_date` datetime DEFAULT NULL ,
  `active_state` varchar(4) DEFAULT NULL ,
  `lock_state` varchar(4) DEFAULT NULL ,
  `use_state` varchar(4) DEFAULT NULL ,
  `creater` varchar(20) DEFAULT NULL ,
  `create_date` datetime DEFAULT NULL ,
  `updater` varchar(20) DEFAULT NULL ,
  `update_date` datetime DEFAULT NULL ,
  PRIMARY KEY (`pk_user`)
) ENGINE=InnoDB AUTO_INCREMENT=2424797 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

 


这里我只是想在表中快速插入千万条数据,因此首先我手动插入2条数据:

SQL code

    insert into tb_user (true_name) values ('jack'); insert into tb_user (true_name) values ('tom');


然后使用下列语句实现快速插入:

SQL code

    insert into tb_user (true_name)(select true_name from tb_user);


但是问题来了,在查询前20条数据就发现pk_user不是按照顺序递增的,中间有很多间隙,前20条数据少了5、10、11、12、21-27,  20条数据直接把pk_user分配到了31.。
另外,这是在本机单机无并发情况下无事务,纯语句级别使用JAVA循环插入实现的
mysql> show variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 1     |
+--------------------------+-------+
1 row in set (0.00 sec)

请教大虾分析下是什么原因产生的,如何解决?拜谢!

问题补充:
robertliudeqiang 写道
可以试试直接在数据库执行:

1 先用你提供的sql语句建表

2 再执行
insert into tb_user (true_name) values ('jack'); insert into tb_user (true_name) values ('tom');

3 再反复执行   
insert into tb_user (true_name) values ('jack'); insert into tb_user (true_name) values ('tom');

看是否仍然会出现你说的问题,如果没有出现这个问题,则很可能是你的java程序写的有问题。


手动插入反复执行是insert into tb_user (true_name) values ('jack'); insert into tb_user (true_name) values ('tom'); 是没有问题的,我其实是觉得会不会是
insert into tb_user (true_name) (select true_name from tb_user);语句出的毛病

问题补充:
robertliudeqiang 写道
手动插入反复执行是insert into tb_user (true_name) values ('jack'); insert into tb_user (true_name) values ('tom'); 是没有问题的,我其实是觉得会不会是 
insert into tb_user (true_name) (select true_name from tb_user);语句出的毛病


1 insert into tb_user (true_name) (select true_name from tb_user);
这条语句是没有问题的

2 如果你手动执行没有出现问题的话,很可能是你写的程序有些小问题,程序不大的话发上来看看

3 另外,看mysql手册
http://dev.mysql.com/doc/refman/5.5/en/innodb-auto-increment-handling.html
有这么一段:

“Lost” auto-increment values and sequence gaps
In all lock modes (0, 1, and 2), if a transaction that generated auto-increment values rolls back, those auto-increment values are “lost.” Once a value is generated for an auto-increment column, it cannot be rolled back, whether or not the “INSERT-like” statement is completed, and whether or not the containing transaction is rolled back. Such lost values are not reused. Thus, there may be gaps in the values stored in an AUTO_INCREMENT column of a table.
意思是说, 如果你执行一条语句失败, 为这条语句产生的auto-increment值将会丢失。结果就是出现你提到的这种现象。

据上推断:是否在你程序中写的sql有些没有执行成功,没有执行成功的sql语句产生的auto-increment值不会被再次使用而造成丢失。




首先,感谢你的回答,问题已经确定是insert into tb_user (true_name) (select true_name from tb_user);的问题,因为我手动反复插入这句语句时(都成功),也会发生这种问题,另外,从间隙的结果分析,上述推断也不成立,比如,我开始有2条数据,在第一次select后插入后pk_user应该递增到4,下一次select4条应该是5-8才是,如果产生间隙也应该是5-8,从9开始才是
2010年3月04日 13:07

5个答案 按时间排序 按投票排序

0 0

采纳的答案

引用
首先,感谢你的回答,问题已经确定是insert into tb_user (true_name) (select true_name from tb_user);的问题,因为我手动反复插入这句语句时(都成功),也会发生这种问题,另外,从间隙的结果分析,上述推断也不成立,比如,我开始有2条数据,在第一次select后插入后pk_user应该递增到4,下一次select4条应该是5-8才是,如果产生间隙也应该是5-8,从9开始才是


我用mysql试了,反复执行
insert into tb_user (true_name) (select true_name from tb_user);
没有问题

很奇怪,你可以换一个mysql试试看,会不会还有这个问题,另外,看看你现在用的mysql的版本,换个不同的版本试试看。

2010年3月05日 13:05
0 0

我一直试到序列号256都是连续的。

2010年3月05日 13:06
0 0

手动插入反复执行是insert into tb_user (true_name) values ('jack'); insert into tb_user (true_name) values ('tom'); 是没有问题的,我其实是觉得会不会是 
insert into tb_user (true_name) (select true_name from tb_user);语句出的毛病


1 insert into tb_user (true_name) (select true_name from tb_user);
这条语句是没有问题的

2 如果你手动执行没有出现问题的话,很可能是你写的程序有些小问题,程序不大的话发上来看看

3 另外,看mysql手册
http://dev.mysql.com/doc/refman/5.5/en/innodb-auto-increment-handling.html
有这么一段:

“Lost” auto-increment values and sequence gaps
In all lock modes (0, 1, and 2), if a transaction that generated auto-increment values rolls back, those auto-increment values are “lost.” Once a value is generated for an auto-increment column, it cannot be rolled back, whether or not the “INSERT-like” statement is completed, and whether or not the containing transaction is rolled back. Such lost values are not reused. Thus, there may be gaps in the values stored in an AUTO_INCREMENT column of a table.
意思是说, 如果你执行一条语句失败, 为这条语句产生的auto-increment值将会丢失。结果就是出现你提到的这种现象。

据上推断:是否在你程序中写的sql有些没有执行成功,没有执行成功的sql语句产生的auto-increment值不会被再次使用而造成丢失。



2010年3月04日 15:12
0 0

如果直接在数据库执行仍然出现这个问题,则检查自己mysql的版本,查看这个版本是不是有这个bug。

2010年3月04日 13:40
0 0

可以试试直接在数据库执行:

1 先用你提供的sql语句建表

2 再执行
insert into tb_user (true_name) values ('jack'); insert into tb_user (true_name) values ('tom');

3 再反复执行   
insert into tb_user (true_name) values ('jack'); insert into tb_user (true_name) values ('tom');

看是否仍然会出现你说的问题,如果没有出现这个问题,则很可能是你的java程序写的有问题。

2010年3月04日 13:39

相关推荐

    mysql_innoDB 事务与锁详解

    4. **间隙锁(Gap Locks)和Next-Key Locks**:为了防止“幻读”问题,InnoDB会自动在会话事务中加上间隙锁,锁定数据行之间的“间隙”,并使用Next-Key Locks来锁定索引键及其之前的数据项,从而保证数据的一致性。...

    最全MySQL面试60题和答案 (2).pdf

    - MyISAM使用表级锁,InnoDB使用行级锁或间隙锁。 - MyISAM不存储行数,InnoDB存储。 - MyISAM索引非聚集,InnoDB索引聚集,主键索引与数据在同一块存储。 4. **事务隔离级别**: - **读未提交(READ ...

    MysqlMVCC机制原理分析.zip

    8. **自增列与事务ID**:InnoDB的自增列(如`AUTO_INCREMENT`字段)与事务ID相关联,有助于确定记录的创建时间。 通过以上机制,MySQL的MVCC能够在保证事务隔离性和一致性的同时,提高并发性能,尤其在高负载的业务...

    mysql死锁分析

    id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'autoid', a varchar(30) NOT NULL COMMENT 'uniq.a', b varchar(30) NOT NULL COMMENT 'uniq.b', c varchar(30) NOT NULL COMMENT 'uniq.c', x varchar...

    mysql之innodb的锁分类介绍

    在初始化 AUTO_INCREMENT 列时,InnoDB会在索引末尾设置独占锁定,但锁定只持续到当前SQL语句结束,而不是整个事务。 4. INSERT INTO T SELECT ... FROM S WHERE ...: 对于这类操作,InnoDB会对插入到T表的每一行...

    InnoDB的七种锁

    - 数据表`t(id AUTO_INCREMENT, name)`已存在如下数据: - `1, shenjian` - `2, zhangsan` - `3, lisi` - 事务A先执行未提交的`INSERT`操作: ```sql INSERT INTO t(name) VALUES('xxx'); ``` - 随后事务B也...

    MySQL 开发规范

    - **举例**:使用`INT AUTO_INCREMENT`作为主键。 - **目的**:减少索引树的高度,提升查询性能。 **2.3 如果不能使用自增,则应考虑构造使用单向递增型主键** - **举例**:如使用时间戳加自增ID组合。 - **目的**...

    MySQL的锁机制解析

    - **自增锁(Auto-Increment Locks)**:处理自增字段的并发。 - **元数据锁(Metadata Locks, MDL)**:保护表结构的锁,确保在表结构更改时不会发生冲突。 5. **死锁**: - 当两个事务相互等待对方释放资源时...

    mysql锁解决并发问题共7页.pdf.zip

    5. **自增锁(Auto-Increment Locks)**:用于保证自增字段的唯一性,防止并发插入时出现重复值。 6. **乐观锁(Optimistic Locking)**:不立即锁定数据,而是先进行操作,再在提交时检查期间是否有其他事务修改了...

    MySQL InnoDB存储引擎的深入探秘

    自增锁自增锁(Auto Increment Locks)在处理自增列时使用,确保在同一时间只有一个事务可以获取自增值,避免自增值的冲突。 索引与聚簇索引InnoDB使用聚簇索引(Clustered Index)的方式来组织表数据,这意味着表...

Global site tag (gtag.js) - Google Analytics