口试被问到了这个问题,找了答案,记录一下
场景:多个用户抢一张票假如有100W个用户,抢一张票,除了负载均衡的办法,怎么支持高并发?
修正字段 :将库存字段number字段设为unsigned,当库存为0时,由于字段不能为负数,将会返回false;

利用悲观锁(不适宜高并发):悲观锁,也便是在修正数据的时候,采取锁定状态,排斥外部要求的修正。碰着加锁的状态,就必须等待;缺陷:不适宜高并发场景。每个要求都须要等待“锁”,某些线程可能永久都没有机会抢到这个“锁”,这种要求就会去世在那里。同时,这种要求会很多,瞬间增大系统的均匀相应韶光,结果是可用连接数被耗尽,系统陷入非常。
Mysql事务:利用MySQL的事务,锁住操作的行;
//仿照下单操作//库存是否大于0mysqli_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");}?>
FIFO行列步队(不适宜高并发):直接将要求放入行列步队中的,采取FIFO(First Input First Output,前辈先出),这样的话,我们就不会导致某些要求永久获取不到锁 ;缺陷:不适宜高并发。高并发的场景下,由于要求很多,很可能一瞬间将行列步队内存“撑爆”,然后系统又陷入到了非常状态。或者设计一个极大的内存行列步队,也是一种方案,但是,系统处理完一个行列步队内要求的速率根本无法和猖獗涌入行列步队中的数目比较。也便是说,行列步队内的要求会越积累越多,终极Web系统均匀相应时候还是会大幅低落,系统还是陷入非常。
文件锁:对付日IP不高或者说并发数不是很大的运用,一样平常不用考虑这些!
用一样平常的文件操作方法完备没有问题。但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就随意马虎造成数据丢失
利用非壅塞的文件排他锁:
<?php//优化方案4:利用非壅塞的文件排他锁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); ?>
乐不雅观锁(主要):乐不雅观锁,是相对付“悲观锁”采取更为宽松的加锁机制,大都是采取带版本号(Version)更新。实现便是,这个数据所有要求都有资格去修正,但会得到一个该数据的版本号,只有版本号符合的才能更新成功,其他的返回抢购失落败。这样的话,我们就不须要考虑行列步队的问题,不过,它会增大CPU的打算开销。但是,综合来说,这是一个比较好的办理方案;如redis的watch;
<?php$redis = new redis(); $result = $redis->connect('127.0.0.1', 6379); echo $mywatchkey = $redis->get("mywatchkey");/ //插入抢购数据 if($mywatchkey>0) { $redis->watch("mywatchkey"); //启动一个新的事务。 $redis->multi(); $redis->set("mywatchkey",$mywatchkey-1); $result = $redis->exec(); if($result) { $redis->hSet("watchkeylist","user_".mt_rand(1,99999),time()); $watchkeylist = $redis->hGetAll("watchkeylist"); echo "抢购成功!
<br/>"; $re = $mywatchkey - 1; echo "剩余数量:".$re."<br/>"; echo "用户列表:<pre>"; print_r($watchkeylist); }else{ echo "手气不好,再抢购!
";exit; } }else{ // $redis->hSet("watchkeylist","user_".mt_rand(1,99999),"12"); // $watchkeylist = $redis->hGetAll("watchkeylist"); echo "fail!
<br/>"; echo ".no result<br/>"; echo "用户列表:<pre>"; // var_dump($watchkeylist); }/$rob_total = 100; //抢购数量if($mywatchkey<=$rob_total){ $redis->watch("mywatchkey"); $redis->multi(); //在当前连接上启动一个新的事务。 //插入抢购数据 $redis->set("mywatchkey",$mywatchkey+1); $rob_result = $redis->exec(); if($rob_result){ $redis->hSet("watchkeylist","user_".mt_rand(1, 9999),$mywatchkey); $mywatchlist = $redis->hGetAll("watchkeylist"); echo "抢购成功!
<br/>"; echo "剩余数量:".($rob_total-$mywatchkey-1)."<br/>"; echo "用户列表:<pre>"; var_dump($mywatchlist); }else{ $redis->hSet("watchkeylist","user_".mt_rand(1, 9999),'meiqiangdao'); echo "手气不好,再抢购!
";exit; }}?>