V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
gibber
V2EX  ›  程序员

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

  •  
  •   gibber · 14 天前 · 1642 次点击
    场景:
    1. 数据库是 mysql
    2. 有一张订单表在插入订单数据的时候需要生成唯一的订单编号,编号规则是当前月订单数据量的流水号
    3. 订单表数据插入的途径有后台程序和存储过程两种

    如何做到并发生成这个唯一编号呢?
    第 1 条附言  ·  14 天前
    补充一下:
    编号规则是当前月订单数据量的流水号
    例如:
    本月第一条数据编号为 DD202111000001
    本月第二条数据编号为 DD202111000002
    ...
    下月第一条数据编号为 DD202112000001
    下月第二条数据编号为 DD202112000002
    ...
    21 条回复    2021-11-25 16:18:48 +08:00
    banlifeather4
        1
    banlifeather4  
       14 天前
    经验来看的话, 不管是单体服务还是分布式服务, 建议 ID 都设计成全局唯一的,增加不了多少成本;

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

    snowflake 等只能是趋势递增。
    gibber
        7
    gibber  
    OP
       14 天前
    @allenzhangSB mysql 存储过程也可以调用 redis 吗?
    xsm1890
        8
    xsm1890  
       14 天前
    可行。但如果只是用 mysql 的存储过程生成的话,这个点可能会成为系统的性能瓶颈。
    clf
        9
    clf  
       14 天前
    在 github 上好像有 snowflake 的 mysql 存储过程实现?(没写过 mysql 的存储过程)可以考虑使用 snowflake 生成的 ID 作为主键。

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

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

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

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

    这样做不会错的,我司就是这样干的。
    uSy62nMkdH
        21
    uSy62nMkdH  
       13 天前
    @gibber 为什么不行呢? prefix 是自定义的,suffix 按表里的 ID 递增,两个组装起来就是你要的 ID 了。
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2649 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 13:19 · PVG 21:19 · LAX 05:19 · JFK 08:19
    ♥ Do have faith in what you're doing.