mysql中雪花算法是什么意思

  

mysql中雪花算法是什么意思?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!

一、为何要用雪花算法

1、问题产生的背景

现如今越来越多的公司都在用分布式、微服务,那么对应的就会针对不同的服务进行数据库拆分,然后当数据量上来的时候也会进行分表,那么随之而来的就是分表以后id的问题。

例如之前单体项目中一个表中的数据主键id都是自增的,mysql是利用autoincrement来实现自增,而oracle是利用序列来实现的,但是当单表数据量上来以后就要进行水平分表,阿里java开发建议是单表大于500w的时候就要分表,但是具体还是得看业务,如果索引用的号的话,单表千万的数据也是可以的。水平分表就是将一张表的数据分成多张表,那么问题就来了如果还是按照以前的自增来做主键id,那么就会出现id重复,这个时候就得考虑用什么方案来解决分布式id的问题了。

2、解决方案

2.1、数据库表

可以在某个库中专门维护一张表,然后每次无论哪个表需要自增id的时候都去查这个表的记录,然后用for update锁表,然后取到的值加一,然后返回以后把再把值记录到表中,但是这个方法适合并发量比较小的项目,因此每次都得锁表。

2.2、redis

因为redis是单线程的,可以在redis中维护一个键值对,然后哪个表需要直接去redis中取值然后加一,但是这个跟上面一样由于单线程都是对高并发的支持不高,只适合并发量小的项目。

2.3、uuid

可以使用uuid作为不重复主键id,但是uuid有个问题就是其是无序的字符串,如果使用uuid当做主键,那么主键索引就会失效。

2.4、雪花算法

雪花算法是解决分布式id的一个高效的方案,大部分互联网公司都在使用雪花算法,当然还有公司自己实现其他的方案。

二、雪花算法

1、原理

mysql中雪花算法是什么意思

雪花算法就是使用64位long类型的数据存储id,最高位一位存储0或者1,0代表整数,1代表负数,一般都是0,所以最高位不变,41位存储毫秒级时间戳,10位存储机器码(包括5位datacenterId和5位workerId),12存储序列号。这样最大2的10次方的机器,也就是1024台机器,最多每毫秒每台机器产生2的12次方也就是4096个id。(下面有代码实现)

但是一般我们没有那么多台机器,所以我们也可以使用53位来存储id。为什么要用53位?

因为我们几乎都是跟web页面打交道,就需要跟js打交道,js支持最大的整型范围为53位,超过这个范围就会丢失精度,53之内可以直接由js读取,超过53位就需要转换成字符串才能保证js处理正确。53存储的话,32位存储秒级时间戳,5位存储机器码,16位存储序列化,这样每台机器每秒可以生产65536个不重复的id。

2、缺点

由于雪花算法严重依赖时间,所以当发生服务器时钟回拨的问题是会导致可能产生重复的id。当然几乎没有公司会修改服务器时间,修改以后会导致各种问题,公司宁愿新加一台服务器也不愿意修改服务器时间,但是不排除特殊情况。

如何解决时钟回拨的问题?可以对序列化的初始值设置步长,每次触发时钟回拨事件,则其初始步长就加1w,可以在下面代码的第85行来实现,将sequence的初始值设置为10000。

三、代码实现

64位的代码实现:

包com.yl.common;/* *
  * Twitter_Snowflake
  *雪花的结构如下(每部分用,分开):& lt; br>   * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 0000000000 & lt; br>   * 1位标识,由于长基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0 & lt; br>   * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截——开始时间截)   *得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的开始时间属性).41点位的时间截,可以使用69年,年T=(1 l & lt; & lt;41)/(1000 l * 60 * 60 * 24 * 365)=69 & lt; br>   * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
  * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号& lt; br>   *加起来刚好64位,为一个长型。你们;br>   *雪花的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,雪花每秒能够产生26万ID左右。   */公开课SnowflakeIdWorker {//==============================字段===========================================/* *开始时间截(2020-01-01)*/私人最终长twepoch=1577808000000 l;/* *机器id所占的位数*/私人最终长workerIdBits=5 l;/* *数据标识id所占的位数*/私人最终长datacenterIdBits=5 l;/* *支持的最大机器id,结果是31(这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)*/私人最终长maxWorkerId=1 l ^ (1 l & lt; & lt;workerIdBits);/* *支持的最大数据标识id,结果是31 */私人最终长maxDatacenterId=1 l ^ (1 l & lt; & lt;datacenterIdBits);/* *序列在id中占的位数*/私人最终长sequenceBits=12 l;/* *机器ID向左移12位*/私人最终长workerIdShift=sequenceBits;/* *数据标识id向左移17位(12 + 5)*/私人最终长datacenterIdShift=sequenceBits + workerIdBits;/* *时间截向左移22位(5 + 5 + 12)*/私人最终长timestampLeftShift=sequenceBits + workerIdBits + datacenterIdBits;/* *生成序列的掩码,这里为4095 (0 b111111111111=0 xfff=4095) */私人最终长sequenceMask=1 l ^ (1 l & lt; & lt;sequenceBits);/* *工作机器ID (0 ~ 31) */私人长workerId;/* *数据中心ID (0 ~ 31) */私人长datacenterId;/* *毫秒内序列(0 ~ 4095)*/私人长=0 l序列;/* *上次生成ID的时间截*/私人长lastTimestamp=1 l;//==============================构造函数=====================================/* *   *构造函数   * @param workerId工作ID (0 ~ 31)   * @param datacenterId数据中心ID (0 ~ 31)   */公共SnowflakeIdWorker(长workerId长datacenterId) {   如果(workerId比;maxWorkerId | | workerId & lt;0) {   把新的IllegalArgumentException (String.format(“员工Id停下来# 39;t % d大于或小于0“,maxWorkerId));   }   如果(datacenterId比;maxDatacenterId | | datacenterId & lt;0) {   把新的IllegalArgumentException (String.format(“数据中心Id停下来# 39;t % d大于或小于0“,maxDatacenterId));   }   这一点。workerId=workerId;   这一点。datacenterId=datacenterId;   }//==============================方法==========================================/* *   *获得下一个ID(该方法是线程安全的)   * @return SnowflakeId   */公共同步长nextId () {   长时间戳=timeGen ();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常   如果(时间戳& lt;lastTimestamp) {   把新的RuntimeException (   String.format(“时钟倒退。拒绝为% d milliseconds"生成id, lastTimestamp -时间戳));   }//如果是同一时间生成的,则进行毫秒内序列   如果(lastTimestamp==时间戳){   序列=(+ 1)序列,sequenceMask;//毫秒内序列溢出   如果(序列==0){//阻塞到下一个毫秒,获得新的时间戳   时间戳=tilNextMillis (lastTimestamp);   }   }//时间戳改变,毫秒内序列重置   其他{=0 l序列;   }//上次生成ID的时间截   lastTimestamp=时间戳;//移位并通过或运算拼到一起组成64位的ID   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null

mysql中雪花算法是什么意思