0

我有一个名为 的表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的 's。数据库负载很重,SELECT需要几秒钟。

4

3 回答 3

4
$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锁被解锁得太早了。

于 2011-02-13T05:23:38.820 回答
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 的同一行。

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

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

于 2016-09-26T15:32:48.600 回答
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 权限。

关于 INNODB 读锁的 MySQL 文档

于 2011-02-12T05:33:56.047 回答