美文网首页
MySQL自增键介绍

MySQL自增键介绍

作者: Ferrari1001 | 来源:发表于2018-07-09 10:26 被阅读23次

在数据库表设计中会纠结于”自然键”和”代理键”的选择,自然键在实现数据“软删除”时实现比较复杂,部分自然键因为键值过长或多列组合导致不适合作为表主键,而比较常见两种代理键为自增列(auto incremnet)和全局唯一标识列(GUID)。

使用自增列作为主键的优缺点:

1、 主键键值长度短,INT列需要4个字节,BIGINT列需要8个字节;

2、 自增主键顺序递增,在INSERT操作时”顺序”写入表;

3、 由于数据集中插入到表尾部,在高并发情况下容易造成”数据页热点”,影响插入效率;

4、 自增主键只能保证在表内数据唯一,对于分库分表场景,可能因错误操作产生相同的“唯一值”。

使用GUID列的优缺点:

1、 32位GUID字符串需要更多的存储空间来存放(具体存储长度与字符集相关),影响主键和其他索引的查询性能。

2、 GUID可实现全局唯一,能保证在多个表之间的数据唯一性

3、 GUID将数据分散到全表,不会产生热点数据页,但会造成大量随机IO读写

在实际使用过程中,很少场景会使用GUID作为主键,大部分业务按照数据量需求使用INT或BIGINT的自增列作为主键,对于需要多表唯一的场景可以通过程序实现全局唯一的自增ID。

MySQL在很早版本便支持自增列并在各版本中优化自增列功能,在MySQL 5.1.22版本引入轻量级互斥自增长实现机制,MySQL 5.5版本中引入change buffer特性,在MySQL 8.0版本引入自增列持久化。针对目前主要使用MySQL 5.5/5.6/5.7三个主版本,罗列部分使用自增列需要掌握的知识点:

知识点1:自增列数据类型选择问题

自增列除常见的TINYINT/SMALLINT/INT/BIGINT等整数数据类型外,还可以使用FLOAT等浮点数数据类型,但强烈建议不使用非整数数据类型作为自增列。

选取数据类型时:

1、 按照所需要范围值进行最小化选取,如果只需要0-20的范围值,则选择可以存放-128到127数值的TINYINT。

2、 当所需自增范围值不确定时,建议选择足够使用的数据类型,先保证数据安全再考虑操作性能,相同数据量下,使用BIGINT并不会比使用INT带来太多性能影响。商城订单号在前期设计时使用INT数据类型,当INT无法满足需求时,商城花费大量资源进行INT到BIGINT的升级改造,同时影响诸多关联系统。

知识点2:自增列跳号问题

1、无论MySQL还是其他关系型数据库,为提高自增列的生成效率,都将生成自增值的操作设计为非事务性操作,表现为当事务回滚时,事务中生成的自增值不会被回滚。

2、当对自增表进行批量插入时(INSERT … SELECT …),即使在单一会话下,MySQL仍不能保证两次获取到的自增ID值连续,批量插入数据量越大,产生的自增ID跳号范围越大。

自增跳号对普通业务没有太多影响,但对于像发票这类要求号码连续的业务,不能通过自增列来实现。

知识点3:自增列持久化问题

在MySQL 5.5/5.6/5.7三个版本中,MySQL并不会将自增列分配的自增值信息固化到磁盘,当MySQL重启后,会根据自增列上当前最大值和参数auto_increment_offset来确定下一次的自增值,为快速获取自增列上最大值,MySQL要求自增列必须建有索引。如果一张自增表的数据在重启实例前被清空,实例重启后该表数据会从”1”开始自增(假设表的自增初始值定义为1)。

在一次某一数据库升级过程中,某张业务表”恰好”因为业务逻辑将表中所有数据删除,重启后该表自增值从1开始生成,当该表数据流转到其他表出现数据冲突,发现问题后,我们紧急手动设置该表自增值,避免事故进一步恶化,并再后期类似操作时,重点关注此类自增表。

建议1:如果业务会对自增表数据进行硬删除,在服务器重启前应重点关注该自增表使用的自增值,可以通过information_schema.tables中的auto_increment列来获取。

