说到id之前,先说一下mysql数据库的Innodb的主键索引,因为这和索引息息相关。
我们知道,在Innodb中,采用的是B+数索引。Innodb的存储结构,是聚簇索引。对于聚簇索引,
(1)顺序主键和随机主键的对效率的影响很大。我们分插入和查询来讲:
插入。在磁盘中,有一个页的概念,而一页的容量是固定的,每一页按顺序存储数据,一般数据量达到一页的15/16,则开始存到下一页。但如果主键是随机的,那么每次插入之前,为了找到它的位置,首先需要进行一次比较,找到它的合适位置,再进行插入。如果是在页尾,那自然是最理想的效果。
UUID和自增长在程序中怎么实现此处不讲,我们直接从性能和实际使用来分析。
说到性能,就不等不提到索引,此处以InnoDB的BTree索引来讲。
uuid类型是varchar(36),而自增长Id则一般是bigInt类型。
首先,大家要知道,id作为唯一索引,并不总是能提高效率。对于非常小的表,大部分情况下简单的全表扫描效率更高,对于中大型的表,索引才非常有效。因为索引帮助存储快速查找到记录的同时,也会带来额外的消耗。
那么索引会带来哪些额外的消耗呢?空间和时间。
下面做了一个实验,在一个有足够内存容纳索引的服务器上插入100万条记录和300百万条记录,时间和空间上的消耗如下:
根据上面的结果,索引是会消耗额外的物理空间的,而聚簇索引消耗的物理空间更大。当数据特别多的时候,相对于bigInt类型的自增长Id,varchar(36)类型的uuid消耗的物理空间更为明显。
在时间上,1)uuid由于占用的内存更大,所以查询、排序速度会相对较慢;2)在存储过程中,自增长id由于主键的值是顺序的,所以InnoDB把每一条记录都存储在上一条记录的后面。当达到页的最大填充因子时(innodb默认的最大填充因子为页大小的15/16,留出部分空间用于以后修改),下一条记录就会写入新的页面中。一旦数据按照这种方式加载,主键页就会被顺序的记录填满。而对于uuid,由于后面的值不一定比前面的值大,所以InnoDB并不能总是把新行插入的索引的后面,而是需要为新行寻找合适的位置(通常在已有行之间),并分配空间,这会增加额外的很多工作。这也是为什么当索引的列过长的时候,需要采用前缀索引,或者哈希索引了。
尽管自增长id的优点这么多,但实际大型项目中却很少采用自增长id的,这是为什么呢?uuid几乎保证了不同数据库的不同表的id唯一,可以进行数据切分合并,而自增长id只能保证一个数据库中的一张表的id唯一,进行数据库合并的话并然会因主键冲突而失败,这是一个硬伤。