每一小时的订单流水号?

// 找到現在的「時間」
$ymdh = date('Y/m/d H');

// 用現在的時間找出訂單目前是有幾筆
$findOrderSoFar = mysqli_fetch_array($pdo->query(
    "SELECT count(public_order_id) as num FROM `order_record`
    WHERE FROM_UNIXTIME(add_time, '%Y/%m/%d %H') = '{$ymdh}' "
));

// 將 $i 直接加入目前幾筆的數量
$i = $findOrderSoFar['num'];

// 如果找到零比則從一開始
$findOrderSoFar['num'] == 0 ? $i = 1 : $i = $findOrderSoFar['num'];

// 開始迴圈
while ($row = mysqli_fetch_array($sql)) {

    // 設定訂單編號長相
    $_SESSION['public_order_id'] = substr(date('YmdH', time()), -8).str_pad($i, 4, '0' ,STR_PAD_LEFT);
    
    // ...
    $i ++;
}

我目前作法是这样,但是却还是会「重复流水号」
就是不同使用者同时下订时仍然会有这情形发生
我的逻辑已经死了LOL ..
订单编号格式会是:
YYMMDDHH0001
YYMMDDHH0002...
但是到了下一个小时,就会变回YYMMDDHH0001

阅读 4.6k
5 个回答

我贴一个我们生产环境上的生成订单号的代码给楼主看看吧,可以照着这个模式改造下

    /**
     * 生成第三方支付的外部订单号
     */
    public static function generateOutTradeNo($paidScene = Constant::PAID_SCENE_SEMINAR_ORDER){
        $prefix = date('YmdHis');
        $paidSceneMap = self::getPaidSceneMapping();
        $scene = formatArrValue($paidSceneMap,$paidScene,'0001');
        $suffix = generateRandomNum(10);
        return $prefix.$scene.$suffix;
    }
    
    /**
     * 获取支付场景的map,这个是为了区分不同的支付场景时候更新不同的业务字段,为了拓展进行的预留
     */
    public static function getPaidSceneMapping(){
        return [
            Constant::PAID_SCENE_SEMINAR_ORDER => '0001',
            Constant::PAID_SCENE_SHOP_GOODS_ORDER => '0002'
        ];
    }
    
    /**
     * 说明: 随机串算法
     * 输出参数: String
     */
    function generateRandomNum($len=8){
    
        $minArr = [];
        $maxArr = [];
        for($i=0;$i<$len;$i++){
            $min = $i == $len -1 ? 1 : 0;
            array_push($minArr,$min);
            $max = 9;
            array_push($maxArr,$max);
        }
        $minLimit = implode('',$minArr);
        $maxLimit = implode('',$maxArr);
        $num = mt_rand($minLimit,$maxLimit);
        return $num;
    }
    

查询最大订单号和 把新生成的订单号保存到数据库。有三种做法
1 。查询订单号时候, 使用 select for update, 锁住表, 然后,查询最大订单号, 生成新的订单号,保存,提交事务。
优点:简单,符合顺序思维
缺点:性能差
2 。创建单独的 订单号序列表 ,简单点 三个字段 id ,key, val,step 。插入一条记录
insert into t(key,val,step)values('orderNo',1,1);
然后生成新的订单号 update t set val=val+step where key='orderNo';
这个时候,不要提交事务, 在同一个事务里面 select val from where key='orderNo';
查完之后,提交事务,val 就是新的订单号 。
优点:性能好
缺点:不符合顺序思维,需要新建表。
3。使用redis,如楼上几位所说,使用redis的 incr 命令,因为它是原子操作也可以保证订单号唯一性。
优点:速度最快
缺点:需要部署redis服务,程序需要添加 访问redis的库,需要熟悉redis命令

订单编号 设为 唯一索引
所以,只会有 插入数据失败,不会有 重复订单编号

插入数据失败后有各种处理方法,比如重试3次,不行就返回 订单创建失败

建议:
将已经生成的订单号用redis来管理,从内存中读取目前的订单号,读取之后先在内存加1,再处理后续逻辑,可以很大程度长避免冲撞的问题

推荐问题
宣传栏