【发布时间】:2013-10-04 08:26:01
【问题描述】:
最近,我的 MySQL 数据库中出现了一些奇怪的重复条目。此表中的条目在对 PHP 页面的 PUT 请求期间插入。该表包含 3 个没有外部引用的字段:
- manga_id(主键;自动递增)- bigint(20);
- 名称 - varchar(255);
- manga_cid - varchar(255)。
PHP 代码如下:
class MangaHandler {
private function getMangaFromName($name) {
$id = $this->generateId($name);
$mangas = new Query("SELECT * FROM tbl_manga WHERE manga_cid = '" . $this->conn->escapeString($id) . "'", $this->conn);
if(!$mangas || $mangas->hasError()) {
logError("getMangaFromName($name): " . $this->conn->getError());
return null;
}
if($mangas->moveNext()) {
return $mangas->getRow();
}
return null;
}
private function addManga($name) {
$manga_row = null;
$error = false;
$cid = $this->generateId($name);
$sql = sprintf("INSERT INTO tbl_manga(name, manga_cid) VALUES ('%s', '%s')", $this->conn->escapeString($name), $this->conn->escapeString($cid));
if(!$this->conn->execute($sql))
$error = true;
// some more code ...
if($error) {
logError("addManga($name): " . $this->conn->getError());
}
return $manga_row;
}
public function addMangaSourceAndFollow($name, $url, $source_id, $user_id, $stick_source = false, $stick_lang = 'English') {
// validate url
$manga = $this->getMangaFromUrl($url, $source_id);
if(!$manga) {
$manga = $this->getMangaFromName($name);
if(!$manga) $manga = $this->addManga($name);
// ...
}
// ...
return true;
}
}
class MangaRestService extends CommonRestService
{
public function performPut($url, $arguments, $accept, $raw) {
header('Content-type: application/json');
header("Cache-Control: no-cache, must-revalidate");
$json = json_decode($raw, true);
// some input validation and auth
$ms = new MangaHandler();
try {
$ret = $ms->addMangaSourceAndFollow(null, $json['url'], $source['source_id'], $user['user_id'], $enforce == 1);
// throw exception if some ret is invalid
// return proper json response
} catch(Exception $e) {
$conn->rollback();
logError("MangaRestService.performPut($url, [" . implode("; ", $arguments) . "], $accept, $raw): " . $e->getMessage());
echo RestResponse::getSomeErrorResponse()->toJSON();
}
}
}
$serv = new MangaRestService();
// performs some checks and invokes the appropriate GET, POST, PUT or DELETE method
// in this case it's the performPut method above
$serv->handleRawRequest();
漫画名称被过滤(只允许使用字母数字字符、下划线和其他一些字符)并变成manga_cid(在表格中应该是唯一的)。
代码主要检查是否存在具有特定 manga_cid 的漫画。只有在没有的情况下才会插入一个新条目。我已经测试了代码并且它可以工作,但是,在部署应用程序之后。我收到了具有重复 manga_cid 的条目(有时超过 2 个) )。这种情况很少见,因为大多数条目都是唯一的。此外,我的日志中没有错误。
可能是由于某种原因,多个 HTTP PUT 请求同时执行,并且由于没有同步,因此 INSERT 被多次调用? 我觉得这不太可能,特别是因为该应用只允许您按下执行此请求的按钮一次,然后它就会消失。
我尝试过使用 MySQL 事务,但没有解决问题。我知道将字段设置为唯一可能会让我避免这些重复的条目,但是我必须对数据库执行一些繁重的维护才能首先删除重复的条目。尽管此表结构简单,但在其他几个表中都引用了 manga_id。 另外,我很好奇为什么会这样:-)
以防万一,这里是 Query 类:
class Query extends QueryBase
{
function Query($query, &$conn)
{
$this->recordset = array();
$this->has_error=0;
$regs = mysqli_query($conn->getConnection(), $query);
if(!$regs)
{
$this->has_error=1;
return;
}
$index = 0;
$this->current_index=-1;
while(($row = mysqli_fetch_array($regs, MYSQL_ASSOC)))
{
$this->recordset[$index]=$row;
$index++;
}
mysqli_free_result($regs);
}
public function moveNext()
{
if($this->current_index<(sizeof($this->recordset)-1))
{
$this->current_index++;
return 1;
}
else
return 0;
}
public function moveBack()
{
if($this->current_index>=1)
{
$this->current_index--;
return 1;
}
else
return 0;
}
public function recordCount()
{
return sizeof($this->recordset);
}
public function get($field)
{
return $this->recordset[$this->current_index][$field];
}
public function getRow()
{
return $this->recordset[$this->current_index];
}
public function hasError()
{
return $this->has_error;
}
}
感谢您的帮助。
【问题讨论】:
-
您可以在
manga_cid列上添加唯一索引。这将防止重复条目,并且您可以在插入时捕获错误。或者使用 mysqlupdate into语法。 -
这是您用于查询类的内容吗? code.google.com/p/class-query
-
Query类是我自己写的,直接调用mysqli api。
-
啊,好吧——问题很可能出在那个班级上。 PDO 可能是一种更加基于标准的方法?
标签: php mysql concurrency duplicates