The UUID in MySQL8

问题

记得刚接手项目时,看了下项目里的库表结构,发现表主键都是varchar(36),就问研发,为啥搞个这么长的主键?研发说这个主键是通过java 程序产生的UUID,考虑到以后数据分片不会产生重复。我顿时无语,考虑的还是挺周到,没有意识到这么长字段做主键的问题:

  • 首先这个主键很长,而主键会自动添加在每一二级索引的后面,这样会导致数据冗余严重,特别是二级索引建多了的情况下,有时会导致索引的数据比表的数据还大!
  • 其次,这样产生UUID是无序的,导致插入性能很差。因为当插入一个数据到B+tree时,系统将这个数据插入到一个合适的位置,这会导致多次IO,而如果数据本身是自增的,那么系统只须将它添加的B+Tree的末端即可,效率要高的多。

因此我还是建议使用整型+auto_increment, 虽然MySQLauto_increment有个众所周知的、历史悠久的bug(这个bug终于在MySQL8的到修复,MySQL8中auto_increment将会被写入redo log 中啦),但大多数情况下还是可靠的,并且MySQL官方也建议使用auto_incremnet做主键。但它也有问题,就是无法适应数据分片的扩容(分库分表),除非你之前规划的非常好。

MySQL8中的解决方案

如今MySQL8 中对UUID提供了增强性支持,不仅使其长度大大缩小,而且解决了顺序的问题!(我之所以称之为增强性支持,是因为他们提供了两个内置函数做了转换,感觉这种方式有点儿low,这样的函数我们也可以写啊)

大家都知道,UUID是由32位16进制字符串组成(不算分隔符'-')如:

62ab1547-710f-11e8-9a58-5254007205d6 

如果直接保存,则需要32个字符,utf8编码下占用96个字节,对于主键来说还是太长。幸运的是UUID中的每个字符都是16进制字符,两个16进制字符占用一个字节,这样可以轻松将UUID转换为binary(16),占用16个字节,所需空间大大减少,而且二进制字符串检索对比效率很高。

但还有一个至关重要的问题是UUID的组成中将timestamp 部分的低位时间段(如毫秒)放在了前面,高位时间段(如年月日)放在了后面,这会导致前面的字符变化很快,后面的变化很慢,从而使产生的UUID不能顺序自增。这会导致索引插入效率大大降低。

为解决这一问题,mysql8提供了两个函数:UID_TO_BIN(arg1)/ BIN_TO_UUID(arg1,arg2)

  • UID_TO_BIN(arg1) 将UUID转化为16位二进制字符串,如果参数arg1为true则将UUID中的timestamp部分中的time-low(第一段字符)和time-high(第三段)调换,这样产生的UUID是顺序递增。
  • BIN_TO_UUID(arg1,arg2)将16位进制字符串转化为可读的UUID,arg1为16位二进制字符串,如果arg2省略或为false,即将二进制字符串原位转换;如果arg2为true,则将原来调换的time-low和time-high再调换回去,返回原本的uuid.

测试

创建一张表如下:

mysql8[test]>create table t (id varbinary(16) primary key,create_time timestamp default current_timestamp());
Query OK, 0 rows affected (0.34 sec)

插入几条数据,注意使用了函数uuid_to_bin:

mysql8[test]>insert into t (id)values(uuid_to_bin(uuid(),true));
Query OK, 1 row affected (0.08 sec)

mysql8[test]>insert into t (id)values(uuid_to_bin(uuid(),true));
Query OK, 1 row affected (0.08 sec)

mysql8[test]>insert into t (id)values(uuid_to_bin(uuid(),true));
Query OK, 1 row affected (0.07 sec)

查看结果:

mysql8[test]>select bin_to_uuid(id) id1,bin_to_uuid(id,true) id2, create_time from t;
+--------------------------------------+--------------------------------------+---------------------+
| id1                                  | id2                                  | create_time         |
+--------------------------------------+--------------------------------------+---------------------+
| 11e87113-f079-024e-8405-5254004332fa | f079024e-7113-11e8-8405-5254004332fa | 2018-06-16 11:18:28 |
| 11e87113-f826-4134-8405-5254004332fa | f8264134-7113-11e8-8405-5254004332fa | 2018-06-16 11:18:41 |
| 11e87113-f88c-c8a6-8405-5254004332fa | f88cc8a6-7113-11e8-8405-5254004332fa | 2018-06-16 11:18:42 |
+--------------------------------------+--------------------------------------+---------------------+
3 rows in set (0.00 sec)

注意字段id1使用了函数bin_to_uuid(id), 而id2使用了bin_to_uuid(id,true),注意他们在结果集中的区别。

如果需要按主键查询,还是需要使用对应的uuid函数:

mysql8[test]>select * from t where id=uuid_to_bin('f079024e-7113-11e8-8405-5254004332fa',true);
+------------------+---------------------+
| id               | create_time         |
+------------------+---------------------+
| 篓󷁎RT C2򞞠 | 2018-06-16 11:18:28 |
+------------------+---------------------+
1 row in set (0.00 sec)

总结

MySQL8通过提供两个内置函数解决了传统UUID作为主键的缺陷,也使UUID成为了设计主键的首选,特别是在数据分片的架构中,其优势十分突出。我当时一看到这个,还很兴奋,但UUID终究还不是一种数据类型,这多少给应用上增加了些麻烦。相信在MySQL的未来版本会出现一种数据类型叫UUID , 总不能一直弄两个函数糊弄人吧?!(其实目前官方也提到了实现一种新的数据类型,但增加一种数据类型属于重量级实现,代码变动太大,所以当前选择了增加两个函数)

参考资料:

https://www.ietf.org/rfc/rfc4122.txt

https://dev.mysql.com/worklog/task/?id=8920
阅读更多

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容

  • MYSQL 基础知识 1 MySQL数据库概要 2 简单MySQL环境 3 数据的存储和获取 4 MySQL基本操...
    Kingtester阅读 7,811评论 5 116
  • 观其大纲 page 01 基础知识 1 MySQL数据库概要 2 简单MySQL环境 3 数据的存储和获取 4 M...
    周少言阅读 3,157评论 0 33
  • 1.创建文件夹 !/bin/sh mkdir -m 777 "%%1" 2.创建文件 !/bin/sh touch...
    BigJeffWang阅读 10,051评论 3 53
  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,148评论 0 13
  • 1.听着歌看着炎热夏季的窗外,听着虫儿叫我失眠,一个人听着王力宏经典歌曲,默默数着自己的伤口,但是谁会理解呢?...
    昂贵的青春阅读 202评论 0 0