原创:商城开发中,真正唯一订单雏形-插入订单,获得订单的id,把订单id拼装到自定义订单号中

@$mysql = mysql_connect('localhost','root','root');
mysql_query('set names utf8');
mysql_select_db('test');

//$sql="insert into ddd2 set uid='".$str."'";
//ddd2表模拟订单表,字段uid模拟订单号字段
$sql="insert into ddd2 set addtime='".date("Y-m-d H:i:s")."'";
mysql_query($sql);
//$id=mysql_insert_id();
$idrs=mysql_fetch_array(mysql_query("SELECT LAST_INSERT_ID()"));//用此代替mysql_insert_id()就可以解决非int型自增长取值不准的bug问题。
$id=$idrs[0];
//我们可以在更新前对$iddiy进行拼装。
$iddiy=$id;
$sql="update ddd2 set uid='".$iddiy."' where id=".$id;//uid模拟订单号字段
mysql_query($sql);
//apache下自带的ab.exe并发高压测试
//ab.exe -c 100 -n 100 http://127.0.0.1/a1.php

附上测试表结构:

CREATE TABLE `ddd2` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `uid` varchar(100) DEFAULT NULL,
  `addtime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uid` (`uid`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

 

$idrs=mysql_fetch_array(mysql_query("SELECT LAST_INSERT_ID()")); 有人会问为什么这样写呢?因为mysql_insert_id()是有bug的。在int型下是可以在的,在非int型如:bigint下,mysql_insert_id()取值就不准了。

本文章由:徐多蔚  徐老师 xuduowei原著。转载请注明来源,谢谢。

 


关注公众号,了解更多it技术(it问答网

Mysql_insert_id的一个缺陷

mysql_insert_id() 将 MySQL 内部的 C API 函数 mysql_insert_id() 的返回值转换成 long(PHP 中命名为 int)。

如果 AUTO_INCREMENT 的列的类型是 BIGINT,则 mysql_insert_id() 返回的值将不正确。

作为替代方案,我们可以在 SQL 查询中用 MySQL 内部的 SQL 函数 LAST_INSERT_ID() 。

$sql="insert into ddd2 set addtime='".date("Y-m-d H:i:s")."'";
mysql_query($sql);
//$id=mysql_insert_id();
$idrs=mysql_fetch_array(mysql_query("SELECT LAST_INSERT_ID()"));//用此代替mysql_insert_id()就可以解决非int型自增长取值不准的bug问题。
$id=$idrs[0];

 

 

 


关注公众号,了解更多it技术(it问答网

批:PHP生成唯一订单号【徐老师对此点评:徐老师对此进行高并发检测,重复率还是非常的高,压根无法实现所谓的唯一性。】

在日常的网站开发中,我们经常需要生成唯一的订单号。订单号太短,在高迸发情况下,很容易造成订单号重复事件,虽然是小概率事件。

下面我们使用PHP多个函数生成一个现在最常用的订单号格式:

提示:以下code经过徐老师进行高并发检测,重复率还是非常的高,压根无法实现所谓的唯一性。不管用哪种方式生成订单号,建议把订单号存储到单个的表中,然后生成新的订单号前,你可能首先考虑:判断存储订单号表中是否存在,若存在则重新生成【这个时候必须要进行文件锁或者表锁定操作或者sql where dingdanhao<>值 条件计算记录三种方式,可参看我这篇文章:www.xuduowei.com/archives/500 ,因为高并发下还是有可能同时判断某一个订单不存在,但是生成的却是相同的情况的发生。】

徐老师推荐一种更简单,高效且不重复的方案!看此链接:http://www.xuduowei.com/archives/524

 

date('Ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);

网上有很多地方推荐写的类似这样的订单写法,其实在高并发下,都是会重复的,这里只是分解下:

简单从内到外解析一下这个订单号生成过程:

  1. date("Ymd"):这个很容易理解,是在最前方拼接一个当前年月日组成的数字。2.uniqid():此函数获取一个带前缀、基于当前时间微秒数的唯一ID。
  2. substr(uniqid(), 7, 13):由于uniqid()函数生成的结果前面7位很久才会发生变化,所以有或者没有对于我们没有多少影响,所以我们截取后面经常发生变化的几位。
  3. str_split(substr(uniqid(), 7, 13),1):我们将刚刚生成的字符串进行分割放到数组里面,str_split()第二个参数是每个数组元素的长度。
  4. array_map('ord', str_split(substr(uniqid(), 7, 13),1))):其中array_map()函数作用为:函数返回用户自定义函数作用后的数组,意思就是ord是函数ord(),而后面第二个参数是ord()函数的参数。可以这么理解ord(str_split(substr(uniqid(),7, 13), 1)))。然后ord()是干啥的,ord()函数php内置函数:返回字符串的首个字符的 ASCII值,意思就是把第二个参数生成的数组每个元素全部转换为数字,因为刚刚我们截取的字符串中含有字母,不适合订单号。
  5. implode():很简单了,把刚刚那个转换成数字的数字在拼接成为一个数字。
  6. 由于刚刚生成的随机数可能会长短不一(原因就是,每个字符转换为ASCII值可能不一样,有些是2位,有些可能是一位),所以我们同意截取0-8
  7. 然后加上刚刚的日期数字,现在就凑成了一个等长的高大上的订单号了~

https://blog.csdn.net/leyuxinsi/article/details/47009177

=========================

订单命名的几种规则:

订单命名的几种规则:
1、不重复。
这点我相信大家都懂,订单的唯一性不用解释。
2、安全性。
你的订单编号不能透露你公司的真实运营信息,比如你的订单就是流水号的话,那么别人就可以从订单号推测出你公司的整体运营概括了。所以订单编码必须是除了你们公司少部分人外,其他人基本看不懂的。参考京东和淘宝的编码规则,基本别人是搞不清是什么意思的。
其实最好的防泄漏编码规则就是在编码中不要加入任何和公司运营的数据。
3、不能使用大规模随机码。
很多人分析订单编码规则的时候,第一个念头肯定是不重复唯一性,那么第二个念头可能就是安全性,那么同时满足前两者的第三个念头就是随机码了。因为大规模的随机码随机生成,因为本身就没有意义所以无所谓泄密了。但是事实上这种编码规则在实现上会有很大问题的。
随机码满足第二点安全性要求,为了满足第一点不重复特性,那就得在生成随机码的时候对比历史数据是否有重复,如果你的订单数量到达了十万次,你每次生成订单编码时就得对比十万条历史数据,你可想而知会造成什么巨大问题。
但是难道随机码就不能在编码中使用了吗?小规模的随机码是可以使用的,比如2~3位,这种随机码一般都是和流水号等结合使用,主要作用是为了隐藏流水号的真实数据而进行使用的。
PS:在这里感谢 @马驰@dad ni @bao xu(这个不知道为何@不到)同学的讨论,马驰同学实际测试估算了生成随机码并且检测重复所花费的时间在纳秒级别。但是我还是保持原来观点,觉得这种生成规则存在方向性问题,可能会造成检测时间过长的问题出现。
希望大家积极参与讨论。
4、防止并发。
这条规则主要针对编码中有时间的设定。
5、控制位数。
这点很好理解,订单号的作用就是便于查询。
一般正常使用场景应该是订单出异状或者退货的时候,用户将订单号报给客服,由客服进行查询。
所以一般在10~15位为好。

京东10位,淘宝15位。

推荐的几种编码规则:

年月日时分秒+用户ID(命名用户ID时也要注意,不要用流水号。可以采用区域ID+随机码+流水号+随机码方式)
1、唯一性:时间是单向的,确保唯一性。
2、安全性:确保用户ID安全即可。
3、随机码不参与判断,因为之前数据已确保无重复。
4、在同1秒钟,同一用户是不会产生2个订单编码的,所以可以防并发。
5、位数可能会在20位之内,位数比较多。
年月日时分秒微秒+随机码(2)+流水号+随机码(3)
1、唯一性:时间是单向的,确保唯一性。
2、安全性:确保流水号不会识别出即可。
3、随机码的位数和前后都是保密的,所以如果不清楚这一点的话,是很难判断出流水号的位数的。因为同时产生的订单数量很多,编码不具备线性对比功能。就算知道了流水号,可以在初始化时进行赋值。
4、在同1秒钟,同一用户是不会产生2个订单编码的,所以可以防并发。

5、位数可能会在20位之内,位数比较多。

(以上来自知乎@benben)

==============================================

订单号常见的几种方式:
1.利用数据库主键值产生一个自增长的订单号(订单号即数据表的主键)
2.日期+自增长数字的订单号(比如:2012040110235662)
3.产生随机的订单号(65865325365966)
4.字母+数字字符串式,字母有包含特别意义,C02356652

订单号设计原则: 按需设计
用来检索订单详细信息的唯一特征码,可以利用订单号检索到下单日期、产品类别、颜色、尺码(或款式)、仓位等信息,订单号包含过多的信息有点“画蛇添足”的意味!只要按需设计即可!

订单号设计用户体验规则:
1.订单号无重复性;
2.如果方便客服的话,最好是“日期+自增数”样式的订单号,客服一看便知道订单是否在退货保障期限内容;
3.订单号长度尽量保持短(10位以内),方便用户,尤其电话投诉时,长的号码报错几率高,影响客服效率;
4.订单号尽量保持数字型(纯整数),在数据库订单索引查询中,长整数字型的数据索引与检索效率,远远高于文本型,因此尽量避免“字母+数字字符串式”!

php商城高并发下订单模拟

https://blog.csdn.net/yxwb1253587469/article/details/50572927?locationNum=1

php 生成唯一订单号,文件锁

https://blog.csdn.net/wmsjlihuan/article/details/51537092

大数据量下高并发同步的讲解(不看,保证你后悔)

https://blog.csdn.net/xcw931924821/article/details/52475742

高并发环境下生成订单唯一流水号方法:SnowFlake

https://blog.csdn.net/qq_24267619/article/details/78624978


关注公众号,了解更多it技术(it问答网

反序列化存入数据库里面的session数据

session数据存取的方法可通过session.serialize_handler方法来判断,反序列化可通过下面的unserialize方法,参考http://stackoverflow.com/questions/27813619/unserializing-php-session-data-from-db-table

public static function unserialize($session_data) {
    $method = ini_get("session.serialize_handler");
    switch ($method) {
        case "php":
            return self::unserialize_php($session_data);
            break;
        case "php_binary":
            return self::unserialize_phpbinary($session_data);
            break;
        default:
            throw new Exception("Unsupported session.serialize_handler: " . $method . ". Supported: php, php_binary");
    }
}

private static function unserialize_php($session_data) {
    $return_data = array();
    $offset = 0;
    while ($offset < strlen($session_data)) {
        if (!strstr(substr($session_data, $offset), "|")) {
            throw new Exception("invalid data, remaining: " . substr($session_data, $offset));
        }
        $pos = strpos($session_data, "|", $offset);
        $num = $pos - $offset;
        $varname = substr($session_data, $offset, $num);
        $offset += $num + 1;
        $data = unserialize(substr($session_data, $offset));
        $return_data[$varname] = $data;
        $offset += strlen(serialize($data));
    }
    return $return_data;
}

private static function unserialize_phpbinary($session_data) {
    $return_data = array();
    $offset = 0;
    while ($offset < strlen($session_data)) {
        $num = ord($session_data[$offset]);
        $offset += 1;
        $varname = substr($session_data, $offset, $num);
        $offset += $num;
        $data = unserialize(substr($session_data, $offset));
        $return_data[$varname] = $data;
        $offset += strlen(serialize($data));
    }
    return $return_data;
}

 


关注公众号,了解更多it技术(it问答网

MySQL锁机制和PHP锁机制

模拟准备–如何模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
例如:cmd: apache安装路径/bin/ab.exe -c 10 -n 10 http://web.test.com/test.php

【切入正题】
MYSQL中的锁:
语法 :
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE ……………… 【锁表】
UNLOCK TABLES 【释放表】

Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!

PHP中的文件锁 (锁的是文件,不是表)
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
测试时,有个文件就行,叫什么名无所谓。

总结:
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。

我的一个项目就是O2O外卖,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。

应用场景:
1. 高并发下单时,减库存量时要加锁
2. 高并发抢单、抢票时要使用

MySQL锁示例代码:

    <?php  
    /** 
    模拟秒杀活动-- 商品100件 
    CREATE TABLE a 
    ( 
        id int comment '模拟100件活动商品的数量' 
    ); 
    INSERT INTO a VALUES(100); 
    模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件 
     */  
    error_reporting(0);  
    mysql_connect('localhost','root','admin123');  
    mysql_select_db('test');  
      
    # mysql 锁  
    mysql_query('LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这  
    $rs = mysql_query('SELECT id FROM a');  
    $id = mysql_result($rs, 0, 0);  
    if($id > 0)  
    {  
        --$id;  
        mysql_query('UPDATE a SET id='.$id);  
    }  
      
    # mysql 解锁  
    mysql_query('UNLOCK TABLES');  

PHP文件锁示例代码:

    <?php  
    /** 
    模拟秒杀活动-- 商品100件 
    CREATE TABLE a 
    ( 
        id int comment '模拟100件活动商品的数量' 
    ); 
    INSERT INTO a VALUES(100); 
    模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件 
     */  
    error_reporting(0);  
    mysql_connect('localhost','root','admin123');  
    mysql_select_db('test');  
    # php中的文件锁  
    $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可  
    flock($fp, LOCK_EX);// 排他锁  
      
      
    $rs = mysql_query('SELECT id FROM a');  
    $id = mysql_result($rs, 0, 0);  
    if($id > 0)  
    {  
        --$id;  
        mysql_query('UPDATE a SET id='.$id);  
    }  
    # php的文件锁,释放锁  
    flock($fp, LOCK_UN);  
    fclose($fp);  

其实还有一种最简单的方案,一个sql就可以解决这个事情。

@$mysql = mysql_connect('localhost','root','');
mysql_query('set names utf8');
mysql_select_db('test');
mysql_query('UPDATE warehouse SET `stock` = `stock` -1 WHERE `stock` > 0');  //可以避免库存为负数

测试的方法是,找到Apache下的ab.exe,拖入CMD终端,然后输入指定参数测试。

具体参数说明Google一下你就知道,比如耗时之类的…这里不做详细说明。

 

其他参考:

php mysql 锁表和解锁详细语句http://blog.sina.com.cn/s/blog_6e637ea701016r01.html

mysql锁(行锁,表锁)同一用户同一秒操作保持唯一性https://blog.csdn.net/webnoties/article/details/22874431

MySQL锁机制和PHP锁机制 http://phpkim.iteye.com/blog/2294464

Mysql的锁机制与PHP文件锁处理高并发简单思路https://www.cnblogs.com/yuandongdong/p/7560327.html


关注公众号,了解更多it技术(it问答网