1.生成位置如何影响选择?
数据库往返时间
使用自增数时,ID是由数据库在执行INSERT操作时生成的;而UUID则可以在应用层生成。
考虑这样的场景:
一个方法需要插入A和B两个实体。其中B的数据需要引用A的ID。
如果使用自增ID,则必须与数据库进行两次网络请求:
1.先插入A,获取A的ID;
2.然后使用A的ID构建B,再插入B。
而如果使用UUID,可以在应用层同时生成A和B的ID,之后一次性将两个实体提交给数据库,从而减少网络往返的次数,提高效率。
ID冲突
自增数在一个表内不会冲突,那两个表呢?
当业务发展到需要分库分表的时候,就不能再使用自增数作为ID了,因为两个库/表会生成相同的ID。这种ID冲突且不报错的问题,会导致数据混乱。
UUID虽然具有全球唯一性,极端情况也会有冲突,所以应用层需要想办法处理,确保ID唯一性,例如IID生成结合机器ID。
2.有序与无序如何影响选择?
ID是否有序对数据库性能有重要影响,特别是使用B-Tree索引。
**有序的ID:**对B-Tree索引来说,键值有序,那么连续插入的数据都会一个或少数几个节点上,这意味着,数据库只需要对少量的物理块进行I/O读写,I/O范围小,性能较好。
eg:连续插入20条数据,都在一个节点上,只要加载一个物理块。
**无序的ID:**键值无序,那么连续插入的数据可能随机分布在各个节点上,数据库就需要对大量的物理块进行I/O读写,性能较差。
eg:连续插入20条数据,经计算它们分布在20个不同节点上,需要加载20个物理块。
显然,自增ID是有序的,所以在大多数情况下,它的性能优于无序的UUID。
值得一提的是,UUID也有多个版本,其中一些是有序的,例如UUIDv7和雪花算法(Snowflake)
而Java JDK默认使用的UUID是v4版本,是无序的。
3.ID大小如何影响选择?
ID大小指占用的存储空间。自增数通常是INT或BIGINT,分别是4字节和8字节。UUID一般由32个字符组成,占16字节。
树的高度
在B-Tree中,对于固定大小的节点,键值大小会影响一个节点能存储的键值数量(这里就是ID数量)。影响了单个节点存储的键值,就会影响节点数量,进而影响树的高度。
而树越高,一次查询需要访问的节点就更多,查询就更慢。
内存开销
相同数量的记录,ID的大小会影响到数据库引擎的缓存(索引和数据行)。同样大小的缓存空间,单个ID越大,能缓存的数据就越少。
例如:InnoDB使用innodb_buffer_pool_size控制缓存池大小
总结
总的来说,有序的UUID(UUIDv7,雪花算法)更适合分布式服务,自增ID更适合单机服务。
但是也要看具体业务,举个例子,如果分表的数据不存在跨表访问的情况,那么ID冲突就冲突了,没有任何影响。
而且,ID策略也能混着用,一个数据库里面不同表可以用不同的ID生成策略。