前言
使用Spring JPA项目进行开发,有关Model的设计,PK如何选择一直是个课题,特别对于集群和微服务的部署架构引入进来之后,一直在找一个平衡的解决方案,最近一个项目中又深入讨论了这个问题,参考国外一个博客https://phauer.com/2016/uuids-hibernate-mysql/的内容,翻译整理如下,供以后参考。
关于数据库PK和UUID选择(MySQL)
目的:考量数据库PK的选择和UUID的使用
使用UUID的优势:
- UUID全局唯一;
- 跨数据库唯一,合并数据,没有主键冲突;
- 分布式复制和存储(分布式数据库)方便简单;
- 可以随时生成UUID,不需要数据库支撑,方便测试和批量数据生成;
- 自动增量ID容易猜测会导致安全问题。
使用UUID的缺点:
- 数据库索引的空间大小增大;单个UUID需要16个字节,普通int只需要4个字节;
- 使用数据库SQL进行特定数据查询时会更加麻烦,下面会详细描述;
- REST接口中使用UUID会增加payload的大小。
使用UUID创建表:
- 不建议使用
VARCHAR(36)
作为UUID数据类型,建议使用BINARY(16)
作为字段类型;会降低空间占用,16个字节,没有字符串中的中线字符,数据库索引也会更快; - 不过,这样会造成SQL查询稍稍复杂些。
1
2
3
4CREATE TABLE product (
`id` BINARY(16) NOT NULL primary key
,`name` varchar(64)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;- 不建议使用
JPA映射:
1
2
3
4
"uuid2") (generator =
"uuid2", strategy = "uuid2") (name =
"BINARY(16)") (columnDefinition =SQL脚本生成UUID
1
SELECT uuid(); /*dbe07414-49d1-11e6-b7a7-0242ac140002*/
插入UUID数据
- uuid()函数返回字符串,包含中线字符,需要使用replace去掉中线字符,然后使用unhex()函数转换十六进制可看字符到UUID数值二进制,最终存入
BINARY(16)
字段中。
1
2
3
4INSERT INTO product VALUES(
unhex(replace(uuid(), '-', ''))
, "car"
);- uuid()函数返回字符串,包含中线字符,需要使用replace去掉中线字符,然后使用unhex()函数转换十六进制可看字符到UUID数值二进制,最终存入
查询UUID
- 使用hex()函数转换UUID到十六进制字符表示。
1
2
3
4
5
6
7
8
9
10
11
12SELECT id, name FROM product;
/* BLOB, 'car' */
SELECT hex(id), name FROM product;
/* 'BFF641BA9F3A4584A1BA53824E7AB3B9', 'car' */
/* 通过特定UUID进行指定Row数据查询 */
SELECT hex(id), name FROM product
WHERE id = unhex('BFF641BA9F3A4584A1BA53824E7AB3B9');
/* or if you have a UUID with dashes: */
SELECT hex(id), name FROM product
WHERE id = unhex(replace("2b08e375-275d-473e-910d-32700e34b61a", '-', ''));小结
- 在工程的角度,使用UUID作为Model的主键,应该是利大于弊;
- 使用
BINARY(16)
存储,性能损耗在可接受范围; - 对于SQL层面操作的不便,个人认为可以忍受(不过不同DB操作语言可能会有差异)。