在MySQL中建立一个可靠的计数器

时间:2017-01-11 07:46:34

标签: php mysql counter

我想在使用MySQL数据库用PHP编写的应用程序中创建一个计数器。用户给出一个字符串作为输入。此字符串表示数据库记录的键。它存储整数计数器值。用户返回增加的值。

这就是我现在这样做的方式:

// connect to database
try {
    $db = new PDO('mysql:host='.$dbhost.';dbname='.$dbname, $dbuser, $dbpasswd);
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch(Exception $ex) {
    echo "DB Error: ".$ex->getMessage();
    exit;
}

// update and get new value
try {
    $db->beginTransaction();
    $nc = updateNC($db, $grundsig, $library);
    $db->commit();
} catch(Exception $ex) {
    $db->rollBack();
    $err = 'DB Error: '.$ex->getMessage();
}

// [...]

function updateNC($db, $grundsig, $library) {

    // increment counter ("nc")
    $query = $db->prepare('
        update numeruscurrens n
        join libraries l using(libid)
        set n.nc = n.nc+1
        where n.grundsig = :grundsig and l.libname = :libname
    ');
    $query->execute([
        ':grundsig' => $grundsig,
        ':libname' => $library
    ]);

    // get new counter value
    $query = $db->prepare('
        select *
        from numeruscurrens n
        join libraries l using(libid)
        where n.grundsig = :grundsig and l.libname = :libname
    ');
    $query->execute([
        ':grundsig' => $grundsig,
        ':libname' => $library
    ]);
    $result = $query->fetchAll();
    return $result[0]['nc'];
}

重要的部分发生在updateNC函数中。我做了一个更新以增加计数器,我希望有新的计数器值。

现在问题出在这两个命令之间,另一个用户可以进行更新。可能会发生两个用户错误的计数器值。

如何可靠地增加计数器并为该用户获取新值?

我还想过使用像Redis这样具有INCR命令的键值数据库。但我认为这有点夸张。

1 个答案:

答案 0 :(得分:1)

如果您想要确定它们将获得立即递增的值,那么您应该使用一种方法来锁定要更新的行,然后再提交事务。这可以使用FOR UPDATE提示和之前的SELECT查询来锁定您即将更新的行。

我只会添加SQL代码以专注于实际问题。

SET @counter = 0;

BEGIN;

SELECT n.nc INTO @counter
FROM numeruscurrens n
JOIN libraries l using(libid)
WHERE n.grundsig = @grundsig and l.libname = @libname
FOR UPDATE;

update numeruscurrens n
join libraries l using(libid)
set n.nc = n.nc+1
where n.grundsig = @grundsig and l.libname = @libname

/* You are always incrementing, aren't you? */
SET @counter = @counter + 1;

SELECT @counter;

COMMIT;

另请注意,这不是免费的。实际上,必须将此增量值正确地返回给用户,因为此锁定会因行锁定而对性能产生负面影响,并可能会造成死锁。最后但同样重要的是,确保该表使用InnoDB引擎。 MyIsam不支持行级锁。