php同时发起并发请求,PHP并发问题,多个并发请求;互斥?

所以我刚刚意识到PHP有可能同时运行多个请求。昨晚的日志似乎表明有两个请求进来,并行处理;每个触发从另一个服务器导入数据;每个都尝试将一条记录插入数据库。一个请求失败,当它试图插入一个记录,其他线程刚刚插入(导入的数据带有PKs;我没有使用增量ID):SQLSTATE [23000]:完整性约束违反:1062重复条目’865020’为key’PRIMARY’….

>我诊断出这个问题是否正确?

>我应该如何解决这个问题?

下面是一些代码。我已经剥离了很多(记录,从数据中创建除患者之外的其他实体),但以下应该包括相关的片段。请求点击import()方法,该方法调用importOne()来导入每条记录。注意importOne()中的保存方法;这是一个Eloquent方法(使用Laravel和Eloquent),将生成SQL以适当地插入/更新记录。

public function import()

{

$now = Carbon::now();

// Get data from the other server in the time range from last import to current import

$calls = $this->getCalls($this->getLastImport(), $now);

// For each call to import, insert it into the DB (or update if it already exists)

foreach ($calls as $call) {

$this->importOne($call);

}

// Update the last import time to now so that the next import uses the correct range

$this->setLastImport($now);

}

private function importOne($call)

{

// Get the existing patient for the call, or create a new one

$patient = Patient::where('id', '=', $call['PatientID'])->first();

$isNewPatient = $patient === null;

if ($isNewPatient) {

$patient = new Patient(array('id' => $call['PatientID']));

}

// Set the fields

$patient->given_name = $call['PatientGivenName'];

$patient->family_name = $call['PatientFamilyName'];

// Save; will insert/update appropriately

$patient->save();

}

我猜想解决方案将需要一个互斥体围绕整个导入块?如果一个请求不能获得互斥,它只需继续处理请求的其余部分。想法?

编辑:只是要注意,这不是一个关键的失败。捕获并记录异常,然后按照常规响应请求。导入在另一个请求上成功,然后该请求按照通常方式响应。用户是不明智的;他们甚至不知道导入,这不是请求的主要焦点进来。所以真的,我可以只是离开这个运行,因为,除了偶尔的异常,没有什么不好的发生。但是如果有一个修复,以防止额外的工作/多个请求被不必要地发送到这个其他服务器,这是值得追求的。

EDIT2:好了,我已经采取了摆动使用flock()实现锁定机制。想法?下面的工作?我如何单元测试这个添加?

public function import()

{

try {

$fp = fopen('/tmp/lock.txt', 'w+');

if (flock($fp, LOCK_EX)) {

$now = Carbon::now();

$calls = $this->getCalls($this->getLastImport(), $now);

foreach ($calls as $call) {

$this->importOne($call);

}

$this->setLastImport($now);

flock($fp, LOCK_UN);

// Log success.

} else {

// Could not acquire file lock. Log this.

}

fclose($fp);

} catch (Exception $ex) {

// Log failure.

}

}

EDIT3:对锁的以下替代实现的想法:

public function import()

{

try {

if ($this->lock()) {

$now = Carbon::now();

$calls = $this->getCalls($this->getLastImport(), $now);

foreach ($calls as $call) {

$this->importOne($call);

}

$this->setLastImport($now);

$this->unlock();

// Log success

} else {

// Could not acquire DB lock. Log this.

}

} catch (Exception $ex) {

// Log failure

}

}

/**

* Get a DB lock, returns true if successful.

*

* @return boolean

*/

public function lock()

{

return DB::SELECT("SELECT GET_LOCK('lock_name', 1) AS result")[0]->result === 1;

}

/**

* Release a DB lock, returns true if successful.

*

* @return boolean

*/

public function unlock()

{

return DB::select("SELECT RELEASE_LOCK('lock_name') AS result")[0]->result === 1;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值