C# 生成用户友好的字母数字id(如业务id、SKU)的选项有哪些
以下是要求: 必须是字母数字,8-10个字符,以便用户友好。这些密钥将作为唯一密钥存储在数据库中。我使用guid作为主键,因此使用guid生成这些唯一id的选项更可取 我想到的是一个base-n转换器,它接受一个Guid并转换成一个8个字符的唯一字符串C# 生成用户友好的字母数字id(如业务id、SKU)的选项有哪些,c#,.net,algorithm,guid,C#,.net,Algorithm,Guid,以下是要求: 必须是字母数字,8-10个字符,以便用户友好。这些密钥将作为唯一密钥存储在数据库中。我使用guid作为主键,因此使用guid生成这些唯一id的选项更可取 我想到的是一个base-n转换器,它接受一个Guid并转换成一个8个字符的唯一字符串 短而轻的算法是首选,因为它经常被调用。您可能想尝试CRC32哈希算法。CRC32生成一个8个字符的字符串 你可以考虑它可以做字母和数字。 考虑从你的集合中删除I(眼睛)和O(OH),这样它们就不会与1(一)和0(零)混淆。有些人可能也会抱怨2和
短而轻的算法是首选,因为它经常被调用。您可能想尝试CRC32哈希算法。CRC32生成一个8个字符的字符串 你可以考虑它可以做字母和数字。 考虑从你的集合中删除I(眼睛)和O(OH),这样它们就不会与1(一)和0(零)混淆。有些人可能也会抱怨2和Z。如果你想要“用户友好型”,你可能想尝试使用完整的单词,而不是简单地将其简短/字母数字化,因此,类似于:
words = [s.strip().lower() for s in open('/usr/share/dict/canadian-english') if "'" not in s]
mod = len(words)
def main(script, guid):
guid = hash(guid)
print "+".join(words[(guid ** e) % mod] for e in (53, 61, 71))
if __name__ == "__main__":
import sys
main(*sys.argv)
产生如下输出:
oranjestad+compressing+wellspring
padlock+discommoded+blazons
pt+olenek+renews
这很有趣。否则,只需获取guid的前8-10个字符或guid的sha1/md5哈希值可能是最好的选择。最简单的方法是每次需要值时都递增的计数器。八个(左零填充)数字为您提供了1亿个可能的值00000000到9999999(尽管您可能会插入空格或连字符以便于人类阅读,如000-000-00) 如果需要超过1亿个值,可以增加长度或在其他位置使用字母。使用A0A0A0到Z9Z9Z9可以提供超过45亿个可能值(4569760000)。获取一个长整数并生成这样的编码(最右边的数字是mod 10,最右边的字母是div 10,然后是mod 26,等等),这是一个很简单的代码。如果你有内存要消耗,最快的方法是将计数器转换成mod 260数组,并将每个mod 260值作为索引,转换成两个字符串的数组(“A0”,“A1”“A2”等通过“A9”、“B0”、“B1”等通过“Z9”) 基数36(在另一个答复中提到)的问题是,你不仅需要担心读者对相似字符的混淆(一对I,零对O,两对Z,五对S),还需要担心相邻字母的组合,这些字母可能会被读者视为拼写令人厌恶或淫秽的单词或缩写
8 characters - perfectly random - 36^8 = 2,821,109,907,456 combinations
10 characters - perfectly random - 36^10 = 3,656,158,440,062,976 combinations
GUID's - statistically unique* - 2^128 = 340,000,000,000,000,000,000,000,000,000,000,000,000 combinations
*
GUID->字符转换的问题;虽然GUID在统计上是唯一的,但通过使用任何子集,可以减少随机性并增加冲突的机会。您当然不想创建非unqiue SKU
解决方案1: 使用与对象和业务规则相关的数据创建SKU i、 e.可能存在使对象唯一的一小部分属性组合。组合自然密钥的元素,编码并压缩它们以创建SKU。通常,您只需要一个日期时间字段(即CreationDate)和一些其他属性就可以实现这一点。在创建sku的过程中可能会有很多漏洞,但sku与您的用户更相关 假设:
Wholesaler, product name, product version, sku
Amazon, IPod Nano, 2.2, AMIPDNN22
BestBuy, Vaio, 3.2, BEVAIO32
解决方案2: 一种方法,它保留一系列数字,然后按顺序释放它们,并且从不两次返回相同的数字。你仍然可以在范围内留下洞。虽然您可能不需要生成足够的sku,但请确保您的需求考虑到这一点 实现方法是在具有计数器的数据库中有一个
键
表。计数器在事务中递增。重要的一点是,软件中的方法不是增加1,而是获取一个块。伪c#-代码如下
-- what the key table may look like
CREATE TABLE Keys(Name VARCHAR(10) primary key, NextID INT)
INSERT INTO Keys Values('sku',1)
// some elements of the class
public static SkuKeyGenerator
{
private static syncObject = new object();
private static int nextID = 0;
private static int maxID = 0;
private const int amountToReserve = 100;
public static int NextKey()
{
lock( syncObject )
{
if( nextID == maxID )
{
ReserveIds();
}
return nextID++;
}
}
private static void ReserveIds()
{
// pseudocode - in reality I'd do this with a stored procedure inside a transaction,
// We reserve some predefined number of keys from Keys where Name = 'sku'
// need to run the select and update in the same transaction because this isn't the only
// method that can use this table.
using( Transaction trans = new Transaction() ) // pseudocode.
{
int currentTableValue = db.Execute(trans, "SELECT NextID FROM Keys WHERE Name = 'sku'");
int newMaxID = currentTableValue + amountToReserve;
db.Execute(trans, "UPDATE Keys SET NextID = @1 WHERE Name = 'sku'", newMaxID);
trans.Commit();
nextID = currentTableValue;
maxID = newMaxID;
}
}
这里的想法是保留足够的密钥,这样代码就不会经常访问数据库,因为获取密钥范围是一项昂贵的操作。您需要知道需要保留的密钥数量,以平衡密钥丢失(应用程序重新启动)与过快耗尽密钥并返回数据库之间的关系。这个简单的实现无法重用丢失的密钥
由于此实现依赖于数据库和事务,因此您可以让应用程序并发运行,并生成唯一的密钥,而无需经常访问数据库
注:以上内容大致基于第222页的键表。该方法通常用于生成主键,而不需要数据库标识列,但您可以看到如何根据自己的目的对其进行调整。这可能是最好的选择,但不幸的是,128位GUID在基数36中仍然超过20个字符。也许GUID不是最好的起点。GUID可以从我的模型对象中随时获得,所以我相信这将非常方便。好的,你可以选择A-Z和A-Z,0-9,这是62的基数。也许这样会更好。但是,人们没有意识到不同大小写的项目是不同的,所以长时间使用Base62会遇到问题,但我最终使用base34,删除了一些难以辨认的字符。不,但前10个字符中的前10个字符给了你2**40个可能性(大约1万亿)的空间因此,根据要查找的标识符的数量,冲突的数量应该非常低。添加唯一性约束、回退和日志冲突。我喜欢你的建议。我唯一关心的是必须在应用程序中管理全局计数器。这就是我希望使用Guid作为序列的原因之一。