innodb 自增列重复值问题
1 innodb 自增列出现重复值的问题
先从问题入手,重现下这个bug
use test;
drop table t1;
create table t1(id int auto_increment, a int, primary key (id)) engine=innodb;
insert into t1 values (1,2);insert into t1 values (null,2);
insert into t1 values (null,2);
select * from t1;
+----+------+
| id | a |
+----+------+
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
+----+------+
delete from t1 where id=2;
delete from t1 where id=3;
select * from t1;
+----+------+
| id | a |
+----+------+
| 1 | 2 |
+----+------+
这里我们关闭mysql,再启动mysql,然后再插入一条数据
insert into t1 values (null,2);
select * FROM T1;
+----+------+
| id | a |
+----+------+
| 1 | 2 |
+----+------+
| 2 | 2 |
+----+------+
我们看到插入了(2,2),而如果我没有重启,插入同样数据我们得到的应该是(4,2);
上面的测试反映了mysql重启后,innodb存储引擎的表自增id可能出现重复利用的情况。
自增id重复利用在某些场景下回出现问题。依然用上面的例子,假设t1有个历史表t1_history用来存t1表的历史数据,那么mysqld重启前,ti_history中可能已经有了(2,2)这条数据,而重启后我们又插入了(2,2),当新插入的(2,2)迁移到历史表时,会违反主键约束。
2 innodb 自增列出现重复值的原因
mysql> show create table t1\G;
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`a` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=innodb AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
建表时可以指定 AUTO_INCREMENT值,不指定时默认为1.这个值表示当前自增列的起始值大小,如果新插入的数据没有指定自增列的值,那么自增列的值即为这个起始值。
对于innodb表,这个值是存在内存中(dict_table_struct.autoinc)。那么又问,为什么我们每次插入新的值后, show create table t1看到AUTO_INCREMENT值是跟随变化的。其实show create table t1是直接从dict_table_struct.autoinc取得的(ha_innobase::update_create_info)。
知道了AUTO_INCREMENT是实时存储内存中的。那么,mysqld 重启后,从哪里得到AUTO_INCREMENT呢? 内存值肯定是丢失了,.实际上mysql采用执行类似select max(id)+1 from t1;方法来得到AUTO_INCREMENT。而这种方法就会造成自增id重复的原因。
3 myisam也有这个问题吗
myisam是没有这个问题的。myisam表.frm文件也存AUTO_INCREMENT值,同innodb一样,这个值也不是实时的。myisam会将这个值实时存储在.MYI文件中(mi_state_info_write)。mysqld重起后会从.MYI中读取AUTO_INCREMENT值(mi_state_info_read)。因此,myisam表重启是不会出现自增id重复的问题。
4 innodb 自增列出现重复问题修复
myisam选择将AUTO_INCREMENT实时存储在.MYI文件头部中。实际上.MYI头部还会实时存其他信息,也就是说写AUTO_INCREMENT只是个顺带的操作。其性能损耗可以忽略。InnoDB 表如果要解决这个问题,有两种方法。1)将auto_increment最大值持久到frm文件中。2)将 auto_increment最大值持久到聚集索引根页trx_id所在的位置。第一种方法直接写文件性能消耗较大,这是一额外的操作,而不是以个顺带的操作。如是我们采用第二种方案。为什么选择存储在聚集索引根页页头trx_id。页头trx_id中存存储trx_id,只对二级索引页和insert buf 页头有效(MVCC).而聚集索引根页页头trx_id这个值是没有使用的,始终保持初始值0.正好这个位置8个字节可存放自增值的值。我们每次更新AUTO_INCREMENT值时,同时将这个值修改到聚集索引根页页头trx_id的位置。 这个写操作跟真正的数据写操作一样,遵守write-ahead log原则,只不过这里只需要redo log ,而不需要undo log。因为我们不需要回滚AUTO_INCREMENT的变化(即回滚后自增列值会保留,即使insert 回滚了,auto_increment值不会回滚)
因此,AUTO_INCREMENT值存储在聚集索引根页trx_id所在的位置,实际上是对内存根页的修改和多了一条redo log(量很小),而这个redo log 的写入也是异步的,可以说是原有事务log的一个顺带操作。因此AUTO_INCREMENT值存储在聚集索引根页这个性能损耗是极小的。
5 修复后的性能对比
我们新增了全局参数innodb_autoinc_persistent 取值on/off; on 表示将AUTO_INCREMENT值实时存储在聚集索引根页。off则采用原有方式只存储在内存。
./bin/sysbench --test=sysbench/tests/db/insert.lua --mysql-port=4001 --mysql-user=root \--mysql-table-engine=innodb --mysql-db=sbtest --oltp-table-size=0 --oltp-tables-count=1 \--num-threads=100 --mysql-socket=/u01/zy/sysbench/build5/run/mysql.sock --max-time=7200 --max-requests run
set global innodb_autoinc_persistent=off;
tps: 22199 rt:2.25ms
set global innodb_autoinc_persistent=on;
tps: 22003 rt:2.27ms
可以看出性能损耗在%1以下。
6 改进
新增参数innodb_autoinc_persistent_interval 用于控制持久化auto_increment值的频率。例如:innodb_autoinc_persistent_interval=100,auto_incrememt_increment=1时,即每100次insert会控制持久化一次auto_increment值。每次持久的值为:当前值+innodb_autoinc_persistent_interval.
测试结果如下
| innodb_autoinc_persistent=OFF |
innodb_autoinc_persistent=ON innodb_autoinc_persistent_interval=1 |
innodb_autoinc_persistent=ON innodb_autoinc_persistent_interval=10 |
innodb_autoinc_persistent=ON innodb_autoinc_persistent_interval=100 |
|
| TPS | 22199 | 22003 |
22069 |
22209 |
| RT(ms) |
2.25 |
2.27 | 2.26 | 2.25 |
注意:如果我们使用需要开启innodb_autoinc_persistent,应该在参数文件中指定,
innodb_autoinc_persistent= on
如果这样指定set global innodb_autoinc_persistent=on;重启后将不会从聚集索引根页读取auto_increment最大值.
两个疑问:
1 对于innodb和 myisam 存储引擎,.frm中的AUTO_INCREMENT是多余的。其他存储引擎没有研究,不知道有没有用处。
2 innodb表,重启通过select max(id)+1 from t1得到AUTO_INCREMENT值,如果id上有索引那么这个语句使用索引查找就很快。那么,这个可以解释mysql 为什么要求自增列必须包含在索引中的原因。 如果没有指定索引,则报如下错误,
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
而myisam表竟然也有这个要求,感觉是多余的。
附:
innodb_autoinc_lock_mode 这个参数主要解决自增列主备复制问题的,用于控制自增列值连续性的。与本文无关,详细可以参考这里
innodb 自增列重复值问题的更多相关文章
- SQLSERVER 自增列,值突然增大1000
SQLSERVER 自增列,值突然增大1000https://blog.csdn.net/lichxi1002/article/details/40074247
- sql server 自增列,值突然增大1000的情况
sql server 自增列,值突然增大1000的情况 解决方法: 1 打开配置管理器2左面点击sql服务3右面 右键点击SQL Server(MSSQLSERVER) 4点击 启动参数5 在参数 ...
- 关于前一篇innodb自增列自己的一点补充
上篇文章是我转载的,忘记注明了出处,在这里深感歉意.但是上篇文章中关于自增列预留ID的计算我当时怎么弄明白,后来自己想了想终于想通了,在这里详细解释一下. 我们以一次性插入10行为例,表格如下: 插 ...
- SQL 重置自增列的值 批量处理
Declare @IdentityTable sysname, @IdentityColumn sysname, @TotalRows int, @i int, @Iden int, @Sql var ...
- sql server 修改表自增列的值
Create PROCEDURE [dbo].[SP_UpdateIdentityId] ( ) , @beforeId INT , @afterId INT ) AS BEGIN IF @befor ...
- SQLServer如何在批量插入后,获取批量插入的自增列的值
解决方法如下: Use the OUTPUT functionality to grab all the INSERTED Id back into a table. 使用output 功能获取所有插 ...
- mysql自增列导致主键重复问题分析。。。
前几天开发童鞋反馈一个利用load data infile命令导入数据主键冲突的问题,分析后确定这个问题可能是mysql的一个bug,这里提出来给大家分享下.以免以后有童鞋遇到类似问题百思不得其解,难 ...
- MySQL -- Innodb是如何处理自增列的
对于那些向带有自增列的表中插入行的语句,Innodb提供一种可配置的锁定机制,这种锁定机制可以显著提高SQL语句的可伸缩性和性能. Innodb中为了使用自增机制,自增列必须是索引的部份,从而可以使用 ...
- (转)mysql自增列导致主键重复问题分析
mysql自增列导致主键重复问题分析... 原文:http://www.cnblogs.com/cchust/p/3914935.html 前几天开发童鞋反馈一个利用load data infile ...
随机推荐
- SQLServer执行命令出现“目录无效的提示”
异常处理汇总-数据库系列 http://www.cnblogs.com/dunitian/p/4522990.html 一般都是清理垃圾清理过头了,把不该删的目录删了 网上说法: 问题描述: 1.s ...
- node模块加载层级优化
模块加载痛点 大家也或多或少的了解node模块的加载机制,最为粗浅的表述就是依次从当前目录向上级查询node_modules目录,若发现依赖则加载.但是随着应用规模的加大,目录层级越来越深,若是在某个 ...
- 游走 bzoj 3143
游走(2s 128MB)walk [问题描述] [输入格式] [输出格式] [样例输入] 3 3 2 3 1 2 1 3 [样例输出] 3.333 [样例说明] 题解: 主要算法:贪心:高斯消元: 题 ...
- 手机web如何实现多平台分享
话说App一般都带有分享到社交平台的入口,web网页的分享也有很不错的框架,但是随着HTML5的不断发展,手机web页面越来越多的进入到我们的生活中,那如何在我们的手机上完成分享呢?话说各大分享平台都 ...
- Windows下Redis缓存服务器的使用 .NET StackExchange.Redis Redis Desktop Manager
Redis缓存服务器是一款key/value数据库,读110000次/s,写81000次/s,因为是内存操作所以速度飞快,常见用法是存用户token.短信验证码等 官网显示Redis本身并没有Wind ...
- 我正在使用Xamarin的跨平台框架—Xamarin.Android回忆录
一.缘起 在自己给别家公司做兼职外包的时候,已经明确知道外包的活不是那么好干的,一般在经历了初期热血澎湃的激情后,逐渐冷淡,愤怒,再冷淡,再愤怒…,听上去好像高潮迭起,但令人尴尬的是,这高潮迭起我们都 ...
- C#跨平台手机应用开发工具Xamarin尝试 与Eclipse简单对比
Xamarin 支持使用C#开发基于Android.IOS.WindowsPhone应用开发,最大特点C#+跨平台,详细说明问度娘. 安装 研究 想体验研究的点击查看页面 Xamarin For Vi ...
- [httpserver]如何解析HTTP请求报文
这个http server的实现源代码我放在了我的github上,有兴趣的话可以点击查看哦. 在上一篇文章中,讲述了如何编写一个最简单的server,但该程序只是接受到请求之后马上返回响应,实在不能更 ...
- 用Excel做出比肩任务管理软件的操作技巧
用Excel做出比肩任务管理软件的操作技巧 在项目管理中,网上有各种各样的工具可以选择,到底用哪个,曾一度困扰着我.我是一个有轻度强迫症的人,总是喜欢试用各种各样的系统,以比较他们之间的不同,试图选择 ...
- 有意思的记录-python
1.变量 类变量紧接在类名后面定义,相当于java和c++的static变量 实例变量在init里定义,相当于java和c++的普通变量 2.日期 #coding:utf-8 import time ...