实习笔记2-mysql主键使用雪花算法

导师安排去写一个DO类,我是如下写法,结果调试的时候mybatis一直报错id不为null

public class XXDO{
   @TableId(type = idtype.auto)
   public Long id;
}

经询问导师,新项目的主键id全都使用雪花算法来生成。通过网络查询得知,生成mysql的主键id主要有三种:自增主键,UUID,雪花算法,下面将对这三种方法来做分析。

自增主键

自增id的主键的值是顺序的,所以InnoDB把每一条记录都存储在一条记录的后面,当达到页面的最大填充因子的时候(InnoDB默认的最大填充因子是页大小的15/16,会留出1/16的空间作为以后的修改)

优点:

1.下一条记录会写入新的页中,一旦数据按照这种顺序方式进行加载,主键页就会近乎于顺序的记录填满,提升了页的最大填充率,不会有页的浪费

2.新插入的行一定会在原有的最大数据行的下一行,mysql定位和寻址快,不会为计算新行的位置而做出额外的消耗

3.减少了页分裂和碎片产生

缺点:

1.高并发的负载,InnoDB在按主键进行插入的时候会造成明显的锁争用,主键的上界会成为争抢的热点,因为所有的插入都发生在这里,并发插入会导致间隙锁竞争

2.Auto_Increment锁机制会造成自增锁的抢夺,有一定的性能损失

问题:

Q:为什么mysql主键id不会自增?

A:在mysql低版本中,InnoDB表中使用自增的auto_increment计数器会把值存放在内存中,不会写入磁盘。一旦mysql服务重启,这个值就会丢失。InnoDB引擎会根据表中现有的数据重新计算该计数器的值,获取表中最大的自增主键ID作为auto-increment计数器的最大计数。当insert数据时,在auto-crement计数器最大值上加1

而在MyISAM引擎中,MyISAM表会把自增主键的最大ID记录到数据文件里面,重启Mysql后,自增主键的最大ID不会丢失

Q:什么是页分裂和碎片?

A:页分裂是如果一个数据应该被插入第10页,但是第10页满了,直接把第10页分裂成两个。碎片产生是由频繁删除,更新和插入操作导致的。例如当执行upate时,原始数据长度为varchar(100),更新数据长度为50,这样有50的空间变为空白了,新入库的数据不能完全利用剩余的50,就会产生碎片

UUIID

uuid相对顺序的自增id来说是无规律的,新行的值不一定要比之前的主键值要大,所以InnoDB无法做到总是把新行插入到索引的最后,而是要寻找新的合适的位置来分配新的空间,这个过程需要额外的操作,数据的毫无顺序会导致数据散乱

优点:

出现数据拆分,合并存储的时候,能达到全局唯一性

缺点:

1.写入的目标页很可能已经刷新到磁盘上并从缓存上移除,或者还没有加载到缓存中,InnoDB在插入前必须先找到并且需要从磁盘读取目标页到内存中,会导致大量的随机IO

2.因为写入是乱序,InnoDB必须做页分裂,以便为新的行分配空间,页分裂会导致移动大量的数据,一次插入最少需要修改三个页以上

3.由于频繁的页分裂,页会变得稀疏并且被不规则的填充,最终导致数据有碎片

雪花算法

由符号位+时间戳+工作进程+序列号位的自增组成

时间范围:2的41次方/(365*24*60*60*1000L)=69.73年

工作进程数量:2的10次方=1024

生成不碰撞序列的TPS:2的12次方*1000=409.6万

优点:

1.不会重复

2.有序,不会造成空间浪费

3.生成速度快

缺点:

1.依赖机器时钟,如果机器时钟回拨,会导致重复ID生成

2.在单机上是递增的但由于涉及到分布式环境,每个机器的时钟不可能完全同步。有时会出现不是全局递增的(在分布式场景下,只要求趋势递增,并不会要求严格递增)

问题:

时钟回拨问题的解决思路:

1.直接抛出异常

if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - currentTimestamp));
        }

2.使用Map<machine_id,max_id>保存过去一段时间内每一台机器在当前这一毫秒产生的ID的最大值,当某台机器发生时钟回拨,直接在这台机器对应的max_id的基础上继续自增生成ID

3.将原本10位机器码拆分成3位时钟序列及机器码,发生变化,那么这时将时钟序列新增一位,重新定义整个雪花id,同时为了避免实例重启引用时间序列丢失,因此时钟序列存到DB或者缓存中

// 处理时间回拨
if (currentTimestamp < lastTimestamp) {
     clockSequence = (clockSequence + 1) & CLOCK_SEQUENCE_MASK;
 }            

参考文章

『SnowFlake』雪花算法的详解及时间回拨解决方案-CSDN博客

面试题:雪花算法(SnowFlake)如何解决时钟回拨问题_雪花算法时钟回拨-CSDN博客

Mysql 页分裂页合并_mysql 页合并-CSDN博客

雪花算法生成ID、UUID生成ID和MySql自增ID优缺点分析_mysql 主键设计用varchar存雪花id,跟用自增有啥区别-CSDN博客

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值