查询主键与查询其他未排列列性能

时间:2015-06-03 02:33:52

标签: mysql sql performance

我有一张表 saleItem

+---------------+--------------+------+-----+---------+-------+
| Field         | Type         | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+-------+
| id            | char(16)     | NO   | PRI | NULL    |       |
| expiry_date   | char(8)      | NO   | MUL | NULL    |       |
| status        | varchar(10)  | NO   | MUL |         |       |
| last_update   | datetime     | NO   | MUL | NULL    |       |
| status_change | datetime     | YES  | MUL | NULL    |       |
+---------------+--------------+------+-----+---------+-------+

此处 id 是主键。此表包含数百万个条目,其中状态可以包含五个值。我想使用状态在此表上使用更新查询。当我使用时:

UPDATE saleItem SET status="aa" where status="bb";

我得到以下异常,因为此表也由其他应用程序更新:

  

SQLException:超出锁定等待超时;尝试重新启动交易

使用如下的子查询可以解决问题:

selectedIds=SELECT id FROM saleItem WHERE status='bb'

UPDATE saleItem SET status="aa" where id in (selectedIds);

此查询是否有效?

此查询的效果怎么样?

有没有更好的方法来解决这个问题?

创建表格查询:

CREATE TABLE `saleItem` (
  `id` char(16) NOT NULL,
  `expiry_date` char(8) NOT NULL,
  `status` varchar(10) NOT NULL DEFAULT '',
  `last_update` datetime NOT NULL,
  `status_change` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `expiry_date_idx` (`expiry_date`),
  KEY `status_idx` (`status`),
  KEY `last_update_idx` (`last_update`),
  KEY `status_change_idx` (`status_change`)
) ENGINE=InnoDB;

3 个答案:

答案 0 :(得分:2)

不,如果尝试更新同一组行,使用子查询将无法解决问题,而不是直接解决问题。我认为问题在于您正在尝试同时更新太多行,并且您正在与锁进行争用(其他会话持有行上的锁。)

我尝试将这个巨大的交易分成更小的块,一次获得几千行。像这样:

  UPDATE saleItem SET status='aa' where status='bb' LIMIT 4000;

重复多次,直到更新的行数为零。

status是索引中的前导列吗?目前尚不清楚表中可用的索引。 SHOW CREATE TABLE saleitem的输出可以让我们更好地了解存在哪些索引。

如果status上没有合适的索引,那么可能MySQL会从表的开头开始,然后开始查看行。找到4000行进行更新不应该花费太长时间。下一次,如果它再次从表的开头开始,则需要查看更多的行。

所以,要做到这一点,我肯定希望得到一个索引。我们可能会尝试像

这样的东西
CREATE TABLE bb_id
( ai INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id CHAR(16) NOT NULL PRIMARY KEY
);
INSERT INTO bb_id (id) SELECT id FROM saleitem WHERE status = 'bb';

然后我可以使用连接操作来更新批量的行。

UPDATE saleitem s
  JOIN bb_id b
    ON b.id = s.id
   SET s.status = 'aa' 
 WHERE s.status = 'bb'
   AND b.ai  > 0
   AND b.ai <= 4000 

下一次运行,

   AND b.ai  > 4000
   AND b.ai <= 8000

并不能保证您不会遇到LOCK TIMEOUT问题,但您正在运行的交易规模会更合理。你可能想要在流行音乐中做更多的4000行,或者更少。但这是我要采取的方法。

答案 1 :(得分:0)

从您提供的表格结构中,我会提出几点:

  1. 您正在使用varchar(10)作为您的状态,并且它没有KEY / INDEX。 这将导致每个间隔发生一次表扫描,以尝试找出哪些记录的状态为“bb”。

  2. 如果您不想放入索引,我会诚实地在2部分查询中执行此操作。问题是,当您更新表时,您还通过更新同一列中的值来更改混合,因此表扫描会锁定更新。

  3. 我会做以下SQL:

    dispatch_async(dispatch_get_main_queue(), {
         // Display alert message with confirmation
        self.signInViewIndicator.stopAnimating()  
        var myAlert = UIAlertController(title: "Alert", message: messageToDisplay, preferredStyle: UIAlertControllerStyle.Alert)
        let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler:nil)
        myAlert.addAction(okAction);
        self.presentViewController(myAlert, animated: true, completion: nil)
    })
    

    SQL Above将首先获取状态为“bb”的所有ID,然后在收到完整列表后更新它们。

答案 2 :(得分:0)

您只有5个不同的status值?将其设为ENUM。那将需要1个字节而不是现在的几个字节。较小 - &gt;更快。

只有5个值?对于某些值,您的UPDATE将执行表扫描而不是使用索引!因此,对低基数的列进行索引通常是个坏主意。

以下是另一个原因:UPDATE正在改变status。这意味着(1)更改表中的数据,(2)删除包含旧值的索引条目,以及(3)插入具有新值的新索引条目。这是3个步骤,而不仅仅是1个步骤!

UPDATE如何在没有INDEX(status)的情况下提高效率?查看另一个chunking technique - 一个涉及有效地遍历PRIMARY KEY的{​​{3}}。 (该博客引用DELETEs,但分块适用于UPDATEs等。