zoukankan      html  css  js  c++  java
  • php怎样应对高并发

    高并发下的数据安全

    我们知道在多线程写入同一个文件的时候,会出现“线程安全”的问题(多个线程同时运行同一段代码,如果每次运行结果和单线程运行的结果是一样的,结果和预期相同,就是线程安全的)。

    如果是MySQL数据库,可以使用它自带的锁机制很好的解决问题,但是在大规模并发的场景中,是不推荐使用MySQL的。秒杀和抢购的场景中,最关键的问题,就是“超发”,如果在这方面控制不慎,会导致实际产生的订单比预售商品还多的问题。

    超发的原因:

    假设某个抢购场景中,我们一共只有100个商品,在最后一刻,我们已经消耗了99个商品,仅剩最后一个。这个时候,系统发来多个并发请求,这批请求读取到的商品余量都是1个,然后都通过了余量的判断,最终导致超发。

    值得注意的是:记得将库存字段number字段设为unsigned,当库存为0时,因为unsigned字段不能为负数,将会返回false

    优化方案

    优化1:使用MySQL的事务,锁住操作的行 BEGIN ; SELECT ... FOR UPDATE ; COMMIT ; ROLLBACK

    <?php	//优化方案1:使用MySQL的事务,锁住操作的行	include('./mysql.php');	function build_order_no(){		return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);	}	//记录日志	function insertLog($event,$type=0){		global $conn;		$sql="insert into ih_log(event,type)		values('$event','$type')";		mysqli_query($conn,$sql);	}		mysqli_query($conn,"BEGIN");  //开始事务	$sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' FOR UPDATE";//此时这条记录被锁住,其它事务必须等待此次事务提交后才能执行	$rs=mysqli_query($conn,$sql);	$row=$rs->fetch_assoc();		if($row['number']>0){		//生成订单		$order_sn=build_order_no();		$sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)			values('$order_sn','$user_id','$goods_id','$sku_id','$price')";		$order_rs=mysqli_query($conn,$sql);		//库存减少		$sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";		$store_rs=mysqli_query($conn,$sql);		if($store_rs){		  echo '库存减少成功';			insertLog('库存减少成功');			mysqli_query($conn,"COMMIT");//事务提交即解锁		}else{		  echo '库存减少失败';			insertLog('库存减少失败');		}	}else{		echo '库存不够';		insertLog('库存不够');		mysqli_query($conn,"ROLLBACK");	}

    优化2:文件锁的思路

    对于日访问量不高或者说并发数不是很大的应用,用一般的文件操作方法完全没有问题。但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失。

    <?php//优化方案2:使用非阻塞的文件排他锁	include ('./mysql.php');	//生成唯一订单号	function build_order_no(){	  return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);	} 	//记录日志	function insertLog($event,$type=0){		global $conn;		$sql="insert into ih_log(event,type)		values('$event','$type')";		mysqli_query($conn,$sql);	}	$fp = fopen("lock.txt", "w+");	if(!flock($fp,LOCK_EX | LOCK_NB)){		echo "系统繁忙,请稍后再试";		return;	} 	//下单	$sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";	$rs =  mysqli_query($conn,$sql);	$row = $rs->fetch_assoc();	if($row['number']>0){//库存是否大于0		//模拟下单操作		$order_sn=build_order_no();		$sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price)		values('$order_sn','$user_id','$goods_id','$sku_id','$price')";		$order_rs =  mysqli_query($conn,$sql);		//库存减少		$sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";		$store_rs =  mysqli_query($conn,$sql);		if($store_rs){			echo '库存减少成功';			insertLog('库存减少成功');			flock($fp,LOCK_UN);//释放锁		}else{			echo '库存减少失败';			insertLog('库存减少失败');		}	}else{		echo '库存不够';		insertLog('库存不够');	}		fclose($fp);
  • 相关阅读:
    使用FolderBrowserDialog组件选择文件夹
    使用OpenFileDialog组件打开多个文
    使用OpenFileDialog组件打开对话框
    获取弹出对话框的相关返回值
    PAT 甲级 1139 First Contact (30 分)
    PAT 甲级 1139 First Contact (30 分)
    PAT 甲级 1138 Postorder Traversal (25 分)
    PAT 甲级 1138 Postorder Traversal (25 分)
    PAT 甲级 1137 Final Grading (25 分)
    PAT 甲级 1137 Final Grading (25 分)
  • 原文地址:https://www.cnblogs.com/68xi/p/11528073.html
Copyright © 2011-2022 走看看