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.

Revisions

  1. 娃娃脾气3 revised this gist Sep 29, 2021. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions Counter.php
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,4 @@

    /**
    * @param $tag
    * @param int $num
    @@ -21,6 +22,7 @@ public static function incr($tag, $num = 1)
    if (!$counter) {
    try {
    // 不存在则创建
    // todo 这里没有处理两个创建冲突的情况,可以用tag的唯一索引来代替
    CounterModel::create(['tag' => $tag, 'num' => 0]);
    $counter = CounterModel::where('tag', $tag)->lockForUpdate()->first();
    } catch (\PDOException $PDOException) {
  2. 娃娃脾气3 created this gist Sep 29, 2021.
    58 changes: 58 additions & 0 deletions Counter.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,58 @@
    /**
    * @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 {
    // 不存在则创建
    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;
    }