精华内容
下载资源
问答
  • 唯一序号
    千次阅读
    2021-01-27 22:08:30

    在数据库分表或者程序自己需要唯一id的情况下,我们需要一个生成唯一id的方案。

    可以编写一个综合时间和某些特征生成唯一id的程序,也可以考虑使用数据库里自增id的特性来实现这个需求,下面举个mysql的例子。

    首先创建一个专门生成id的表,其中id字段是主键,replace_key字段为唯一键。CREATE TABLE `ticket` ( `id` bigint(20) unsigned NOT NULL auto_increment, `replace_key` char(1) NOT NULL default '', PRIMARY KEY (`id`), UNIQUE KEY `replace_key` (`replace_key`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=10001;

    每次需要生成id时,利用replace into语句生成新的记录将旧的记录替换掉,然后返回此id即可。REPLACE INTO `ticket` (`replace_key`) VALUES ('a');SELECT LAST_INSERT_ID();

    推荐mysql视频教程,地址:https://www.php.cn/course/list/51.html

    更多相关内容
  • 全局唯一序列号设计方案 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结。生成ID的方法有很多,适应不同的场景、需求以及性能要求。所以有些比较复杂的系统会有多个ID生成的策略。 ...

    全局唯一序列号设计方案

    系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结。生成ID的方法有很多,适应不同的场景、需求以及性能要求。所以有些比较复杂的系统会有多个ID生成的策略。

    一、全局唯一ID具备下面几个特性

    1、全局唯一性:不能出现重复的ID
    2、趋势递增:按照一定规则有序递增
    3、单调递增:保证下一个ID一定大于上一个ID
    4、信息安全:特定场景下连续递增ID的安全性

    单调递增与信息安全两个特性是互斥的,无法同时满足
    分布式系统架构中,除了需要满足ID生成自身的需求外,还需要高可用性,高性能。

    二、方案一:数据库自增长序列或字段

    比如oracle的sequence,mysql的auto_increment

    【1】、优点

    实现简单
    能够保证唯一性
    能够保证递增性

    【2】、缺点

    扩展性差,性能有上限
    可用性难以保证,有单点故障的风险
    数据合并,数据迁移比较麻烦

    三、方案二:UUID

    常见的方式。可以利用数据库也可以利用程序生成,一般来说全球唯一。

    【1】、优点

    实现简单
    性能高,本地生成,不会有网络开销
    数据合并,数据迁移比较简单

    【2】、缺点

    没有排序,无法保证趋势递增
    可读性差,不直观
    UUID过长,往往用32或48位字符串表示,作为主键建立索引查询效率低

    四、方案三:Redis生成ID

    当使用数据库来生成ID性能达不到要求的时候,可以采用Redis来生成ID。利用Redis的原子操作 INCR和INCRBY来实现。使用Redis集群可以获取更高的吞吐量,还可以防止单点故障问题。假如一个集群中有3台Redis。可以初始化每台Redis的值分别是1,2,3,然后步长都是3(主Redis机器数)。每个Redis生成的ID为:
    A:1,4,7,10,13
    B:2,5,8,11,14
    C:3,6,9,12,15

    【1】、优点

    不依赖于数据库,性能优于数据库
    数字ID排序,对分页或排序很有帮助

    【2】、缺点

    1、如果系统中没有Redis,还需要引入新的组件,增加系统复杂度
    2、需要编码和配置的工作量比较大

    五、方案四:数据库segment

    通过一个序列表记录当前序列号,机器每次从序列表中获取一定步长的序列数然后缓存再本地,等用完后再重新从步长表获取,本地可以用的序号数可以再做放大,如放大1000倍 X 1000;那么实际上本地可用的序列个数=步长大小 X 放大倍数。读写数据库的频率从1减小到了1/(step * 放大倍数)

    在这里插入图片描述

    【1】、优点:

    大大降低了数据库的访问频率

    【2】、缺点:

    性能较差,例如几百个应用同时来获取这条序列记录的话行锁等待时间长

    TP999数据波动大,当号段使用完之后还是会hang在更新数据库的I/O上,tg999数据会出现偶尔的尖刺。

    DB宕机会造成整个系统不可用。

    【3】、优化方案:双buffer

    ​ 取号段的时机是在号段消耗完的时候进行的,也就意味着号段临界点的ID下发时间取决于下一次从DB取回号段的时间,并且在这期间进来的请求也会因为DB号段没有取回来,导致线程阻塞。如果请求DB的网络和DB的性能稳定,这种情况对系统的影响是不大的,但是假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢。

    ​ 为此,我们希望DB取号段的过程能够做到无阻塞,不需要在DB取号段的时候阻塞请求线程,即当号段消费到某个点时就异步的把下一个号段加载到内存中。而不需要等到号段用尽的时候才去更新号段。这样做就可以很大程度上的降低系统的TP999指标。

    六、方案五:数据库自增序列改进方案

    ​ 跟通过数据库步长的方式类似,生成一张序列表,表结构如下所示,当应用需要获取序号的时候向该表中插入一条数据,记录访问应用的ip信息,然后得到一个唯一的自增的id,这个【(id-1)X 应用设置的步长】 就是应用起始的序列号,【id X 应用设置的步长】就是应用最大的序列号;例如每台机器可得步长为1000,那么插入完记录后该应用得到的序列范围就是0-1000;当这1000个序号用完后就在访问步长表再插入一条记录,得到新的id重新计算新的序列号,同时删除该机器ip id比当前id小的记录。

    CREATE TABLE `coupon_no_sequence` (
      `ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
      `HOST_NAME` varchar(64) NOT NULL COMMENT 'host name',
      `PORT` varchar(64) NOT NULL COMMENT 'port',
      `TYPE` int(2) NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',
      PRIMARY KEY (`ID`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1659 DEFAULT CHARSET=utf8 COMMENT='券号序列表';
    

    实例:券号生成策略

    实现代码

    【1】、spring配置

    <bean id="couponService" class="com.suning.ebuy.cous.components.CouponSequenceService" lazy-init="false">
    		<!-- ID号分段的最大值,如果不配置,代码中默认是999999999999-->
    		<property name="segmentMaxIdValue" value="999999999999"/>
    		<!-- 每个ID号分段的ID数(必须是10的整数次幂,如果不配置,代码中默认是1000) -->
    		<property name="perSegmentQty" value="1000"/>
    		<!--号段表名-->
    		<property name="sequenceTableName" value="coupon_no_sequence"/>
    		<property name="idSegmentDao" ref="IdSegmentDao"/>
    	</bean>
    

    【2】、获取券号的生成方法

    券号获取服务,前面补0补齐13位并在前面拼接上券类型的标记

    public class CouponSequenceService {
    
        /**
         * 当前序号
         */
        private long currentId = 0L;
    
        /**
         * 当前分段最大的序号
         */
        private long currentMaxId = 0L;
    
        /**
         * 最大段号
         */
        private long segmentMaxIdValue = 999999999999L;
    
        /**
         * 每段段长
         */
        private int perSegmentQty = 1000;
    
        /**
         * 序列表表名
         */
        private String sequenceTableName = "id_segement";
        
        private IdSegmentDao idSegmentDao;
    
        /**
         *
         * 功能描述: 获取券号
         *
         * @param 
         * @return 
         * @see [相关类/方法](可选)
         * @since [产品/模块版本](可选)
         */
        public synchronized String getNextLongSequence() {
            // 如果本地没有序号剩余,获取新的分段好,并重置起始序号
            if (this.currentId == this.currentMaxId) {
                this.getNextSegmentAndReset();
            }
    
            // 本地序号有剩余,直接+1
            ++this.currentId;
            return Long.toString(this.currentId);
        }
    
        /**
         *
         * 功能描述: 获取新的分段号
         *
         * @param
         * @return
         * @see [相关类/方法](可选)
         * @since [产品/模块版本](可选)
         */
        private void getNextSegmentAndReset() {
            try {
                long segment = assignIdSegment(sequenceTableName);
                if (segment > this.segmentMaxIdValue) {
                    throw new UidGenerateException("ID超过了最大范围!");
                }
    
                // mysql id 是从0开始,因此当前起始序号 = (id -1) * 段长
                this.currentId = (segment - 1L) * (long) this.perSegmentQty;
    
                // 最大的序列号 = id * 端长
                this.currentMaxId = segment * (long) this.perSegmentQty;
            } catch (Exception e) {
                throw new UidGenerateException("获取ID号分段异常", e);
            }
        }
    
        /**
         *
         * 功能描述: 从数据库获取段号,段号即id
         *
         * @param tableName
         * @return
         * @see [相关类/方法](可选)
         * @since [产品/模块版本](可选)
         */
        @Transactional
        public long assignIdSegment(String tableName) {
            IdSegementEntity idSegementEntity = new IdSegementEntity();
            idSegementEntity.setHostName(NetUtils.getLocalAddress());
            idSegementEntity.setTableName(tableName);
    
            // 插入一条记录
            idSegmentDao.addIdSegment(idSegementEntity);
    
            // 拿到id = 段号
            Long idSegment = this.idSegmentDao.queryIdSegmentByIP(idSegementEntity);
    
            idSegementEntity.setId(idSegment);
    
            // 删除该ip id < idSegment 的记录
            idSegmentDao.deleteIdSegment(idSegementEntity);
    
            return idSegment;
        }
    
        public long getSegmentMaxIdValue() {
            return segmentMaxIdValue;
        }
    
        public void setSegmentMaxIdValue(long segmentMaxIdValue) {
            this.segmentMaxIdValue = segmentMaxIdValue;
        }
    
        public int getPerSegmentQty() {
            return perSegmentQty;
        }
    
        public void setPerSegmentQty(int perSegmentQty) {
            this.perSegmentQty = perSegmentQty;
        }
    
        public String getSequenceTableName() {
            return sequenceTableName;
        }
    
        public void setSequenceTableName(String sequenceTableName) {
            this.sequenceTableName = sequenceTableName;
        }
    
        public IdSegmentDao getIdSegmentDao() {
            return idSegmentDao;
        }
    
        public void setIdSegmentDao(IdSegmentDao idSegmentDao) {
            this.idSegmentDao = idSegmentDao;
        }
    }
    

    【3】、SQL

    <!--删除过期的ID号段-->
    	<sql id="delete_id_segment"  jdbcTimout="3">
    		<![CDATA[
                DELETE FROM ${TABLENAME} WHERE HOST_NAME = :HOSTNAME AND ID < :ID
            ]]>
    	</sql>
    	
    <!--查询正在使用的ID号段-->
    	<sql id="query_id_segment_by_ip"  jdbcTimout="3">
    		<![CDATA[
                SELECT t.ID FROM ${TABLENAME} t WHERE t.HOST_NAME = :HOSTNAME ORDER BY t.ID DESC LIMIT 1
            ]]>
    	</sql>
        
    <!--新增ID号段-->
    	<sql id="insert_id_segment"  jdbcTimout="3">
    		<![CDATA[
                INSERT INTO ${TABLENAME} (HOST_NAME,PORT,TYPE) VALUES (:HOSTNAME,:PORT,:TYPE)
            ]]>
    	</sql>    
    

    七、方案六:雪花算法

    在这里插入图片描述

    【1】雪花算法原理

    1. 1bit,不用,因为二进制中最高位是符号位,1表示负数,0表示正数。生成的id一般都是用整数,所以最高位固定为0。
    2. 41bit-时间戳,用来记录时间戳,毫秒级。
      41位可以表示2199023255552个数字,
      如果只用来表示正整数(计算机中正数包含0),可以表示的数值范围是:0 至 2199023255552,减1是因为可表示的数值范围是从0开始算的,而不是1。
      也就是说41位可以表示2199023255552个毫秒的值,转化成单位年则是69年
    3. 10bit-工作机器id,用来记录工作机器id。
      可以部署在1024个节点,包括5位datacenterId和5位workerId
      5位(bit)可以表示的最大正整数是31,即可以用0、1、2、3、…31这32个数字,来表示不同的datecenterId或workerId
    4. 12bit-序列号,序列号,用来记录同毫秒内产生的不同id。
      12位(bit)可以表示的最大正整数是4095,即可以用0、1、2、3、…4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号。

    由于在Java中64bit的整数是long类型,所以在Java中SnowFlake算法生成的id就是long来存储的。

    【2】Snowflake 存在的问题

    ​ snowflake 不依赖数据库,也不依赖内存存储,随时可生成 ID,这也是它如此受欢迎的原因。但因为它在设计时通过时间戳来避免对内存和数据库的依赖,所以它依赖于服务器的时间。上面我们提到了 Snowflake 的 4 段结构,实际上影响 ID 大小的是较高位的值,由于最高位固定为 0,遂影响 ID 大小的是中位的值,也就是时间戳。

    ​ 试想,服务器的时间发生了错乱或者回拨,这就直接影响到生成的 ID,有很大概率生成重复的 ID且一定会打破递增属性。这是一个致命缺点,你想想,支付订单和购买订单的编号重复,这是多么严重的问题!

    ​ 另外,由于它的中下位和末位 bit 数限制,它每毫秒生成 ID 的上限严重受到限制。由于中位是 41 bit 的毫秒级时间戳,所以从当前起始到 41 bit 耗尽,也只能坚持 70 年。

    ​ 再有,程序获取操作系统时间会耗费较多时间,相比于随机数和常数来说,性能相差太远,这是制约它生成性能的最大因素。

    展开全文
  • 随着互联网飞速发展,尤其像微博,移动支付行业,用户数据成几何倍数增长,传统... 在享受分库分表带来查询速度提升便利的同时,这种切分方案也带来一些问题,例如数据路由,唯一序号等,今天主要说一下唯一序号解决方

            随着互联网飞速发展,尤其像微博,移动支付行业,用户数据成几何倍数增长,传统的单库已无法满足业务的增长速度,于是就有了分库分表,对业务表进行垂直切分和水平切分,将数据存放在多个数据库中,这每个数据库中存放的业务数据就会相应减少,利于应用访问数据库速度。

           在享受分库分表带来查询速度提升便利的同时,这种切分方案也带来一些问题,例如数据路由,唯一序号等,今天主要说一下唯一序号解决方案

           第一种解决方案:使用UUID随机数,这种方案优点是使用简单,缺点是产生的随机数是128位,并且没有业务规则,不连续,占用数据库空间多,在业务数据量大时,会有性能下降

           第二种解决方案:使用Twitter的雪花算法(snowflake),此算法产生序号效率极高,每秒能产生26W个ID,但是也存在缺点,非常依赖NTP服务,如果NTP失效,服务器的时间滞后,则此雪花算法就会等待服务器时间追上,才会生成新的ID

           今天就说一下新的解决方案,其架构图如下所示

                

            此方案的好处是,在同城双机房的情况下,可以部署2台mysql数据库,做双主半同步,这样可以保证数据的一致性,如果2台mysql服务器都运行正常,则同机房的应用则访问同机房的mysql数据库,可以事先定制好规则,机房A的应用访问机房A的mysql数据库表seq_a,并只获取奇数的序号,机房B的应用访问机房B的mysql数据库表seq_b,并只获取偶数的序号,2个机房业务均衡,基本无序号空洞产生。

            如果机房A的mysql数据库宕机,则分布式ID生成应用会自动切换数据源,去访问机房B的mysql数据库,并正常读写seq_a表,并只获取奇数的序号,以此来实现架构的高可用。

                                            下面是我的公众号二维码,欢迎添加

                                    


    展开全文
  • 自增序号唯一ID、唯一编码生成器 1、序号发生器生成唯一编码 import com.github.pagehelper.util.StringUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data....

    自增序号、唯一ID、唯一编码生成器

    1、序号发生器生成唯一编码

    import com.github.pagehelper.util.StringUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Component;
    import java.util.List;
    
    @Component
    public class SeqGeneratorUtil {
        @Autowired
        private StringRedisTemplate redisTemplate;
        /**
         * 自增序列号
         * prefix 前缀
         * numLength 要生成多少位的数字
         */
        public String SeqGenerator(String prefix, int numLength) {
            String upperCode = "";
            Long size = redisTemplate.opsForList().size(prefix);//查找以prefix作为key值的数据长度
            if (size > 0) {//有数据
                List leve = redisTemplate.opsForList().range(prefix, 0, -1);//获取该key下面的所有值 (-1所有值;1下一个值)
                 upperCode = Collections.max(leve).toString();//返回最大值
            }
            String returnCode = "";
            int Suffix;//后缀数字
            if (!StringUtil.isEmpty(upperCode)) {//有数据
                String sequence = upperCode.substring(prefix.length());//截取前缀开始的后面数字
                Suffix = Integer.parseInt(sequence);
                Suffix++;//最后的序号加一
            } else {//没有数据
                Suffix = 1;
            }
            returnCode = prefix + String.format("%0" + numLength + "d", Suffix);//后缀不够numLength长,前面补充0
            redisTemplate.opsForList().rightPush(prefix, returnCode);//存入Redis
            return returnCode;
        }
    }
    
    

    2、利用redis的incr生成唯一编码

    展开全文
  • 由以下几部分的组合:当前日期和时间(UUID的第一个部分与时间有关,如果你在 生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同),时钟序列,全局唯一的IEEE机器识别号(如果有网卡,从网卡获得...
  • 保留当前序列-可以使用以下命令将值重置为当前存储在表中的最大值:-- ---------------------------------- Purpose..: Resets the sequences-- --------------------------------DECLARE-- record of temp data ...
  • 首先需要生成一个序号用来确定表单验证的目标row,通过广度优先遍历,以1,1.1,1.1.1的规则对树形列表生成确定唯一值的索引,因为列表自身可以做CURD,因此需要每次列表的item增加或减少时重新调用生成索引的方法。...
  • U盘物理编号查看器 查看物理编号 u盘的身份
  • 用sql生成唯一的特殊序列号

    千次阅读 2019-06-17 08:51:38
  • SqlServer数据库之给表添加序号

    千次阅读 2019-09-24 23:18:16
    简单给查询表添加序号 SELECT row_number() over(order by inserttime asc) as id,* FROM [RecordDrawScore] 转载于:https://www.cnblogs.com/nnnnnn/p/10948496.html
  • PHP获取唯一标识UUID

    千次阅读 2020-08-17 14:26:51
    封装函数如下: function uuid($prefix = '') { $chars = md5(uniqid(mt_rand(), true)); $uuid = substr($chars, 0, 8) .... $uuid .= substr($chars, 8, 4) .... $uuid .= substr($chars, 12, 4) ....
  • c# 自动序号生成

    2018-11-09 11:14:17
    针对硬件绑定 软件序列号生成 , 序列号检测, 相当的好用,都加了注释
  • 全局唯一序列号的生成

    万次阅读 2020-03-21 18:28:22
    概要:借助数据库自增主键实现全局唯一序列号的生成;将自增主键放大后,形成区间号段,在内存中分配,从而避免频繁的IO,当达到号段最大值时,重新从数据库获取号段。 一、搭建测试 application.yml配置 server: ...
  • 1、SQL server中创建表,带有自增列。 create table Test_Table(Id int Identity(1,1), Name varchar(20)); 这里用到了IDENTITY 关键字。IDENTITY(a,b),a b均为正整数,a表示开始数,b表示步长。...
  • Java按规则生成唯一编号

    千次阅读 2021-01-23 11:08:14
    生成规则:两位年+两位月+两位日+8位随机数 public static void main(String[] args) { String s = new SimpleDateFormat("yyMMdd").format(new Date()); System.out.println(s); StringBuilder str=new ...
  • Java并发编程-生成唯一序列号

    千次阅读 2021-02-12 14:37:11
    所用到的并发编程库import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.locks.ReentrantReadWriteLock;.../**** ID生成器接口, 用于生成全局唯一的ID流水号** @author Ivan.Ma*/public...
  • 怎么才能获取唯一的CPU序列号,看到网上有说 Intel Pentium III 以后CPU唯一的序列号都获取不了? 经过验证,用 wmic CPU get ProcessorID 命令得到的CPU序列号不是唯一的,想问问有什么方法可以得到CPU唯一的...
  • 如何根据当前时间生成唯一编号

    千次阅读 2021-02-26 11:34:03
    是当前时间的唯一的编号……System.currentTimeMillis() 只是获取当前的时间戳,单位是毫秒,但是这并不是唯一的.如果你在1毫秒中进行了两次操作,那么这两个ID就是相等的.问题的解决看你要求的精度如何了.一般可以使用...
  • 主要为大家详细介绍了java数据库唯一id生成工具类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 生成唯一编号的方法

    千次阅读 2019-02-21 09:39:51
    1、这种方法是通过时间来生成唯一的编号,在页面获取年月日时分秒毫秒不重复的编号,首先获取每个时间然后在把各个时间全部相加起来,得到编号,最后给需要生成编号的文本框赋值。如果只获取到秒都可能重复,所以...
  • java生成8位UUID

    2015-09-18 12:49:04
    java 生成8位UUID,解决UUID2太长的问题,欢迎下载。后续代码,陆续放出
  • 获取SD卡的唯一标识

    千次阅读 2017-10-13 17:24:48
    因为项目的需求,需要获取SD卡的唯一标识,找了多方资料,最后在google上面看到解决办法,在这里做笔记以防止忘记 我的demo代码 private String getSDCid(){ StringBuilder stringB = new StringBuilder(); ...
  • Guid生成唯一ID编号

    千次阅读 2017-10-13 16:27:56
    function GetGUID: string; var i: Integer; GUID: TGUID; begin i := 0; Result := ''; while i begin if CoCreateGuid(GUID) = S_OK then begin SetLength(Result, 32);
  • java生成唯一序列号,UUID的应用

    千次阅读 2017-03-25 10:47:49
    java中生成不重复的唯一序列号具有很大的应用场景,UUID提供了这样的一个方法。生成如下格式的序列号:46049c6d-282e-434b-9c16-025c91ddd23d只需要调用UUID类的静态方法,如下所示:UUID.randomUUID().toString();...
  • import java.util.UUID... * 通过UUID随机生成36位、32位唯一识别码(唯一字符串) * @author 小强 * */ public class Test { public static void main(String[] args) { int i = 0; while(i &lt; 10)...
  • 在网上搜了半天,都没有看到满意的程序。只好参照别人写的例子,自己写了。编号是按照年份和月份+三位序号自动生成的,用的是SQL server数据库,用VS2010编写的。调试已通过。希望给同为新手的各位一个参考。
  • mysql 加序号

    2021-01-18 22:30:00
    名称 该图元的标识,请确保在当前服务编排中唯一。 系统自动填充。 描述 该图元的描述信息。 选填项,建议填写图元的作用。 功能面栏目介绍 通过对数据管理页面介绍,让您更直观、快捷地了解数据管理服务。 图2 ...
  • 生成36位全局唯一数(GUID、UUID)

    万次阅读 2016-09-21 20:28:31
    1.Java import java.util.UUID; UUID.randomUUID();  2.C# using System; Guid.NewGuid().ToString() 3.SQL Server SELECTcast(NEWID() AS VARCHAR(36)) AS UUID ...SELECT sys_gu
  • 如何获取一个能唯一标识每台Android设备的序号? 这个问题有很多答案,但是他们中的大部分只在某些情况下有效。 根据测试: 所有的设备都可以返回一个TelephonyManager.getDeviceId() 所有的GSM设备 (测试设备都...
  • 输出:[123(1) , 132(2) , 213(3) , 231(4) , 312(5) , 321(6)]—>X=2 首先看到题目想到的是生成一个从少到大的全排列的数组,然后再遍历数组得到对应的序号(数组下标加1),又或者想到一个个从小到大的生成push进...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 98,809
精华内容 39,523
关键字:

唯一序号