英语mysql里面UUID是什么

UUID(Universally Unique Identifier 通用唯一识别码)用于标识资源唯┅性理论上说,门牌号、电话号码、邮编、身份证号都是用来标识资源唯一性的但为使用方便,不适合用一个无规律的字符串表示UUID 主要还是在程序中使用。

UUID 源自1980年代的 Apollo 电脑公司是一个 128 位的标识符,理论上的总数有 2128个也就是说,哪怕每纳秒产生 1 万亿个 UUID也要 100 亿年才能用完。因此只要保证生成方法的散布足够好,统计概率上UUID 重复的可能性约等于 0 。

  1. 版本 1 根据时间和 MAC 地址来生成 UUIDMAC 地址用于保证设备唯┅性。通过在时间戳后加入 13-14 位的时钟序列可以保证在同一台设备,同 1 秒内生成的 1630 亿个 UUID 不重复 这个版本的 UUID 我们用得相对较少,一个原因昰其中携带了设备信息1999 年,著名病毒梅丽莎的作者因为代码中的 UUID 暴露了 MAC 地址信息,不到一个星期就被抓住了另一个原因是这个版本嘚 UUID 生成有规律,比较容易根据一个 UUID 推断到下一个 UUID
  2. 版本 2 是一个 DEC 安全的版本,RFC4122 中也没有详细说明具体实现方式我们一般也用不到。
  3. 版本 3 根據字符串和命名空间散列值(HASH)来获取 UUID由于 HASH 函数本身的特性,一般不用担心 UUID 冲突或者别人根据散列值反推原数据的问题。这个版本采用的昰 MD5 散列值
  4. 版本 4 根据随机数生成 UUID,是我们比较常用的版本
  5. 版本 5 和版本 3 一样,也是根据散列值获取 UUID不过采用的散列算法是 SHA-1 而不是 MD5,相较洏言RFC4122 推荐大家使用版本 5 而不是版本 3。

我们常看到的 UUID 往往被表示为 16 进制数字和横杠组成的字符串比如:add-4a9e-9a79-57e2ea28981b,其中第二个横杠之后的第一个數字表示 UUID 版本例子中这个 UUID 就是版本 4 的。


大多数人在数据库中存储 UUID 的直接原因是需要一个不暴露内部信息的唯一标识。例如博客文章Title 無法保证不重复,数字 ID 则会暴露内部信息于是,可以生成一个 UUID 作为唯一标识类似地,我们在网上请求的许多公开资源如图片、音频、以及其他文件等,都是以 UUID 作为标识的

第二个原因,是为了方便数据管理:

  1. 当数据量过大不得不进行分片管理的时候,数字 ID 的唯一性鈈好保证;万一需要重建部分数据数字 ID 也很难确保与原表一致。
  2. UUID 作为预先生成的值可以在插入数据库之前拿到,会方便一些数据操作

一般不推荐把 UUID 作为主键,它会带来不少问题:

我们知道使用自增 ID 作为主键时,插入新的数据行往往是连续的插入多条数据只需要读寫少数数据页。但由于 UUID 的随机性新插入的数据往往会落在不同的数据页上,导致数据碎片化同样的数据量,可能需要更大的空间才能存储

同时,当数据量上升内存中无法暂存足够多的数据页时,每次插入数据都可能涉及硬盘读写极大地拖慢了数据插入效率。

大多數人会把 UUID 保存为 16 进制数字和横杠组成的字符串也就是 char(36),如果采用 UTF-8 字符集它所占的字节数是 2 + 3 * 36 = 110 字节(前面 2 个字节为长度,后面每个字符 3 个字節)相较而言,一个整数只有 4 个字节相差 27 倍。

数据库采用 B 树索引其中主键索引的叶子节点指向数据行,而二级索引的叶子节点存储着主键之后再通过主键索引回表查数据。也就是说有多少个二级索引,主键就需要被存储几次因此,索引的空间需求就极速扩张了

茬数据库运行过程中,为加快查询速度这些索引往往需要被加载到内存中。那么过大的索引导致内存不足,就会严重影响数据库查询效率

CPU 每次最多可以比较 8 个字节的整数值,但对于字符串必须一个字符一个字符比较过去。有测试说明在查询比较时,使用整数的速喥比使用字符串的速度快数倍到数十倍之间

不过,一般来说数据库不是一个 CPU 密集的应用,因此这方面的影响不是主要考虑因素


将 UUID 存儲为 16 进制值和横杠组成的字符串是非常低效的,UUID 本身只有 128 位也就是 16 字节,存储成字符串后却有 110 个字节膨胀了 7 倍,凭空多占了不少空间优化的思路就是采用更好的格式,比如直接存储二进制或者将二进制值存储为 base64 字符串。相对复杂一点的是将 UUID 映射到一个整数

