最近在某微信技术群,有人问到如何生成5位唯一数字+字母字符串的算法,要保证生成的字符串唯一,且字符串内部也要唯一。

  怎么样,这个需求是不是很简单,也有点特殊呢?简单是指需求简单,特殊是指,字符串长度要求是5位,而不是更长。根据群里的讨论,有以下解决方案:

  • UUID生成随机字符串,并控制字符串长度。估计这个群友是指UUID.randomUUID().toString.substring(0,5) 吧,不过这样是没法达到唯一的。也就是说每次生成的字符串可能重复。
  • apache的StringUtils。我在org.apache.commons.lang3.StringUtils这个类里面没有找到相关的函数。
  • 雪花算法。毫无疑问,雪花算法是可以的,不过没法保证是5位。或许这位兄台的需求有点奇葩了,还必须要求5位。
  • 内存表或数据库表。其实我是比较赞同这种方案的,一共就5位字符和数字,排列组合一下,不考虑内部唯一,一共就pow(36,5).=60466176。6千万数据保存到内存中去,用的时候取一条,用完为止。如果考虑内部唯一,那就是36*35*34*33*32=45239040,4千万的表。不过从表里面去,会有并发的问题。

  其实群友给的方案都是不错的,除了那个StringUtils,但我思考这个问题就比较简单、粗暴了。一共5位长度,要求字符串+数字,也就4千万的事儿,4千万很大妈?说实话,真不大。那怎么搞的?计数器就可以了!

  啥 ?计数器,你确定没说错?嗯,没说错。我刚提出这个方案的时候,就有脑残、喷子开始无脑的怼我了,不等我说完就开始怼我了。那计数器真的就不行吗?

  其实我个人比较倾向于上面的第五种方案,也就是把所有可用的字符串算出来,然后再取就行了。那么这样做有一个不好的地方就是,比较占用内存,大概215MB,说实话吧,这215MB也不算大,如果是我直接就用内存表了。不过还能不能优化一下内存空间?答案是能,那就是用计数器。

  其实我们在生成这4千万的字符串的时候,就是用计数器算的啊,就是从0到36^5,把数字转换成36进制,然后判断是否有重复字符串,这个过程就用了计数器。那能不能用计数器动态计算下一个符合条件的字符串呢?当然可以了啊。

object LimitStringIdGenerator{
private [idgenerator] val MaxStringLength = 5
private [idgenerator] val MaxNumberOfString = math.pow(36,5).toInt
private [idgenerator] def to36String(num:Int):String = BigInt(num).toString(36)
private [idgenerator] def notHasSameChar(charSequence: String):Boolean = charSequence.toCharArray match {
case Array(a,b,c,d,e) if ! ( a == b || a == c || a == d || a == e || b ==c || b == d || b ==e || c ==d || c ==e || d == e) => true
case Array(a,b,c,d) if ! ( a == b || a == c || a == d || b ==c || b == d || c ==d) => true
case Array(a,b,c) if ! ( a == b || a == c || b == c ) => true
case Array(a,b) if ! ( a == b ) => true
case _ => false
}
}
class LimitStringIdGenerator extends IDGenerator {
import LimitStringIdGenerator._
private var start = 0
override def nextId:Int = {
var nextId = start + 1
while(!notHasSameChar(to36String(nextId)) && nextId < MaxNumberOfString){
nextId = nextId + 1
} nextId
} override def setStart(start: Int): Unit = {
this.start = start
}
}

  上面是可执行的代码,非常简单,就是用一个nextId做计数器,依次递增,找到下一个符合条件的字符串。其实上面的代码还是可以优化一下的,为啥呢?因为有时候计数器的步长不一定是1,此话怎讲呢?

  我们来看看当计数器的值是01000时,步长是1会造成很大浪费,因为下一个可用的字符串是01234;当记数字的值是11000时,下一个可用的字符串值是12345,如果步长为1也会造成很大的浪费。

  那有人会问了,并发时候怎么办呢?如果停机再启动怎么办呢?嗯,这两个问题问的非常好,不过就有点刁难的意味了,为啥呢?因为楼主只是需要一个算法来获取5位唯一字符串而已,而且数据量不大。但我还是很喜欢这两个问题的。

  我们先来看并发的问题。为啥需要并发呢?有时候是效率的考虑,并发计算会稍微快点;还有时候是多线程导致的;当然还有其他原因。

  其实上面的算法效率已经非常高了,为啥?因为就是对变量自增,然后做个判断而已。经测试,把36^5月6千1百万的数据计算一遍,大概28秒,计算下一个可用的字符串,最长耗时大概400毫秒。那多线程的时候怎么办呢?那就用AtomicInteger来替换呗,应该可以保证线程安全。

  那停机再启动怎么办?其实吧,你停机的时候把nextId保存到数据库、缓存或者其他第三方存储,不就好了?啥,你说程序是异常宕机,没有保存?那就根据生成的最后一个字符串反推一下当前计数器的值喽。啥?你生成的字符串没保存?那你用来干啥呢?

  当然还是可以分布式的,就是有多个节点,每个节点都只从某个值开始计算下一个可用字符串,不过也没啥必要,一共4千万字符串,很快就会耗尽了。

  脑残粉和喷子就不要留言了,当然如果你有更好的方案,还是可以发个博客怼一怼的,如果没有那就闭嘴。

  

