请教大家利用 mysql 存储过程生成规则唯一编号的方式可行吗?

2021-11-24 09:00:27 +08:00
 gibber
场景:
1. 数据库是 mysql
2. 有一张订单表在插入订单数据的时候需要生成唯一的订单编号,编号规则是当前月订单数据量的流水号
3. 订单表数据插入的途径有后台程序和存储过程两种

如何做到并发生成这个唯一编号呢?
2886 次点击
所在节点    程序员
21 条回复
banlifeather4
2021-11-24 09:14:15 +08:00
经验来看的话, 不管是单体服务还是分布式服务, 建议 ID 都设计成全局唯一的,增加不了多少成本;

但是不这么搞的话,如果一旦做分表分库等,涉及的数据清洗的工作量应该够你加班 2 个月的( 1 个月,2.am 下班)
allenzhangSB
2021-11-24 09:17:03 +08:00
你这个需求, 最简单实现可以用 redis 的 incr
bglucas
2021-11-24 09:18:33 +08:00
看起来像是分布式唯一 ID 生成问题?可以参考下 snowflake 和给予 mysql 的号段模式。
onhao
2021-11-24 09:27:06 +08:00
貌似触发器可以?但是需要警惕用了触发器后 last_insert_id() 获取不到最近插入的 ID
https://wuhao.pw/archives/241/
dk7952638
2021-11-24 09:33:47 +08:00
借楼一问,现在全局 ID 的开源方案有没有比较靠谱的,最好是可以自增的那种
jorneyr
2021-11-24 09:54:17 +08:00
全局唯一自增:
1. ID 生成服务 (只用一个服务,不要用集群,性能要高,分段获取,保证全局唯一递增,持久化到 DB)
2. 每个服务需要 ID 时使用 RPC 去 ID 生成服务实时拿取,每次只拿一个 (RPC 保证效率)

snowflake 等只能是趋势递增。
gibber
2021-11-24 10:03:43 +08:00
@allenzhangSB mysql 存储过程也可以调用 redis 吗?
xsm1890
2021-11-24 10:34:33 +08:00
可行。但如果只是用 mysql 的存储过程生成的话,这个点可能会成为系统的性能瓶颈。
clf
2021-11-24 10:34:41 +08:00
在 github 上好像有 snowflake 的 mysql 存储过程实现?(没写过 mysql 的存储过程)可以考虑使用 snowflake 生成的 ID 作为主键。

@dk7952638 #5 目前用的比较多的就是上面说的雪花算法,id bigint ,唯一需要注意的是后端序列化为 json 的时候需要把超过前端 number 范围的数值转为 string ,不然会精度丢失。
Bigglesworth
2021-11-24 10:40:51 +08:00
如果没有分库分表,业务也不大的话,虽然说全局 ID 啥的更好,但是还是按需来嘛,可以写个类似 sequence 的存储过程,后台程序和存储过程都可以调一下生成 ID ,生成规则你可以按月啥的自增。
yidinghe
2021-11-24 11:19:01 +08:00
参考一下这个项目:
https://github.com/yiding-he/mysql-sequence-generator
在数据库中建一个表,各个服务都可以并发的用这张表生成全局 ID ,原理也是利用 last_insert_id()
jinzhongyuan
2021-11-24 14:13:47 +08:00
以前在小公司就是存储过程生成订单号
allenzhangSB
2021-11-24 14:35:13 +08:00
@gibber 没必要一定要在 MySQL 侧做吧, 代码中插入前最后一步调用下 redis 生成个序号, 插入前做好校验, 避免插入失败就可以了
uSy62nMkdH
2021-11-24 14:49:10 +08:00
你这种需求直接建一个仅有 ID 的表就行了吧,每次需要的时候 insert 并返回 ID 即可。如果有分库分表,在单调自增序列前加一个数据标识。
考虑引入中间件的话,雪花、美团 leaf ,或者 zk+redis 自建。
fengpan567
2021-11-24 15:14:21 +08:00
snowflake 不合适吗?用存储过程,分库分表的时候不就扯淡了
gibber
2021-11-24 16:25:26 +08:00
@allenzhangSB 是的, 我知道利用 redis 很好做到唯一编号, 主要这个业务还有另外一个需求就是利用存储过程插入数据,所以要考虑到数据库端和程序同时并发
gibber
2021-11-24 16:27:46 +08:00
@uSy62nMkdH 流水号是每月重置,你说的这种方法可以实现吗?
gavindexu
2021-11-24 20:29:18 +08:00
用触发器 在插入前执行一个?
5wunian
2021-11-24 21:17:17 +08:00
避免避免,之前的项目里是在创建的时候单独请求一个唯一 ID,创建的时候带上。虽然浪费了部分未提交的 ID ,但是避免了并发限制。
crclz
2021-11-25 00:39:54 +08:00
仅仅考虑楼主提出的需求,提出以下解决方案:

表 X(年月 int, 流水号 int)。例如(202111, 1)

def GenerateOrderId() -> str:
年月 = 获取当前年月()
num=( update 表 X set 流水号=流水号+1 returning 流水号)
return 格式化(年月,num )

如果数据库并发不够,可以流水号=流水号+10 之类的,然后服务端将生成的 id 全局缓存起来。

这样做不会错的,我司就是这样干的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/817553

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX