id 列
首先说 id 列一般作为主键,没有什么业务含义,设计的目的是为了查询和索引方便,如果用单表的情况下一般会设置为自增,有序了以后,无论数据库还是业务检索起来效率都非常高。
在分布式场景中,单表已经不能满足我们的需求了,所以用自增 id 的方案也就不合适了。当比如我们进行分表设计时,主键列到底如何生成就成了一个问题,流行的方法是利用像 snowflake 这样的算法计算出一个趋势有序的值作为 id。(当然还有其他多种方法)这样就满足了扩展性和一定程度上解决了检索性能的问题。
订单号
订单号的生成一般有相应的规则,而这些规则是各自产品根据自己情况自行设计的结果。
举几个例子
京东的:
- 京东 2013 年的订单号 :479000238
- 京东 2014 年的订单号 :7783813454
- 京东 2018 年的订单号 :81467423041
- 京东 2020 年的订单号 :132971864529
淘宝的:
- 淘宝 2012 年的订单号 :230447522918072
- 淘宝 2016 年的订单号 :2131062693288072
- 淘宝 2019 年的订单号 :329062467847108072
- 淘宝 2021 年的订单号 :2075316735066108072
可以看到京东和淘宝的订单号都是在递增的,而且淘宝的看起来是有规律的,这个查了一下有说从 2017 年淘宝升级了订单,原先是用订单号后 4 位识别用户,2017 年以后改为用后 6 位了。
“那么淘宝订单编号后 6 位用户 id 后 6 位的目的是什么?翻遍了百度、知乎,没有找到答案。我是偶然间翻到一份淘宝技术演变 PPT,看到订单表分库的逻辑时才恍然大悟。一般的平台型电商,订单量大,为保证查询检索速度,都会采用分库的形式,将巨量的订单信息分库存储,一般情况下订单系统同时维护了一个订单号和 userid 的关联关系,先根据订单号查到 userid,再根据 userid 确定分表进而查询得到内容。而淘宝在订单号上下功夫,通过订单号后 6 位直接锁定库表,大大提升高并发下的系统查询性能。从这个策略我们也可以看到淘宝用户订单库是按照用户 id 后 6 位存储的,例如:XXXX452154 格式的用户订单都是储存在一个分库中。”
我通过查询发现自己的淘宝 id 后 6 位是 107280,而我的订单号后 6 位是 108072,还是有规律的。
找到了一个淘宝 2012 年的 PPT,可能 12 年之前的设计又不一样。
订单号生成规则
不重复显然,这种具有唯一标识的号码是不能重复的,安全不能被人为的猜测或推测出来,易识别易识别就代表位数不要太长,位数控制好了就容易查询检索,占用空间小。
几种常见的规则
- 年月日+自增长数字的订单号(比如:2012040110235662)
- 字母+数字字符串式,字母有包含特别意义,C02356652
- Snowflake 算法生成
- 年月日+机构编码+后四位随机
订单号可不可以作主键 id 列
前提是你根据你自己设计或选择的订单号规则生成了一个可行的订单号,这种情况下,如果你的订单号包含字母等字符串那么是不合适作主键的。虽然唯一,但索引查询性能较差。不过可以做唯一索引。
如果订单号是纯数字呢?
也不建议,数字当然有它的优点,性能好(位数长了也不好),但是订单号毕竟是跟业务相关的,与业务相关的列作主键本身可能会有问题。
假如未来的一天我们要改变业务含义,也许想把数字改字母,加几位或少几位,那么就必须修改主键了,核心数据表的主键修改,可是牵一发而动全身的,会造成极大的维护开销。
虽然直观上感觉“多了一列”,但并不是无用的,对未来的扩展性会有很大帮助。综上,回答我们标题的问题:“订单号和 id 列可不可以是同一列?” 或者说订单号可不可以作主键,我认为可以,但不适合,所以建议不要用订单号做主键。用与业务无关的 id 列作主键比较合适。
主键列怎么设置?
如果是单表,当然是用自增 id, 如果考虑到分库分表,可以采用像 snowflake 这样的生成方案。