有哪些选项可用于生成用户友好的字母数字ID(如业务ID,SKU)

时间:2008-10-20 00:56:33

标签: c# .net algorithm guid

以下是要求:

必须是字母数字,8-10个字符,以便用户友好。这些将作为唯一键存储在数据库中。我使用Guids作为主键,因此使用GUID生成这些唯一ID的选项将更为可取。

我正在思考一个base-n转换器的行,它接受一个Guid并转换为一个8个字符的唯一字符串。

短,轻量级算法是首选,因为它经常被调用。

5 个答案:

答案 0 :(得分:10)

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

* Is a GUID unique 100% of the time? [stackoverflow]

您的GUID问题 - >字符转换;虽然您的GUID在统计上是唯一的,但通过采用任何子集,您可以减少随机性并增加碰撞机会。你当然不想创造非unqiue SKU。


解决方案1:

使用与对象和业务规则相关的数据创建SKU。

即。可能存在一些小的属性组合,使对象成为唯一(a natural key)。组合自然键的元素,对其进行编码和压缩以创建SKU。通常您只需要一个日期时间字段(即CreationDate)和一些其他属性来实现此目的。你可能在sku创建中有很多漏洞,但sku与你的用户更相关。

假设:

Wholesaler, product name, product version, sku
Amazon,     IPod Nano,    2.2,             AMIPDNN22
BestBuy,    Vaio,         3.2,             BEVAIO32

解决方案2:

一种方法,它保留一系列数字,然后继续按顺序释放它们,并且永远不会返回相同的数字两次。您仍然可以在该范围内找到洞。虽然你不需要生成足够的sku来解决问题,但确保你的要求允许这样做。

实现是在具有计数器的数据库中具有key表。计数器在事务中递增。重要的一点是,软件中的方法不是递增1,而是抓取一个块。伪c#-code如下。

-- 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;
        }
    } 

这里的想法是你保留足够的密钥,以便你的代码不经常进入数据库,因为获取密钥范围是一项昂贵的操作。您需要很好地了解需要保留的密钥数量,以便平衡密钥丢失(应用程序重新启动)与过快地耗尽密钥并返回数据库。这个简单的实现无法重用丢失的密钥。

由于此实现依赖于数据库和事务,因此您可以让应用程序同时运行,并且所有应用程序都生成唯一键,而无需经常访问数据库。

请注意,上述内容基于key table,第222页Patterns of Enterprise Application Architecture (Fowler)。该方法通常用于生成主键而无需数据库标识列,但您可以看到它如何适应您的目的。

答案 1 :(得分:8)

你可以考虑base 36.,因为它可以做字母和数字。 考虑从你的集合中删除I(眼睛)和O(哦),这样它们就不会与1(一)和0(零)混淆。有些人可能会抱怨2和Z.

答案 2 :(得分:3)

如果您正在寻找“用户友好”,您可能想尝试使用整个单词,而不是简单地将其缩短/使用alphanumberic,因此,如下所示:

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的shaid或sha1 / md5哈希的前8-10个字符,这可能是你最好的选择。

答案 3 :(得分:3)

最简单的方法是每次需要一个值时递增的计数器。八个(左零填充)数字为您提供了1亿个可能值00000000到99999999(尽管您可能插入空格或连字符以便于人类可读性,如000-000-00)。

如果您需要超过1亿个值,您可以增加长度或使用替代位置的字母。使用A0A0A0A0至Z9Z9Z9Z9可为您提供超过45亿的可能值(4,569,760,000)。取一个长整数并生成这样的编码是一个微不足道的代码(最右边的数字为mod 10,最右边的数字为div,然后为最右边的字母,等等26)等等。如果你有内存要刻录,最快的方式是将计数器转换为mod 260数组,并使用每个mod 260值作为两个字符串数组的索引(“A0”,“A1”,“A2”,依此类推到“A9”,“ B0“,”B1“等通过”Z9“)。

基础36的问题(在另一个回复中提到)是你不仅要担心读者对类似人物的混淆(一对一,零与O,两对Z,五对S),但关于相邻字母的组合,可能被读者认为是拼写错误或淫秽的单词或缩写。

答案 4 :(得分:2)

您可能想尝试CRC32哈希算法。 CRC32生成一个8个字符的字符串。

http://en.wikipedia.org/wiki/Cyclic_redundancy_check

http://textop.us/Hashing/CRC