6

例如,我有一个非常简单的选择语句:

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

请记住,以上返回以下 id:1,2

我接下来要做的就是在 for each 中循环这些行并更新一些记录:

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,这些 ID 已经更新为状态“1”。

注意这些更新之间的时间。甚至不是同一时间。我首先认为这些查询将在完全相同的时间执行(这是不可能的,对吧?)。或者这可能吗?或者可能是查询已被缓存,我应该在我的 mysql.conf 文件中编辑一些设置吗?

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

编辑:

我更新了脚本,添加了微时间来检查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 秒

现在您看到3341233395再次由 SELECT 返回。


第三位会员(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.cnfmysql正在做这个奇怪的文件可能是什么吗?我仍然不明白为什么 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)));
4

3 回答 3

3

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

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

于 2014-01-30T18:27:16.863 回答
0

如果没有我的手,很难说出这种奇怪行为的原因。但更好的方法是在一个查询中完成所有操作:

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

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

于 2013-10-15T10:14:17.153 回答
0

我建议使用这种语法:

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

当您使用带有 '' 的整数时,编译器会引发错误。当你有一个数组时,记得使用 {}。

于 2013-12-30T09:03:42.040 回答