我的编程空间,编程开发者的网络收藏夹
学习永远不晚

mysql中雪花算法是什么意思

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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位的代码实现:

package com.yl.common;

public class SnowflakeIdWorker {

 // ==============================Fields===========================================
 
 private final long twepoch = 1577808000000L;

 
 private final long workerIdBits = 5L;

 
 private final long datacenterIdBits = 5L;

 
 private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

 
 private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

 
 private final long sequenceBits = 12L;

 
 private final long workerIdShift = sequenceBits;

 
 private final long datacenterIdShift = sequenceBits + workerIdBits;

 
 private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

 
 private final long sequenceMask = -1L ^ (-1L << sequenceBits);

 
 private long workerId;

 
 private long datacenterId;

 
 private long sequence = 0L;

 
 private long lastTimestamp = -1L;

 //==============================Constructors=====================================
 
 public SnowflakeIdWorker(long workerId, long datacenterId) {
 if (workerId > maxWorkerId || workerId < 0) {
 throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
 }
 if (datacenterId > maxDatacenterId || datacenterId < 0) {
 throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
 }
 this.workerId = workerId;
 this.datacenterId = datacenterId;
 }

 // ==============================Methods==========================================
 
 public synchronized long nextId() {
 long timestamp = timeGen();

 //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
 if (timestamp < lastTimestamp) {
 throw new RuntimeException(
  String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
 }

 //如果是同一时间生成的,则进行毫秒内序列
 if (lastTimestamp == timestamp) {
 sequence = (sequence + 1) & sequenceMask;
 //毫秒内序列溢出
 if (sequence == 0) {
 //阻塞到下一个毫秒,获得新的时间戳
 timestamp = tilNextMillis(lastTimestamp);
 }
 }
 //时间戳改变,毫秒内序列重置
 else {
 sequence = 0L;
 }

 //上次生成ID的时间截
 lastTimestamp = timestamp;

 //移位并通过或运算拼到一起组成64位的ID
 return ((timestamp - twepoch) << timestampLeftShift) //
 | (datacenterId << datacenterIdShift) //
 | (workerId << workerIdShift) //
 | sequence;
 }

 
 protected long tilNextMillis(long lastTimestamp) {
 long timestamp = timeGen();
 while (timestamp <= lastTimestamp) {
 timestamp = timeGen();
 }
 return timestamp;
 }

 
 protected long timeGen() {
 return System.currentTimeMillis();
 }

 //==============================Test=============================================
 
 public static void main(String[] args) {
 SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
 
 for (int i = 0; i < 100; i++) {
 long id = idWorker.nextId();
 System.out.println(id);
 }
 }
}

补充知识:雪花算法实现分布式自增长ID

我就废话不多说了,大家还是直接看代码吧~


public class IdWorker {
 // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
 private final static long twepoch = 1288834974657L;
 // 机器标识位数
 private final static long workerIdBits = 5L;
 // 数据中心标识位数
 private final static long datacenterIdBits = 5L;
 // 机器ID最大值
 private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
 // 数据中心ID最大值
 private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
 // 毫秒内自增位
 private final static long sequenceBits = 12L;
 // 机器ID偏左移12位
 private final static long workerIdShift = sequenceBits;
 // 数据中心ID左移17位
 private final static long datacenterIdShift = sequenceBits + workerIdBits;
 // 时间毫秒左移22位
 private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

 private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
 
 private static long lastTimestamp = -1L;
 // 0,并发控制
 private long sequence = 0L;

 private final long workerId;
 // 数据标识id部分
 private final long datacenterId;

 public IdWorker(){
 this.datacenterId = getDatacenterId(maxDatacenterId);
 this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
 }
 
 public IdWorker(long workerId, long datacenterId) {
 if (workerId > maxWorkerId || workerId < 0) {
  throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
 }
 if (datacenterId > maxDatacenterId || datacenterId < 0) {
  throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
 }
 this.workerId = workerId;
 this.datacenterId = datacenterId;
 }
 