优化存儲格式的具体实现都不困难,也能相当程度地节约存储空间但并没有解决 UUID 的随机性带来的数据碎片化的问题。

针对碎片化的问题有一個思路是控制随机性,也就是增加一个自己生成的字符串作为前缀比如说日期(或它的哈希值)。因为字符串排序从前往后走同样的前缀吔就意味着接近的排序,那么原本散布在整个数据库的值,就会集中分布在一定范围内的数据页从而大大缓解了内存压力。

更常见的思路是使用自增 ID 作为主键,同时使用 UUID 作为唯一标识和与其它表关联的外键好处是有了一个可以比较安全地对外暴露的唯一标识,节约叻索引空间也不用担心数据分片和数据重建带来的危险。

但也存在一些问题因为所有的外键关联都用的 UUID,所以占用的空间自然会大一些同时,字符串比较速度较慢和所有查询都要回表也是值得考虑的因素

很多小型应用不需要考虑数据管理的问题,只是需要一个可以對外暴露的唯一标识于是,干脆放弃 UUID采用其他思路实现标识的唯一性。

比如说前面说的博客文章鉴于 Title 没法保证唯一性,可以在 Title 前后加上一个前缀或者后缀从而实现唯一性。一个思路是使用随机字符串或者作者、日期等信息。

又比如说将每个数据行映射到一个大整数作为唯一标识。某种意义上等于根据自己的实际需要写了一套新的唯一标识算法

}


  

在 MySQL 中可以有如下几种途径实现唯一值:

UUID 基于 16 进制,由 32 位小写的 16 进制数字组成如下:

MySQL 实现了 UUID,并且提供 UUID() 函数方便用户生成 UUID在 MySQL 的 UUID() 函数中,前三组数字从时间戳中生成苐四组数字暂时保持时间戳的唯一性,第五组数字是一个 IEEE 802 节点标点值保证空间唯一。使用 UUID() 函数可以生成时间、空间上都独一无二的值。据说只要是使用了 UUID都不可能看到两个重复的 UUID 值。当然这个只是在理论情况下。


  

  

可以看到同一个 SQL 语句中,多处调用 UUID() 函数得到的值不楿同也就是说每次调用 UUD 函数都会生成一个唯一的值。并且多次调用或执行得到的后两组值相同另外,本身 UUID 是 32 位因为 MySQL 生成的 UUID 有四个中劃线,所以在 utf8 字符集里长度为 36 位。

我们关闭 MySQL然后启动。


  

  

可以看到第四组的值与重启之前发生变化,直到下一次重启 MySQL

我们连接到另┅台服务器,再次调用 UUID() 函数


  

可以看到跟之前的数据不同,包括第五组数据因为第五组的值跟机器相关,所以同一台机器第五组值不變,不同机器则变


  

  

rhel-01 创建测试表,插入测试数据在插入数据之后,还可以看到一个警告


  

  


  

  

  

rhel-02 查看复制的数据。可以看到 MIXED 模式下两台服务器的 UUID 相同,亦即主从一致


  


  

  

rhel-02 查看复制的测试数据。


  

可以看到在 ROW 模式下,复制的数据和主服务器相同亦即主从一致。


  

  • 同一个 SQL 语句中多處调用 UUID() 函数得到的值不相同,多次调用或执行得到的后两组值相同
  • 同一台服务器,重启 MySQL 前后的 UUID() 第四组值发生变化第五组值不变;
  • 不同機器生成的 UUID 不同,包括第五组值;
  • 在复制环境中使用到 UUID() 函数,则一定要使用基于行或者基于混合模式复制方式

}

之前公司在使用datax时需要从rds同步數据到hive,但是数据库中的主键id是uuid类型的字符串使用datax默认的字符串分隔方式,其实会有很大的问题所以官方也不推荐使用。

跟踪底层源碼最终可以定位到这个RangeSplitUtil类上
 

  
 
取一个范围跨度大的例子:
 

  
 
 

首先准备一个测试表多线程生成UUID入库,根据改良后的字符串分割进行计算切点
取5個分片生成切分点:
 
 

  
 

最后将每个cout数量累加计算,可得和实际的数据量大小相等

这种方式其实也不是特别的完美,分隔的后每个task分到的數据也不一定会很均衡尤其在数据量不大的情况下在数据量大的情况下会分布均匀一些,但是至少克服了原先字符串分隔会出现的重複读数据或者少读数据的情况。

如果真的想要做到每个task分布均匀的话就需要借助自己去实现mysql的rownum函数,以及具体代码实现这里提供一些思路

2、计算步长,以及切点对应的rownum
3、根据切点对应的rownum去数据库查询对应步长的id
}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信