我有一个名为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
。数据库负载很重,SELECT
需要几秒钟。
答案 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权限。