zoukankan      html  css  js  c++  java
  • 使用mysql设计一个全局订单生产计数器

    2018年8月10日08:53:50

    一般生产订单号的方式

    1,使用时期+随机数1+随机数2

    缺点,有可能在并发的时候会出现重复,解决办法就是加唯一索引,在插入数据的做查询是否已经被使用

    2,使用时间+统计当前订单数,按订单数+1,很多系统使用这种

    缺点,如果有需要删除数据,当然脑残的需求,但是还是会有,再次下单就会出现订单重复,这种非常不好改动

    个人建议:时间按照年+月,不要用日  201810+000000000011这种

    参考1:金蝶k3,会有一个全局使用的ID,通过触发器增长

    参考2:经验之谈,全局计数器解决2个根本问题,订单号不重复,使用一次请求一次,返回最近的计算结果,2不会因为删除订单出现订单号重复

    数据库

    --计数器表
    CREATE TABLE `counter` (
     `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
     `name` varchar(100) DEFAULT NULL COMMENT '名称',
     `value` bigint(20) unsigned DEFAULT '1' COMMENT '存储的值',
     `create_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间',
     `update_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',
     `tag` varchar(50) DEFAULT NULL COMMENT '标签',
     `type` tinyint(1) NOT NULL DEFAULT '10' COMMENT '类型10采购订单20销售订单30自营物流号',
     PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='计数器表';

    注意我存储的 value是bigint 20,足够大

     //计数器
        public static function counter($type = null) {
            if (empty($type)) {
                throw new Exception('tag标签不能为空');
            }
            $now = time();
            $time = date('Ym', $now);
            $time1 = date('Ym', $now);
            $tag = self::counter_array($type);
            $name = $time . '_' . $tag;
    
            DB::beginTransaction();
            try {
                $Counter = Counter::where('name', $name)->lockForUpdate()->first(['value', 'id']);
                if (empty($Counter)) {
                    //没有就插入
                    $Counter = new Counter;
                    $Counter->name = $name;
                    $Counter->type = $type;
                    $Counter->tag = $tag;
                    $Counter->save();
                    $Counter = Counter::where('name', $name)->first(['value', 'id']);
                }
    
                $Counter = $Counter->toArray();
                //加锁 防止生成的订单号出错,没有在name上加索引
                Counter::where('id', $Counter['id'])->lockForUpdate()->first();
    
    
                if ($type == 40) {
                    $new_count = (float) $Counter['value'] + 1;
                    Counter::where('name', $name)->update(['value' => $new_count]);
                    $string = $time1 . str_pad($new_count, 6, '0', STR_PAD_LEFT);
                } else {
                    $new_count = (float) $Counter['value'] + 3;
                    Counter::where('name', $name)->update(['value' => $new_count]);
                    $string = $time . str_pad($new_count, 12, '0', STR_PAD_LEFT);
                }
    
    
                DB::commit();
                return $string;
            } catch (Exception $e) {
                DB::rollBack();
                throw new Exception($e->getMessage());
            }
        }
    
        //订单计数器映射数组
        public static function counter_array($type = null) {
            //采购订单号
            $array['10'] = 'sales_order_number';
            //销售订单号
            $array['20'] = 'purchase_order_number';
            //自营物流订单号
            $array['30'] = 'logistics_order_number';
            //入库订单号
            $array['40'] = 'inbound_order_number';
            //出库订单号
            $array['50'] = 'outbound_order_number';
            if (empty($type)) {
                return $array;
            }
            return $array[$type];
        }
    Counter::where('id', $Counter['id'])->lockForUpdate()->first();必须在事务里面使用

    解释一下为什么不适用 全局订单不是+1而是+3,防止有特殊订单需要插入,同时不让计数器的数字增长太快 

    还有必须加锁,虽然有事务,但是还是要加锁,订单长度自己选择填充长度,我定义的是20个长度,足够了14位长度的除以3的长度,足够了

    有人会担心新能,我只能说你想多了,这点压力都扛不住,不可能,见过随时写入mysql,随时读取的操作吗?这么糙的逻辑代码mysql都能满足

     订单号给个建议就是 采用 

    $time = date('Ym', $now);不要使用年月日,因为有些行业特殊性,最好采用年月这种模式,统计时间去使用创建时间做数据统计
  • 相关阅读:
    支付宝支付内容 尚未完成
    mvc 高并发解决方案之一---存储过程
    微信第三方平台开头篇--MVC代码(第三方获取ticket和公众号授权)
    卡券类字段
    Jquery 方法学习
    c# 对象反射赋值未知属性需类型转换
    C# enum 枚举 反射
    MVC c# 调用sql的存储过程
    Javascript装饰器原理
    关于阿里云的产品应用思考
  • 原文地址:https://www.cnblogs.com/zx-admin/p/9452952.html
Copyright © 2011-2022 走看看