在现代软件开发中,唯一标识符(Unique Identifier,简称 UUID)扮演着非常重要的角色,用于保证数据的唯一性和标识对象。UUID 是一串由 16 字节组成的字符序列,通常使用标准的 36 个字符表示法(例如:550e8400-e29b-41d4-a716-446655440000)。然而,传统的 UUID 存在一些问题,比如无法按时间排序、长度较长等。
为了解决这些问题,ULID(Universally Unique Lexicographically Sortable Identifier)应运而生。ULID 是由 Alizain Feerasta 在 2016 年提出的一种可排序的随机标识生成方式。与传统的 UUID 相比,ULID 具有以下优点:
1. ULID 与 UUID 的对比1.1 可排序性
ULID 能够根据生成时的时间戳进行排序,使得生成的标识能够按照时间顺序排列,并且支持范围查询和快速索引。
1.2 长度更短
ULID 使用 26 个字符的 Base32 编码表示,相比标准的 UUID 使用的 36 个字符长度更短,减少网络传输和存储的开销。
1.3 高性能
由于 ULID 可以根据时间戳排序,它非常适合在分布式系统中使用,尤其是在高并发环境下,可以有效减少冲突和竞争。
尽管 ULID 的冲突概率很低,但并不能保证完全唯一。因此,在高度依赖唯一性的场景中,仍建议使用更长的 UUID。
2. ULID 的结构ULID 的结构由两部分组成:时间部分和随机数部分。如果在短时间内生成多个 ULID,那么结果可能是这样:
01H69Y96VND6PYACNN2P0RX2RV01H69Y96VN8QBBYS7QNFK3BC6F01H69Y96VNM8CMCY0Y2PMPYJY701H69Y96VNDD5D8PNMR736TB8401H69Y96VN9KY23BVBW1Y8NQJV01H69Y96VNQVGT2V9TCWV0RNYM01H69Y96VNFYXZT4HCB4Q51H9801H69Y96VNSFZ8C18AXHB9JHVE01H69Y96VNWN1FV7QYWN8806WV01H69Y96VNHPW743GANKP6F3BY
具体而言,一个 ULID 标识符包含了以下信息:
2.1 时间戳
ULID 使用 48 位存储时间戳,精确到毫秒级别。时间戳表示生成 ULID 的时间,且是自增的,因此可以用来对 ULID 进行排序。
2.2 随机数
ULID 使用 80 位的随机数部分来提高唯一性。这部分随机数通过加密强度较高的随机数生成器产生,确保了生成的 ULID 具有高度的唯一性。
为了更好地可视化 ULID,通常将其表示为一个由 26 个字符组成的字符串。其中,前面部分包含了时间戳信息,后面部分是随机数,均使用 Base32 编码表示。
3. 在 .NET 中使用 ULID在 .NET 中,我们可以使用 Ulid NuGet 包来轻松地生成和使用 ULID 标识。首先,在项目中引入 Ulid NuGet 包:
https://www.nuget.org/packages/Ulid
然后就可以通过以下代码示例生成 ULID:
// 生成一个新的 ULIDUlid ulid = Ulid.NewUlid;// 将 ULID 转换为字符串形式string ulidString = ulid.ToString;
除了生成 ULID,我们还可以在 .NET 中对 ULID 进行解析和比较:
// 解析 ULID 字符串string ulidString = "01ARYZ6S41TSV4RRFFQ69G5FAV";Ulid ulid = Ulid.Parse(ulidString);// 比较 ULIDUlid ulid1 = Ulid.NewUlid;Ulid ulid2 = Ulid.NewUlid;int comparisonResult = ulid1.CompareTo(ulid2);bool isEqual = ulid1.Equals(ulid2);Console.WriteLine($"ULID1: {ulid1}");Console.WriteLine($"ULID2: {ulid2}");Console.WriteLine($"Comparison result: {comparisonResult}");Console.WriteLine($"Equal?: {isEqual}");
一个简单的方法列表如下(方法含义不言自明,且和 Guid 相似):
Ulid.NewUlidUlid.ParseUlid.TryParsenew Ulid//以下为实例方法.ToString.ToByteArray.TryWritebytes.TryWriteStringify.ToBase64.Time.Random4. Ulid 与其他组件集成
4.1 System.Text.JSON
System.Text.Json 是 .NET 中的 JSON 序列化库,我们可以使用自定义的转换器将 ULID 对象序列化和反序列化为字符串。
NuGet: Ulid.SystemTextJson
您可以使用自定义 Ulid 转换器 – Cysharp.Serialization.Json.UlidJsonConverter。
var options = new JsonSerializerOptions{ Converters = {new Cysharp.Serialization.Json.UlidJsonConverter }};JsonSerializer.Serialize(Ulid.NewUlid, options);
如果应用程序的目标框架是 netcoreapp3.0,转换器就是内置的,不需要添加 Ulid.SystemTextJson 包,也不需要使用自定义选项。
4.2 Dapper
Dapper 是一个简单、快速的 ORM(对象关系映射)工具,我们可以使用 ULID 作为实体类的标识,并在数据库操作中无缝使用 ULID。
对于 Dapper 或其他 ADO.NET 数据库映射器,可注册 Ulid 到二进制或 Ulid 到字符串的自定义转换器。
public class BinaryUlidHandler : TypeHandler<Ulid>{public override Ulid Parse(object value) {return new Ulid((byte[])value); }public override void SetValue(IDbDataParameter parameter, Ulid value) { parameter.DbType = DbType.Binary; parameter.Size = 16; parameter.Value = value.ToByteArray; }}public class StringUlidHandler : TypeHandler<Ulid>{public override Ulid Parse(object value) {return Ulid.Parse((string)value); }public override void SetValue(IDbDataParameter parameter, Ulid value) { parameter.DbType = DbType.StringFixedLength; parameter.Size = 26; parameter.Value = value.ToString; }}// setup handlerDapper.SqlMapper.AddTypeHandler(new BinaryUlidHandler);
4.3 Entity Framework Core
Entity Framework Core 是 .NET 的对象关系映射框架,它提供了对多种数据库的支持。
在 EF 中使用时,需要创建 ValueConverter 并将其绑定。
public class UlidToBytesConverter : ValueConverter<Ulid, byte>{private static readonly ConverterMappingHints defaultHints = new ConverterMappingHints(size: 16);public UlidToBytesConverter(ConverterMappingHints mappingHints = ) : base( convertToProviderExpression: x => x.ToByteArray, convertFromProviderExpression: x => new Ulid(x), mappingHints: defaultHints.With(mappingHints)) { }}public class UlidToStringConverter : ValueConverter<Ulid, string>{private static readonly ConverterMappingHints defaultHints = new ConverterMappingHints(size: 26);public UlidToStringConverter(ConverterMappingHints mappingHints = ) : base( convertToProviderExpression: x => x.ToString, convertFromProviderExpression: x => Ulid.Parse(x), mappingHints: defaultHints.With(mappingHints)) { }}总结
总结而言,ULID 是一种可排序的随机标识生成方式,相比传统的 UUID 具有更短的长度、可排序性和高性能等优点。在 .NET 中,我们可以使用开源库轻松地生成、解析和比较 ULID。这使得在分布式系统中处理唯一标识符变得更加方便和高效。同时,借助强大的开源社区,我们也可以很容易的将 ULID 与其他第三方组件集成。因此,对于需要在 .NET 中生成唯一标识符的开发者来说,ULID 是一个值得考虑的选择。
,

















