谈谈id那些事(一)——数据库的自增ID

数据库的自增ID(Auto Increment ID)是一种常用的主键生成机制,用于在表中为每一行自动分配一个唯一的标识符。


01

自增 ID 的基本原理



自增 ID 的本质是一个单调递增的整数序列。当插入一条新记录时,数据库会自动为自增列分配一个唯一的、递增的值,无需用户手动设置。

  • 默认起始值:通常从 1 开始(可以配置)。

  • 增量步长:默认为 1(可以调整)。

  • 保证唯一性:数据库底层确保分配的自增值是唯一的。


02

自增 ID 的实现方式



2.1 MySQL 的自增 ID 实现

InnoDB 存储引擎

  • 自增值存储位置:

    • 自增值保存在内存中的表元数据中,而不是持久化存储在磁盘上。

    • 每次重启数据库时,InnoDB 会通过 AUTO_INCREMENT 列的最大值重新初始化自增值。

  • 分配规则:

    • 插入新行时,分配 AUTO_INCREMENT 列的当前值,然后将该值递增。

    • 如果事务回滚,分配的自增值不会被回收,造成 ID 间的“空洞”。

MyISAM 存储引擎

  • 自增值存储位置:

    • 自增值存储在表的 .MYD 文件中,并且是持久化的。

  • 分配规则:

    • 插入新行时,MyISAM 会锁住整个表,分配一个新的自增值。

    • 自增值是连续的,回滚事务也不会出现“空洞”。


2.2 PostgreSQL 的自增 ID 实现

PostgreSQL 使用序列(Sequence)实现自增 ID。序列是一个独立的对象,专门用于生成唯一的自增值。

特点:

  • 独立性:序列对象与表分离,允许多个表共享同一个序列。

  • 持久化存储:序列的当前值存储在磁盘中,数据库重启后不会丢失。

  • 分配规则:

    • 通过 nextval() 函数分配下一个自增值。

    • 回滚事务后,自增值不会回退,仍然可能出现“空洞”。

示例:

CREATE SEQUENCE my_sequence START 1 INCREMENT 1;SELECT nextval('my_sequence'); -- 获取下一个值

2.3 Oracle 的自增 ID 实现

Oracle 数据库也使用序列实现自增 ID,但其语法与 PostgreSQL 不同。Oracle 在较新版本(12c)中提供了类似 MySQL 的 AUTO_INCREMENT 功能。

序列的使用:


CREATE SEQUENCE my_sequence START WITH 1 INCREMENT BY 1;INSERT INTO my_table (id, name) VALUES (my_sequence.NEXTVAL, 'John');

自动生成主键(12c+):

CREATE TABLE my_table (    id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,    name VARCHAR2(100));


03

自增 ID 的常见问题



3.1 自增 ID 的“空洞”

原因:

  • 事务回滚。

  • 数据被删除。

解决方法:

  • 允许“空洞”,通常不影响业务逻辑。

  • 如果对连续性要求严格,可以使用序列回填,但操作复杂且影响性能。


3.2 自增 ID 的并发问题

在高并发插入场景下,自增值的分配可能导致性能瓶颈或死锁。

解决方法:

  • 分布式自增 ID:

    • 使用全局唯一 ID(如 Twitter 的 Snowflake 算法)。

  • 调整锁机制:

    • InnoDB 使用表级锁或行级锁管理自增值。

    • 通过分库分表降低单点压力。


3.3 自增 ID 不适合分布式系统

  • 在分布式架构中,单节点生成自增 ID可能导致冲突。

解决方法:

  • UUID:

    • 通过算法生成全局唯一标识符。

    • 缺点:长度较长,索引性能较差。

  • 雪花算法(Snowflake):

    • Twitter 提出的分布式 ID 生成算法,基于时间戳、机器编号等生成唯一 ID。

  • 数据库序列(Sequence):

    • 各节点使用独立的序列段(分段分配)。


04

自增 ID 的优势与劣势



4.1 优势

  • 简单易用:无需额外的生成逻辑,数据库自动管理。

  • 高效存储:整型自增 ID 索引性能高。

  • 唯一性保证:通过数据库约束确保唯一性。


4.2 劣势

  • 不能保证连续性:可能出现“空洞”。

  • 分布式场景局限性:单节点生成 ID 不适用于分布式架构。

  • 可预测性风险:自增 ID 可预测,可能存在安全隐患(如通过 URL 推测数据库结构)。



05

直击面试考点



5.1 数据库的自增ID是如何工作的?

问题解析:这个问题考察你对数据库自增ID机制的理解。面试官可能希望你解释数据库如何确保自增ID唯一、递增以及如何管理ID生成的过程。

参考答案:数据库中的自增ID通常由数据库系统自动生成。每次插入一条新记录时,数据库会自动为自增字段分配一个新的ID。常见的实现方式有:

  • MySQL:使用 AUTO_INCREMENT,每插入一条数据,ID都会递增。

  • PostgreSQL:使用 SERIAL 类型,背后会创建一个序列(sequence)来生成ID。

  • SQL Server:使用 IDENTITY 关键字。

