1、在Mysql的分布式环境中,为什么不推荐使用自增主键?
--自增主键在分布式环境下有严重问题,例如有一张商品表:
有3个表分片,分别是表分片1(0-1亿),表分片2(1-2亿),表分片3(2-3亿)
自增主键必须连续,只能采用‘范围分片’形式,会产生‘尾部热点’效应
--当有表A、B、C三个数据库时,存储数据时都是通过自增来进行存储的,那么当A库没有存满时,那么B库和C库就不会存储数据,那么某一个时间段,数据库的压力是集中在其中一个数据库的,并没有做到负载均衡
--当我们将数据同步到其他库的时候,那么由于采用了自增ID,在进行插入的过程中,入非常损耗性能,因为在同步过程中是加锁来进行实现的
--每个库都维护一份自己的自增序列,那么当进行跨库查询的时候,就会产生ID重合的冲突。
2、UUID可以用来做主键吗?会存在什么问题?
--UUID是不能用来作为主键的,因为UUID是无序的,作为主键会涉及大量的索引重排
3、雪花算法可以用做主键吗?原理是什么?有什么优缺点?如何解决缺点?
--可以用来作为主键
是由符号位+时间戳+工作进程+序列号位的自增组成
优点: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; }