做过项目的同学都知道,给数据起个唯一的"身份证号"是个常见需求。比如用户注册、订单编号、日志记录等等,都需要保证每条数据都有个独一无二的标识。
以前可能直接用数据库的自增ID就完事了,但现在系统越来越复杂,分布式、微服务满天飞,简单的自增ID就不够用了。今天咱们就来聊聊C#里三种常用的唯一ID生成方案:GUID、UUID和ULID。
别被这些英文缩写吓到,其实都挺简单的。
GUID全称叫"全局唯一标识符",说白了就是一个128位(16字节)的随机数,长得像这样:
f47ac10b-58cc-4372-a567-0e02b2c3d479
看起来挺唬人的,其实就是用连字符分割的一串十六进制数字。
优点:
缺点:
UUID其实就是GUID的"国际标准版",格式完全一样,只是叫法不同。就像可乐和百事可乐,本质上都是碳酸饮料。
UUID有好几个版本:
在.NET里,Guid.NewGuid()
生成的就是UUID v4。
ULID是个相对较新的东西,全称"通用唯一字典序可排序标识符"。听名字就知道,它最大的特点就是可排序。
ULID长这样:
01GZHT44KMWWT5V2Q4RQ6P8VWT
看起来比GUID简洁多了,没有连字符,而且都是大写字母和数字。
ULID很聪明,它把时间戳放在了前面:
这样设计的好处是,按字符串排序就等于按时间排序,非常方便。
优点:
缺点:
这是个重点话题。很多同学可能不知道,用GUID做主键其实挺坑的。
想象一下,你有一本通讯录,按姓名排序。如果每次都往中间随机插入新联系人,你得不停地挪动其他条目,很麻烦对吧?
数据库索引也是这个道理。GUID是随机的,每次插入都可能在索引的中间位置,导致:
ULID因为前面是时间戳,新生成的ID总是比旧的大,所以总是插入在索引末尾,就像在通讯录最后加新人一样简单。
实际测试数据(以10万条插入为例):
指标 | GUID | ULID |
---|---|---|
插入耗时 | 8.5秒 | 3.2秒 |
索引大小 | 245MB | 156MB |
查询速度 | 普通 | 更快 |
差距还是很明显的。
// 最简单的方式
var guid = Guid.NewGuid();
Console.WriteLine($"GUID: {guid}");
// 转换为不同格式
Console.WriteLine($"无连字符: {guid:N}");
Console.WriteLine($"大括号: {guid:B}");
Console.WriteLine($"小括号: {guid:P}");
// 输出示例:
// GUID: f47ac10b-58cc-4372-a567-0e02b2c3d479
// 无连字符: f47ac10b58cc4372a5670e02b2c3d479
// 大括号: {f47ac10b-58cc-4372-a567-0e02b2c3d479}
// 小括号: (f47ac10b-58cc-4372-a567-0e02b2c3d479)
// 在.NET中,UUID就是GUID
var uuid = Guid.NewGuid();
Console.WriteLine($"UUID: {uuid}");
// 如果需要特定版本的UUID,可能需要第三方库
// 比如 UUIDNext 包
首先安装NuGet包:
dotnet add package Ulid
然后使用:
using System;
classProgram
{
static void Main()
{
// 生成ULID
var ulid = Ulid.NewUlid();
Console.WriteLine($"ULID: {ulid}");
// ULID可以转换为GUID
var guid = ulid.ToGuid();
Console.WriteLine($"转换为GUID: {guid}");
// 也可以从时间戳生成ULID
var timestamp = DateTimeOffset.UtcNow;
var timedUlid = Ulid.NewUlid(timestamp);
Console.WriteLine($"指定时间的ULID: {timedUlid}");
}
}
如果你的项目比较传统,单体架构,用户量不是特别大:
多个服务需要生成唯一ID,不能依赖数据库自增:
需要按时间查询,写入频繁:
需要给外部系统提供资源标识:
CREATE TABLE Users (
Id UNIQUEIDENTIFIER DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
Name NVARCHAR(100)
);
public class Order
{
public int SequenceId { get; set; } // 自增,聚集索引
public Guid OrderId { get; set; } // GUID,对外暴露
// 其他属性...
}
三种方案各有千秋:
选择建议:
最重要的是,不要为了用新技术而用新技术。根据实际需求选择最合适的方案,才是明智之举。
记住:没有银弹,只有最适合的解决方案。