批: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

反序列化存入数据库里面的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;
}

 

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

在不改变php.ini中 session.save_handler = files 的前提下,实现不生成文件session,后期可写入到数据库。

<?php
//原创:xuduowei,想学习php开发的可以找我。微信号:weilanweb
class session_mysql{
		//private static $db=null;
		private static $ip=null;
		private static $lifetime=null;
		private static $time=null;

		private static function init(){
			//self::$db=$db;
			self::$ip=!empty($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : "unknown";
			self::$lifetime=ini_get('session.gc_maxlifetime');
			self::$time=time();
		}

		public static function start(){
			self::init();
			
			session_set_save_handler(
				array(__CLASS__, "open"),
				array(__CLASS__, "close"),
				array(__CLASS__, "read"),
				array(__CLASS__, "write"),
				array(__CLASS__, "destroy"),
				array(__CLASS__, "gc")
			);
			session_start();
		}

		public static function open($path, $name){
			//return true;
		}
	
		public static function close() {
			//return true;
		}
		
		public static function read($id){
			echo "read读取信息<br>";			
		}
		
		public static function write($id ,$data){
			echo "write写信息<br>";			
			//return true;
		}
		
		public static function destroy($id){
			echo "destroy清除信息<br>";
			
		}
		
		private static function gc($lifetime){
			echo "gc回收信息<br>";
		
		}

	
}

session_mysql::start();
echo session_name().'='.session_id()."<br>";
?>

输出结果:

read读取信息
PHPSESSID=2jo24tkjjqgu8jq1p6u2aijnd3
write写信息
=========================================
以phpstudy环境为例:默认的J:\phpStudy\tmp\tmp session存储路径下并不会有session文件的存储。php.ini配置文件中依然是默认的:session.save_handler = files
=========================================

这样做的目的就是方便后期把session写入数据库中。

以下为数据库融合版【xuduowei 原创】:

class db{
	var $conn;
	function __construct(){
		$conn=mysqli_connect("主机","用户名","密码");//为了方便,我这直接写。
		if(!$conn){
			echo "数据库连接失败";
			die;
		}
		mysqli_select_db($conn,"数据库");//为了方便,我这直接写。
		$this->conn=$conn;	
		//return $conn;
	}

	function select($sql){	
		$rst=mysqli_query($this->conn,$sql);
		$arr=array();
		while($rs=mysqli_fetch_assoc($rst)){
			$arr[]=$rs;
		}
		return $arr;
	}

	function query($sql){
		//用户数据库的删除操作	
		return mysqli_query($this->conn,$sql);		
	}
}//end db


class session_mysql{
		private static $db=null;
		private static $ip=null;
		private static $lifetime=null;
		private static $time=null;

