最近在某微信技术群,有人问到如何生成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. Help him--hdu5059(模拟 大坑)

    http://acm.hdu.edu.cn/showproblem.php?pid=5059 直接说可能出现的情况 #include <iostream> #include <cst ...

  2. spring/spring boot/spring mvc中用到的注解

    在spring Boot中几乎可以完全弃用xml配置文件,本文的主题是分析常用的注解. Spring最开始是为了解决EJB等大型企业框架对应用程序的侵入性,因此大量依靠配置文件来“非侵入式”得给POJ ...

  3. SSH移植

    1. 下载需要的源码,直接google最新的版本吧 mkdir -p ~/arm/fs ;mkdir -p ~/arm/source 下载zlib: zlib-1.2.3.tar.gz 下载ssl : ...

  4. MySQL注释(转)

    MySQL支持3种注释风格: 1.从‘#’字符从行尾. 2.从‘-- ’序列到行尾.请注意‘-- ’(双破折号)注释风格要求第2个破折号后面至少跟一个空格符(例如空格.tab.换行符等等). 3.从/ ...

  5. HDTVI,HDCVI

    HDTVI是海康的同轴高清标准,HDCVI是大华的同轴高清标准,二者互相不兼容.

  6. Angular2.x-显示heroes列表

    在此页面中,您将展开Tour of Heroes应用程序以显示heroes列表,并允许用户选择heroes并显示heroes的详细信息. 6.X 你需要一些heroes来展示. 最终你会从远程数据服务 ...

  7. 非计算机专业的伟伯是怎样拿到阿里Offer的。求职励志!!!

    写在前面: 2015 年 7 月初.參加阿里巴巴校招内推, 8 月 15 日拿到研发project师 JAVA 的 offer .我的专业并不是计算机,也没有在互联网公司实习过,仅仅有一些学习和面试心 ...

  8. Node 即学即用 笔记 思维导图

    Node即学即用   REPL(Read-Evaluate-Print-Loop)     console.log     .clear .help .exit     require('http') ...

  9. 5 微信票据 access_token--开发微信的第二道坎儿

    一 access_token基本概念 定义:access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token.开发者需要进行妥善保存. 时效性:access_ ...

  10. jsp引用JSTL核心标签库

    一.引用JSTL 1.  JSTL的引入可以让JSP代码中<%%>等代码消失掉,再结合EL表达式,会更加方便以及美观. 2.  各套框架(还没有学习,比如struts,SpringMVC等 ...