Блокировка строки MySQL INNODB в PHP - PullRequest
0 голосов
/ 12 февраля 2011

У меня есть таблица с именем meta, с двумя столбцами name и value.

В сценарии 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 занимает несколько секунд.

Ответы [ 3 ]

3 голосов
/ 13 февраля 2011
$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 голос
/ 26 сентября 2016

Сначала попробуйте эксперимент ниже, а затем попробуйте отобразить в соответствии с вашими требованиями.

Создать таблицу:

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 секунд.

create 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 до ее освобождения путем блокировки строк.После 10 секундного ожидания в режиме блокировки строк можно будет получить доступ к id строки = 5.

Попробуйте сопоставить эту концепцию с вашим сценарием, вы получите блокировку на уровне строк innoDB.Оставьте комментарий, если вам нужно подробное объяснение.

0 голосов
/ 12 февраля 2011

Это то, что вы ищете?

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 по INNODB READ LOCKS

...