依托于互联网的发达,我们可以随时随地利用一些等车或坐地铁的碎片时间学习以及了解资讯。同时发达的互联网也方便人们能够快速分享自己的知识,与相同爱好和需求的朋友们一起共同讨论。
但是过于方便的分享也让知识变得五花八门,很容易让人接收到错误的信息。这些错误最多的都是因为技术发展迅速,而且没有空闲时间去及时更新已经发布的内容所导致。为了避免给后面学习的人造成误解,我们今天来看一看 MySQL 设计规范中几个常见的错误例子。
主键的设计错误的设计规范:主键建议使用自增 ID 值,不要使用 UUID,MD5,HASH,字符串作为主键
这个设计规范在很多文章中都能看到,自增主键的优点有占用空间小,有序,使用起来简单等优点。
下面先来看看自增主键的缺点:
自增值由于在服务器端产生,需要有一把自增的 AI 锁保护,若这时有大量的插入请求,就可能存在自增引起的性能瓶颈,所以存在并发性能问题;
自增值做主键,只能在当前实例中保证唯一,不能保证全局唯一,这就导致无法在分布式架构中使用;
公开数据值,容易引发安全问题,如果我们的商品 ID 是自增主键的话,用户可以通过修改 ID 值来获取商品,严重的情况下可以知道我们数据库中一共存了多少商品。
MGR(MySQL Group Replication) 可能引起的性能问题;
因为自增值是在 MySQL 服务端产生的值,需要有一把自增的 AI 锁保护,若这时有大量的插入请求,就可能存在自增引起的性能瓶颈。比如在 MySQL 数据库中,参数 innodb_autoinc_lock_mode 用于控制自增锁持有的时间。虽然,我们可以调整参数 innodb_autoinc_lock_mode 获得自增的最大性能,但是由于其还存在其它问题。因此,在并发场景中,更推荐 UUID 做主键或业务自定义生成主键。
我们可以直接在 MySQ L使用 UUID() 函数来获取 UUID 的值。
MySQL> select UUID(); +--------------------------------------+ | UUID() | +--------------------------------------+ | 23ebaa88-ce89-11eb-b431-0242ac110002 | +--------------------------------------+ 1 row in set (0.00 sec)需要特别注意的是,在存储时间时,UUID 是根据时间位逆序存储, 也就是低时间低位存放在最前面,高时间位在最后,即 UUID 的前 4 个字节会随着时间的变化而不断“随机”变化,并非单调递增。而非随机值在插入时会产生离散 IO,从而产生性能瓶颈。这也是 UUID 对比自增值最大的弊端。
为了解决这个问题,MySQL 8.0 推出了函数 UUID_TO_BIN,它可以把 UUID 字符串:
通过参数将时间高位放在最前,解决了 UUID 插入时乱序问题;
去掉了无用的字符串"-",精简存储空间;
将字符串其转换为二进制值存储,空间最终从之前的 36 个字节缩短为了 16 字节。
下面我们将之前的 UUID 字符串 23ebaa88-ce89-11eb-b431-0242ac110002 通过函数 UUID_TO_BIN 进行转换,得到二进制值如下所示:
MySQL> SELECT UUID_TO_BIN('23ebaa88-ce89-11eb-b431-0242ac110002',TRUE) as UUID_BIN; +------------------------------------+ | UUID_BIN | +------------------------------------+ | 0x11EBCE8923EBAA88B4310242AC110002 | +------------------------------------+ 1 row in set (0.01 sec)除此之外,MySQL 8.0 也提供了函数 BIN_TO_UUID,支持将二进制值反转为 UUID 字符串。
虽然 MySQL 8.0 版本之前没有函数 UUID_TO_BIN/BIN_TO_UUID,还是可以通过自定义函数的方式解决。应用层的话可以根据自己的编程语言编写相应的函数。
当然,很多同学也担心 UUID 的性能和存储占用的空间问题,这里我也做了相关的插入性能测试,结果如下表所示:
可以看到,MySQL 8.0 提供的排序 UUID 性能最好,甚至比自增 ID 还要好。此外,由于 UUID_TO_BIN 转换为的结果是16 字节,仅比自增 ID 增加 8 个字节,最后存储占用的空间也仅比自增大了 3G。
而且由于 UUID 能保证全局唯一,因此使用 UUID 的收益远远大于自增 ID。可能你已经习惯了用自增做主键,但是在并发场景下,更推荐 UUID 这样的全局唯一值做主键。
当然了,UUID虽好,但是在分布式场景下,主键还需要加入一些额外的信息,这样才能保证后续二级索引的查询效率,推荐根据业务自定义生成主键。但是在并发量和数据量没那么大的情况下,还是推荐使用自增 UUID 的。大家更不要以为 UUID 不能当主键了。
金融字段的设计