Skip to content

Instantly share code, notes, and snippets.

@shellus
Last active September 29, 2021 06:17
Show Gist options
  • Save shellus/b655a82a09a74e33e85d3c8aa4ba24be to your computer and use it in GitHub Desktop.
Save shellus/b655a82a09a74e33e85d3c8aa4ba24be to your computer and use it in GitHub Desktop.
Laravel mysql Counter code
/**
* @param $tag
* @param int $num
* @return int|mixed
*/
public static function incr($tag, $num = 1)
{
if ($num < 1) {
throw new \Exception('num err');
}
// const TAG_MAX_LEN = 64;
if (strlen($tag) > self::TAG_MAX_LEN) {
throw new \Exception('tag too long');
}
$db = CounterModel::query()->getConnection();
try {
$db->beginTransaction();
$counter = CounterModel::where('tag', $tag)->lockForUpdate()->first();
if (!$counter) {
try {
// 不存在则创建
// todo 这里没有处理两个创建冲突的情况,可以用tag的唯一索引来代替
CounterModel::create(['tag' => $tag, 'num' => 0]);
$counter = CounterModel::where('tag', $tag)->lockForUpdate()->first();
} catch (\PDOException $PDOException) {
// SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction
if (strpos($PDOException->getMessage(), 'Deadlock') === false) {
throw $PDOException;
}
// 如果创建失败,就用其他事务创建的
// 等待其他事务完成创建
sleep(1);
$counter = CounterModel::where('tag', $tag)->lockForUpdate()->first();
if (!$counter) {
throw new \Exception('放弃创建,但还是读不到数据');
}
}
}
if ($counter) {
$counter->num = $counter->num + $num;
if ($counter->num > self::NUM_MAX_MYSQL) {
// const NUM_MAX_MYSQL = 255 * 255 * 255 * 255;
throw new \Exception('递增达到数据库最大值');
}
$counter->save();
$result = $counter->num;
}
$db->commit();
} catch (\Exception $exception) {
$db->rollBack();
throw $exception;
}
return $result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment