×

PHP解决并发问题的几种实现

96
金星show
2017.03.16 10:23* 字数 1530

-对于商品抢购等并发场景下,可能会出现超卖的现象,这时就需要解决并发所带来的这些问题了.在PHP语言中并没有原生的提供并发的解决方案,因此就需要借助其他方式来实现并发控制。

#######方案一:使用文件锁排它锁

flock函数用于获取文件的锁,这个锁同时只能被一个线程获取到,其它没有获取到锁的线程要么阻塞,要么获取失败
在获取到锁的时候,先查询库存,如果库存大于0,则进行下订单操作,减库存,然后释放锁
flock()函数锁定或释放文件 若成功,则返回 true。若失败,则返回 false。
flock($fp,lock,block)
lock
共享锁定(读取) LOCK_SH
独占锁定(写入) LOCK_EX
释放锁定LOCK_UN
block 若设置为true 则当进行锁定时阻挡其他进程

注意

1.使用共享锁LOCK_SH,如果是读取,不需要等待,但如果是写入,需要等待读取完成。
2.使用独占锁LOCK_EX,无论写入/读取都需要等待。
3.LOCK_UN,无论使用共享/读占锁,使用完后需要解锁。
4.LOCK_NB,当被锁定时,不阻塞,而是提示锁定。

穿透到数据库,而使数据库奔溃,这里可用文件锁来解决

    $data = $cache->get('key');  
    if(!$data){  
        $fp = fopen('lockfile');  
        if(flock($fp, LOCK_EX)){  
            $data = $cache->get('key');//拿到锁后再次检查缓存,这时可能已经有了  
            if(!$data){  
                $data = mysql->query($sql);  
                $cache->set('key', $data);  
            }  
            flock($fp, LOCK_UN);  
        }  
        fclose($fp);  
    }  

#######方案二:使用MySQL数据库提供的悲观锁

Innodb存储引擎支持行级锁,当某行数据被锁定时,其他进程不能对这行数据进行操作
先查询并锁定行:
select stock_num from table where id=1 for update
if(stock_num > 0){
//下订单
update table set stock_num=stock-1 where id=1
}

#######方案三:使用队列

将用户的下单请求依次存入一个队列中,后台用一个单独的进程处理队列中的下单请求
1.创建队列数据表
2.把任务(比如 订单创建)插入队列表中
3.linux上做个定时任务,异步执行队列表中的任务,一个一个排队执行。

#######方案四:使用Redis/Memcached

redis的操作都是原子性的,可以将商品的库存存入redis中,下单之前对库存进行decr操作,如果返回的值大于等于0等可以下单,否则不能下单,这种方式效率较高

if(redis->get('stock_num') > 0){
     stock_num = redis->decr('stock_num')
     if(stock_num >= 0){
      //下订单
      }else{
      //库存不足
     }
}else{
//库存不足
}

其他并发问题:

在现实应用中,很多情况下会把数据存入缓存,当缓存失效时,去数据库取数据并重新设置缓存,如果这时并发量很大,会有很多进程同时去数据库取数据,导致很多请求
说白了,要解决并发问题就必须要加锁,各种方案的本质都是加锁

注释:
第一 首先,确认服务器硬件是否足够支持当前的流量。

第二 其次,优化数据库访问。

前台实现完全的静态化当然最好,可以完全不用访问数据库,不过对于频繁更新的网站,
静态化往往不能满足某些功能。

缓存技术就是另一个解决方案,就是将动态数据存储到缓存文件中,动态网页直接调用
这些文件,而不必再访问数据库,WordPress和Z-Blog都大量使用这种缓存技术。

如果确实无法避免对数据库的访问,那么可以尝试优化数据库的查询SQL.避免使用
Select * from这样的语句,每次查询只返回自己需要的结果,避免短时间内的大
量SQL查询。

第三,禁止外部的盗链。

外部网站的图片或者文件盗链往往会带来大量的负载压力,因此应该严格限制外部对
于自身的图片或者文件盗链,好在目前可以简单地通过refer来控制盗链,Apache自
己就可以通过配置来禁止盗链,IIS也有一些第三方的ISAPI可以实现同样的功能。当
然,伪造refer也可以通过代码来实现盗链,不过目前蓄意伪造refer盗链的还不多,
可以先不去考虑,或者使用非技术手段来解决,比如在图片上增加水印。

第四,控制大文件的下载。

大文件的下载会占用很大的流量,并且对于非SCSI硬盘来说,大量文件下载会消耗
CPU,使得网站响应能力下降。因此,尽量不要提供超过2M的大文件下载,如果需要
提供,建议将大文件放在另外一台服务器上。

第五,使用不同主机分流主要流量

将文件放在不同的主机上,提供不同的镜像供用户下载。比如如果觉得RSS文件占用
流量大,那么使用FeedBurner或者FeedSky等服务将RSS输出放在其他主机上,这
样别人访问的流量压力就大多集中在FeedBurner的主机上,RSS就不占用太多资源了。

第六,使用流量分析统计软件。

在网站上安装一个流量分析统计软件,可以即时知道哪些地方耗费了大量流量,哪些页
面需要再进行优化,因此,解决流量问题还需要进行精确的统计分析才可以。我推荐使
用的流量分析统计软件是Google Analytics(Google分析)。我使用过程中感觉其
效果非常不错,稍后我将详细介绍一下Google Analytics的一些使用常识和技巧。

php知识
Web note ad 1