mysql分布式主键_跟我学shardingjdbc之分布式主键及其自定义

博客地址:朝·闻·道​www.wuwenliang.net33983b6f01bea79bcb94c486cb70d9b5.png本文是 “跟我学Sharding-JDBC” 系列的第三篇,我将带领读者一起了解下Sharding-JDBC的分布式主键,并实现业务性更强的自定义主键。

首先了解下,什么是分布式主键。

传统的关系型数据库,如MySQL中,数据库本身自带自增主键生成机制,但在分布式环境下,由于分库分表导致数据水平拆分后无法使用单表自增主键,因此我们需要一种全局唯一id生成策略作为分布式主键。

当前业界已经有不少成熟的方案能够解决分布式主键的生成问题,如:UUID、SnoWflake算法(Twitter)、Leaf算法(美团点评)等。

UUIDUUID是Universally Unique Identifier的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符。

UUID具有如下特点: 1. 经由一定的算法机器生成,算法定义了网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。 2. 非人工指定,非人工识别。UUID的复杂性决定了“一般人“不能直接从一个UUID知道哪个对象和它关联。 3. 在特定的范围内重复的可能性极小。

UUID能够保证最少在3000+年内不会重复。因此它的唯一性是很可靠的。但也有不足之处,就是可读性差,不能直接用来做分片键并进行取模分库表的操作,需要进行额外的开发,如:转换UUID为unicode/ASCII码,对数字进行叠加后取模。

SnoWflake

雪花算法(SnoWflake)是Twitter公布的分布式主键生成算法,也是ShardingSphere默认提供的配置分布式主键生成策略方式。在ShardingSphere的类路径为:io.shardingsphere.core.keygen.DefaultKeyGenerator

SnoWflake能够保证不同进程主键的不重复性,以及相同进程内主键的有序性。

在同一个进程中,SnoWflake首先是通过时间位保证不重复,如果时间相同则是通过序列位保证。 同时由于时间位是单调递增的,且各个服务器如果大体做了时间同步,那么生成的主键在分布式环境可以认为是总体有序的,这就保证了对索引字段的插入的高效性。例如MySQL的Innodb存储引擎的主键。

雪花算法生成的主键的二进制表示形式包含4部分,从高位到低位分别为:1bit符号位、41bit时间戳位、10bit工作进程位以及12bit序列号位。

雪花算法能够保证全局唯一,同时也存在一些问题,如时钟回拨可能导致产生重复序列。为了解决这个问题,ShardingSphere默认分布式主键生成器提供了一个最大容忍的时钟回拨毫秒数。

如果时钟回拨的时间超过最大容忍的毫秒数阈值,则程序报错;如果在可容忍的范围内,默认分布式主键生成器会等待时钟同步到最后一次主键生成的时间后再继续工作。 最大容忍的时钟回拨毫秒数的默认值为0,可通过调用静态方法DefaultKeyGenerator.setMaxTolerateTimeDifferenceMilliseconds()设置。

其他方案这里再简单介绍下其他的分布式主键生成的方案。

Leaf算法

Redis计数器

我们还可以通过第三方的组件的特性二次开发自己的分布式id生成器。如:使用Redis的 INCR key自增计数器,它是 Redis 的原子性自增操作最直观的模式,其原理相当简单:每当某个操作发生时,向 Redis 发送一个 INCR 命令。

比如在一个 web 应用中,想知道用户在一年中每天的点击量,那么只要将用户ID及相关的日期信息作为键,并在每次用户点击页面时,执行一次自增操作即可。

它有着多种扩展模式,如: 1. 通过组合使用 INCR 和 EXPIRE达到只在规定的生存时间内进行计数(counting)的目的 2. 客户端通过使用 GETSET 命令原子性地获取计数器当前值并将计数器清零,更多信息请参考 GETSET 命令。 3. 通过用其他自增/自减操作,比如 DECR 和 INCRBY ,用户可以在完成业务操作之后增加或减少计数器的值,如在游戏中的记分器就是一个典型的场景。

它的优点在于: 1. 不依赖数据库且性能优于数据库。 2. ID天然有序,对分页或者需要排序的场景很友好。

但是它还存在如下的缺点: 1. 如果系统中没有Redis需要引入Redis增加了系统复杂度。 2. 需要额外的编码和配置工作。

但总体来讲,这是个不错的方案,分布式环境下,我们通过集群Redis能够保证生成器高可用运行,集群之间通过复制能够保证序列生成不会有单点故障。

Zookeeper

通过利用zookeeper的持久顺序节点特性,多个客户端同时创建同一节点,zk可以保证有序的创建,创建成功并返回的path类似于/root/generateid0000000001这样的节点,能够看到是顺序有规律的。利用这个特性,我们能够实现基于zk的分布式id生成器。

不过一般我们很少会使用zookeeper来生成唯一ID。主要是由于需要依赖zookeeper,并且是多步调用API,如果在竞争较大的情况下,需要考虑使用分布式锁。因此,在高并发的分布式环境下,性能不甚理想。

MySQL自增id

这种方式很好理解,就是建立一张序列表,执行插入操作,并获取记录的id值。

它的优点如下: 1. 容易理解,开发量不多,且性能可以接受。 2. 通过自增主键生成的ID天然排序,对分页或者需要排序的结果很有帮助。

同时它存在如下的缺点: 1. 不同数据库语法的和实现不同,如果需要切换数据库或多数据库版本支持的时候需要在每个库中单独处理。 2. 在单数据库或读写分离或一主多从的情况下,只有一个主库可以生成。有单点故障风险。 3. id的生成与数据库的性能强关联。 4. 如果存在数据的迁移,则id序列表也需要同步迁移。 5. 分表分库场景下会有麻烦。

当然这些问题都有针对的解决方案: 1. 对于不同的数据库,只需要将id的生成作为单独的服务开发,不同的业务通过接口调用id生成,屏蔽后方的实现细节 2. 针对主库单点,可以改造为多Master架构 3. 如果条件允许,使用高性能磁盘及主机部署数据库 4. 通过双写操作的方式进行数据迁移 5. 分库分表场景下,只需要在每个数据分片上设置对应表的序列生成表即可,序列表与业务表使用相同的分片规则,这样就能保证序列与业务是一一对应的,在每个片上,都是唯一且自增的。

我的选择

通过了解各种分布式主键生成策略,我最终选择了Redis的计数器作为自定义分布式主键的核心技术方案。

原因如下: 1. 业务id如果直接使用UUID、snowflake等可读性较差,需要有业务属性,最好能直观的看到分片属性 2. 业务中本身就引入了Redis集群,不需要额

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值