使用的函数:

microtime()返回当前unix时间戳和微秒数:
如果调用时不带可选参数,本函数以 “msec sec” 的格式返回一个字符串,其中 sec 是自 Unix 纪元(0:00:00 January 1, 1970 GMT)起到现在的秒数,msec 是微秒部分。字符串的两部分都是以秒为单位返回的。

1秒(s)=1000毫秒(ms) 1 毫秒=1000 微秒 1秒(s)=1百万微秒(μs)

  1. echo microtime();//0.64255200 1524652129 返回值类型是string(21),前面是微妙,后面是秒。
  2. 微妙部分,最后两位都是0,一秒等于1百万微妙,所以999999999999就是微妙能表示的最大值,所以微妙部分有效值只有6位。
  3. echo microtime(true); // 1524652129.6426,返回值类型是float,是sec+msec的和,超过四位只保留四位小数
  4. substr(microtime(),2,4);//6425 从第二位开始截取,取4位。截取微妙部分4位
  5. sprintf date('ymdHis'); //180425185946 小写的ymd只取年份的后两位,2018和2118会重复,所以100年后会重复。

长度16位订单号

虽然可以保证重复的几率低,但是也不是一定不重复。所以随机出订单号之后,先在数据库查找是否有这个订单号,如果没有的话直接下单,有的话重新随机订单号,不能重复。

6位年月日+6位时分秒 + 四位微妙

4位微妙有9千9百99个数,一秒内重复的可能性是1万分之一

//日期中小写的y只有两位表示如2018年只显示18
//6位年月日+6位时分秒 + 四位微妙
$order_sn = date('ymdHis').substr(microtime(),2,4);    //180425185910 7287

6位年月日+5位秒+5位微妙

date(‘His’)所表达的结果其实就是000000到235959(最小为:0时0分0秒,最大为23时59分59秒),而且其中很多数字不会被用到,比如236998(没有69分98秒一说);
一天86400秒,如果从一天的0:0:0算起直到23:59:59使用00000-86400就可以完全表示,这样下来我们就完全可以把date(‘His’)换成五位数字。
time()函数就是按秒计数,那咱就取time()结果的后五位,同一天之内后五位不会重复出现
5位微妙有9万9千9百99个数,一秒内重复的可能性是10万分之一

//6位年月日+5位秒+5位微妙
$order_sn = date('ymd').substr(time(),-5).substr(microtime(),2,5);  //180425 54218 66351

2位年+8位秒+6位微妙

同理一年的时间戳是8位,又节约一位。但只有同一年之内不会重复,不推荐使用
重复几率是百万分之一秒

$order_sn = date('y').substr(time(),-8).substr(microtime(),2,6);

千万分之一的概率重复

date(‘ymd’)产生6位字符,而前两位年的表示,在一年之内都不会变化,第三到第四位月份也就是01-12。
前两位我们可以使用A-Z的字母来表示,系统开始运行的那一年用A,第二年用B,第三年用C……类推,字母有26位,可以表示26年,当字母用完之后,可以在使用数字0-9,又能表示10年;共36年不重复;如果要考虑十几年使用,等36年之后,只使用字母表示年,将年放到16位订单号的最后一位,这样就又能跑26年;因为之前的订单号,最后一位都是数字,将字母表示的年放到最后肯定不会重复;

第三第四位完全可以使用一位16进制数表示,
这样咱就又节约了两位字符,这就可以在末尾加上00-99的随机数。
也可以把天也用0-9,a-z的方式表示,一个月最多31天。就可以节约3位了

<?php
// intval — 获取变量的整数值,如系统运行第一年是2015年,那么第一年就是A,第二年就是b
// dechex() 函数把十进制转换为十六进制。月份转换成十六进制都是一位数字或者字母
// strtoupper — 将字符串转化为大写
// sprintf('%02d',mt_rand(0,99));随机两位数,不足的前补0
function create_code(){
    $year_code = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9');
    $startY=2018;//系统运行的时间
    $order_sn = $year_code[intval(date('Y'))-$startY].strtoupper(dechex(date('m'))).date('d').substr(time(),-5).substr(microtime(),2,5).sprintf('%02d',mt_rand(0,99));
    return $order_sn;
}

