在软件开发中,生成唯一标识符(ID)是一个常见的需求,尤其是在设计数据库表结构时。以下是几种常用的 ID 生成解决方案,适用于不同的应用场景和技术栈。

1. 自增整数(Auto-Incrementing Integer)

这是最简单且常用的方式,适用于大多数关系型数据库(如 MySQL、PostgreSQL)。TypeORM 支持自增主键。

优点:

  • 简单易用:实现简单,易于理解和维护。
  • 性能高:数据库内部优化良好,插入速度较快。
  • 有序性:生成的 ID 是按顺序递增的,便于排序和分页。

缺点:

  • 单点依赖:依赖于数据库的自增机制,不适合分布式系统。
  • 范围限制:整数类型的范围有限(例如,32 位整数最大值为 21 亿)。

示例代码:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn() // 默认使用自增整数
  id: number;

  @Column({ unique: true })
  username: string;

  @Column()
  password: string;
}

@Entity()
export class Article {
  @PrimaryGeneratedColumn() // 默认使用自增整数
  id: number;

  @Column()
  title: string;

  @Column()
  content: string;

  @Column({ type: 'int' })
  userId: number; // 关联用户ID
}

2. UUID(Universally Unique Identifier)

UUID 是一种 128 位的值,通常表示为由连字符分隔的 32 位 16 进制数字字符串。UUID 可以确保全局唯一性,适用于分布式系统或需要跨多个数据库实例保持唯一性的场景。

优点:

  • 全局唯一:确保在任何系统中都是唯一的。
  • 分布式支持:适用于分布式系统,不需要依赖中心化生成器。
  • 无序性:生成的 ID 无序,不适合按顺序排序。

缺点:

  • 长度较长:UUID 通常为 36 个字符,占用存储空间较大。
  • 可读性差:生成的 ID 不易读,不适合直接显示给用户。

示例代码:

import { v4 as uuidv4 } from 'uuid';
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn('uuid') // 使用 TypeORM 自动生成 UUID
  id: string;

  @Column({ unique: true })
  username: string;

  @Column()
  password: string;
}

@Entity()
export class Article {
  @PrimaryGeneratedColumn('uuid') // 使用 TypeORM 自动生成 UUID
  id: string;

  @Column()
  title: string;

  @Column()
  content: string;

  @Column({ type: 'uuid' })
  userId: string; // 关联用户ID
}

3. NanoID

NanoID 是一个生成短且唯一的字符串的库,可以通过配置生成纯数字的 ID。NanoID 适用于需要短且唯一的标识符的场景。

优点:

  • 短且唯一:生成的 ID 短且唯一,适合存储和传输。
  • 自定义字符集:可以自定义字符集,生成纯数字、字母或混合字符的 ID。
  • 性能好:生成速度快,适合高并发场景。

缺点:

  • 依赖库:需要安装和引入外部库。
  • 可读性:生成的 ID 可能不易读,不适合直接显示给用户。

示例代码:

import { nanoid } from 'nanoid';
import { customAlphabet } from 'nanoid/non-secure';
import { Entity, PrimaryColumn, Column } from 'typeorm';

// 创建一个只包含数字的字符集
const nanoidNumbers = customAlphabet('0123456789', 10); // 生成10位纯数字的ID

@Entity()
export class User {
  @PrimaryColumn({ type: 'varchar', length: 10 })
  id: string = nanoidNumbers(); // 生成10位纯数字的ID

  @Column({ unique: true })
  username: string;

  @Column()
  password: string;
}

@Entity()
export class Article {
  @PrimaryColumn({ type: 'varchar', length: 10 })
  id: string = nanoidNumbers(); // 生成10位纯数字的ID

  @Column()
  title: string;

  @Column()
  content: string;

  @Column({ type: 'varchar', length: 10 })
  userId: string; // 关联用户ID
}

4. 雪花算法(Snowflake Algorithm)

雪花算法是 Twitter 提出的一种分布式 ID 生成算法,生成的 ID 是一个 64 位的整数,具有全局唯一性和有序性。适用于高并发场景下的分布式系统。

