在PHP中锁定MySQL INNODB行

时间:2011-02-12 05:16:44

标签: php mysql innodb mariadb xtradb

我有一个名为meta的表格,其中有两列namevalue

在许多客户端同时调用的php脚本中,我这样做: -

$mysqli->multi_query("SELECT id FROM links WHERE id > (SELECT value FROM meta WHERE name='scan') LIMIT 1000;UPDATE meta SET value=value+1000 WHERE name='scan';");

或者: -

$mysqli->multi_query("SELECT id FROM links WHERE id > (SELECT value FROM meta WHERE name='scan' <b>FOR UPDATE</b>) LIMIT 1000;UPDATE meta SET value=value+1000 WHERE name='scan';");

不幸的是,这似乎不起作用,因为客户端最终会出现重复的id。数据库负载很重,SELECT需要几秒钟。

3 个答案:

答案 0 :(得分:3)

$mysqli->autocommit(FALSE);
$mysqli->query("BEGIN;");
$mysqli->multi_query("SELECT id FROM links WHERE id > (SELECT value FROM meta WHERE name='scan' FOR UPDATE) LIMIT 1000;UPDATE meta SET value=value+1000 WHERE name='scan';");
$mysqli->commit();

这是一个复杂的问题;锁定和事务级别,但上面的魔术是BEGIN语句。没有它,每个语句都在自己的事务级别运行,并且FOR UPDATE锁定过早解锁。

答案 1 :(得分:1)

首先尝试以下实验,然后尝试根据您的要求进行映射。

创建表格:

CREATE TABLE `t1` (                       
  `id` int(11) NOT NULL AUTO_INCREMENT,                 
  `notid` int(11) DEFAULT NULL,                         
  PRIMARY KEY (`id`)                                    
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

然后创建:

创建rowlocking.php

<?php
    require_once('connectvars.php');
    // Connect to the database 
    $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);

    $query = "START TRANSACTION";
    $data = mysqli_query($dbc, $query);

    $query = "SELECT * FROM t1 WHERE id=5 FOR UPDATE";
    $data = mysqli_query($dbc, $query);

    if (mysqli_num_rows($data) != 0) {
        $row = mysqli_fetch_array($data);
        echo $row['id'];
        echo $row['notid'];
    }

    //$query = "COMMIT";
    //$data = mysqli_query($dbc, $query);
    sleep(10);
    echo "After 10 seconds";
?>

以上脚本将访问id = 5的行,并锁定其他事务,直到10秒休眠时间。

创建rowlocking1.php

  <?php
    require_once('connectvars.php');
    // Connect to the database 
    $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);

    $query = "START TRANSACTION";
    $data = mysqli_query($dbc, $query);

    $query = "SELECT * FROM t1 WHERE id=5 FOR UPDATE";
    $data = mysqli_query($dbc, $query);

    if (mysqli_num_rows($data) != 0) {
        $row = mysqli_fetch_array($data);
        echo $row['id'];
        echo $row['notid'];
    }
    //sleep(10);
    //$query = "COMMIT";
    //$data = mysqli_query($dbc, $query);
    //echo "After 10 seconds";
?>

以上脚本尝试访问id = 5的同一行。

现在,如果您运行脚本行锁定并且在该10秒休眠时间内运行rowlocking1,它将无法访问行id = 5,直到通过rowlocking释放它。一旦10秒的睡眠时间超过行锁定就能访问行id = 5。

尝试使用您的脚本映射此概念,您将获得innoDB行级锁定。如果您需要详细说明,请发表评论。

答案 2 :(得分:0)

这是你在找什么?

query("SELECT id FROM links WHERE id > (SELECT value FROM meta WHERE name='scan' LOCK IN SHARE MODE) LIMIT 1000 LOCK IN SHARE MODE;
UPDATE meta SET value=value+1000 WHERE name='scan';");

我不确定这是否是您要查找的内容,但如果是,请记住在必要时解锁表格,并确保您使用的帐户具有LOCK权限。

MySQL Documentation on INNODB READ LOCKS