5位ID生成方案
最近在某微信技术群,有人问到如何生成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生成方案的更多相关文章
- 【php】mysql全局ID生成方案
生产系统随着业务增长总会经历一个业务量由小变大的过程,可扩展性是考量数据库系统高可用性的一个重要指标;在单表/数据库数据量过大,更新量不断飙涨时,MySQL DBA往往会对业务系统提出sharding ...
- MySQL分库分表环境下全局ID生成方案 转
在大型互联网应用中,随着用户数的增加,为了提高应用的性能,我们经常需要对数据库进行分库分表操作.在单表时代,我们可以完全依赖于数据库的自增ID来唯一标识一个用户或数据对象.但是当我们对数据库进行了分库 ...
- MySQL分库分表环境下全局ID生成方案
在大型互联网应用中,随着用户数的增加,为了提高应用的性能,我们经常需要对数据库进行分库分表操作.在单表时代,我们可以完全依赖于数据库的自增ID来唯一标识一个用户或数据对象.但是当我们对数据库进行了分库 ...
- 【转】MySQL分库分表环境下全局ID生成方案
转载一篇博客,里面有很多的知识和思想值得我们去思考. —————————————————————————————————————————————————————————————————————— 在大 ...
- 分布式唯一ID生成方案是什么样的?(转)
一.前言 分布式系统中我们会对一些数据量大的业务进行分拆,如:用户表,订单表.因为数据量巨大一张表无法承接,就会对其进行分库分表. 但一旦涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题, ...
- 基于数据库构建分布式的ID生成方案
在分布式系统中,生成全局唯一ID,有很多种方案,但是在这多种方案中,每种方案都有有缺点,下面我们之针对通过常用数据库来生成分布式ID的方案,其它方法会在其它文中讨论: 1,RDBMS生成ID: 这里我 ...
- 搞懂分布式技术12:分布式ID生成方案
搞懂分布式技术12:分布式ID生成方案 ## 转自: 58沈剑 架构师之路 2017-06-25 一.需求缘起 几乎所有的业务系统,都有生成一个唯一记录标识的需求,例如: 消息标识:message-i ...
- 【系统设计】分布式唯一ID生成方案总结
目录 分布式系统中唯一ID生成方案 1. 唯一ID简介 2. 全局ID常见生成方案 2.1 UUID生成 2.2 数据库生成 2.3 Redis生成 2.4 利用zookeeper生成 2.5 雪花算 ...
- 分布式id生成方案总结
本文已经收录自 JavaGuide (60k+ Star[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.) 本文授权转载自:https://juejin.im/post/ ...
随机推荐
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown error 1054
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown error 1054 这个错误困扰了我一个下午 插入数据总是错误 ...
- 2018.11.3 PION模拟赛
期望:100 实际:100 #include<cstdio> #include<cstring> #include<iostream> #include< ...
- 关于oracle存储过程的若干问题备忘
1.在oracle中,数据表别名不能加as,如: select a.appname from appinfo a;-- 正确select a.appname from appinfo as a;-- ...
- 【Todo】Java线程面试题 Top 50 (转载)
原文链接:http://www.importnew.com/12773.html 这里有一个排版好看一点的:http://www.cnblogs.com/dolphin0520/p/3958019.h ...
- 【转】Wireshark技巧-过滤规则和显示规则
原文: http://www.cnblogs.com/icez/p/3973873.html ----------------------------------------------------- ...
- webpack-Hot Module Replacement(热更新)
模块热替换(Hot Module Replacement) 模块热替换(HMR - Hot Module Replacement)功能会在应用程序运行过程中替换.添加或删除模块,而无需重新加载整个页面 ...
- CSDN-markdown基本的语法说明
文件夹 概述 简介Markdown CSDN Markdown的功能支持 标题 Setext形式 atx形式 区块引用 分隔线 强调 列表 无序列表 有序列表 注意事项 链接 自己主动链接 普通文本链 ...
- Idea 13 新建maven项目
1.此时生成的maven项目没有web文件夹 file→New Project→Maven→Next→GID.AID (NewDemo)→Next→ProjectName(NewDemo)→Finis ...
- Android进阶图片处理之三级缓存方案
图片的三级缓存 一.概述 一開始在学习Android的时候.处理图片的时候,每次获取图片都是直接从网络上面载入图片. 可是在开发项目的过程中,每次点击进入app里面,图片都要慢慢的再一次从网络上面载入 ...
- 《The Swift Programming Language》的笔记-第28页
页 本页里的主要内容是: 1 介绍swift语言里的各类数制,或者说某一整数的不同表示形式. 1)十进制,无前缀 的序列 3)八进制,以0o开头.后边是若干0~7的序列 进制,以0x开头.后是0~9. ...