短链接生成的核心是将长URL映射为唯一的短标识,结合雪花算法和Base62编码是常用的实现方案,既能保证ID唯一性,又能缩短最终短链接的长度。

一、核心原理说明
1. 雪花算法
雪花算法是Twitter开源的分布式ID生成算法,生成的ID是64位整数,结构分为时间戳、机器ID、序列号三部分,能在分布式场景下生成全局唯一的ID,且趋势递增,适合作为短链接的原始标识。
2. Base62编码
Base62编码使用0-9、a-z、A-Z共62个字符对数据进行编码,相比Base64去掉了容易混淆的+/字符,编码后的字符串长度更短,且没有特殊字符,适合作为短链接的路径部分。
二、C#实现步骤
1. 实现雪花算法生成唯一ID
首先编写雪花算法的核心实现类,负责生成唯一的64位长整型ID:
using System;
public class SnowflakeIdGenerator
{
// 起始时间戳,可自定义为项目上线时间
private static readonly long StartTimestamp = new DateTime(2024, 1, 1).Ticks / 10000;
// 机器ID所占位数
private const int WorkerIdBits = 5;
// 数据中心ID所占位数
private const int DatacenterIdBits = 5;
// 序列号所占位数
private const int SequenceBits = 12;
// 机器ID最大值
private static readonly long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
// 数据中心ID最大值
private static readonly long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
// 机器ID左移位数
private const int WorkerIdShift = SequenceBits;
// 数据中心ID左移位数
private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
// 时间戳左移位数
private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
// 序列号掩码
private static readonly long SequenceMask = -1L ^ (-1L << SequenceBits);
private readonly long _workerId;
private readonly long _datacenterId;
private long _sequence = 0L;
private long _lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId, long datacenterId)
{
if (workerId > MaxWorkerId || workerId < 0)
{
throw new ArgumentException($"workerId不能大于{MaxWorkerId}或小于0");
}
if (datacenterId > MaxDatacenterId || datacenterId < 0)
{
throw new ArgumentException($"datacenterId不能大于{MaxDatacenterId}或小于0");
}
_workerId = workerId;
_datacenterId = datacenterId;
}
public long NextId()
{
lock (this)
{
long timestamp = GetCurrentTimestamp();
if (timestamp < _lastTimestamp)
{
throw new Exception("时钟回拨,无法生成ID");
}
if (timestamp == _lastTimestamp)
{
_sequence = (_sequence + 1) & SequenceMask;
if (_sequence == 0)
{
timestamp = WaitNextMillis(_lastTimestamp);
}
}
else
{
_sequence = 0;
}
_lastTimestamp = timestamp;
// 组合生成最终ID
return ((timestamp - StartTimestamp) << TimestampLeftShift)
| (_datacenterId << DatacenterIdShift)
| (_workerId << WorkerIdShift)
| _sequence;
}
}
private long GetCurrentTimestamp()
{
return DateTime.Now.Ticks / 10000;
}
private long WaitNextMillis(long lastTimestamp)
{
long timestamp = GetCurrentTimestamp();
while (timestamp <= lastTimestamp)
{
timestamp = GetCurrentTimestamp();
}
return timestamp;
}
}
2. 实现Base62编码转换
编写Base62编码工具类,将雪花算法生成的long类型ID转换为短字符串:
public class Base62Encoder
{
// Base62字符集,顺序可自定义,只要保证编解码一致即可
private static readonly string Base62Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static string Encode(long num)
{
if (num == 0)
{
return "0";
}
string result = "";
long temp = num;
while (temp > 0)
{
int remainder = (int)(temp % 62);
result = Base62Chars[remainder] + result;
temp = temp / 62;
}
return result;
}
public static long Decode(string str)
{
long result = 0;
foreach (char c in str)
{
int index = Base62Chars.IndexOf(c);
if (index == -1)
{
throw new ArgumentException("输入字符串包含Base62字符集外的字符");
}
result = result * 62 + index;
}
return result;
}
}
3. 短链接生成完整调用示例
将雪花算法和Base62编码结合,实现短链接生成逻辑:
class Program
{
static void Main(string[] args)
{
// 初始化雪花算法生成器,机器ID为1,数据中心ID为1
SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
// 生成唯一ID
long uniqueId = idGenerator.NextId();
Console.WriteLine($"生成的唯一ID:{uniqueId}");
// 转换为Base62短字符串
string shortCode = Base62Encoder.Encode(uniqueId);
Console.WriteLine($"Base62编码后的短码:{shortCode}");
// 拼接短链接,假设域名是ipipp.com
string shortUrl = $"https://ipipp.com/{shortCode}";
Console.WriteLine($"最终短链接:{shortUrl}");
// 反向解析示例
long decodedId = Base62Encoder.Decode(shortCode);
Console.WriteLine($"短码解析回的原始ID:{decodedId}");
}
}
三、注意事项
- 雪花算法的起始时间戳建议设置为项目上线时间,避免ID过长。
- 分布式部署时需要保证不同实例的机器ID和数据中心ID不重复,避免生成重复ID。
- Base62字符集的顺序编解码必须一致,否则会导致短链接无法正确还原。
- 实际业务中需要将短码和长URL的映射关系存储到数据库或缓存中,解析短链接时根据短码查询对应的长URL进行跳转。