1. ID的要求
在分布式系统中,需要对大量的数据进行唯一标识,有时会对数据采用分库分表,所以我们需要一个全局id
主键id的需要保证的一些要求
- 全局唯一
不能出现重复的id,这是最基本的要求。 - 趋势递增
MySQL使用InnoDB引擎,使用的是聚簇索引,所以主键尽量使用有序的。 - 单调递增
保证下一条数据的id一定大于上一个id。 - 信息安全
如果id是连续的,容易被恶意爬取,如果是订单号的表,别人可以轻而易举知道订单数量。所以在一些场景下需要id无规则。 - 含时间戳
能在开发中快速了解分布式id生成的时间。
2. UUID
标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的32个字符。
UUID的性能非常高,它在本地就能生成,没有网络消耗,确保唯一性的能力较强,但是入数据库的能力较差,这和UUID的无序性息息相关。
- UUID太长,作为主键的话,索引表占用存储空间比较大,如果数据量很大,就要考虑存储量的问题。
- UUID往往以字符串形式存储在数据库,查询的效率比较低。
- 索引的底层是B+树的结构,由于UUID是无序的,每一条新的数据插入,都会对索引B+树进行大幅度修改,这样不仅会导致一些中间节点产生分裂,还会创造很多不饱和节点,大大降低了数据库的性能。
3. 主键自增
基于数据库自增id和MySQL的replace into实现。
数据库自增id不适合做分布式id,原因如下
- 系统水平扩展比较困难
如果做分库分表,假如第一张表已经排到了1000,下一张表的起始值要设置多少呢?10000?两张表还好说,如果是10张表呢?所以系统水平扩展几乎很难做到。
第二点就是,如果要与其他系统集成,涉及到合表操作等时,很容易造成主键冲突。 - 安全性低
主键规律自增,容易被非法获取数据。
4. 雪花算法
是twitter发明的,自增可排序的id,Long数据类型的64bit大小的整数,由1bit的符号位、41bit的时间戳、10bit的工作机器id、12bit序列号组成。可以保证:所有生成的id按时间趋势递增,整个分布式系统内不会产重复id。
它有以下三个缺点
- 依赖机器时钟,如果机器时钟回拨,会导致有重复id生成。
- 在单机上递增,但是如果在分布式环境下,每台机器时钟未必同步,可能出现不是全局递增情况。(可忽略)
- 64位的二进制数,化为10进制存储一般为19位,但是前端js只能保证前16位的精度,前端拿到这条数据时,会对后三位进行四舍五入的处理,丢失了精度。
5. 如何选择
- 如果是单机项目,数据量比较大时,选择自增主键是ok的,最好做一些安全工作。数据量比较小时,UUID的性能差异可以忽略不计,使用UUID就对了。
- 如果是分布式项目,首选UUID,因为分布式一般对速度和存储要求不高。但如果数据量很大,比如千万级别,对速度和存储有要求时,我们可以考虑使用自增算法。
- 雪花算法是更优的自增算法,处理好精度丢失的问题,我们可以用雪花算法代替自增算法。