核心问题
一个项目中,我把 id 划分为两类,一种是要求“可读性”,另一种是单纯用来表示 id,不要求“可读性”。
比如:
- 要求“可读性”:用户的 id,不能用一串 32 位或者 64 位长的数字,这样人眼看起来体验很差。常见的 id 比如 qq 号,使用一串 10 位长的数字。像这种我称其为需求“可读性”强的 id
- 不要求“可读性”:系统内部的 id,比如某个文件的 id 号,某个数据统计行的 id。这些都是人不需要肉眼看的 id,格式和长度可以不考虑人眼的可读性。
请问这两种类别的 id,各自选择什么数据类型,用什么方法生成比较好呢?
数据规模
目前项目是一个微信小程序的后端,预计数据量一张表最多 10w 行。
当然数据量规模不大的话,主键选择什么数据类型、生成方法都没关系。虽然我的项目预计数据量不多,但我想尽量规范一些,用更好的办法。
目前项目中使用的方法
目前在我的项目中,关系型数据库为 mysql,引擎 Innodb。使用考虑到 Innodb 的 B+ 树结构,主键采用 auto_increment 的方式,可以更好地一页一页排下去。详细请见:为什么 InnoDB 表最好要有自增列做主键 ?。但是我不想使用自增主键作为业务主键。于是采用:“代理主键”和“自然主键”:
自然主键:就是充当主键的字段本身具有一定的含义,是构成记录的组成部分,比如学生的学号,除了充当主键之外,同时也是学生记录的重要组成部分。
代理主键:就是充当主键的字段本身不具有业务意义,只具有主键作用,比如自动增长的 ID。
建表所用 sql:
@Slf4j
public class UniqueKeyUtil {
/**
* 生成不重复的11位字符串
* 10进制转16进制
* 高并发情况下可能会出现重名,所以需要使用synchronized关键词来修饰
* @return
*/
public static synchronized String getUniqueKey() {
long randomInteger = System.currentTimeMillis();
// 睡眠1ms,避免出现重复的key
try {
Thread.sleep(1);
} catch (InterruptedException e) {
log.error("[UniqueKeyUtil]Thread.sleep() error, errMsg = {}", e.getMessage());
}
String hexString = Long.toHexString(randomInteger);
return hexString;
}
}
在开发项目的过程中,我突然意识到一个问题:自然主键使用 varchar 类型,检索速度不如 int, bigint 这样的类型。
现在有些懊悔,想要把自然主键的 varchar 类型改为 int 或者 bigint 类型。但更改主键是一件高风险、复杂度较高的事情。例如:
- 更改自然主键的数据类型后,现有数据的 id 转换到新数据类型的可行性存疑。
- 项目中很多处用到了 id,贸然更改数据类型,遇到坑的概率可能比较大。
当前项目中的自然主键,全部使用的 varchar(32),我感到惶恐不安,感觉自己拖累了项目的运行速度。我不知道我是否应该变更自然主键的数据类型。