PS1:在MySQL 8.0中引入自增列持久化特性,可以避免上述问题。

知识点4:自增列初始值问题

在运维过程中,会遇到研发同事问为什么新创建的表不是从1开始自增,该问题可以从以下两个角度排查:

1、 建表语句,在使用SHOW CREATE TABLE或MySQLDump等命令导出表结构时,会包含该表当前使用的自增值,如:

在数据库表设计中会纠结于”自然键”和”代理键”的选择,自然键在实现数据“软删除”时实现比较复杂,部分自然键因为键值过长或多列组合导致不适合作为表主键,而比较常见两种代理键为自增列(auto incremnet)和全局唯一标识列(GUID)。

使用自增列作为主键的优缺点:

1、 主键键值长度短,INT列需要4个字节,BIGINT列需要8个字节;

2、 自增主键顺序递增,在INSERT操作时”顺序”写入表;

3、 由于数据集中插入到表尾部,在高并发情况下容易造成”数据页热点”,影响插入效率;

4、 自增主键只能保证在表内数据唯一,对于分库分表场景,可能因错误操作产生相同的“唯一值”。

使用GUID列的优缺点:

1、 32位GUID字符串需要更多的存储空间来存放(具体存储长度与字符集相关),影响主键和其他索引的查询性能。

2、 GUID可实现全局唯一,能保证在多个表之间的数据唯一性

3、 GUID将数据分散到全表,不会产生热点数据页,但会造成大量随机IO读写

在实际使用过程中,很少场景会使用GUID作为主键,大部分业务按照数据量需求使用INT或BIGINT的自增列作为主键,对于需要多表唯一的场景可以通过程序实现全局唯一的自增ID。

MySQL在很早版本便支持自增列并在各版本中优化自增列功能,在MySQL 5.1.22版本引入轻量级互斥自增长实现机制,MySQL 5.5版本中引入change buffer特性,在MySQL 8.0版本引入自增列持久化。针对目前京东主要使用MySQL 5.5/5.6/5.7三个主版本,罗列部分使用自增列需要掌握的知识点:

知识点1:自增列数据类型选择问题

自增列除常见的TINYINT/SMALLINT/INT/BIGINT等整数数据类型外,还可以使用FLOAT等浮点数数据类型,但强烈建议不使用非整数数据类型作为自增列。

选取数据类型时:

1、 按照所需要范围值进行最小化选取,如果只需要0-20的范围值,则选择可以存放-128到127数值的TINYINT。

2、 当所需自增范围值不确定时,建议选择足够使用的数据类型,先保证数据安全再考虑操作性能,相同数据量下,使用BIGINT并不会比使用INT带来太多性能影响。京东订单号在前期设计时使用INT数据类型,当INT无法满足需求时,商城花费大量资源进行INT到BIGINT的升级改造,同时影响诸多关联系统。

知识点2:自增列跳号问题

1、无论MySQL还是其他关系型数据库,为提高自增列的生成效率,都将生成自增值的操作设计为非事务性操作,表现为当事务回滚时,事务中生成的自增值不会被回滚。

2、当对自增表进行批量插入时(INSERT … SELECT …),即使在单一会话下,MySQL仍不能保证两次获取到的自增ID值连续,批量插入数据量越大,产生的自增ID跳号范围越大。

自增跳号对普通业务没有太多影响,但对于像发票这类要求号码连续的业务,不能通过自增列来实现。

知识点3:自增列持久化问题

在MySQL 5.5/5.6/5.7三个版本中,MySQL并不会将自增列分配的自增值信息固化到磁盘,当MySQL重启后,会根据自增列上当前最大值和参数auto_increment_offset来确定下一次的自增值,为快速获取自增列上最大值,MySQL要求自增列必须建有索引。如果一张自增表的数据在重启实例前被清空,实例重启后该表数据会从”1”开始自增(假设表的自增初始值定义为1)。