// 第一位D代表年,第二位4表示十六进制月,第三位25是日,第四位60833是秒,第5位97203是微秒,最后一位是两位随机数
echo create_code(); // D 4 25 60833 97203 25

优点:
1、不用操作数据库,性能较高。
2、较为直观,不难看出订单产生的大致时间
3、订单号重复的概率极小,微妙有5位 99999 ,随机数是两位,00到99,共有9百99万9千9百99中可能。几率为千万分之一。

一亿分之一的概率重复

在上面的基础上,把天也用0-9,a-z的方式表示,一个月最多31天。就可以节约3位了

<?php
// intval — 获取变量的整数值,如系统运行第一年是2015年,那么第一年就是A,第二年就是b
// dechex() 函数把十进制转换为十六进制。月份转换成十六进制都是一位数字或者字母
// strtoupper — 将字符串转化为大写
// sprintf('%02d',mt_rand(0,99));随机两位数,不足的前补0
function create_code(){
    $code = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9');
    $startY=2018;//系统运行的时间
    $order_sn = $code[intval(date('Y'))-$startY].strtoupper(dechex(date('m'))).$code[date('d')].substr(time(),-5).substr(microtime(),2,5).sprintf('%02d',mt_rand(0,999));
    return $order_sn;
}

// 第一位D代表年,第二位4表示十六进制月,第三位25是日,第四位60833是秒,第5位97203是微秒,最后一位是两位随机数
echo create_code(); // D 4 25 60833 97203 25

订单号生成规则:

摘自知乎:https://www.zhihu.com/question/19805896
订单号怎样生成才好用
订单号是用来标记/查询订单(查询的时候可能更关注于物流单号)用的,一般会在订单有支付/售后/异常问题的时候会用到,也就是说订单号主要是拿给客服/运营/开发部门用的。
首先订单号中最好避免数字以外的其它字符类型,订单号尽量短,订单号尽量能结合当前的业务情况有特定的标识,如渠道编号(包括平台、下单渠道、支付方式)、业务类型和时间信息等

好用的订单规则
下单渠道1位+支付渠道1位+业务类型(或者产品大分类)1位+时间信息4位+下单时间的Unix时间戳后8位(加上随机码随机后的数字)+用户user id后4位。然后你会说,这样算下来就订单号就19位了啊,一点都不精简啊,不好记不好念不好输的。但我说的上面的这些业务标记,你不一定要全部加上啊。
分库分表的话,那单号的最后几位(或者其它固定位置的几位)是数字,这样可以用数字作为分库分表的规则。
如果你们的系统特别复杂,甚至是多地分布式的部署。那可能还要有一两位来表达系统部署的路由规则。

一定不重复订单

就需要用到递增,订单池

递增:

尽量不要依赖数据库的递增服务,尽量是有一个专门的服务,来负责 这个计数器,这样在以后的迁移、扩展上,会有灵活的余地。
递增数字可以归零。只要确保同一天,同一个店铺的订单量,不超出 递增数字预留空间的最大位即可。
前面的年月日占用6位,10位可以为日递增,日最多可以10亿的订单,肯定可以满足需求了;

订单池:

做了一个实时监控订单号熟练的系统,当低于xxxxx的时候迅速生成新的订单号,并且买了更多的服务器,做了更多的集群,可以同时预留出更多的订单号等等等等。
这就是现在订单池的概念,随着订单号的被消费还继续生成着订单号,这个涉及的内容就很复杂了。

最简单粗暴的建议:
-店铺ID+日期+路由ID+递增数字。

注意
生成订单号的时候,可以检测数据库中是否存在相同的订单号,存在的话修改
如果有重复订单号的时候,在线支付是会提示已存在相同订单号的,而且在线支付相同订单号的金额不同,商品描述不同等都会提示重复提交订单号。