导师安排去写一个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;
}
参考文章
雪花算法生成ID、UUID生成ID和MySql自增ID优缺点分析_mysql 主键设计用varchar存雪花id,跟用自增有啥区别-CSDN博客