优点:

  • 全局唯一:确保在任何系统中都是唯一的。
  • 有序性:生成的 ID 按时间顺序递增,便于排序和分页。
  • 分布式支持:适用于分布式系统,不需要依赖中心化生成器。

缺点:

  • 依赖库:需要安装和引入外部库。
  • 复杂性:实现相对复杂,需要理解算法原理。

示例代码:

可以使用 snowflake-id-generator 等库来实现。

import { Snowflake } from 'snowflake-id-generator';
import { Entity, PrimaryColumn, Column } from 'typeorm';

const snowflake = new Snowflake({ workerId: 1 });

@Entity()
export class User {
  @PrimaryColumn({ type: 'bigint' })
  id: string = snowflake.generate().toString(); // 生成64位整数的ID

  @Column({ unique: true })
  username: string;

  @Column()
  password: string;
}

@Entity()
export class Article {
  @PrimaryColumn({ type: 'bigint' })
  id: string = snowflake.generate().toString(); // 生成64位整数的ID

  @Column()
  title: string;

  @Column()
  content: string;

  @Column({ type: 'bigint' })
  userId: string; // 关联用户ID
}

5. 时间戳(Timestamp)

有时你可能希望使用时间戳作为 ID,特别是当你需要按时间排序时。但需要注意的是,时间戳本身并不是唯一的,因此通常会结合其他信息(如机器 ID)来确保唯一性。

优点:

  • 有序性:生成的 ID 按时间顺序递增,便于排序和分页。
  • 简单:实现简单,易于理解和维护。

缺点:

  • 唯一性问题:时间戳本身不是唯一的,需要结合其他信息确保唯一性。
  • 长度:时间戳较长,占用存储空间较大。

示例代码:

import { Entity, PrimaryColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryColumn({ type: 'varchar', length: 13 })
  id: string = Date.now().toString(); // 使用时间戳生成ID

  @Column({ unique: true })
  username: string;

  @Column()
  password: string;
}

@Entity()
export class Article {
  @PrimaryColumn({ type: 'varchar', length: 13 })
  id: string = Date.now().toString(); // 使用时间戳生成ID

  @Column()
  title: string;

  @Column()
  content: string;

  @Column({ type: 'varchar', length: 13 })
  userId: string; // 关联用户ID
}

6. ULID(Universally Unique Lexicographically Sortable Identifier)

ULID 是一种 128 位的唯一标识符,生成的字符串是按时间顺序排序的,适用于需要按时间排序的场景。

优点:

  • 全局唯一:确保在任何系统中都是唯一的。
  • 有序性:生成的字符串按时间顺序排序,便于排序和分页。
  • 可读性:生成的字符串可读性较好,适合直接显示给用户。

缺点:

  • 长度:ULID 通常为 26 个字符,占用存储空间较大。
  • 依赖库:需要安装和引入外部库。

示例代码:

import { ulid } from 'ulid';
import { Entity, PrimaryColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryColumn({ type: 'varchar', length: 26 })
  id: string = ulid(); // 生成26位ULID

  @Column({ unique: true })
  username: string;

  @Column()
  password: string;
}

@Entity()
export class Article {
  @PrimaryColumn({ type: 'varchar', length: 26 })
  id: string = ulid(); // 生成26位ULID

  @Column()
  title: string;

  @Column()
  content: string;

  @Column({ type: 'varchar', length: 26 })
  userId: string; // 关联用户ID
}

总结

选择哪种 ID 生成方案取决于你的具体需求和技术栈。以下是一些常见的选择:

  • 自增整数:适用于中小型应用,简单易用,性能较好。
  • UUID:适用于分布式系统或需要全局唯一性的场景,推荐用于用户和文章 ID。
  • NanoID:适用于需要短且唯一的标识符的场景,可以通过配置生成纯数字的 ID。
  • 雪花算法:适用于高并发场景下的分布式系统,保证有序性和唯一性。
  • 时间戳:适用于需要按时间排序的场景,但需要注意唯一性问题。
  • ULID:适用于需要全局唯一且按时间排序的标识符的场景。

根据你的具体需求和应用场景,选择最适合的 ID 生成方案。

最后修改:2024 年 12 月 27 日
如果觉得我的文章对你有用,请随意赞赏