5位ID生成方案的更多相关文章

  1. 【php】mysql全局ID生成方案

    生产系统随着业务增长总会经历一个业务量由小变大的过程,可扩展性是考量数据库系统高可用性的一个重要指标;在单表/数据库数据量过大,更新量不断飙涨时,MySQL DBA往往会对业务系统提出sharding ...

  2. MySQL分库分表环境下全局ID生成方案 转

    在大型互联网应用中,随着用户数的增加,为了提高应用的性能,我们经常需要对数据库进行分库分表操作.在单表时代,我们可以完全依赖于数据库的自增ID来唯一标识一个用户或数据对象.但是当我们对数据库进行了分库 ...

  3. MySQL分库分表环境下全局ID生成方案

    在大型互联网应用中,随着用户数的增加,为了提高应用的性能,我们经常需要对数据库进行分库分表操作.在单表时代,我们可以完全依赖于数据库的自增ID来唯一标识一个用户或数据对象.但是当我们对数据库进行了分库 ...

  4. 【转】MySQL分库分表环境下全局ID生成方案

    转载一篇博客,里面有很多的知识和思想值得我们去思考. —————————————————————————————————————————————————————————————————————— 在大 ...

  5. 分布式唯一ID生成方案是什么样的?(转)

    一.前言 分布式系统中我们会对一些数据量大的业务进行分拆,如:用户表,订单表.因为数据量巨大一张表无法承接,就会对其进行分库分表. 但一旦涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题, ...

  6. 基于数据库构建分布式的ID生成方案

    在分布式系统中,生成全局唯一ID,有很多种方案,但是在这多种方案中,每种方案都有有缺点,下面我们之针对通过常用数据库来生成分布式ID的方案,其它方法会在其它文中讨论: 1,RDBMS生成ID: 这里我 ...

  7. 搞懂分布式技术12:分布式ID生成方案

    搞懂分布式技术12:分布式ID生成方案 ## 转自: 58沈剑 架构师之路 2017-06-25 一.需求缘起 几乎所有的业务系统,都有生成一个唯一记录标识的需求,例如: 消息标识:message-i ...

  8. 【系统设计】分布式唯一ID生成方案总结

    目录 分布式系统中唯一ID生成方案 1. 唯一ID简介 2. 全局ID常见生成方案 2.1 UUID生成 2.2 数据库生成 2.3 Redis生成 2.4 利用zookeeper生成 2.5 雪花算 ...

  9. 分布式id生成方案总结

    本文已经收录自 JavaGuide (60k+ Star[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.) 本文授权转载自:https://juejin.im/post/ ...

随机推荐

  1. Delphi控件大全

    首先来大体上为控件分一下类,以方便我们后面的讨论.   但因为控件的种类太多,所以就粗略的分为如下几个类别∶   ---界面风格类   ---Shell外观类   ---Editor类   ---Gr ...

  2. 检查nginx配置,重载配置以及重启的方法

    原文  http://blogread.cn/it/article/4549?f=hot1 几个常用的nginx命令 Nginx 安装后只有一个程序文件,本身并不提供各种管理程序,它是使用参数和系统信 ...

  3. 报错: The type ByteInputStream is not accessible due to restriction on required library

    报错: Access restriction:The type JPEGCodec is not accessible due to restriction on required library C ...

  4. OpenWrt 安装python-sqlite3失败

    https://dev.openwrt.org/ticket/12239 #12239 reopened defect Sqlite3 missing in python 汇报人: dgspai@- ...

  5. Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的使用方法总结

    具体解读Jquery各Ajax函数: $.get(),$.post(),$.ajax(),$.getJSON() 一 $.get(url,[data],[callback]) 说明:url为请求地 ...

  6. 将Python打印的内容进行高亮的输出

    将打印的内容进行高亮的显示 内容: 格式: echo "\033[字背景颜色;字体颜色m字符串\033[0m" 例如: "\033[41;36m something he ...

  7. CSS 相对|绝对(relative/absolute)定位系列(一)

    一.有话要说 以前写内容基本上都是:眼睛一亮——哟呵,这个不错,写!然后去古人所说的茅房里蹲会儿,就有写作的思路了.但是,构思相对/绝对(relative/absolute)定位系列却有好些时日,考虑 ...

  8. VS code - code Snippet

    For anyone working on the UI and using VS Code, you can create a user Snippet and keyboard shortcut ...

  9. Yii2 mongodb 扩展的where的条件增加大于 小于号

    1. mongodb的where中有比較丰富的 条件.例如以下: static $builders = [ 'NOT' => 'buildNotCondition', 'AND' => ' ...

  10. udhcp源码详解(四) 之租赁IP的管理

    Server端对于租赁出去的IP的管理是基于结构体dhcpOfferedAddr的,该结构体的定义是在leases.c文件里:(结构体的成员介绍说明见详解之数据结构) 1: struct dhcpOf ...