PHP高并发Redis MySQL重复插入测试笔记
发布时间:2018-08-31, 19:59:41 分类:PHP | 编辑 off 网址 | 辅助
图集1/9
正文 13717字数 1,359,840阅读
<?php
error_reporting(E_ALL); //E_ALL
function cache_shutdown_error() {
$_error = error_get_last();
if ($_error && in_array($_error['type'], array(1, 4, 16, 64, 256, 4096, E_ALL))) {
echo '<font color=red>你的代码出错了:</font></br>';
echo '致命错误:' . $_error['message'] . '</br>';
echo '文件:' . $_error['file'] . '</br>';
echo '在第' . $_error['line'] . '行</br>';
}
}
register_shutdown_function("cache_shutdown_error");
function dump($arr){
echo '';
var_dump($arr);
echo '
';
}
function glog($file,$content,$file_type='.txt'){
//return false;
$file=$file?$file:date('Y-m-d');
$content=$content?$content:date('Y-m-d h:m:s');
$f=file_put_contents($file.$file_type,$content.PHP_EOL,FILE_APPEND);
return $f;
}
//写入数据
function tin($login){
global $dbh;
/*添加*/
//$sql = "INSERT INTO `user` SET `login`=:login AND `password`=:password";
$sql = "INSERT INTO `user` (`login` ,`password`)VALUES (:login, :password)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login,':password'=>'123'));
$c_id=$stmt->rowCount();
return $c_id;
try {
$dbh->beginTransaction(); // 开启一个事务
$sql = "SELECT * FROM `user`";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$slogin));
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
if(empty($row)){
/*添加*/
//$sql = "INSERT INTO `user` SET `login`=:login AND `password`=:password";
$sql = "INSERT INTO `user` (`login` ,`password`)VALUES (:login, :password)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$slogin,':password'=>'123'));
$c_id=$dbh->lastinsertid();
}else{
foreach($row as $k=>$v){
if($v['login']!=$slogin){
/*添加*/
//$sql = "INSERT INTO `user` SET `login`=:login AND `password`=:password";
$sql = "INSERT INTO `user` (`login` ,`password`)VALUES (:login, :password)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$slogin,':password'=>'123'));
$c_id=$dbh->lastinsertid();
}
}
}
$dbh->commit();
} catch (PDOException $e) {
$dbh->rollback(); // 执行失败,事务回滚
glog('',$e->getMessage());
}
return $c_id;
}
//ab -n 50000 -c 100 http://oc.com/r.php
//select login,count(*) as count from user group by login having count>1;
Global $redis,$dbh;
$redis = new redis();
$redis->connect('127.0.0.1', 6379);
/*$redis->delete('test');
$redis->lpush("test","111");
$redis->lpush("test","222");
print_r($redis->lgetrange("test",0,-1)); //结果:Array ( [0] => 222 [1] => 111 )*/
$dbh = new PDO('mysql:host=localhost;dbname=test', 'root', 'DRsXT5ZJ6Oi55LPQ');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, false);//关闭自动提交
$dbh->exec('set names utf8');
try {
$now = time();
//限流
$qps_num=$redis->incr("qps_num");
$redis->expireAt('qps_num', $now + 1);
if($qps_num>100){
//echo '限流'.$qps_num;
//glog('','限流'.$qps_num);
return false;
}
//dump($redis->lgetrange("test",0,-1));exit;
//$login=date('s',time());
//$redis->delete('login_set');exit;
//$redis->expireAt('x', $now + 3); // x will disappear in 3 seconds.
$s_x=$redis->setnx('x',1); //加一个标志位
$redis->expireAt('x', $now + 60); //后于集合过期
if(!$s_x){
//glog('','请排队');
return false;
}
//事务 写在前面 意外退出 ?没有提交 commit 不回滚?
$dbh->beginTransaction(); // 开启一个事务
//$set = $redis->smembers('login_set');
//dump($set);exit;
//echo $redis->ttl('login_set');exit;
$login_set_num=$redis->sCard('login_set'); //返回SET容器的成员数
//echo $login_set_num;exit;
if(!$login_set_num){
glog('','查询');
//查询 缓存数据 假设字段login唯一
$sql = 'SELECT login FROM user';
$stmt = $dbh->prepare($sql);
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
//多个成员元素加入到集合中 注意:在Redis2.4版本以前, SADD 只接受单个成员值
foreach($row as $k=>$v){
$redis->sAdd('login_set',$v);
}
unset($row,$k,$v);
}
//模拟生成插入值 login
static $login;
$login=rand(100,9999);
//检查是否成员 是已经存在数据库 否录入
//$login_set_is=$redis->sIsMember('login_set',$login);
$login_set_is=$redis->sAdd('login_set',$login); //设计修改操作 才是原子性?
if($login_set_is){
$in_id=tin($login);
//if($in_id) $redis->sAdd('login_set',$login); //插入成功 加入集合缓存
}else glog('','已经存在-'.$login);
$redis->expireAt('login_set', $now + 30);
//glog('','ttl-'.$redis->ttl('login_set'));
$dbh->commit();
} catch (Exception $e) {
$dbh->rollBack();
glog('',"Failed: " . $e->getMessage());
$redis->sRem('login_set', $login);
}finally{
}
$redis->delete('x');
exit;
//防止重复写入
$hnum=$redis->incr('num');
//glog('',$hnum);
$oldstr=$redis->get('login');
if($hnum>50 || ($oldstr && $oldstr==$login)){
$redis->set('num',-99);
exit;//已经存在
}else if($hnum>0){
$redis->set('login',$login);
if(tin($login)) glog('','incr22-'.$login.'-'.$oldstr);
else glog('','EE-incr22-'.$login.'-'.$oldstr);
}
exit;
$tadd=$redis->rpush("test",$login);
while(true){
try{
$slogin = $redis->BLPOP('test',1);
if(!$slogin){
break;
}
//var_dump($value)."\n";
if(tin($slogin)) glog('','incr22-'.$login.'-'.$slogin);
else glog('','EE-incr22-'.$login.'-'.$slogin);
/*
* 利用$value进行逻辑和数据处理
*/
}catch(Exception $e){
glog('',$e->getMessage());
}
}
exit;
//$tadd=$redis->rpush("test",$login);
//exit;
//$gnum=$redis->flushall();
$gnum=$redis->get('gnum');
if(!$gnum){
$redis->set('gnum','1');
if(tin($login)) glog('','rob_result_ok-'.$login);
else glog('','rob_result_ok-E-'.$login);
$redis->delete('gnum');
}else glog('','rob_result_EE-'.time());
exit;
$redis->incr('gnum');
$gnum=$redis->get('gnum');
$redis->watch("gnum");
$redis->multi();
if(tin($login)) glog('','rob_result_ok-'.$login);
else glog('','rob_result_ok-E-'.$login);
//$redis->set('sdf',1);
$rob_result=$redis->exec();
//dump($rob_result);
if($rob_result=='nil'){
$tadd=$redis->rpush("test",$login);
if($tadd) glog('','rob_result_E-'.$login);
else glog('','rob_result_E-_EL'.$login);
$redis->unwatch("gnum");
}
exit;
//$redis->delete('gnum test');exit;
$tadd=$redis->rpush("test",$login);
//$redis->watch("gnum");
//$redis->multi();
while(true){
try{
$slogin = $redis->LPOP('test');
if(!$slogin){
break;
}
//var_dump($value)."\n";
if(tin($slogin)) glog('','incr22-'.$login.'-'.$slogin);
else glog('','E-incr22-'.$login);
/*
* 利用$value进行逻辑和数据处理
*/
}catch(Exception $e){
glog('',$e->getMessage());
}
}
//$rob_result = $redis->exec();
//if(!$rob_result) $redis->unwatch("gnum");
exit;
//$redis->delete('test');
$tadd=$redis->rpush("test",$login);
//dump($redis->lpop("test"));
//dump($redis->lgetrange("test",0,-1));
//$list_arr=$redis->lgetrange("test",0,-1);
while(true){
try{
$slogin = $redis->LPOP('test');
if(!$slogin){
break;
}
//var_dump($value)."\n";
/*
* 利用$value进行逻辑和数据处理
*/
}catch(Exception $e){
glog('',$e->getMessage());
}
}
exit;
//$redis->expire('test',1);
//glog('',$tadd);exit;
$redis->watch("test");
$redis->multi();
if($tadd<2){
glog('',$tadd);exit;
/*查询*/
//$login = 'kevin2';
$sql = "SELECT * FROM `user`";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login));
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
if(empty($row)){
/*添加*/
//$sql = "INSERT INTO `user` SET `login`=:login AND `password`=:password";
$sql = "INSERT INTO `user` (`login` ,`password`)VALUES (:login, :password)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login,':password'=>'123'));
if($dbh->lastinsertid()){
$redis->delete('test');
}
}else{
foreach($row as $k=>$v){
if($v['login']!=$login){
/*添加*/
//$sql = "INSERT INTO `user` SET `login`=:login AND `password`=:password";
$sql = "INSERT INTO `user` (`login` ,`password`)VALUES (:login, :password)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login,':password'=>'123'));
if($dbh->lastinsertid()){
$redis->delete('test');
}
}
}
}
}else if($tadd<50){
glog('','并发五百内');
}else{
glog('','高并发大于五百,队列完毕,重置');
$redis->delete('test');
}
$rob_result = $redis->exec();
exit;
//$redis->lpush("test",$login);
$redis->sadd('isdata',$login);
$rcnum=$redis->scard('isdata');
if($rcnum>3){
echo '抢完了';
$set = $redis->smembers('isdata');
dump($set);
exit;
}
exit;
//$redis->sadd('isdata',$login);exit;
/*$redis->sadd('isdata',$login);//$redis->delete('isdata');
var_dump($redis->sort('isdata'));exit;*/
//dump($redis->sort('isdata'));exit;
$inarr=$redis->sort('isdata');
$redis->delete('isdata');
if(!count($inarr)){
$result=json_encode(array("errcode" => 2004, "errmsg" => "no data", 'data' => 'dataLength:' . $dataLength . 'liveKey:' . $listKey));
//glog('',$result);
exit($result);
};
$sql = "INSERT INTO user(login,password) values ";
$data=array();
foreach($inarr as $k=>$v){
$sql.='(?,?),';
array_push($data,$v,'test'.$k);
//$data[$k]=$v;
//$data[($k+1)]='test'.$k;
//$stmt->execute(array(':login'=>$v,':password'=>'123'));
}
$inarr=null;
$sql = substr($sql,0,strlen($sql)-1);
//echo $sql;exit;
//dump($data);exit;
try {
$stmt = $dbh->prepare($sql);
$stmt->execute($data);
} catch (PDOException $e) {
glog('',"Error!: " . $e->getMessage());
return false;
}
/*if($dbh->lastinsertid()){
//$redis->sremove('isdata',$v);
//unset($inarr[$k]);
$redis->delete('isdata');
}*/
exit;
/*查询*/
//$login = 'kevin2';
$sql = "SELECT * FROM `user`";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login));
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
/*print_r($row);
exit; */
if($row['login']!=$login){
/*添加*/
//$sql = "INSERT INTO `user` SET `login`=:login AND `password`=:password";
$sql = "INSERT INTO `user` (`login` ,`password`)VALUES (:login, :password)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login,':password'=>'123'));
echo $dbh->lastinsertid();
}
}
exit;
/*修改*/
$sql = "UPDATE `user` SET `password`=:password WHERE `user_id`=:userId";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':userId'=>'7', ':password'=>'4607e782c4d86fd5364d7e4508bb10d9'));
echo $stmt->rowCount();
/*删除*/
$sql = "DELETE FROM `user` WHERE `login` LIKE 'kevin_'"; //kevin%
$stmt = $dbh->prepare($sql);
$stmt->execute();
echo $stmt->rowCount();
/*查询*/
$login = 'kevin%';
$sql = "SELECT * FROM `user` WHERE `login` LIKE :login";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login));
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
print_r($row);
}
print_r( $stmt->fetchAll(PDO::FETCH_ASSOC));
/*添加*/
/*//$sql = "INSERT INTO `user` SET `login`=:login AND `password`=:password";
$sql = "INSERT INTO `user` (`login` ,`password`)VALUES (:login, :password)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login,':password'=>'123'));
$dbh->lastinsertid();
$row=null;
$login=null;
exit;*/
/*查询*/
//$login = 'kevin2';
$sql = "SELECT * FROM `user`";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login));
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
/*print_r($row);
exit; */
if($row['login']!=$login){
/*添加*/
//$sql = "INSERT INTO `user` SET `login`=:login AND `password`=:password";
$sql = "INSERT INTO `user` (`login` ,`password`)VALUES (:login, :password)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':login'=>$login,':password'=>'123'));
$dbh->lastinsertid();
}
}
$row=null;
$login=null;
exit;
Run code
Cut to clipboard
(支付宝)给作者钱财以资鼓励 (微信)→
有过 12 条评论 »
算法复杂度分为时间复杂度和空间复杂度。
其作用:
时间复杂度是指执行算法所需要的计算工作量;
而空间复杂度是指执行这个算法所需要的内存空间。
(算法的复杂性体现在运行该算法时的计算机所需资源的多少上,计算机资源最重要的是时间和空间(即寄存器)资源,因此复杂度分为时间和空间复杂度)。
简单来说,时间复杂度指的是语句执行次数,空间复杂度指的是算法所占的存储空间
时间复杂度
计算时间复杂度的方法:
用常数1代替运行时间中的所有加法常数
修改后的运行次数函数中,只保留最高阶项
去除最高阶项的系数
按数量级递增排列,常见的时间复杂度有:
常数阶O(1),对数阶O(log2n),线性阶O(n),
线性对数阶O(nlog2n),平方阶O(n2),立方阶O(n3),…,
k次方阶O(nk),指数阶O(2n)。
随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
举个栗子:
sum = n*(n+1)/2; //时间复杂度O(1) for(int i = 0; i < n; i++){ printf("%d ",i); } //时间复杂度O(n) for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ printf("%d ",i); } } //时间复杂度O(n^2) for(int i = 0; i < n; i++){ for(int j = i; j < n; j++){ printf("%d ",i); } } //运行次数为(1+n)*n/2 //时间复杂度O(n^2) int i = 1, n = 100; while(i < n){ i = i * 2; } //设执行次数为x. 2^x = n 即x = log2n //时间复杂度O(log2n)
最坏时间复杂度和平均时间复杂度
最坏情况下的时间复杂度称最坏时间复杂度。一般不特别说明,讨论的时间复杂度均是最坏情况下的时间复杂度。
这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上界,这就保证了算法的运行时间不会比任何更长。
平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,算法的期望运行时间。设每种情况的出现的概率为pi,平均时间复杂度则为sum(pi*f(n))
常用排序算法的时间复杂度
最差时间分析 平均时间复杂度 稳定度 空间复杂度 冒泡排序 O(n2) O(n2) 稳定 O(1) 快速排序 O(n2) O(nlog2n) 不稳定 O(log2n)~O(n) 选择排序 O(n2) O(n2) 稳定 O(1) 二叉树排序 O(n2) O(nlog2n) 不稳定 O(n) 插入排序 O(n2) O(n2) 稳定 O(1) 堆排序 O(nlog2n) O(nlog2n) 不稳定 O(1) 希尔排序 O O 不稳定 O(1)
空间复杂度
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。
对于一个算法来说,空间复杂度和时间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。
有时我们可以用空间来换取时间以达到目的。
时间复杂度O(n)表示程序运行时间跟n有关,并且是线性关系。
空间复杂度O(1),表示所需空间为常量,并且与n无关。
要在 hash 表中找到一个元素就是 O(1)
要在无序数组中找到一个元素就是 O(n)
访问数组的第 n 个元素是 O(1)
访问链表的第 n 个元素是 O(n)
我给你一个简单的判断方法:
如果实现中没有循环就是 O(1)
如果实现中有一个循环就是 O(n)
int sum = 0;
for(int i = 0; i<=n; ++i)
{
sum += i;
}
一共算了n次加法,那么就说这个时间复杂度是O(n)。当然O(n)的精确的概念是,是n的最高次方,比如,某个计算共计算了3n + 2次,那么这个时间复杂度也是O(n),因为3n + 2中的最高次方是n。
如果代码这么写:
int sum = 0;
for(int i = 0; i<=n; ++i)
{
for(int j = 0; j <=n; ++j)
{
sum += (i + j);
}
}
很显然一共算了n^2次加法,那么就说这个时间复杂度是O(n^2),和上面类似,如果某个算法计算了3*n^2 + n + 1次,其时间复杂度仍然是O(n^2),因为3*n^2 + n + 1中最高的次方是n^2
所谓O(1)就是计算的次数是个常量,我们还以上面从0加到n的例子来说,如果我们用等差数列的公式,那么,代码可以这么写:
int sum = n * (n + 1) / 2
不管n有多大(当然不能溢出了),通过上面的公式只需计算一次,也就说计算的次数是不变的,这种情况的时间复杂度就可以说成O(1)。 再比如如果某个计算,不管其他条件怎么变化,均只需计算5次即可得出结果,那么这种情况的时间复杂度,也是O(1)。
时间复杂度简单的理解就是执行语句的条数。如果有循环和递归,则忽略简单语句,直接算循环和递归的语句执行次数。
比如:
int x = 1;//时间复杂度为O(1) for(int i=0; i<n; i++) { System.out.println(i); }//时间复杂度为O(n)
具体例子:
1、O(1)
int x = 1;
2、O(n)
for(int i=0; i<n; i++) { System.out.println(i); }
int n = 8, count = 0;; for(int i=1; i<=n; i *= 2) { count++; }
int n = 8, count = 0;; for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { count++; } }
int n = 8, count = 0;; for(int i=1; i<=n; i *= 2) { for(int j=1; j<=n; j++) { count++; } }
所举例子都比较简单。
空间复杂度
空间复杂度也很简单的理解为临时变量占用的存储空间。一个简单例子:
//交换两个变量x和y int x=1, y=2; int temp = x; x = y; y = temp;
一个临时变量temp,所以空间复杂度为O(1)。
除了常数阶、线性阶、平方阶、对数阶,还有如下时间复杂度:
f(n)=nlogn时,时间复杂度为O(nlogn),可以称为nlogn阶。
f(n)=n³时,时间复杂度为O(n³),可以称为立方阶。
f(n)=2ⁿ时,时间复杂度为O(2ⁿ),可以称为指数阶。
f(n)=n!时,时间复杂度为O(n!),可以称为阶乘阶。
f(n)=(√n时,时间复杂度为O(√n),可以称为平方根阶。
将请求存入redis 为了模拟多个用户的请求,使用一个for循环替代 //redis数据入队操作 $redis = new Redis(); $redis->connect('127.0.0.1',6379); for($i=0;$i<50;$i++){ try{ $redis->LPUSH('click',rand(1000,5000)); }catch(Exception $e){ echo $e->getMessage(); } } 在后台进行数据处理 守护进程 //redis数据出队操作,从redis中将请求取出 $redis = new Redis(); $redis->pconnect('127.0.0.1',6379); while(true){ try{ $value = $redis->LPOP('click'); if(!$value){ break; } //var_dump($value)."\n"; /* * 利用$value进行逻辑和数据处理 */ }catch(Exception $e){ echo $e->getMessage(); } }
update T_Money set totalMoney= totalMoney-50
begin tran declare @totalMoney decimal(12,4); select @totalMoney=totalMoney from T_Money with(updlock) update T_Money set totalMoney= @totalMoney-100 commit
1、T1进来 查询到了totalMoney
2、T2进来 想查询totalMoney 但发现这一行有个更新锁而且T1没有commit,所以等待
3、T1提交,释放更新锁
4、T2能查询totalMoney了 继续执行了
简单回答你的问题的话,update确实是原子操作