 public synchronized long nextId() {
 long timestamp = timeGen();
 if (timestamp < lastTimestamp) {
  throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
 }

 if (lastTimestamp == timestamp) {
  // 当前毫秒内,则+1
  sequence = (sequence + 1) & sequenceMask;
  if (sequence == 0) {
  // 当前毫秒内计数满了,则等待下一秒
  timestamp = tilNextMillis(lastTimestamp);
  }
 } else {
  sequence = 0L;
 }
 lastTimestamp = timestamp;
 // ID偏移组合生成最终的ID,并返回ID
 long nextId = ((timestamp - twepoch) << timestampLeftShift)
  | (datacenterId << datacenterIdShift)
  | (workerId << workerIdShift) | sequence;

 return nextId;
 }

 private long tilNextMillis(final long lastTimestamp) {
 long timestamp = this.timeGen();
 while (timestamp <= lastTimestamp) {
  timestamp = this.timeGen();
 }
 return timestamp;
 }

 private long timeGen() {
 return System.currentTimeMillis();
 }

 
 protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
 StringBuffer mpid = new StringBuffer();
 mpid.append(datacenterId);
 String name = ManagementFactory.getRuntimeMXBean().getName();
 if (!name.isEmpty()) {
  
  mpid.append(name.split("@")[0]);
 }
 
