MySQL选择或更新行为很奇怪

时间:2013-09-30 17:46:20

标签: php mysql

我得到了一个非常简单的select语句:

SELECT     id, some_more_fields_that_do_not_matter 
FROM       account 
WHERE      status = '0' 
LIMIT      2

请注意,上面的内容会返回以下内容:1,2

我接下来要做的是将这些行循环到每个行中并更新一些记录:

UPDATE     account 
SET        connection = $_SESSION['account_id'], 
           status = '1' 
WHERE      id = $row_id

现在,id为1,2的表中的行的状态为“1”(我会检查以确保正确更新的行)。如果它没有这样做,我会撤消一切。一旦一切正常,我在第一个位置有一个计数器,在这种情况下为2,所以应该更新2行,并用简单的COUNT(*)检查。 此信息也将通过电子邮件发送,例如以下数据(这意味着所有内容都已正确更新):

- Time of update: 2013-09-30 16:30:02
- Total rows to be updated (selected) = 2
- Total rows successfully updated after completing queries = 2
The following id's should have been updated 
(these where returned by the SELECT statement):
1,2

到目前为止一切顺利。现在是奇怪的部分。 然而,其他用户的下一个查询有时会返回例如id的1,2(但这是不可能的,因为SELECT语句永远不会返回它们,因为它们不再包含状态'0'。所以会发生什么下列: 我现在收到一封电子邮件,例如:

- Time of update: 2013-09-30 16:30:39
- Total rows to be updated (selected) = 10
- Total rows successfully updated after completing queries = 8
The following id's should have been updated 
(these where returned by the SELECT statement):
1,2,3,4,5,6,7,8,9,10

现在真的很奇怪,更新选择了1和2。在大多数情况下,它很好,但很少只是没有,并返回一些相同的ID已经更新状态为'1'。

请注意这些更新之间的时间。它甚至不是同一时间。我首先想到的是,这些查询会在同一时间执行(这是不可能的吗?)。或者这可能吗?或者它可能在某种程度上是缓存的查询,我应该在我的mysql.conf文件中编辑一些设置吗?

我从来没有遇到过这个问题,我尝试过各种更新方式,但似乎一直在发生。是否可能以某种方式将这两个查询组合在一个巨大的更新查询中?所有数据都是一样的,并没有做任何奇怪的事情。我希望有人知道这可能导致什么问题,以及为什么这是随机(很少)发生的。

修改

我更新了脚本,添加了microtime来检查SELECT, UPDATE and CHECK-(SELECT)一起花了多长时间。

  

第一位会员(ID 20468)致电: 2013-10-01 08:30:10

     

已正确更新以下2个ID的2/2行:   的 33412,33395

     

查询一起 0.878005027771秒


  

第二位会员(ID 10123)致电: 2013-10-01 08:30:14

     

20/22行已正确更新以下22个ID:    33392,33412,33395,33396,41489,130​​11,12555,27971,22811还有一些但不重要

     

查询结合 3.3440849781036秒

现在您看到SELECT再次返回 33412 33395


  

第三位会员(ID 20951)致电: 2013-10-01 08:30:16

     

9/9行已正确更新以下9个ID:   的 33392,33412,33395,33396,41489,130​​11,12555,27971,22811

     

查询聚在一起没有回复任何与我有关的事情   一点点

由于我们不知道上次查询花了多长时间,我们只知道第一个和第二个查询应该正常工作而没有问题,因为如果你看它们之间有4秒钟。并且执行时间是3.34秒。除此之外,第一个开始于 2013-10-01 08:30:17 ,因为为电话记录的时间(通过电子邮件发送时)是在脚本的末尾。查看查询所花费的时间是从第一个查询开始到最后一个查询后直接停止,这是在我发送电子邮件之前(当然)。

这可能是my.cnf文件中mysql做这个奇怪的事吗? 我仍然不明白为什么id没有为最后一次(第三次)调用返回任何执行时间。 对此的解决方案是通过首先将它们保存到表中并通过cron作业一次执行一个来对这些操作进行排队。但这并不是我想要的,当一个成员打电话时应该是即时的。感谢您的帮助。

无论如何,这是我的my.cnf,以防有人对我提出建议(服务器安装了16GB RAM):

[client]
port                    = 3306
socket                  = /var/run/mysqld/mysqld.sock

[mysqld_safe] 
socket                  = /var/run/mysqld/mysqld.sock
nice                    = 0

[mysqld]
user                    = mysql
pid-file                = /var/run/mysqld/mysqld.pid
socket                  = /var/run/mysqld/mysqld.sock
port                    = 3306
basedir                 = /usr
datadir                 = /var/lib/mysql
tmpdir                  = /tmp
lc-messages-dir         = /usr/share/mysql
skip-external-locking
key_buffer              = 16M 
max_allowed_packet      = 16M
thread_stack            = 192K
thread_cache_size       = 8
myisam-recover          = BACKUP
max_connections         = 20
query_cache_type        = 1 
query_cache_limit       = 1M 
query_cache_size        = 4M
log_error               = /var/log/mysql/error.log
expire_logs_days        = 10
max_binlog_size         = 100M
innodb_buffer_pool_size = 333M
join_buffer_size        = 128K 
tmp_table_size          = 16M
max_heap_table_size     = 16M
table_cache             = 200

[mysqldump]
quick
quote-names
max_allowed_packet      = 16M

[mysql]
#no-auto-rehash # faster start of mysql but no tab completition

[isamchk]
key_buffer              = 16M

!includedir /etc/mysql/conf.d/

编辑2:

    $recycle_available = $this->Account->Membership->query("
    SELECT Account.id,
    (SELECT COUNT(last_clicks.id) FROM last_clicks WHERE last_clicks.account = Account.id AND last_clicks.roulette = '0' AND last_clicks.date BETWEEN '".$seven_days_back."' AND '".$tomorrow."') AS total, 
    (SELECT COUNT(last_clicks.id)/7 FROM last_clicks WHERE last_clicks.account = Account.id AND last_clicks.roulette = '0' AND last_clicks.date BETWEEN '".$seven_days_back."' AND '".$tomorrow."') AS avg 
    FROM membership AS Membership 
    INNER JOIN account AS Account ON Account.id = Membership.account
    WHERE Account.membership = '0' AND Account.referrer = '0' AND Membership.membership = '1'
    HAVING avg > 0.9
    ORDER BY total DESC");
    foreach($referrals as $key => $value)
    {
        $this->Account->query("UPDATE account SET referrer='".$account_id."', since='".$since_date."', expires='".$value['Account']['expires']."', marker='0', kind='1', auction_amount='".$value['Account']['auction_amount']."' WHERE id='".$recycle_available[$key]['Account']['id']."'");
        $new_referral_id[] = $recycle_available[$key]['Account']['id'];
        $counter++;
    }
    $total_updated = $this->Account->find('count',array('conditions'=>array('Account.id'=>$new_referral_id, 'Account.referrer'=>$account_id, 'Account.kind'=>1)));

3 个答案:

答案 0 :(得分:3)

您在评论中表明您正在使用交易。但是,我在您发布的PHP代码段中看不到任何$dataSource->begin();$dataSource->commit();。因此,您必须在代码段前面$dataSource->begin();,在代码段之后$dataSource->commit();$dataSource->rollback();

问题是你正在更新,然后在提交之前尝试选择。没有创建隐式提交,因此您看不到更新的数据:http://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html

答案 1 :(得分:0)

如果不掌握数据库,很难说出这种奇怪行为的原因。但更好的做法是在一个查询中完成所有操作:

UPDATE     account 
SET        connection = $_SESSION['account_id'], 
           status = '1' 
WHERE      status = '0'

这很可能会解决您所面临的问题。

答案 2 :(得分:0)

我建议使用以下语法:

$query="UPDATE account SET connection = {$_SESSION['account_id']}, status = 1 WHERE id=$row_id;";

使用带''的整数时,编译器会抛出错误。并且记得在有数组时使用{}。