【问题标题】:Mysql Innodb does not return the last row on select with desc orderMysql Innodb 不使用 desc 顺序返回 select 的最后一行
【发布时间】:2013-05-03 13:37:23
【问题描述】:

我们有如下表格方案:

CREATE TABLE IF NOT EXISTS `offers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `campaign_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `price` double NOT NULL,
  `ip` varchar(15) NOT NULL,
  `cdate` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `campaign_id` (`campaign_id`,`price`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin5 AUTO_INCREMENT=190514 ;

对于用户的每个新报价,我们都会检查最后一个订单是否由同一用户给出:

"select user_id from offers where campaign_id='".$campaign['id']."' order by id desc limit 1"

如果 user_id 相同,我们会阻止新提议以保护用户免受意外双击。

如果报价没有任何问题,我们会插入报价:

"insert into offers(campaign_id,user_id,price,ip,cdate) values (".$campaign['id'].",".$user['id'].",'".$price."','".$_SERVER['REMOTE_ADDR']."',".time().")"

但问题是 select 仅在大约 1 秒后返回最后插入的行。这意味着如果用户点击按钮太快,他们可以插入多个优惠。

我们使用 5.5.30-30.2-log Percona Server 作为我们的数据库服务器。 以下是我们的 my.cnf 文件:

[mysqld]
datadir                         = /var/lib/mysql
tmpdir                          = /var/lib/mysqltmp
socket                          = /var/lib/mysql/mysql.sock
skip-external-locking           = 1
skip-name-resolve
open-files-limit                = 40000
max_heap_table_size             = 64M
tmp_table_size                  = 64M
log-error                       = /var/log/mysqld.log
thread-cache-size               = 50
table-cache                     = 4096
table-open-cache                = 4096
table-definition-cache          = 512
query-cache-size                = 0
query-cache-limit               = 16M
query-cache-type                = 0
sort-buffer-size                = 1M
read-buffer-size                = 1M
read-rnd-buffer-size            = 8M
join-buffer-size                = 1M
tmp-table-size                  = 64M
max-heap-table-size             = 64M
back-log                        = 100
max-connections                 = 10000
max-connect-errors              = 10000
max-allowed-packet              = 256M
interactive-timeout             = 360
wait-timeout                    = 360
innodb                          = FORCE
key-buffer-size                 = 32M
myisam-sort-buffer-size         = 4M
innodb-buffer-pool-size         = 60G
innodb-log-file-size            = 256M
innodb_log_files_in_group       = 2
innodb-log-buffer-size          = 4M
innodb-file-per-table          = 1
innodb-thread-concurrency       = 8
innodb-flush-log-at-trx-commit  =2
server-id                       = 1
slow-query-log                  = 1
slow-query-log-file             = /var/lib/mysqllogs/slow-log

【问题讨论】:

    标签: php mysql innodb percona


    【解决方案1】:

    您是否尝试在更新/插入事件上使用 SQL 触发器来检查例如是否

    SELECT count(*) FROM `offers` 
    WHERE campaign_id = [Your_value] AND user_id = [Your_value];
    

    等于 0,并按照它行事。 (如果已经存在,请勿进行任何修改 [...])

    编辑:顺便说一句,这似乎更像是 Ajax / Php / 无论您使用什么问题,而不是 MySQL 数据库的错误使用。您应该阻止用户在短时间内发送许多请求。

    【讨论】:

      【解决方案2】:

      MySQL 查询缓存。

      MySQL 将在第一次点击时检查并返回一个空结果,然后它会插入记录,然后每次后续点击 MySQL 返回空结果的缓存值并允许再次插入。

      在我正在处理的请求非常接近的网站上发生了类似的事情。

      我们最终不得不先运行这个

      SET SESSION query_cache_type=0;
      

      【讨论】:

        【解决方案3】:

        您是否尝试过将插入显式包装到事务中?做类似的事情:

        START TRANSACTION
        INSERT ...
        COMMIT
        SELECT ...
        

        这应该保证 SELECT 将返回最后插入的数据。

        有关 InnoDB 隔离级别的更多信息:[1][2]

        【讨论】:

          【解决方案4】:

          我认为问题是由多个连接在插入之前选择同一行引起的,如下所示:

          php连接1:选择最后一个offers user_id

          php连接2:选择最后一个offer user_id(给同一个)

          php连接1:插入offer

          php连接2:插入offer

          我通过在选择之前锁定表并在插入后解锁它来解决问题:

          锁表提供写入

          选择最后一个offer的user_id

          如果与当前用户插入报价不同

          解锁表优惠

          【讨论】:

            猜你喜欢
            • 2015-07-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-03-18
            • 2011-06-08
            • 1970-01-01
            • 2014-07-21
            • 2023-03-09
            相关资源
            最近更新 更多