自增ID的生成机制保证了每次插入的数据都有一个唯一的ID,而且ID通常是递增的。这样,ID对于大多数业务场景是有序的。


5.2 自增ID会不会有间隙?

问题解析:这个问题考察你对数据库自增ID机制的理解,特别是它在高并发情况下如何处理事务回滚等情况。

参考答案:是的,自增ID会有间隙。这是因为自增ID的生成是基于数据库内部的计数器。当一个事务插入数据并生成ID后,如果事务回滚,那么生成的ID会被丢弃,这样就会产生间隙。

  • 比如,事务1生成ID 1并插入,但事务1回滚;事务2生成ID 2并插入。

  • 结果是ID 1被丢弃,而ID 2仍然存在,从而产生了ID 1的间隙。

这在某些应用场景中可能是一个问题,尤其是在需要ID严格连续的情况下,但在大多数情况下,间隙并不会对系统造成太大影响。


5.3 自增ID的性能瓶颈是什么?

问题解析:这个问题考察你对数据库性能的理解,尤其是自增ID在高并发情况下的性能表现。

参考答案:自增ID的性能瓶颈主要体现在以下几个方面:

  • 锁竞争:在多并发插入的情况下,数据库需要确保自增ID的唯一性。通常情况下,数据库会在生成ID时对自增字段加锁,这会导致并发插入时的性能瓶颈。

  • 单节点瓶颈:如果ID生成完全依赖于单一数据库节点,那么随着并发量的增加,生成ID的速度可能会成为瓶颈。

  • 回滚导致的浪费:当事务回滚时,已经生成的ID将被丢弃,这会导致ID的浪费,尤其是在高频插入的场景中。

为了解决这些问题,可以通过分库分表、使用数据库的主从复制架构、或者采用其他分布式ID生成方式(如Snowflake、UUID等)来提升性能。


5.4 如果一个数据库节点挂掉,生成的自增ID怎么办?

问题解析:这个问题考察你对分布式系统中自增ID生成的理解,特别是在高可用性要求下的处理方式。

参考答案:在分布式环境中,单节点的自增ID生成方式存在单点故障风险。如果一个数据库节点宕机,可能会导致自增ID生成的中断。在这种情况下,通常的解决方案有:

  • 主从复制:通过数据库的主从复制机制,当主节点宕机时,系统可以自动切换到从节点,从而保证ID生成的连续性。

  • 分布式ID生成:为了避免依赖单一数据库节点,可以使用分布式ID生成算法,如Twitter的Snowflake算法。Snowflake算法通过使用时间戳、机器ID和序列号的组合来生成全局唯一的ID,避免了依赖单一数据库的问题。


5.5 自增ID如何在分布式系统中保证全局唯一性?

问题解析:这个问题考察你对分布式系统中全局唯一ID生成的理解,尤其是如何在多台机器中生成不重复的ID。

参考答案:在分布式系统中,生成全局唯一的ID通常面临以下挑战:

  • 避免冲突:不同机器之间生成的ID可能会重复,如何保证每台机器生成的ID唯一?

  • 分布式协调:不同节点需要保证生成的ID没有重叠。

为了解决这些问题,可以使用如下几种方案:

  • 分布式ID生成器:比如Snowflake算法。每个节点都有一个唯一的机器ID,通过将机器ID、时间戳、序列号等结合来生成唯一ID,保证了全局唯一性。

  • 号段模式:通过数据库提前分配号段,每台机器从号段中取ID,保证了全局唯一。


5.6 如何解决自增ID的顺序问题?

问题解析:这个问题考察你如何在分布式系统中解决ID顺序问题,尤其是在高并发和分布式环境下。

参考答案:自增ID通常是递增的,这对于需要按顺序生成ID的场景非常有用,例如订单号、流水号等。但在分布式系统中,使用单一数据库的自增ID可能会造成瓶颈,且无法保证跨机器生成ID的顺序。

为了解决这个问题,可以考虑以下方案:

  • 分布式ID生成算法:如Snowflake算法,生成的ID虽然具有时间序列性,但不同节点生成的ID之间是可以排序的。这样可以保证ID的顺序性。

  • 全局ID服务:可以通过搭建一个全局ID服务来生成ID,该服务负责跨机器分配ID,确保所有节点生成的ID按顺序递增。


5.7 自增ID的步长有什么用?

问题解析:这个问题考察你对自增ID配置项(如步长、初始值等)的理解,尤其是在高并发和分布式系统中的调优。


参考答案:自增ID步长(auto_increment_increment)是指每次生成ID时增加的值,步长影响了ID的分配方式。

  • 默认步长通常是1,表示每次插入的ID值递增1。

  • 步长的作用:

    • 在单机环境下,步长对ID生成的影响不大,但在分布式环境中,步长可以用来减少数据库的锁竞争。

    • 在多个数据库或表之间分配ID时,可以通过调整步长来避免ID冲突。例如,设置步长为10,分别给不同的数据库分配不同的步长(1, 2, 3, …),从而确保每个数据库生成的ID不会冲突。


后续会陆续推出其他id相关的知识


 

互联网行业
   后端学习交流
欢迎关注~~