Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/77.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 主键查询中的Where与其他非限定列性能的Where查询_Mysql_Sql_Performance - Fatal编程技术网

Mysql 主键查询中的Where与其他非限定列性能的Where查询

Mysql 主键查询中的Where与其他非限定列性能的Where查询,mysql,sql,performance,Mysql,Sql,Performance,我有一张桌子saleItem +---------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------+--------------+------+-----+---------+-------+ | id | char(16) | NO | PRI

我有一张桌子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是主键。此表有数百万个条目,status可以包含五个值。我想使用状态对该表使用更新查询。当我使用:

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;

根据您提供的表格结构,我想提出几点:

  • 您正在使用varchar(10)作为您的状态,并且上面没有键/索引。 这将导致每隔一段时间进行一次表扫描,试图找出哪些记录的状态为“bb”

  • 如果你不想把索引放在上面,我会诚实地在一个由两部分组成的查询中这样做。问题在于,当您更新表时,您还通过更新同一列中的值来更改组合,因此表扫描会锁定更新

  • 我将执行以下SQL操作:

    UPDATE saleItem 
    SET status= "aa" 
    WHERE id IN (SELECT 
                 Id 
                 FROM saleItem 
                 WHERE status="bb");
    

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

    否,如果试图更新同一组行,使用子查询将不会直接解决问题。我认为问题在于,您试图同时更新太多行,并且遇到了锁争用(其他会话持有行上的锁)

    我会尝试将那个巨大的事务分解成更小的块,一次得到几千行。大概是这样的:

      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 
    
    更新销售项目
    加入bb_id b
    在b.id=s.id上
    设置s.status='aa'
    其中s.status='bb'
    b.ai>0
    和b.ai 4000
    
    和b.ai您只有5个不同的
    状态值
    ?使其成为
    枚举
    。这将需要1个字节,而不是现在的几个字节。更小-->更快

    只有5个值?对于某些值,您的
    更新将执行表扫描,而不是使用索引!由于这个原因,对基数较低的列进行索引通常不是一个好主意

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


    如果没有
    索引(状态)
    更新如何有效?看看另一种方法,它需要高效地遍历
    主键。(该博客指的是
    删除
    ,但分块适用于
    更新
    ,等等)

    您应该向我们展示导致您出现问题的查询。您还应该向我们展示查询的
    explain
    输出。和表中的索引。当然,我会更新queryLet的调用事务(尝试)T2。T1在做什么?Innodb?一般来说,子查询
    更新saleItem SET status=“aa”where id in(从saleItem where status='bb'中选择id)
    不会解决“锁定等待超时超过”问题。我认为第一步是找出哪个查询的锁更长。是否已运行
    显示进程列表
    显示引擎INNODB状态
    以检查哪一个运行时间长?@AsConfused:T2类似。更新saleItem SET status=“cc”其中id=“xyz”。此id可能在“SelectID fromSaleItemwhere status='bb'”中,也可能不在其中。如您所述,我们已将此限制为500。但即使进行了这些更改,我还是遇到了锁超时问题。@user2627846:我想知道500行批处理中是否有一行成功,或者单行更新是否成功。我怀疑其他一些数据库会话可能在需要更新的某些(或全部)行上锁定了很长时间(可能是未提交的事务?)。如果您没有足够新的MySQL版本,(选择…
    中的
    ,构造将执行得非常慢。
    
       AND b.ai  > 4000
       AND b.ai <= 8000