在软件开发中,生成唯一标识符(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 生成方案。