 return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
 }

 
 protected static long getDatacenterId(long maxDatacenterId) {
 long id = 0L;
 try {
  InetAddress ip = InetAddress.getLocalHost();
  NetworkInterface network = NetworkInterface.getByInetAddress(ip);
  if (network == null) {
  id = 1L;
  } else {
  byte[] mac = network.getHardwareAddress();
  id = ((0x000000FF & (long) mac[mac.length - 1])
   | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
  id = id % (maxDatacenterId + 1);
  }
 } catch (Exception e) {
  System.out.println(" getDatacenterId: " + e.getMessage());
 }
 return id;
 }


}

感谢各位的阅读!看完上述内容,你们对mysql中雪花算法是什么意思大概了解了吗?希望文章内容对大家有所帮助。如果想了解更多相关文章内容,欢迎关注亿速云行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

mysql中雪花算法是什么意思

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

mysql中*是什么意思

在 mysql 中,* 的含义MySQL 中的星号 (*) 代表“所有”。在不同上下文中,它具有不同的用法。1. 选择所有列使用 * 可以选择表中的所有列:SELECT * FROM table_
mysql中*是什么意思
2024-04-26

mysql中%是什么意思

mysql 中的百分号 % 是一个通配符,可匹配任意字符序列。它可用于:1. 字符串匹配(如:like '%string%'); 2. 范围查询(如:between '%start%' and '%end%'); 3. 正则表达式匹配(如:
mysql中%是什么意思
2024-04-26

mysql中@是什么意思

mysql 中 @ 符号有两种用途:指定变量(@变量名)和连接表或列名(@表名或@列名),表示数据库中一个临时或派生表的别名。MySQL 中 @ 符号的含义@ 符号在 MySQL 中有两种主要用途:1. 指定变量@ 符号用于指定变量,
mysql中@是什么意思
2024-04-26

mysql中的%是什么意思

mysql 中的 % 通配符用于在 like 操作符中进行模式匹配,表示零个或多个字符。它可用于查找以给定模式开头的所有字符串或包含给定模式的字符串,还可以可选地查找以给定模式结尾的字符串。除了 % 之外,mysql 还提供了其他通配符,包
mysql中的%是什么意思
2024-04-26

mysql中field是什么意思

mysql 中 field 关键字用于在表中查找列的序号,通过指定表达式和要比较的列名,即可返回匹配列的索引,否则返回 0 或 null,常用于动态 sql 和确定列顺序。MySQL 中的 FIELDFIELD 是 MySQL 中的一个关
mysql中field是什么意思
2024-04-29

mysql中as是什么意思

mysql 中 as 用于创建表或列别名,以简化查询中表或列的引用:表别名:在一个查询中使用多个表时,指定表别名,以便引用表中的列更方便。列别名:指定列别名,以便在查询结果中使用不同的名称或别名。MySQL 中 AS 的含义AS 在 My
mysql中as是什么意思
2024-04-29

mysql中schema是什么意思

mysql 中的 schema 是用于组织和管理数据库对象(如表、视图)的逻辑结构,以确保数据一致性、数据访问控制和简化数据库设计。schema 的功能包括:1. 数据组织;2. 数据一致性;3. 数据访问控制;4. 数据库设计。MySQL
mysql中schema是什么意思
2024-05-01

mysql中value是什么意思

mysql 中的 value 关键字:1. 获取列值:select value from table_name where column_name = value;2. 设置列值:update table_name set column_n
mysql中value是什么意思
2024-05-01

MySQL中的/*是什么意思

在MySQL中,/ /用于多行注释。它可以在SQL语句中的任何位置使用,用于注释掉一段代码或提供对代码的解释。多行注释可以跨越多个行,并且可以包含任何文本,而不会被MySQL解释为有效的SQL语句。这在调试代码或进行文档编写时非常有用。例如
2023-10-20

mysql中groupby是什么意思

答案: group by 是 mysql 中用于对数据分组和聚合的 sql 语句。详细描述:将具有相同分组依据值的记录分组在一起。对每个组应用聚合函数(如 sum、count、avg)生成汇总结果。优点包括汇总数据、识别模式、消除重复记录和
mysql中groupby是什么意思
2024-04-26

mysql中asc是什么意思

asc 在 mysql 中表示升序排列,适用于数字和字符串类型。在 select 语句中使用 asc 关键字后跟列名,即可对数据进行升序排序,与 desc 相反。asc 是默认排序顺序,如果未指定,数据将按升序排列。ASC 在 MySQL
mysql中asc是什么意思
2024-04-29

mysql中nvl是什么意思

在 mysql 中,nvl 函数用于替换空值,语法为:nvl(expression, replacement)。它根据规则工作:如果 expression 不为空,则返回 expression;如果 expression 为空,则返回 re
mysql中nvl是什么意思
2024-05-02

mysql中user是什么意思

mysql 中的 user 是一个数据库对象,它表示可以与数据库交互的实体,包含用户名、密码、权限和主机信息。通过 create user 可创建用户,grant 授予权限,revoke 撤销权限。管理 user 对于数据库安全至关重要,因
mysql中user是什么意思
2024-05-01

mysql中sc是什么意思

在 mysql 中,"sc" 代表 second contract,它是一种事务隔离级别。second contract 隔离级别保证了读已提交、不可重复读取和幻读属性,可防止脏读和不可重复读取,同时允许幻读。它通常用于需要高度一致性和并发
mysql中sc是什么意思
2024-05-01

mysql中server_id是什么意思

MySQL中的server_id是用于标识唯一服务器的一个设置选项,每个MySQL服务器都需要具有唯一的server_id值,以确保在复制和集群环境中正确地识别和处理数据,主要用于两个功能:1、主从复制,用于标识主服务器和从服务器;2、My
2023-07-28

MySQL中的/*是什么意思?

这是一种评论。 是评论的结束。让我们实现并展示如何创建评论mysql> MySQL 将忽略上面的注释。让我们看一个例子。在这里,我们用 /*
2023-10-22

mysql中decimal是什么意思

mysql 中的 decimal 数据类型用于存储精确的带小数点的十进制数字。其特点包括:精度:由位数和固定小数位数指定。范围:允许存储大或小十进制值,取决于精度。精度保证:不像浮点类型,decimal 保证存储确切的值,避免舍入误差。语法
mysql中decimal是什么意思
2024-05-01

编程热搜

目录