03.数据库分库分表-唯一主键生成

摘要

数据库分库分表同时因为服务是多节点部署的,所以就会引申出分布式系统中唯一主键ID的生成问题。一般我们唯一ID的有以下特性:

  • 整个系统ID唯一
  • ID是数字类型,而且是趋势递增的
  • ID简短,查询效率快

下面我们看下常见的几种ID生成方式


UUID/GUID

使用guid或者uuid作为主键id
优点

  1. 代码实现简单。
  2. 本机生成,没有性能问题
  3. 因为是全球唯一的ID,所以迁移数据容易

缺点

  1. 每次生成的ID是无序的,无法保证趋势递增
  2. UUID的字符串存储,查询效率慢
  3. 存储空间大
  4. ID无业务含义,不可读

MySQL主键自增

这个方案就是利用了MySQL的主键自增auto_increment,默认每次ID加1。对于我们分表的业务场景中通过卫不同的表设置不同的步长以及初始值实现。
在这里插入图片描述
优点

  1. 数字化,id递增
  2. 查询效率高
  3. 具有一定的业务可读

缺点

  1. 存在单点问题,如果mysql挂了,就没法生成iD了
  2. 数据库压力大,高并发抗不住

雪花算法

雪花算法生成64位的二进制正整数,然后转换成10进制的数。64位二进制数由如下部分组成:
在这里插入图片描述

  • 1位标识符:始终是0
  • 41位时间戳:41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截 )得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的
  • 10位机器标识码:可以部署在1024个节点,如果机器分机房(IDC)部署,这10位可以由 5位机房ID + 5位机器ID 组成
  • 12位序列:毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

优点

  1. 此方案每秒能够产生409.6万个ID,性能快
  2. 时间戳在高位,自增序列在低位,整个ID是趋势递增的,按照时间有序递增
  3. 灵活度高,可以根据业务需求,调整bit位的划分,满足不同的需求

缺点

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

Redis生成方案

利用redis的incr原子性操作自增,一般算法为:

年份 + 当天距当年第多少天 + 天数 + 小时 + redis自增

优点

  1. 有序递增,可读性强

缺点

  1. 占用带宽,每次要向redis进行请求

应用

由于我们存在高并发场景,而且期望id是去世递增,所以上述方法都不能完全适用我们的业务场景。我们选择改造自增主键的方式。
改造数据库主键自增
自增主键有两个问题:

  1. 一旦步长定下来,不容易扩容
  2. 数据库压力大

为什么压力大?是因为我们每次获取ID的时候,都要去数据库请求一次。那我们可以不可以不要每次去取?思路我们可以请求数据库得到ID的时候,可设计成获得的ID是一个ID区间段。
在这里插入图片描述
这种方式如果多个节点同时发起请求获取id区间会有并发问题,所以需要加锁,可以通过分布式锁或者数据库自身的锁解决。虽然加锁可以解决并发问题但是还是会有阻塞问题。此时可以通过双buffer缓存解决。
双buffer方案
在这里插入图片描述

1、当前获取ID在buffer1中,每次获取ID在buffer1中获取
2、当buffer1中的Id已经使用到了100,也就是达到区间的70%
3、达到了70%,先判断buffer2中有没有去获取过,如果没有就立即发起请求获取ID线程,此线程把获取到的ID,设置到buffer2中。
4、如果buffer1用完了,buffer2会升级到buffer1
6、依次往返

这样不仅达到了业务场景用的ID,都是在jvm内存中获得的,从此不需要到数据库中获取了。允许数据库宕机时间更长了。而且因为会有一个线程,会观察什么时候去自动获取。两个buffer之间自行切换使用。就解决了突发阻塞的问题。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章