Mysql 主键查询中的Where与其他非限定列性能的Where查询
我有一张桌子saleItemMysql 主键查询中的Where与其他非限定列性能的Where查询,mysql,sql,performance,Mysql,Sql,Performance,我有一张桌子saleItem +---------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------+--------------+------+-----+---------+-------+ | id | char(16) | NO | PRI
+---------------+--------------+------+-----+---------+-------+
| 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;
根据您提供的表格结构,我想提出几点:
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