>  기사  >  백엔드 개발  >  PHP에서 높은 동시성을 해결하는 방법

PHP에서 높은 동시성을 해결하는 방법

小云云
小云云원래의
2018-03-26 10:39:506039검색

이 기사에서는 급매, 깜짝 세일, 복권 추첨, 티켓 잡기 및 기타 활동과 관련하여 과잉 판매를 피하기 위해 재고 수량이 제한되어 있지만 PHP의 높은 동시성을 해결하는 방법을 주로 공유합니다. 동시에 주문하는 사람이 재고 수량을 초과하면 과매도 문제가 발생합니다. 그러면 이 문제를 어떻게 해결해야 할까요? 제 생각은 다음과 같습니다(의사 코드):

sql1: Query product Inventory

if(库存数量 > 0)
{
  //生成订单...
  sql2:同时库存-1
}

동시성이 없으면 위 프로세스는 두 사람이 동시에 로그인한다고 가정합니다. .주문했는데 재고가 1개밖에 없습니다. sql1 단계에서 두 사람이 쿼리한 재고가 > 0이어서 결국 sql2를 실행했는데, 결국 재고가 -1이 되어 과매도된 결과는 아닙니다. 우리는 바.

이 문제를 해결하기 위해 더 널리 알려진 아이디어를 요약했습니다.
1. 추가 단일 프로세스를 사용하여 대기열을 처리하고 주문 요청을 대기열에 넣은 다음 하나씩 처리하므로 동시성 문제가 발생하지 않습니다. 그러나 추가 백그라운드 프로세스 시작 및 지연 문제는 당분간 여기에서 고려되지 않습니다. 여기서는 메시지 대기열을 사용할 수 있으며 종종 Memcacheq 및 Radis를 사용합니다. 예를 들어, 사용자가 확보할 수 있는 티켓이 100개인 경우 이 100개의 티켓을 캐시에 넣고 읽고 쓸 때 잠그지 않을 수 있습니다. 동시성이 큰 경우 약 500명 정도가 티켓을 성공적으로 획득할 수 있기 때문에 500명 이후의 요청은 이벤트 종료 시 정적 페이지로 직접 전송될 수 있습니다. 500명 중 400명이 물품을 구하는 것은 불가능하다. 따라서 대기열에 입장한 순서에 따라 선착순 100명만 구매에 성공하실 수 있습니다. 다음 400명은 이벤트 종료 페이지로 바로 이동됩니다. 물론 500명을 입력하는 것은 예시일 뿐이며, 숫자를 직접 조정할 수도 있습니다. 활동 종료 페이지는 데이터베이스가 아닌 정적 페이지를 사용해야 합니다. 이렇게 하면 데이터베이스에 대한 부담이 줄어듭니다.
2.MySQL 낙관적 잠금은 예를 들어 총 재고가 2이고 급매입 이벤트가 제출되면 즉시 재고가 +1이 되고 이때 재고가 3이 되며 주문이 발생한 후, (주문이 생성되기 때문에) 재고를 업데이트하기 전에 재고를 다시 쿼리합니다. 물론 재고는 -1이지만 걱정하지 마세요. 재고를 다시 확인하면 결과는 3입니다. 예상 재고 수량과 일치하는지 확인하세요. (여기서 예상되는 인벤토리는 3입니다.) 일관성이 없으면 롤백되고 사용자에게 인벤토리가 부족하다는 메시지가 표시됩니다. 여기서는 비관적 잠금에 대해 이야기합니다. 그러면 낙관적 잠금이 있어야 한다는 친구들이 있을 수 있습니다. 그렇죠?? 여기서는 제가 알고 있는 비관적 잠금과 낙관적 잠금에 대해 간략하게 설명하겠습니다.

비관적 잠금과 낙관적 잠금은 두 가지 일반적인 리소스 동시성 잠금입니다. . 디자인 아이디어는 동시 프로그래밍에서도 매우 기본적인 개념입니다. 이 기사에서는 데이터베이스 데이터에 대한 이러한 두 가지 일반적인 잠금 메커니즘의 구현을 비교하고 체계적으로 소개합니다.

비관적 잠금

비관적 잠금의 특징은 잠금을 먼저 획득한 다음 비즈니스 작업을 수행하는 것입니다. 즉, "비관적"은 잠금 획득이 실패할 가능성이 매우 높다고 믿기 때문에 먼저 잠금을 획득했는지 확인해야 합니다. 사업 운영을 수행하기 전에 성공적으로. 일반적으로 "1개의 잠금, 2개의 확인, 3개의 업데이트"라고 불리는 것은 비관적 잠금의 사용을 의미합니다. 일반적으로 데이터베이스에 대한 비관적 잠금은 데이터베이스 자체의 지원이 필요합니다. 즉, 비관적 잠금은 일반적으로 사용되는 select...for 업데이트 작업을 통해 구현됩니다. 데이터베이스가 업데이트를 위해 선택을 실행하면 선택 항목의 데이터 행에 대한 행 잠금을 획득하므로 동시에 실행되는 다른 항목이 선택됩니다. 업데이트 시 동일한 행을 선택하려고 하면 제외가 발생하여(행 잠금이 해제될 때까지 기다려야 함) 잠금 효과를 얻습니다. 업데이트용 선택으로 획득한 행 잠금은 현재 트랜잭션이 끝나면 자동으로 해제되므로 트랜잭션 내에서 사용해야 합니다.

여기서 주목해야 할 점은 데이터베이스마다 업데이트 선택에 대한 구현 및 지원이 다르다는 것입니다. 예를 들어 Oracle은 대기 없이 업데이트 선택을 지원합니다. 즉, 잠금을 얻을 수 없는 경우 오류가 대신 즉시 보고됩니다. MySQL은 대기 옵션이 없습니다. MySQL의 또 다른 문제점은 select for update 문을 실행하는 동안 스캔된 모든 행이 잠기므로 쉽게 문제가 발생할 수 있다는 것입니다. 따라서 mysql에서 비관적 잠금을 사용하는 경우 전체 테이블 스캔 대신 인덱스를 사용해야 합니다.

낙관적 자물쇠

낙관적 자물쇠의 특징: 업무 운영을 먼저 수행하고 꼭 필요한 경우가 아니면 자물쇠를 가져가지 마세요. 즉, "낙관적"은 대부분의 경우 잠금이 성공할 것이라고 믿으므로 비즈니스 작업을 완료한 후 실제로 데이터를 업데이트하는 마지막 단계 후에 잠금을 수행하면 됩니다.

데이터베이스에 대한 낙관적 잠금 구현은 완전히 논리적이며 데이터베이스의 특별한 지원이 필요하지 않습니다. 일반적인 접근 방식은 잠가야 하는 데이터에 버전 번호나 타임스탬프를 추가한 후 다음과 같이 구현하는 것입니다.

1. SELECT data AS old_data, version AS old_version FROM …;2. 根据获取的数据进行业务操作,得到new_data和new_version3. UPDATE SET data = new_data, version = new_version WHERE version = old_versionif (updated row > 0) {    // 乐观锁获取成功,操作完成
} else {    // 乐观锁获取失败,回滚并重试
}

乐观锁是否在事务中其实都是无所谓的,其底层机制是这样:在数据库内部update同一行的时候是不允许并发的,即数据库每次执行一条update语句时会获取被update行的写锁,直到这一行被成功更新后才释放。因此在业务操作进行前获取需要锁的数据的当前版本号,然后实际更新数据时再次对比版本号确认与之前获取的相同,并更新版本号,即可确认这之间没有发生并发的修改。如果更新失败即可认为老版本的数据已经被并发修改掉而不存在了,此时认为获取锁失败,需要回滚整个业务操作并可根据需要重试整个过程。好吧,在此唠叨总结下这两个锁:

  • 乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能

  • 乐观锁还适用于一些比较特殊的场景,例如在业务操作过程中无法和数据库保持连接等悲观锁无法适用的地方

3.根据update结果来判断,我们可以在sql2的时候加一个判断条件update table set 库存=xxx where 库存>0,如果返回false,则说明库存不足,并回滚事务。
4.借助文件排他锁,在处理下单请求的时候,用flock锁定一个文件,如果锁定失败说明有其他订单正在处理,此时要么等待要么直接提示用户"服务器繁忙"

大致代码如下:
阻塞(等待)模式

<?php$fp = fopen("lock.txt", "w+");if(flock($fp,LOCK_EX))   //锁定当前指针,,,{  //..处理订单
  flock($fp,LOCK_UN);
}fclose($fp);?>

非阻塞模式

<?php$fp = fopen("lock.txt", "w+");if(flock($fp,LOCK_EX | LOCK_NB))
{  //..处理订单
  flock($fp,LOCK_UN);
}else{  echo "系统繁忙,请稍后再试";
} 
fclose($fp);?>

5.如果是分布式集群服务器,就需要一个或多个队列服务器 小米和淘宝的抢购还是有稍许不同的,小米重在抢的那瞬间,抢到了名额,就是你的,你就可以下单结算。而淘宝则重在付款的时候的过滤,做了多层过滤,比如要卖10件商品,他会让大于10的用户抢到,在付款的时候再进行并发过滤,一层层的减少一瞬间的并发量。

6.使用redis锁 product_lock_key 为票锁key 当product_key存在于redis中时,所有用户都可以进入下单流程。 当进入支付流程时,首先往redis存放sadd(product_lock_key, “1″),如果返回成功,进入支付流程。如果不成,则说明已经有人进入支付流程,则线程等待N秒,递归执行sadd操作。

当然类似于淘宝双11的疯抢架构远远比我说滴这些复杂多啦....更多解决方案需要不停滴去实战中获取心得....大家有好的解决思路清随时共享留言哈。

相关推荐:

php高并发应当用Apache还是IIS

php高并发访问写文件

php高并发下的疑问。

위 내용은 PHP에서 높은 동시성을 해결하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.