MySQL 的数据库里面字段有一个自增的属性,Oracle 也有Sequence 序列。如果是一个数据库,那么可以保证ID 是不重复的,但是水平分表以后,每个表都按照自己的规律自增,肯定会出现ID 重复的问题,这个时候我们就不能用本地自增的方式了。
我们有几种常见的解决方案:
1)UUID(Universally Unique Identifier 通用唯一识别码)
UUID 标准形式包含32 个16 进制数字,分为5 段,形式为8-4-4-4-12 的36 个字符,例如:c4e7956c-03e7-472c-8909-d733803e79a9。
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
M 表示UUID 版本,目前只有五个版本,即只会出现1,2,3,4,5,数字N 的一至三个最高有效位表示UUID 变体,目前只会出现8,9,a,b 四种情况。
1、基于时间和MAC 地址的UUID
2、基于第一版却更安全的DCE UUID
3、基于MD5 散列算法的UUID
4、基于随机数的UUID——用的最多,JDK 里面是4
5、基于SHA1 散列算法的UUID
UUID 是主键是最简单的方案,本地生成,性能高,没有网络耗时。但缺点也很明显,由于UUID 非常长,会占用大量的存储空间;另外,作为主键建立索引和基于索引进行查询时都会存在性能问题,在InnoDB 中,UUID 的无序性会引起数据位置频繁变动,导致分页。
2) 数据库
把序号维护在数据库的一张表中。这张表记录了全局主键的类型、位数、起始值,当前值。当其他应用需要获得全局ID 时,先for update 锁行,取到值+1 后并且更新后返回。并发性比较差。
3)Redis
基于Redis 的INT 自增的特性,使用批量的方式降低数据库的写压力,每次获取一段区间的ID 号段,用完之后再去数据库获取,可以大大减轻数据库的压力。
4)雪花算法Snowflake(64bit)
核心思想:
a)使用41bit 作为毫秒数,可以使用69 年
b)10bit 作为机器的ID(5bit 是数据中心,5bit 的机器ID),支持1024 个节点
c)12bit 作为毫秒内的流水号(每个节点在每毫秒可以产生4096 个ID)
d)最后还有一个符号位,永远是0。
代码:snowflake.SnowFlakeTest
优点:毫秒数在高位,生成的ID 整体上按时间趋势递增;不依赖第三方系统,稳定性和效率较高,理论上QPS 约为409.6w/s(1000*2^12),并且整个分布式系统内不会产生ID 碰撞;可根据自身业务灵活分配bit 位。
不足就在于:强依赖机器时钟,如果时钟回拨,则可能导致生成ID 重复。
当我们对数据做了切分,分布在不同的节点上存储的时候,是不是意味着会产生多个数据源?既然有了多个数据源,那么在我们的项目里面就要配置多个数据源。
现在问题就来了,我们在执行一条SQL 语句的时候,比如插入,它应该是在哪个数据节点上面执行呢?又比如查询,如果只在其中的一个节点上面,我怎么知道在哪个节点,是不是要在所有的数据库节点里面都查询一遍,才能拿到结果?
那么,从客户端到服务端,我们可以在哪些层面解决这些问题呢?