分析生成数据库主键的方式

251 阅读3分钟

声明:
  本文章查询各方资料做的汇总,途径来源网上。具体参考了很多作者的文章。还有一些无名人士,就不做一一例举了。本人看到可以在评论下署名纪念。本文章仅仅为自己学习的笔记和他人查询的一种资料。
背景:
  有的项目用的是uuid生成主键,有的项目用的是数据库自增,而有的等等。今天就好奇查了一下资料。总结如下。
生成数据库主键的方式主要有三种:

  • 数据库自增ID
  • 采用随机数生成不重复的ID
  • 采用jdk提供的uuid
    这三种方案,在数据量少的时候没有任何差异。当数据量达到百万级别的时候就出现问题了。

1、数据库自增代码演示

1.1 实体类

public class IdOnselfAdd implements Serializable{
	private Long id;
	private String name;
	//省略set、get等其他操作
}

1.2 数据持久操作

public interface IdOnselfAddMapper{
	//自增长插入
	@Insert("insert into tb_addId values(#{name})")
	void insert(IdOnselfAdd idOnselfAdd);
}

1.3 单元测试

@Test
public void testInsert(){
	long start = System.currentTimeMillis();
	for(int i=; i<1000000; i++){
		IdOnselfAddMapper.insert(new IdOnselfAdd().setName("李四"));
	}
	long end = System.currentTimeMillis();
	System.out.println("消耗时间:"+(end-start));
}

2 采用随机数生成ID

  这里我们采用随机数生成主键的最先进的雪花算法来实现生成ID

public class SnowAlgorithmicCreateId {
	private static SnowAlgorithmicCreateId instance = new SnowAlgorithmicCreateId(0, 0);
	//开始时间戳 2021-04-26 16:02:50
	private final long twepoch = 1619424170000L;
	//机器id所占位数
	private final long workerIdBits = 5L;
	//数据标识id所占的位数
	private final long datacenterIdBits = 5L;
	//支持的最大机器id,结果是31(这个位移算法可以很快的计算出几位二进制数所能表示的最大十进制数)
	private final long maxWorkerId = -1L ^(-1L  << workerIdBits)
}
//其他操作与上面的自增id操作类似

3 uuid

public class UUIDGenerator{
	//获取uuid
	public static String getUUid(){
		return UUID.randomUUID().toString();
	}
}

测试

@RunWith(SpringRunner.class)
@SpringBootTest()
public class UUIDTest{
	private static final Integer MAX_COUNT = 1000000;
	@Autowired
	private UUIDMapper uuidMapper;
	@Test
	public void testInsert(){
		long start = System.currentTimeMillis();
		for(int i=; i<MAX_COUNT; i++){
			String id = UUIDGenerator.getUUid();
			uuidMapper.insert(new UUid.setId(id).
			setName("李四"));
		}
		long end = System.currentTimeMillis();
		System.out.println("消耗时间:"+(end-start));	} 
}

结果分析:

  当数据在100w左右的时候,性能会出现差距
时间占用总体效率排名:
  自增id>雪花算法生成ID>>uuid生成的id

1、当数据量较大的情况下,uuid生成的id不如自增id原因分析:

自增id内部结构分析:
  自增的主键值是顺序的,所以innodb把每一条记录都存储在一条记录的后面。

  当达到页面的最大填充因子时候(innodb默认的最大填充因子是页大小的15/16,会留出1/16的空间做以后的修改),会进行如下操作:

  • 下一条记录就会写入新的页中,数据按这种顺序的方式加载,提升了页面的最大填充率,不会有页的浪费。
  • 新插入的行一定会在原有的最大数据行下一行,mysql定位和寻址很快,不会为计算新行的位置做额外的消耗。
    2、使用uuid的索引内部结构
      uuid相对顺序的自增id来说是毫无规律可言的,新行的值不一定要不之前的主键的值要大,所以innodb无法做到总是把新行插入到索引的最后,而是需要为新行寻找新的合适的位置从而来分配新的空间。

除此之外还有其他生成主键方式:

时间戳作为主键
  优点:本地生成;延时低,索引性能高   缺点:1秒内请求过1000后id肯定重复,微秒同理。原因和他的原理有关。时间戳是以数学时间作为单位衡量的,那么他就会有极限。极限就是进制和他的最大量,难以满足高并发背景下的应用。 数据库自增主键   优点:简单、唯一、递增、增幅固定
  缺点:写性能决定每秒生成数量上限,扩展差,分布式数据库,主节点挂掉,备节点上时可能有问题(主节点写入成功,日志未同步到备节点,导致id重复)
雪花算法
  他的出现就是为了解决上面的问题。目前不考虑他和数据库自增id性能问题(文章上面有分析雪花算法和数据库自增主键性能比较不在赘述),他是最优解。

其他

自己建立了一个技术群,大家愿意主动学习和分享,愿意一块实现一些有意思的技术,进行理论和实践的交流。这里面有前端和后段,不是单一的一种,方便大家沟通前后端兼容问题

4530a92078099847d0eec48ac91353b.jpg