PHP生成订单号算法

PHP生成订单号算法

本文来自:https://www.douban.com/note/357854924/?type=like

首先,订单号不适合用自增字段,因为会暴露一个网站的业务量(参见“德国坦克问题” http://en.wikipedia.org/wiki/German_tank_problem)。另外,通常在订单在写入数据库之前,业务就需要用到订单号了。

网上多数用microtime生成的时间戳生成唯一订单序列号,事实上高并发情况下有一定的重复几率,就连uniqid($more_entropy参数为false)函数生成的序列号都可能有重复的可能(真坑爹),而$more_entropy设置为true的话返回的序列号又太长了。

不依赖外部流水号,完全靠时间戳和随机数生成订单号无法避免冲突,所以必须引入外部的流水号生成机制。或使用数据库,或使用APC之类的缓存。用APC之类的缓存存在一个问题,就是无法持久保持数据,服务器重启或者PHP宿主进程重启都会清空流水号计数器,所以可以采取缓存+数据库结合的模式——如果缓存中有流水号计数器数据则读取并累加计数,如果缓存中没有流水号计数器从数据库中还原计数器。计数器可以每隔一段时间重置一次。

既然引入了自增流水号计数器,又会导致文章开头的“德国坦克问题”,所以需要用skip32算法把流水号加密(https://github.com/nlenepveu/Skip32)。

基于前文所述,得到如下的订单号生成规则:

订单号 = 日期前缀 + 加密流水号

// Skip32 算法加密密钥
const ENCRYPTED_KEY = 'xxxxxxxxxxxx';

// 使用 Wincache 作为流水号计数器缓存
function getOrderSerialNumber() {
        $timestamp = time();
        $datePrefix = date('ymd', $timestamp);
        // 如果流水号计数器数据不在缓存中,则尝试从数据库中恢复
        if (false === ($value = wincache_ucache_inc($datePrefix))) {
                wincache_lock($datePrefix);
                // 从数据库中获取今日的订单数
                $counter = getNumberOfOrdersTodayFromDatabase($timestamp);
                $value = $counter + 1;
                if (!wincache_ucache_add($datePrefix, $value, 60*60*24)) {
                        $value = wincache_ucache_inc($datePrefix);
                }
                wincache_unlock($datePrefix);
        }
        return $datePrefix.str_pad(Skip32::encrypt($datePrefix.ENCRYPTED_KEY, $value), 10, '0', STR_PAD_LEFT);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值