投票统计

是否原创:100 %

100 % Complete (success)

是否有价值:100 %

100% Complete

是否有素质:100 %

100% Complete (warning)

是否合法:100 %

100% Complete

2022年5月份,LoserHub网站发生了一起攻击事件。攻击者利用了自己注册的账号,使用模拟浏览器的工具,给LoserHub网站增加了几十万的帖子。由于使用云服务器,数据库每日有备份,所以损失不大。不过,倒是帮我做了一次很好的性能测试,让我发现了使用二进制UUID作为主键的性能问题。

 

几十万帖子就让MySQL8.0查询异常缓慢

 

使用二进制UUID作为主键,查询性能差点意思,我是提前知道的。但是,几十万帖子就把MySQL干趴了,我是没想到的。实际上MySQL8.0对于使用二进制UUID作为主键,是有优化的。这也是,当时升级8.0最让我激动的事情之一。有人会说,有现成的雪花算法,为啥一定要用二进制UUID。

 

说说自己不喜欢雪花算法的一些原因

 

1,雪花算法只能用69年,虽然可以通过扩充时间戳来扩大这个使用时间,但是我查了一下GitHub现有的库不支持。

2,雪花算法,有一个服务器时间回档可能会产生重复ID的问题。虽然可以通过每次生成ID记住最后一次生成时间的方法来规避,但是麻烦。

3,雪花算法,生成的主键ID太长。19位长整型整数,搞到前端JS的整数只认16位,需要换成字符,又增加麻烦。而且二进制UUID压缩后只有16位,长度比雪花算法短,可以节省数据库的空间,减少索引的大小,降低查询时间。简单说,就是性能更好。

4,实际上,现在已经可以通过生成顺序的二进制UUID,来提高性能了。理论上说,跟雪花算法的数字ID,性能差不了太多。

 

为什么LoserHub使用二进制UUID作为主键性能那么差?

 

虽然说,MySQL8.0已经可以通过写入顺序生成的二进制UUID作为主键,来提高索引性能。但是,LoserHub目前的二进制UUID不是通过MySQL8.0数据库生成的。因为当时,开发LoserHub的时候,MySQL8.0还没发布呢。所以,使用的是PHP框架自带的类库生成的UUID。

由于年代久远,我都忘记了这个生成的UUID到底是什么版本的。经过查询,框架自带的类库,生成的是完全无序的符合RF4122规范的V4版本的UUID。即使二进制压缩后,作为主键由于不是循序生成的,性能就很垃圾了。

各位PHP开发人员,如果也想使用二进制UUID作为主键,一定要注意这个问题。

 

如何生成顺序递增的UUID?

 

其实根本不需要我们操心这个问题,负责制定UUID规范的国际组织IETF正在制定第6版本的UUID,这个uuid6就是针对性的解决了目前UUID存在的问题。

第一:分散的数据库记录。

第二:无法以有意义的方式按标识符排序(即插入顺序)。

虽然是IETF实验性的草案,但是一些PHP的类库已经支持生成V6版本的UUID了。比如说Ramsey\Uuid了,官方仓库的地址在 ramsey/uuid: A PHP library for generating universally unique identifiers (UUIDs). (github.com).

哇哦,我之前就认为UUID是有前途的主键方案,目前来看是预测准确了。说不定有一天,我们终于可以摆脱雪花算法了,而正大光明的使用UUID作为主键了。

 

PHP各大框架也有可能跟进

 

目前PHP的一些关于UUID的类库仅仅作为实验性的功能支持,可以生成V6版本的UUID。随着V6版本的UUID成为正是规范,各大PHP框架也有可能跟进,把生成V6版本的UUID直接集成到框架本身。

作为PHP的开发者,如果不着急,可以像我这样暂时等一等。相信过不了多久,包括PHP在内的各大编程语言,都有可能集成V6版本的UUID功能了。

 

顺序排列的二进制UUID与BigInt作为主键的性能对比

 

早在2014年,Pecona官方就发表了一篇文章,《在MySQL中存储UUID值》。就对顺序排列的二进制UUID和雪花算法生成的BigInt作为主键的性能,进行了对比。

大概2500万条数据,顺序排列的二进制UUID,在索引大小,表大小,查询时间等都有优势。当时Pecona的建议是,在数据库里写个函数,把UUID的顺序调整一下。而现在,我们可以在代码层面,更加轻松的做到这一点。

 

不要使用MySQL的UUID()函数生成UUID

 

MySQL的UUID()函数生成的UUID,貌似是V1版本的UUID,是无序的。

 

.NET的GUID同样不适合做主键

 

最后,再来讲一下.NET框架的GUID。微软的.NET框架自己搞了一个GUID,实际上它是在V4版本的UUID的基础上修改得到的。V4版本的UUID是大端顺序排列的,也被称为网络字节顺序。

但是GUID的字节顺序不同,采用了小端顺序排列。虽然比V4版本的UUID有改进,但是他依然不适合做主键的。除了字节顺序不同,GUID和V4版本的UUID字符串是完全相同的。

 

总结

 

不管是MySQL官方,还是负责制定UUID规范的IETF,还是PHP的一些类库作者,大家都看到了二进制UUID作为主键ID的未来发展潜力。我们不需要19位长度的长整型数字ID作为主键,就能实现类似的需求。

或许在不久的将来,这会成为事实上的标准,也说不定呢。

帖子投票

名称 是否有价值
kideny