		private static function init($db){
			self::$db=$db;
			self::$ip=!empty($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : "unknown";
			self::$lifetime=ini_get('session.gc_maxlifetime');
			self::$time=time();
		}

		public static function start($db){
			self::init($db);
			
			session_set_save_handler(
				array(__CLASS__, "open"),
				array(__CLASS__, "close"),
				array(__CLASS__, "read"),
				array(__CLASS__, "write"),
				array(__CLASS__, "destroy"),
				array(__CLASS__, "gc")
			);
			session_start();
		}

		public static function open($path, $name){
			return true;
		}
	
		public static function close() {
			return true;
		}
		
		public static function read($id){
				echo "read读取信息<br>";
			
				$sql="select sessionid,updatetime,data from xdw_session where sessionid='".$id."'";	
				$result=self::$db->select($sql);
				if(!$result[0]){//若没有值,就返回空。
					return "";
				}

				if ((strtotime($result[0]["updatetime"])+self::$lifetime) < self::$time){
					//echo "时间过期";
					self::destroy($id);
					return '';
				}

				return $result[0]['data'];
		 
				
		}

	
		public static function write($id ,$data){//sessionid和session 值
			echo "write写信息<br>";	

			$sql="select sessionid,updatetime,data from xdw_session where sessionid='".$id."'";	
			
			$result=self::$db->select($sql);
			//若有值
			if($result[0]){
				//若session内容不一致,或者超出一段时间300秒了【还没有过期】。我们就重新更新一次。
				if($result[0]['data'] != $data || self::$time > (strtotime($result[0]['updatetime'])+300)){
					$sql="update xdw_session set updatetime = '".date("Y-m-d H:i:s")."', data ='".$data."' where sessionid ='".$id."'";
					self::$db->query($sql);
				}

			}else{//若没有值,就所以这个sessionid不存在。我们就重新写入一次。
				$sql="insert into xdw_session set updatetime = '".date("Y-m-d H:i:s")."', data ='".$data."' ,sessionid ='".$id."'";
				self::$db->query($sql);
			}

			return true;
		}
	

		public static function destroy($id){
			echo "destroy清除信息<br>";				
			$sql="delete from xdw_session where sessionid ='".$id."'";			
			self::$db->query($sql);

			return true;				
			
		}
		
		private static function gc($lifetime){
			echo "gc回收信息<br>";//也就是删除过期的

			$time=self::$time-$lifetime;
			$sql = "delete from xdw_session where unix_timestamp(updatetime) < ".$time;
			self::$db->query($sql);		
			return true;
		
		}

	
}

session_mysql::start(new db());
echo session_name().'='.session_id()."<br>";

$_SESSION['username']="xuduowei";
附上数据库表结构:
#有想学习php开发的,可以联系我。电话:15309695130  php著名老师:xuduowei

DROP TABLE IF EXISTS `xdw_session`;
CREATE TABLE `xdw_session` (
  `session_id` varchar(32) NOT NULL,
  `data` text NOT NULL,
  `expire` datetime NOT NULL,
  PRIMARY KEY (`session_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

xuduowei 原创作品,请勿转载,谢谢合作。

js jquery中判断checkbox是否被选中的方法

在js中:

document.getElementById(“checkboxID”).checked   返回true或者false

jQuery中:

$(“input[type=’checkbox’]”).is(‘:checked’) 返回true或false

1、attr()方法  设置或者返回备选元素的值

attr(属性名)    //获取属性的值

attr(属性名,属性值)   //设置属性的值

$(“#id]”).attr(“checked”)  JQ1.6之后返回checked或者是undefined  (1.6之钱返回true或者是false)

$(“input[type=’checkbox’]”).attr(“chacked”,true);  将多选框设为全选中状态  false为不选中状态

2、prop()方法
$(“input[type=’checkbox’]”).prop(“checked”)  返回true或者false

还有removeAttr(属性名)、removeProp(属性名)删除该属性

例:

$(“input[‘tupe=checkbox’]”).removeAttr(“checked”);移除多选框的选中。

如下代码实现多个checkbox得到对应的属性值。非常经典,项目中必用。

$(document).on("change",'.sku_value',function(){			
					str=Array();
					$(".sku_value").each(function () { 
						var s=$(this).attr('propvalid');
						if($(this).is(":checked")){
							str.push(s);
						}
					});

					alert(str);

			});

 

mysql表数据发生变化时,主动通知业务系统(mysql-udf-http)

近期接到一个任务,在商家的商品信息发生改变后,要及时通知商家的业务系统,并将变更后的数据同步过去。
第一时间想法是,在主系统的商家编辑模块插入对应代码,当商品编辑后,发送商品id至MQ中,子系统消息订阅,并进行相应处理,感觉不错,但是在主系统进行扩展,系统代码会越来越臃肿。
有没有更好的解决方案,比如让mysql主动通知业务系统,数据发生变化了?于是找到了mysql-udf-http。

mysql-udf-http 是一款简单的MySQL用户自定义函数,具有http_get()、http_post()、http_put()、http_delete()四个函数,可以在MySQL数据库中利用HTTP协议进行REST相关操作,它的安装方式如下:

tar zxvf mysql-udf-http-1.0.tar.gz
cd mysql-udf-http-1.0/
./configure --prefix=/usr/local/mysql --with-mysql=/usr/local/mysql/bin/mysql_config
make && make install

如果提示缺少libcurl,就安装curl

yum install curl*

正常的情况mysql-udf-http.so等文件将安装至/usr/local/mysql/lib/plugin下,不知什么原因我的装在了/usr/local/mysql/lib/mysql/plugin下,于是加个软链

正常的情况mysql-udf-http.so等文件将安装至/usr/local/mysql/lib/plugin下,不知什么原因我的装在了/usr/local/mysql/lib/mysql/plugin下,于是加个软链

安装成功后,进到mysql控制台,注册相关函数

create function http_get returns string soname 'mysql-udf-http.so';
create function http_post returns string soname 'mysql-udf-http.so';
create function http_put returns string soname 'mysql-udf-http.so';
create function http_delete returns string soname 'mysql-udf-http.so'; 

然后在业务表中加入更新操作的触发器

DELIMITER |  
DROP TRIGGER IF EXISTS test_update;  
CREATE TRIGGER test_update  
AFTER UPDATE ON test  
FOR EACH ROW BEGIN  
    SET @tt_re = (SELECT http_get(CONCAT('http://192.168.0.1:8080/my.do?id=', OLD.id)));  
END |  
DELIMITER ;

经测试,当商品表中的数据有更新时,mysql会发送get请求至业务系统。

INSERT 触发器

DELIMITER |  
DROP TRIGGER IF EXISTS test_insert;  
CREATE TRIGGER test_insert  
AFTER INSERT ON test
FOR EACH ROW BEGIN  
    SET @tt_re = (SELECT http_get(CONCAT('http://192.168.0.1:8080/my.do?id=', OLD.id))); 
END |  
DELIMITER ;

DELETE 触发器

DELIMITER |  
DROP TRIGGER IF EXISTS test_delete;  
CREATE TRIGGER test_delete  
AFTER DELETE ON test  
FOR EACH ROW BEGIN  
    SET @tt_re = (SELECT http_get(CONCAT('http://192.168.0.1:8080/my.do?id=', OLD.id)));  
END |  
DELIMITER ; 

下载地址
http://code.google.com/p/mysql-udf-http

csdn下载
http://download.csdn.net/detail/cyantide/9455805

来源:https://blog.csdn.net/cyantide/article/details/50828746

借鉴:https://www.cnblogs.com/phpper/p/7587031.html

我们项目开发中,应该从哪几个方面提高项目性能?

我们项目开发中,应该从哪几个方面提高项目性能?【原创】

1、硬件;服务器要稳定,可靠,带宽大,允许的并发数要高;
2、软件【开发的程序】。

a、数据库
1、创建合适的字段类型及分配合适的长度;

例如:

密码:md5加密,对应的字段:char 32
年龄:tinyint 无符号

提示:整型的长度是结合0填充一起使用的。

2、默认值尽可能不要设置null

3、索引的创建;

4、合理的分表设计。

b、程序中值的调取【从数据库中调取】

单表查询,多表查询

子查询;【性能较低】
左右链接;
关联查询;

合理的 select 字段1,字段2 from 表
不合理的 select * from 表

不合理的 select count(*) from 表 where >>>
不合理的 select count(姓名) from 表 where >>>
合理的 select count(主索引ID) from 表 where >>>

c、优化。
1、静态化;
2、缓存[1、页面缓存;2、内存缓存]。

php 控制同一个用户只能同时一个人在线

有时候项目要求:控制同一个用户只能同时一个人在线

网上有说到很多种方法。不管哪种方法,都要注意2个特殊情况:

1、关闭浏览器;2、电脑断电【计算机系统中断】;

徐多蔚,徐老师,合肥php老师 这里分享一个稳定,高效,安全的解决方案:

核心效果:类似QQ登录一样,以最后一次登录为准。

若你当前在A电脑登录中【login_session 表记录:session_id,登录名,状态=1】,然后又在B电脑登录【首先:删除login_session表中和当前用户名同名的记录,或者更新和当前用户名同名的记录登陆状态为0;然后login_session 表记录:session_id,登录名,状态=1】,当在A电脑操作的时候,会判断当前登录状态为1才可以操作。这个时候发现为0了,则提示你的账号在其他地方登陆。

徐多蔚,徐老师,合肥php老师

JAVA和JSP之间的关系

我现在给你一个JAVA和JSP之间的关系,以及JAVA的完整认识

JAVA分为J2EE,J2SE.J2ME,下面分别介绍:

一.J2EE:Java 2 Platform Enterprise Edition 企业版,用于企业应用,支持分布式部署。

J2EE平台由一整套服务(Services)、应用程序接口(APIs)和协议构成,
它对开发基于Web的多层应用提供了功能上的支持。它包含13种核心技术规范:
(1)Java Database Connectivity (JDBC)
以一种统一的方式来对各种各样的数据库进行存取
(2)Java Naming and Directory Interface (JNDI)
用于名字和目录服务,它提供了一致的模型来存取和操作企业级的资源如DNS和LDAP,本地文件系统等
(3)Enterprise Java Beans (EJB)
提供了一个框架来开发和实施分布式商务逻辑,显著地简化了具有可伸缩性和高度复杂的企业级应用的开发
(4)JavaServer Pages (JSPs) 这里就是你所说的JSP!!!!!!用以创建动态网页
(5)Java servlets
提供的功能大多与JSP类似,不过实现的方式不同
(6)Remote Method Invocation (RMI)
在远程对象上调用一些方法,使用了连续序列方式在客户端和服务器端传递数据
(7)Interface Description Language (IDL)
将Java和CORBA集成在一起
(8)Java Transaction Architecture (JTA)
可以存取各种事务
(9)Java Transaction Service (JTS)
规定了事务管理器的实现方式
(10)JavaMail
用于存取邮件服务器的API,它提供了一套邮件服务器的抽象类
(11)JavaBeans Activation Framework(JAF)
JavaMmail利用JAF来处理MIME-编码的邮件附件,MIME的字节流可以被转换成JAVA对象,或者转换自JAVA对象
(12)Java Messaging Service (JMS)
是用于和面向消息的中间件相互通信的应用程序接口(API)
(13)Extensible Markup Language (XML)
XML是一种可以用来定义其它标记语言的语言
上面的JSP是主流,基于MVC的实现,最流行使用,也最安全(比较其他语言做的网站)
其中EJB是java最难学的东西
但是现在的sun公司出来一种新的技术:java FX,
可以像FLASH一样在浏览器中执行,但是也要安装插件,具体可以参见www.sun.com
二.J2SE:Java 2 Platform Standard Edition 标准版,用于桌面应用,也是J2EE的基础。

包括JAVA基础,IO,Swing,AWT,线程,集合等知识.也是学习JAVA的必经之路(基础)!
三.J2ME:Java 2 Platform Micro Edition 移动版用于小型设备,是J2SE的一个子集。

主要的技术如下:

Connected Limited Device Configuration【CLDC】:是组成资源有限的移动信息设备的 Java 运行时环境的两种配置之一。CLDC 描述最基本的库和虚拟机特性,所有包含 K 虚拟机(K virtual machine,KVM)的 J2ME 环境实现中都必须提供这些库和特性。
Mobile Information Device Profile【MIDP】:是组成资源有限的移动信息设备的 Java 运行时环境的两种配置之一。MIDP 提供核心应用程序功能,包括用户界面、网络连接、本地数据存储和应用程序生命周期管理。
Connected Device Configuration【CDC】:是一个基于标准的框架,用来构建和交付可以跨许多连接网络的消费类设备和嵌入式设备共享的应用程序。
Mobile 3D Graphics API for J2ME【M3G】:是一种轻量的交互式 3D 图形 API,它作为可选的包与 J2ME 和 MIDP 结合使用。

所以你所说的JAVA和JSP的关系就这样豁然开朗!

JAVA是一种编程语言,可以编写应用程序,主要应用在网络编程上。 
JSP是建立在JAVA基础上的一种网络编程语言,只能在网页上应用。 
JavaScript是一种Web脚本语言,可以嵌入在HTML中,可以由浏览器解释执行。

可以把jsp理解为脚本

java为面向对象语言

jsp作用主要是负责页面展现,java负责逻辑,业务处理

jsp 本质 是servlet,servlet本质是 java类,就是这么个关系。