ab命令做压测测试

1. 背景

互联网发达的今天,大大小小的网站如雨后春笋,不断出现,但是想要做出一个网站很简单,但是想要做好一个网站,非常非常难,首先:网站做好之后的功能怎么样这都是次要的,主要的是你的网站能承受怎么样的访问量,一个在高压访问下,能承受很高峰值的访问并发才能称得上是一个好的网站,那么作为一个程序员,当你搭建好你的网站之后,你应该怎么测试你的网站并发访问量呢?

接下来要介绍的就是apache的ab命令压测:

2.在学习使用ab命令之前,首先要了解压力测试的几个概念:(自己可以上网查下具体的概念)

  1. 吞吐率(Requests per second)
    概念:服务器并发处理能力的量化描述,单位是reqs/s,指的是某个并发用户数下单位时间内处理的请求数。某个并发用户数下单位时间内能处理的最大请求数,称之为最大吞吐率。
    计算公式:总请求数 / 处理完成这些请求数所花费的时间,即
    Request per second = Complete requests / Time taken for tests
  2. 并发连接数(The number of concurrent connections)
    概念:某个时刻服务器所接受的请求数目,简单的讲,就是一个会话。
  3. 并发用户数(The number of concurrent users,Concurrency Level)
    概念:要注意区分这个概念和并发连接数之间的区别,一个用户可能同时会产生多个会话,也即连接数。
  4. 用户平均请求等待时间(Time per request)
    计算公式:处理完成所有请求数所花费的时间/ (总请求数 / 并发用户数),即
    Time per request = Time taken for tests /( Complete requests / Concurrency Level)
  5. 务器平均请求等待时间(Time per request: across all concurrent requests)
    计算公式:处理完成所有请求数所花费的时间 / 总请求数,即
    Time taken for / testsComplete requests
    可以看到,它是吞吐率的倒数。
    同时,它也=用户平均请求等待时间/并发用户数,即
    Time per request / Concurrency Level

    3.ab工具的介绍

    ab是apache自带的压力测试工具。ab非常实用,它不仅可以对apache服务器进行网站访问压力测试,也可以对或其它类型的服务器进行压力测试。比如nginx、tomcat、IIS等。
      安装:
                    1.公司应该有程序员吧,可以安装一个wamp或者phpstudy,这样apache服务器和mysql数据库都有了,一举多得。
    文件位置:打开你安装的apache的位置:找到 bin文件夹下面的ab.exe
    在该文件夹下打开命令行,输入 ab.exe -help
    对上面的Options做下解释吧:
    -n即requests,用于指定压力测试总共的执行次数。
    -c即concurrency,用于指定压力测试的并发数。
    -t即timelimit,等待响应的最大时间(单位:秒)。
    -b即windowsize,TCP发送/接收的缓冲大小(单位:字节)。
    -p即postfile,发送POST请求时需要上传的文件,此外还必须设置-T参数。
    -u即putfile,发送PUT请求时需要上传的文件,此外还必须设置-T参数。
    -T即content-type,用于设置Content-Type请求头信息,例如:application/x-www-form-urlencoded,默认值为text/plain
    -v即verbosity,指定打印帮助信息的冗余级别。
    -w以HTML表格形式打印结果。
    -i使用HEAD请求代替GET请求。
    -x插入字符串作为table标签的属性。
    -y插入字符串作为tr标签的属性。
    -z插入字符串作为td标签的属性。
    -C添加cookie信息,例如:”Apache=1234″(可以重复该参数选项以添加多个)。
    -H添加任意的请求头,例如:”Accept-Encoding: gzip”,请求头将会添加在现有的多个请求头之后(可以重复该参数选项以添加多个)。
    -A添加一个基本的网络认证信息,用户名和密码之间用英文冒号隔开。
    -P添加一个基本的代理认证信息,用户名和密码之间用英文冒号隔开。
    -X指定使用的代理服务器和端口号,例如:”126.10.10.3:88″。
    -V打印版本号并退出。
    -k使用HTTP的KeepAlive特性。
    -d不显示百分比。
    -S不显示预估和警告信息。
    -g输出结果信息到gnuplot格式的文件中。
    -e输出结果信息到CSV格式的文件中。
    -r指定接收到错误信息时不退出程序。
    -h显示用法信息,其实就是ab -help

    4.实际测试:

5.分析上面的压测结果:

Server Software: Apache/2.4.23(服务器软件名称及版本信息)

Server Hostname: 127.0.0.1(服务器主机名)

Server Port: 80 (服务器端口)

Document Path:  /a1.php (供测试的URL路径)

Document Length: 0 bytes (供测试的URL返回的文档大小)

Concurrency Level: 300 (并发数)

Time taken for tests: 19.750 seconds (压力测试消耗的总时间)

Complete requests: 300(压力测试的的总次数)

Failed requests: 0 (失败的请求数)

Total transferred: 62100 bytes (传输的总数据量)

HTML transferred: 0 bytes (HTML文档的总数据量)

Requests per second: 15.19 [#/sec] (mean) (平均每秒的请求数)

Time per request: 19749.634 [ms] (mean) (所有并发用户(这里是300)都请求一次的平均时间)

Time per request: 65.832 [ms] (mean, across all concurrent requests) (单个用户请求一次的平均时间)

Transfer rate: 3.07 [Kbytes/sec] received (传输速率,单位:KB/s)

数据库sql常见优化方案

为什么要优化:
随着实际项目的启动,数据库经过一段时间的运行,最初的数据库设置,会与实际数据库运行性能会有一些差异,这时我们就需要做一个优化调整。

数据库优化这个课题较大,可分为四大类:
》服务器硬件性能:主机,内存;
》网络传输性能
》SQL语句执行性能【软件工程师】

下面列出一些数据库SQL优化方案:

(01)创建表的时候,分配合适的字段类型和大小;

(02)给经常参与条件检索的单个字段或者多个字段加索引,特例中需要唯一性的,记得加唯一索引。

(03)选择最有效率的表名顺序(笔试常考)
数据库的解析器按照从右到左的顺序处理FROM子句中的表名,
FROM子句中写在最后的表将被最先处理,
在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表放在最后,
如果有3个以上的表连接查询,那就需要选择那个被其他表所引用的表放在最后。
例如:查询员工的编号,姓名,工资,工资等级,部门名
select emp.empno,emp.ename,emp.sal,salgrade.grade,dept.dname
from salgrade,dept,emp
where (emp.deptno = dept.deptno) and (emp.sal between salgrade.losal and

salgrade.hisal)
1)如果三个表是完全无关系的话,将记录和列名最少的表,写在最后,然后依次类推
2)如果三个表是有关系的话,将引用最多的表,放在最后,然后依次类推

(04)WHERE子句中的连接顺序(笔试常考)
数据库采用自右而左的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之左,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的之右。
例如:查询员工的编号,姓名,工资,部门名
select emp.empno,emp.ename,emp.sal,dept.dname
from emp,dept
where (emp.deptno = dept.deptno) and (emp.sal > 1500)

(05)SELECT子句中避免使用*号
数据库在解析的过程中,会将*依次转换成所有的列名,这个工作是通过查询数据字典完成的,这

意味着将耗费更多的时间
select empno,ename from emp;

(06)用TRUNCATE替代DELETE

(07)用WHERE子句替换HAVING子句
WHERE先执行,HAVING后执行

(08)多使用内部函数提高SQL效率

(9)使用表的别名和列的别名

使用表的别名
salgrade s

使用列的别名
ename e

(10)尽可能少使用子查询,可使用left join代替

 

原创:商城开发中,真正唯一订单雏形-插入订单,获得订单的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原著。转载请注明来源,谢谢。

 

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];

 

 

 

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

			});