在一次亚一数据库升级过程中,某张业务表”恰好”因为业务逻辑将表中所有数据删除,重启后该表自增值从1开始生成,当该表数据流转到其他表出现数据冲突,发现问题后,我们紧急手动设置该表自增值,避免事故进一步恶化,并再后期类似操作时,重点关注此类自增表。

建议1:如果业务会对自增表数据进行硬删除,在服务器重启前应重点关注该自增表使用的自增值,可以通过information_schema.tables中的auto_increment列来获取。

PS1:在MySQL 8.0中引入自增列持久化特性,可以避免上述问题。

知识点4:自增列初始值问题

在运维过程中,会遇到研发同事问为什么新创建的表不是从1开始自增,该问题可以从以下两个角度排查:

1、 建表语句,在使用SHOW CREATE TABLE或MySQLDump等命令导出表结构时,会包含该表当前使用的自增值,如:

image.png

2、 全局参数auto_increment_increment和auto_increment_offset,这两全局参数可以作用实例下所有自增表,主要应用在分库分表的场景。

知识点5:修改数据列为自增数据列

当数据类型为数值类型且表中数据唯一时,可以将该数据列转换为自增列,修改操作会保持列中现有数据,不会重新生成新数据。

知识点6:修改普通表为自增表

在MySQL中允许使用ALTER TABLE方式为普通表新增一个自增列,但由于ALTER操作为DDL语句,在主从复制时会将该DDL语句传递给从库执行,MySQL并不能保证相同记录在主从服务器上获得相同的自增ID,会导致主从数据差异。

模拟测试:

主库上创建表:

CREATE TABLE TB1001

(

C1 INT

);

会话1开启事务并执行:

START TRANSACTION;

INSERT INTO TB1001(C1) SELECT 1;

会话2执行:

INSERT INTO TB1001(C1) SELECT 2;

会话1提交事务。

然后将表修改为自增表:

ALTER TABLE TB1001 ADD ID INT PRIMARY KEY AUTO_INCREMENT;

主库数据为:


image.png

从库数据为:

image.png

原因分析:

在主库上,C1=2的数据晚于C1=1的数据被插入,但由于C1=2的数据所在事务被先提交,因此C1=2的记录先于C1=1的记录在从库上执行,因此两条记录在主库和从库上的插入顺序不同,在生成自增ID时获得到自增ID不同,最终导致数据差异。

建议:在将普通表修改为自增表时,如果表中存在数据,请勿使用ALTER TABLE的方式修改,建议新建自增临时表,然后将数据导入到该表中,再兑换表名。

相关文章

  • MySQL自增键介绍

    在数据库表设计中会纠结于”自然键”和”代理键”的选择,自然键在实现数据“软删除”时实现比较复杂,部分自然键因为键值...

  • 分布式主键

    mysql 自增id 自增id 缺点 :1 mysql insert 后 必须 SELECT LAST_INSE...

  • (3)分布式ID之数据库自增ID实现分布式ID

    数据库自增ID机制原理介绍 在分布式里面,数据库的自增ID机制的主要原理是:数据库自增ID和mysql数据库的re...

  • mysql操作数据库

    MYSQL主键自增用法

  • 2019-02-24

    算术运算符 主要介绍一下 %(取模)、自增(++)、自减(--) 自增 (++) 自增分为自增前(++a) 和自增...

  • Mysql的锁和自增主键

    Mysql中的常用命令 Mysql的自增主键 Mysql中维护这个自增主键的值是存放在内存中的,这一点就很关键,也...

  • 关于 mysql 事务中的自增 id 的疑问

    mysql 的自增id 在事务回滚后会怎么样? mysql 的自增id 是什么时候生成id的? 开始一个试验: 创...

  • mysql关于自增

    自增,如果为某列设置自增列,插入数据时无需设置此列,默认将自增(表中只能有一个自增 列) create table...

  • MySQL自增id

    select last_insert_id();SELECT auto_increment FROM inform...

  • MySQL InnoDB锁类型

    1)自增锁(AUTO-INC Locks),表级锁用于AUTO_INCREMENT的自增主键,MySQL 8.0....

网友评论

      本文标题:MySQL自增键介绍

      本文链接:https://www.haomeiwen.com/subject/bmdzuftx.html